Calling a method from the init method? - iphone

Say I have two properties defined as such:
#property (nonatomic, strong) UITableView *parentTableView;
#property (nonatomic, strong) NSMutableArray *headersArray;
and a method:
-(void)prepareTags;
and say I have an init method like this:
-(id)initWithParentTableView:(UITableView*)parentTable
{
if(self = [super init])
{
//1
NSMutableArray *array = [[NSMutableArray alloc] init];
headersArray = array;
//2
self.parentTableView = parentTable;
//3
[self prepareTags];
}
return self;
}
Is this the correct way to set up the headers array in an init method?
Am I allowed to call self.parentTableView from the init method?
Am I allowed to call a method from the init method (in this case, the prepareTags method calls self too. Will self be ready to use, even though the init method hasn't returned yet?

Respectively (I'd use list formatting but I can't make it work with blockquotes...!):
Is this the correct way to set up the headers array in an init method?
Yes, but there's no point having the array variable, you might as well just do: headersArray = [[NSMutableArray alloc] init];
Am I allowed to call self.parentTableView from the init method?
No, to quote the Apple docs on Practical Memory Management:
Don’t Use Accessor Methods in Initializer Methods and dealloc
. You should access the ivar directly (as you do with headersArray)
Am I allowed to call a method from the init method (in this case, the prepareTags method calls self too. Will self be ready to use, even though the init method hasn't returned yet?
Yes. Just be very careful (your object may not have been fully initialised, it shouldn't use accessor methods so as to comply with the previous restriction, et cetera)

There's no need to use a local variable here:
NSMutableArray *array = [[NSMutableArray alloc] init];
headersArray = array;
Just assign to the instance variable directly:
headersArray = [[NSMutableArray alloc] init];
Am I allowed to call self.parentTableView from the init method?
Yes, although some people might consider that poor design. Consider the fact that properties sometimes have non-obvious complex setter methods that look at other instance variables. Is it wise to do this when your object hasn't been fully initialised?
Am I allowed to call a method from the init method?
Same as above. So long as you aren't relying on anything you haven't initialised yet, it's fine.

The code looks pretty good. Just a couple notes...
-(id)initWithParentTableView:(UITableView*)parentTable
{
// avoid compiler warning about the assignment and the condition in the same statement
self = [super init];
if(self)
{
//1
// no need for the extra stack variable
self.headersArray = [[NSMutableArray alloc] init];
//2
// this is all fine from here
self.parentTableView = parentTable;
//3
[self prepareTags];
}
return self;
}

Related

What are the difference between following 2 lines of Objective C code?

#interface Foo : NSObject
#property (nonatomic, retain) Bar * bar;
#end
#implementation Foo
#synthesize bar = _bar;
- init {
self = [super init];
if (self) {
_bar = [[Bar alloc] init];
// Or
_bar = [[[Bar alloc] init] autorelease];
}
return self;
}
- (void)dealloc {
[_bar release];
[super dealloc];
}
#end
When I run the analyzer, both
_bar = [[Bar alloc] init];
and
_bar = [[[Bar alloc] init] autorelease];
are fine.
Which one I should use?
You should use the first. It creates a retained object, whereas the second "autoreleases" that retain.
The important consideration is that you're assigning it to instance variable _bar. If you were, instead, assigning it to property self.bar, then the retain directive in the property declaration would cause the object to be retained, so assigning an autoreleased value would be appropriate. But since you're assigning to the "bare" instance variable instead, you need to handle the retain yourself, so you need the first form.
PS: I'm a bit surprised that the analyzer doesn't complain about the second version.
PPS: It should be noted that the choice here is highly context dependent. But you included enough context (the property definition) to make the choice. Without seeing the property definition (or other info, in the case of a non-property) it would be hard to say.
The autorelease version is not correct and may cause crashes in some situations—your first line results in _bar having a retain count of 1, and thus sticking around until it's released in -dealloc when you no longer need it.
The second line, however, releases the object soon-ish (specifically, at the end of the run loop), and thus could cause it to disappear when you still need it.
Read Apple's Guide on Memory Management for more information.

Memory Management Objective-C Question

So assume I declared an object and created a retained property for it which is synthesized. So something like that in the header file:
NSArray *array;
#property (retain)....
After it is synthesized, I called release in the dealloc.
Now in the init method, if I want to also dynamically allocate that array, what do I do in terms of releasing it? So:
array = [[NSArray alloc] initWithObjects...
How do I keep the object retained as long as the class is running without leaking?
Thank you
self.array = [[[NSArray alloc] initWithObjects:...] autorelease];
or
NSArray *newArray = [[NSArray alloc] initWithObjects:...];
self.array = newArray;
[newArray release];
With both options, you additionally have to call [array release]; in dealloc.
By using its setter method, you normally don't have do worry about retains and releases.
All the init* (init, initWith..., etc.) methods return retained objects. The convenience constructors provided by some classes, on the other hand, provide objects that are not retained - or rather, retained, then autoreleased.
More here.
So you are doing the right thing by assigning a retained object to your ivar in the init method, then releasing it in dealloc.
For the rest of the object's life cycle, it would be smart to only use the synthesized accessors, as they take care of retaining and releasing.
All in all, you're good.
In order to take advantage of the goodness of properties, you need to prefix the variable name with self.
array = [[NSArray alloc] initWithObjects:...];
is not the same as
self.array = [[[NSArray alloc] initWithObjects:...] autorelease];
The former will assign the array directly to the instance variable. The latter will invoke the synthesized setArray method, which gives you retain/release for free. This will be useful should you decide to assign a new reference to array at any other point in time in the lifecycle of your class.

Objective C: Proper way to init an NSArray that is a #property

I have a property in my class, which is an NSArray. I am retaining the property.
My question is, what is the proper way to add objects to that array without leaking and making the retain count too high?
This is what I am using:
.h:
NSArray *foodLocations;
#property (nonatomic, retain) NSArray *foodLocations;
// I make sure to synthesize and release the property in my dealloc.
.m
- (void)viewDidLoad {
[super viewDidLoad];
NSArray *tempFood = [[NSArray alloc] initWithArray:[self returnOtherArray]];
self.foodLocations = tempFood;
[tempFood release];
}
Is this the correct way to do it?
Yes this is correct and my preferred way of doing it as it renders the code more readable.
You are essentially allocating a temporary array and then assigning it to your property with a retain attribute, so it is safe to dealloc it as your property now "owns" it. Just remember that you still need to release it in your dealloc method.
You could also initialise the array and assign it to the property in the view controllers init method, depending on whether you need the property to be available to you before the view actually loads (i.e. in case you want to read the value of the property before pushing the view controller etc...)
you will typically want to declare the property copy in this case.
in most cases, immutable collection accessors should be copy, not retain. a lot of people get this wrong, and end up writing a lot of copying manually and sharing objects which should not be shared, thinking they are doing themselves good by cutting a corner.
copying in this form (the collection) is shallow. the objects in the array are not copied, just the array's allocation.
a good implementation of an immutable collection can simply implement copy by retaining self. if the argument is mutable, you want a copy anyhow (in the majority of cases).
your program is then simplified to a declaration of:
// note: copy, not retain. honor this if you implement the accessors.
#property (nonatomic, copy) NSArray * foodLocations;
and then the setter:
self.foodLocations = [self returnOtherArray];
of course, you must still init, dealloc, and handle thread-safety appropriately.
good luck
That looks fine. You don't actually need the tempFood variable, you can just do:
self.foodLocations = [[NSArray alloc] initWithArray:[self returnOtherArray]];
[self.foodLocations release];
or:
self.foodLocations = [[[NSArray alloc] initWithArray:[self returnOtherArray]] autorelease];
Or:
#synthesize foodLocations=_foodLocations;
then in code
_foodLocations = [[NSArray alloc] initWithArray:someOtherArray];
This avoids the autorelease required by
self.foodLocations = [[[NSArray alloc] initWithArray:someOtherArray] autorelease];
Yes, that is correct. Also good to keep in mind is what #synthesize is, in effect, doing for you. A synthesized (& retained) setter is functionally equivalent to the following code:
- (void)setVar:(id)_var {
[_var retain];
[var release];
var = _var;
[var retain];
[_var release];
}
So, basically, every time you call self.var = foo, it releases the previously stored value and retains the new one. You handle the reference counting in your code, and the setter handles its own.

Memory management - how best to initialise an instance declared in the header

I've read a few posts on this, but there's still one thing that's not clear for me. I know this might be rather a n00b question, but I've actually got rather far into development without quite grasping this fundamental issue. A symptom of being self taught I guess.
You declare a variable in your header, like so:
#interface SomeClass : NSObject {
NSMutableArray *anArray;
}
#property (nonatomic, retain) NSMutableArray *anArray;
end
And then in your main file you synthesise it and set it to an initial value:
#implementation SomeClass
#synthesize anArray
- (SomeClass *)init{
if (self = [super init]) {
self.anArray = [[NSMutableArray alloc] initWithCapacity:10];
}
[return self];
And release it when your Class deallocs:
- (void)dealloc {
[anArray release];
[super dealloc];
}
Now, when I run instruments, the line
self.anArray = [[NSMutableArray alloc] initWithCapacity:10];
is identified as a memory leak. Is it a memory leak because when you define the variable anArray in the header it allocates memory? (Because I thought it was a null pointer.) Therefore when you want to initialise it, and you call [[NSMutableArray alloc] initWithCapacity:10], you are reallocating the memory, and losing the pointer to the original allocation?
So instead, I use the convenience class method:
#implementation SomeClass
#synthesize anArray
- (SomeClass *)init{
if (self = [super init]) {
self.anArray = [NSMutableArray arrayWithCapacity:10];
}
[return self];
This is no longer identified as a memory leak in instruments. And since it's a convenience method, anArray is autoreleased. However, if I am to assume that the instance declaration in the header allocates memory, which would explain the previous issue, then should I still release anArray? Does setting the initial values in this way retain it perhaps?
I understand the difference between
NSMutableArray *anArray = [[NSMutableArray alloc] initWithCapacity:10];
and
NSMutableArray *anArray = [NSMutableArray arrayWithCapactiy:10];
but what I'm not sure I understand is when you've declared NSMutableArray *anArray in your header, which of the two approaches you should use and why. And whether or not if you use the second approach, you should still release anArray when you call dealloc.
I might add that I've found the following posts/links useful:
Suggest the best way of initialization of array ( or other objects )
What is the cost of using autorelease in Cocoa?
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/MemoryMgmt/MemoryMgmt.html
What is the difference between class and instance methods?
alloc'ing an object starts it off with a reference count of 1.
Setting a property that has the 'retain' attribute also increases the reference count.
So, that means this is usually bad:
#property (nonatomic, retain) Object * variable;
...
self.variable = [[Object alloc] init];
Because variable now has a reference count of 2.
When setting a object's member variable, just do this:
variable = [[Object alloc] init];
You should also realize that this works
self.anArray = [NSMutableArray arrayWithCapacity:10];
Because "arrayWithCapacity" (and other similar factor methods) autoreleases the object it returns, so after you set the property, it essentially has a reference count of 1.
It's not the instance that allocates the memory. You're right to assume that in Objective-C (at least on all Apple-based operating systems), newly initialized classes have all their ivars set to 0 (or nil or NULL as appropriate).
The problem you're seeing is that you're using the property, not the ivar in your initialization. Since you declared your property as retain, using the property accessor to set it automatically retains it.
So, when you initialize you either have to take ownership and set the ivar directly, or do like you're doing and use the property accessor to set the property and then relinquish ownership in the init method (by either releasing an object you own or, as you did in your second instance, using the convenience constructor so that you never owned the returned instance).
So just remember, if you ever use the property accessors, even within the class itself, you will get the features you set on the property (e.g., nonatomic, retain, etc.). You use the property accessors whenever you do one of the following:
// in these cases the property takes ownership through the
// retain keyword, so you must not take ownership yourself
self.anArray = something;
[self setAnArray:something];
[self setValue:something forKey:#"anArray"];
You would access your ivar directly like:
anArray = something; // in this case you must take ownership

EXC_BAD_ACCESS when debugging

I'm getting this error when trying to see the contents of a NSMutableArray:
Program received signal EXC_BAD_ACCESS, Could not access memory.
Reason: KERN_PROTECTION_FAILURE at address: 0x00000021
0x94d5a688 in objc_msgSend ()
ViewController.h:
#interface PeopleViewController : UITableViewController {
NSMutableArray *people;
}
#property (nonatomic, retain) NSMutableArray *people;
ViewController.m:
#implementation PeopleViewController
#synthesize people;
In viewDidLoad:
- (void)viewDidLoad {
[super viewDidLoad];
// initialize our people array with an autoreleased object
people = [NSMutableArray array];
... Populate the people array with Person objects.
}
When I'm at the point where I'm modifying the contents of a cell in the tableview, I'm unable to access the people array in gdb when typing 'po self.people':
Person *person = [[Person alloc] init];
person = [self.people objectAtIndex: indexPath.row]; // <--- 'po self.people' called
cell.textLabel.text = person.personName;
Any ideas why I can't access it?
The line
people = [NSMutableArray array];
returns an autoreleased array that will be released on the next iteration of the current run loop. You should retain that:
people = [[NSMutableArray array] retain];
and of course release it in your dealloc method.
However: Apple engineers have often mentioned in conferences to avoid autoreleased instances like this whenever possible in the iPhone, for performance reasons. Try using alloc/init instead:
people = [[NSMutableArray alloc] initWithCapacity:1];
with the corresponding release in the dealloc method. In this case you don't even need to retain (init returns an instance with a retain count of 1, which is what you need).
And justin's comment is correct: you should do this instead:
Person *person = [people objectAtIndex:indexPath.row];
cell.textLabel.text = person.personName;
and this should work.
is indexPath.row > [people count]?
Also, why are you doing this:
Person *person = [[Person alloc] init]
You're allocating memory, and then pointing to completely different memory.
You can avoid having to fuss with retaining properties by using the self notation to call the accessor and setter methods created by the #synthesize directive.
When you set the people property directly in viewDidLoad it sets the property but does nothing for memory management. However, if you set it with self.people you actually call the synthesized setter method that because of the retain setting of the #property directive will automatically retain the assigned array.
As an aside, I would recommend always using -[NSMutableArray initWithCapacity:] instead of a bare init. It is the actual initializer for the class. You can call it with just '1' if you don't know how big it will be. In the past, I have seen odd problem arise from just using bare init.