I don't really have a solid understanding of Setters and Getters for objective-c. Can someone provide a good guide for beginners? I noticed that this comes into play when trying to access variables in another class, which I am trying to do right now. I have two classes, lets say A and B. I have a NSString variable in A with the #property (retain) NSString *variable. Then I go ahead and synthesize it. Now when the view loads in the class I set the value for the variable to "hello". Now what I want to do is access the string from class B. I have imported the the class A, and initialized it with this code:
AClass *class = [[AClass alloc] init];
NSLog(#"Value:%#", class.variable);
[class release];
However in the debugger it returns a value of "(null)", which I don't really understand. If someone could lead me into the right path I would greatly appreciate it.
Thanks,
Kevin
The particular section of interest to you is Declared Properties.
b's interface should look like:
#interface b : NSObject {
NSString *value;
}
#property (nonatomic, retain) NSString *value;
- (id) initWithValue:(NSString *)newValue;
#end
Your implementation of b should look something like:
#implementation b
#synthesize value;
- (id) initWithValue:(NSString *)newValue {
if (self != [super init])
return nil;
self.value = newValue;
return self;
}
#end
Which you could then call like:
b *test = [[b alloc] initWithValue:#"Test!"];
NSLog(#"%#", test.value);
The Getting Started with iOS guide in the iOS Reference Library outlines the reading material you should go through to nail down basics like this. Apple's guides are clearly written and thorough, and you'll be doing yourself a huge favor by hunkering down and just reading them.
Related
I am writing a simple calculator app. I want to implement a stack to store the operators the user enters. Originally, I wanted to use chars but ran into some issues when I tried to use the removelastobject and last object for my pop method and addobject for my push method.
Instead I have used id as the type. Is this wrong or are there better ways of solving this issue?
In my .h:
//Brain.h
-(void)pushOperator:(id)op;
In my .m:
//Brain.m
#import Brain.h
#interface CalculatorBrain()
#property (nonatomic,strong) NSMutableArray *operandStack;
#property (nonatomic,strong) NSMutableArray *operatorStack;//This is the one I'm asking about
#end
#implementation CalculatorBrain
#synthesize operatorStack = _operatorStack;
- (NSMutableArray *) operatorStack
{
if (! _operatorStack)
{
_operatorStack = [[NSMutableArray alloc] init];
}
return _operatorStack;
}
-(void)pushOperator:(id)op
{
[self.operatorStack addObject:op];
}
-(id)popOperator
{
NSObject *op = [self.operatorStack lastObject];
if (op)
{
[self.operatorStack removeLastObject];
}
return op;
}
From what I understand, your operator isn't an object type, but rather a basic data type. You can pass an NSValue which can then be claimed as an id, but I'm pretty sure you'd be better suited with a specific primitive value.
If you setup your methods to receive char pointers, then you should be fine.
Using id would be okay, but I think you may want to use an abstract class and derive your operators from that. Then you can use
-(void)pushOperator:(MyOperator)op;
NSArray only stores objects; you can't store primitive types in one. If you just want to store values, you can wrap them in instances of NSNumber or NSValue.
What I am doing is
//ClassB.h
#property (strong, nonatomic) NSString *name;
and
//ClassA.h
#interface ClassA : NSObject
+(ClassA*)methodA:(NSData*)data;
-(id)initWithData:(NSData*)data;
#property (nonatomic, strong) NSMutableArray *arr;
#property (nonatomic, strong) RXMLElement *rxmlRoot;
#end
//ClassA.m
#implementation ClassA
#synthesize arr;
#synthesize rxmlRoot;
+(ClassA*)methodA:(NSData*)data {
return [[ClassA alloc] initWithData:data];
}
-(id)initWithData:(NSData*)data {
self = [super init];
if (self) {
arr = [NSMutableArray array];
rxmlRoot = [RXMLElement elementFromXMLData:data];
/*****edit : just been added to make codes clear*****/
NSString *node = #"players.player";
[rxmlRoot iterate:node with:^(RXMLElement *e){
ClassB *classB = [[[ClassB alloc] init] autorelease];
[classB setName: [e attribute:#"name"]];
// adding ClassB into arr
[arr addObject:classB];
}];
}
return self;
}
#end
So now I am having ClassA object whose arr contains ClassB
Question : later on, when I try to access an particular property of ClassB like
((ClassB*)[classA.arr objectAtIndex:0]).name
and I am getting EXC_BAD_ACCESS at above line..
Please advice me on this issue and how to correct the error. Any comments are welcomed here
Thanks
This line
[arr addObject:ClassB]
makes no sense. Is your intention to put an instance of ClassB into that array, or the class itself (i.e. [ClassB class])? Presumably you must intend to put an instance of ClassB in there, otherwise trying to access its properties later on (e.g. firstName) would make no sense. Also, does your ClassB even have a firstName property, because the piece of ClassB's interface that you show us only mentions a name property.
Update:
Since you are using manual memory management, you need to retain the objects (arr, rxmlRoot) you create in your initializer using convenience constructors, which return autoreleased objects. For example, the code should be
arr = [[NSMutableArray array] retain];
Post your ClassB.m .
Are you making the #synthesize name?
Also make the Alloc for arr.
This line is so wrong:
((ClassB*)[classA.arr objectAtIndex:0]).firstName
Your string is called name , not firstName. It should be :
((ClassB*)[classA.arr objectAtIndex:0]).name
The code in the question has changed substantially, so my previous answer now makes no sense and I have removed it. Given revised code, the first thing to do is to log what's going on.
NSLog(#"classA: %#", classA);
NSLog(#"classA.arr: %#", classA.arr);
((ClassB*)[classA.arr objectAtIndex:0]).name
If it blows up on the first log statement, things are really bad. But then at least you know that classA is pointing to something rotten and you can work back from there.
You can achieve the same thing in the debugger, by setting a break point ahead of the line and inspecting. Given that you are getting an EXC_BAD_ACCESS one of the pointers is pointing to a dodgy object, e.g. one that has been released. It looks as if you are using ARC (because you have strong in your property), which should help manage the memory - but then again, you have an autorelease in there, so maybe not.
I have en error that I suspect could be the compiler... I'm not an expert myself, but I checked some other code with the exact same behavior with an expert and we couldn't figure it out.
I setup a property as such:
#interface aViewController ()
#property (nonatomic, strong) NSArray *listOfTitles;
#end
#implementation aViewController
#synthesize listOfTitles = _listOfTitles;
- (NSArray *)listOfTitles
{
if (!_listOfTitles)
_listOfTitles = [NSArray arrayWithObjects:#"first", #"second", #"third", #"fourth", nil];
return _listOfTitles;
}
However, the app crashes (the array would be more complex and serve to setup textLabel.text properties on UITableViewCell).
What's bothering me, it's that the NSArray always have an invalid address (0x00000001). What am I missing? Isn't this the proper way to do lazy instantiating?
Like I mentionned, I experienced the exact same thing (property instantiated with 0x00000001 ) with other classes, in other projects. That's why I'm suspecting the compiler to be the issue. But really, I'm all ears for solutions!...
The most obvious solution is to set the ivar to nil in your init function.
-(id)init{
if (self = [super init]){
_listOfTitles = nil;
}
}
The real question is why isn't this being done for you, since all ivars should be initialized to nil. I suspect it has something to do with the property being declared in the .m file. Either way it will cause no harm to nil out the ivar yourself.
In this example:
#interface something : something
{
NSString *saveString;
}
-(void)saveStringForLater:(NSString*)myString
{
//do stuff with myString
...
//then save it for later
saveString = myString;
}
-(void)someOtherTimeInFuture
{
//do stuff with saveString
...
}
So given the above, my questions are:
1) Is this safe/proper way of doing this?
2) Will I need to worry about releasing saveString?
3) Should I be copying the string instead of just saving the pointer?
Excuse my ignorance as I am fairly new to Obj-C but have a C++ and C# background.
Thanks!
This is what #properties are for. They manage getter and setter code for you, so you don't have to think about these questions.
.h
#interface MyClass : NSObject
{
NSString *myString;
}
#property (nonatomic, retain) NSString *myString;
#end
.m
#implementation MyClass
#synthesize myString;
-(void)dealloc
{
[myString release];
[super dealloc];
}
#end
With those things in place, you can now talk about self.myString and not worry about memory. When you assign to it it'll do a retain. If you assign again, it'll release the first object and retain the new one. And then it'll stick around retained until your viewcontroller unloads.
You can by all means accomplish this same end with an iVar (which is what you're doing in your code sample), but then memory management is yours to handle, and it can be a bit fiddly. Best to use the #property system to create appropriately memory-managing setter code.
You have 3 optins,
Copy - should be using if the string can change or when getting called from a code you have on control of like third party
Retain - will increase the reference count to the object and will prevent destruction of it
In both these options you have to release it when you done with it
Last you can define a property with retain,copy attribute - this will let the system worry about managing it and probably the best option in most cases
Whilst learning Objective C I've gotten used to how the #property and #synthesize works.
//usual way
#synthesize world;
But recently I've come across practices were #synthesize is like this:
//new way
#synthesize world=world_;
Could someone explain why this technique is used please?
Thanks,
Mark
I believe this is used to avoid confusion between the actual ivar name and the property name.
so you can have
#interface MyClass : NSObject
{
NSObject *_myObjActualIVar;
}
#property (retain) NSObject *myObjProperty;
#end
#implementation MyClass
#synthesize myObjProperty = _myObjActualIVar;
- (void) someMethod: (NSObject *)someOtherObject
{
[_myObjActualIVar release]; // here I know I'm accessing the ivar
self.myObjProperty = someOtherObject; // here I know I'm using the setter method
//myObjProperty = someOtherObject; // if I accidentally try to do this
//self._myObjActualIVar; // or this, the compiler will tell me
}
#end
and then it makes it harder for you or some other developer working on this class to inadvertently access the ivar when you actually wanted the property or vice-versa. The different names make it clearer which is which.
from the Objective-C 2 manual:
You can use the form property=ivar to
indicate that a particular instance
variable should be used for the
property, for example:
#synthesize firstName, lastName, age = yearsOld