Reassigning an object with ARC - objective-c

If I've created an NSArray in the init of an object.
Then later on I want to recreate a new NSArray to the same property should I set the old one to nil first?
i.e.
is it ok to just go...
self.arrayProperty = [[NSArray alloc] init];
or should I do...
self.arrayProperty = nil;
self.arrayProperty = [[NSArray alloc] init];
(I'm just using an array for the sake of this example but it's a general questions about properties).
If it makes any difference, it's a strong property.

The first approach is fine, you don't need to set it explicitly to nil before assigning a new object, since the setter releases the backing object of the property before retaining and assigning the new one. Just what you would do under MRC (except that here you don't autorelease the object).

It's just the same, as with ARC an object dies when there are no more references to it. The only difference is that in the second code you're doing a useless extra operation.

Related

Basic of Objective C

#implementation GroupedInexedViewController
{
NSDictionary *names;
}
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *path = [[NSBundle mainBundle] pathForResource:#"PropertyList"
ofType:#"plist"];
NSDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile:path];
names = dict;
[dict release];
}
Is deallocating 'dict' affects 'names'? I mean does it deallocate 'names' too? I saw in another post that its a bad practice? But why?
Edit: ARC is disabled.
In short, if you are not using ARC, yes: deallocating dict will affect names. This is because you are assigning the names pointer to the single NSDictionary you have allocated.
If you wanted to have names retain the NSDictionary when you dealloc dict, you would need to send dict a retain message:
names = [dict retain];
Since you're manually calling release, I'm going to assume you're not using Automatic Reference Counting (ARC).
There is some terminology mix up here.
It doesn't deallocate names. You're decrementing the reference count of dict when you call release. Once that reference count hits 0, the memory will be deallocated.
The problem is you assigned dict to names without first calling retain on dict.
Retaining an object increases it's reference count.
You can either choose to make *names a property, which will handle the memory management for you, or you can manually increment the reference count by calling retain: names = [dict retain];
If you do this, you must also implement a dealloc method and release names inside the dealloc method.
Your code assigns names with the value of dict. dict is a pointer to an object, so when you assign its value to another pointer (names), both pointers are referencing the same object and can be considered identical.
So yes, when you release dict, you are also releasing names.
BTW, you can assign to names directly without going through dict:
names = [[NSDictionary alloc] initWithContentsOfFile:path];
And if you can enable ARC, you never need to worry about releasing objects.
No answer so far seems to mention the difference between ARC and non-ARC (MRC) usage and the difference between properties and instance variables.
First of all, properties are really just setter and getter methods backed by an instance variable. When you set a property like self.dict = someObject; and the property was declared as strong or retain, then someObject is retained.
However, if you only have an instance variable (not a property) and you're using MRC, then merely writing dict = someObject; duplicates the pointer only but does not increase the reference count - if you write [someObject release] after this, you should assume that dict is invalidated as well (even if the object pointed to by these two pointers is not actually deallocated - this is a rule of reference counting).
If you are using ARC, then assigning to a variable increases the reference count by one as well - so if someObject has a reference count of 1, then writing dict = someObject; will increase the reference count of the object (now pointed to both by dict and someObject) to 2.
dict and names are two different variables, do not mix them!!!
"release" only when you alloc, copy, mutablecopy, retain.
Otherwise if gets created in autorelease mode.
And if you are using ARC, then no need of "release" compiler will take care of all these newly allocated spaces.

Objective-C - Initializing an already initialized object?

What happens if I call [alloc] init] on an object which already was initialized and alloc'ed?
In my particular case I have an NSMutableArray which I initialize in superclass Parent using NSMutableArray* someArray = [NSMutableArray alloc] init];
In subclass Child I need to insert an object in someArray but at a specific index, for example 3.
So if the array has no items, or if it has less items than the index I'm trying to insert at (array has 4 items, and I want to insert at index 10) it will crash.
What would happen if I initialized someArray again in Child class? Would the pointer stored in someArray be replaced with the new one I'm initializing and the "old" one would just leak?
EDIT:
Sorry, my terminology was a bit off. I don't mean doing [someObject alloc], but doing someObject = [SomeClass alloc] init]; where someObject had previoulsy been initialized with an instance of SomeClass
Just for clarity when you say "What happens if I call [alloc] init] on an object..." your terminology is wrong.
The following line:
NSMutableArray* someArray = [[NSMutableArray alloc] init];
Reads in English:
"Send the alloc message to the NSMutableArray class object, then send the init message to the object returned from the first message, then store the object returned from init into the pointer variable named someArray."
I say that to emphasize the fact that you're not "calling alloc/init" on an existing object, you're making a new object, and storing a reference to this new object over the reference you had to the previous object. Since you no longer have a reference to that previous object, you've lost the ability to properly release its memory, so yes, you'll leak it.
correct, it will leak. Use NSMutableArray insertObject:atIndex‎:
There are a couple of ways that come to mind to do what I think you want. A sort of clumsy one is to put as many [NSNull null] objects into the array as you need so that it's filled up to the spot where you need to add the new object. Then you would replace an existing NSNull if you were storing your own object.
Probably a better approach is to use a dictionary instead of an array and turn your index value into a key.

NSMutableArrays - can I do this?

In my app, the singleton class (SharedData) allocates memory for a NSMutableArray:
[self sharedMutableArray] = [[NSMutableArray alloc] init];
Class A populates the this sharedMutableArray:
NSObject *obj = [NSObject alloc] init];
[sharedMutableArray addObject];
obj = nil;
Class B does this - and that's my question:
NSMutableArray *tmpArray = sharedMutableArray;
... uses the tmpArray locally
[tmpArray removeAllObjects];
tmpArray = nil;
This is an inherited code and my hunch is that this is a NO-NO. Can some one confirm that assigning nil to tmpArray will release memory for sharedMutableArray also.... I guess the author wanted to release tmpArray only...
Assigning nil to tmpArray only sets your pointer to the object to nil. It does not affect the object itself (or its lifecycle) at all. In this case, setting the objects you've created to nil does nothing, since their variable declaration is in local scope - if you want the objects to be deallocated from memory you need to send them release before setting the pointer to the object to nil.
However, sending removeAllObjects is affecting your original sharedArray, because you didn't copy the array, you simply set a new pointer to point to the 'singleton'. You probably want this:
NSMutableArray *tmpArray = [NSMutableArray arrayWithArray:sharedMutableArray];
You won't need to use removeAllObjects in the above case because it will be autorelease'd. I suggest you read this.
tmpArray is a pointer, and it's initialized to point to the same mutable array that sharedMutableArray points to. For that reason, the line:
[tmpArray removeAllObjects];
will empty out the array, and anyone using sharedMutableArray will see that change. In other words, the assignment
NSMutableArray *tmpArray = sharedMutableArray;
doesn't make a copy of the array itself, it only copies the pointer. Any messages you send using that pointer will go to the shared array. Likewise, assigning nil to tmpArray sets the pointer tmpArray, but doesn't do anything to the array itself.
Finally, setting a variable to nil never releases memory. Setting a property to nil, on the other hand, will release memory under some conditions (e.g. when the property is declared to retain its contents). You're setting a variable here, not a property, so there's no chance that the array will be released.

Is it ok to allocate released object again?

if i did this
Object * myObject = [[Object alloc]init];
[myObject release];
is there anything wrong about allocating my object in next line
myObject = [[Object alloc]init];
again?
This is safe to do.
The reason is that myObject is not an object, it's a reference (or pointer if you want to be exact) to the object. That means you've got 2 completely independent objects, but you forget about the reference to the first.
No problem at all.
[myobject release]; releases the object pointed at by myObject.
Later, myobject = [[Object alloc] init]
will make myobject point to another object.
There is nothing wrong with that. That is how you make sure you don't leak your first object.
However, you are not technically allocating the released object again. You are just using the old pointer again.
Object * myObject = [[Object alloc]init];
myObject = [[Object alloc]init];
will result in leaking the first object you created.
yes of course. this technique is specially useful in local method variables where you can reuse the object declared once by reallocating it again as new object..!!

Can I reuse my pointer after it's been added to a mutable array?

Let's say I've got an array with strings.
NSArray *names = [NSArray arrayWithObjects: #"One", #"Two", #"Three", nil];
What I want is to initiate objects of some custom class and them add them to a mutable array. I'm using a custom init method that takes a string argument.
To be more specific, I want to [SomeClass alloc] initWithName: aName] and add the resulting object to a NSMutableArray.
I'm thinking of using Objective-C fast enumeration. So what I get is:
NSMutableArray *objects = [NSMutableArray arrayWithCapacity: [names count];
for (NSString *name in names) {
[objects addObject: [[[SomeClass alloc] initWithName: name] autorelease]];
}
The problem is that I can't add nil to the array and I don't like exception handling. However, my initiation method may return nil. So I decide to check first before adding (prevention). My new for-in-loop is:
SomeClass *someObject;
for (NSString *name in names) {
someObject = [[[SomeClass alloc] initWithName: name] autorelease];
if (someObject) {
[objects addObject: someObject];
}
}
Now, instead of immediately passing the new object to the array, I'm setting up a pointer someObject first and then passing the pointer to the array instead.
This example raises a question to me. When I someObject = [[[SomeClass alloc] initWithName: name] autorelease] in the loop, do the existing objects (which are added using the same pointer) in the array change too?
To put it in other words: does the addObject: (id)someObject method make a new internal copy of the pointer I pass or do I have to create a copy of the pointer — I don't know how — and pass the copy myself?
Thanks a lot! :-)
It's fine to reuse someObject; if you think about it, you're already reusing name each time you go through the loop.
-addObject: may or may not copy the object that you pass in. (It doesn't -- it retains the object rather than copying it, but it's conceivable that some NSMutableArray subclass could copy instead.) The important thing is that this code really shouldn't care about what -addObject: does.
Also, don't lose sight of the distinction between a pointer and the object that it points to. Pointers are just references, and a pointer is copied each time you pass it into a method or function. (Like C, Objective-C passes parameters by value, so passing a pointer into a method results in putting the value of the pointer on the stack.) The object itself isn't copied, however.
Short answer: no, you don't have to worry about reusing someObject.
Slightly longer answer: the assignment—someObject = ... assigns a new pointer value to the someObject variable; addObject: is then getting that value, not the address of someObject itself.
I think you're getting confused in the concept of pointer here. When you say someObject = [[[SomeClass alloc] init... you are basically pointing the someObject pointer to a new object. So to answer your question- your current code is fine.
As for whether arrays maintain copies of the objects added to them - NO, the array retains the object you add to it. However, that doesn't matter to your code above.
Three20 provides the answer!