ARC weird behavior - iphone

With ARC enabled I have a property
#property (strong, nonatomic) NSMutableArray *arr;
I Allocate it as
self.arr = [NSMutableArray array];
I have a custom object ERSearchView, I allocate it and add it to the arr
ERSearchView *SV = [[ERSearchView alloc] initWithDelegate:nil];
[arr addObject:SV];
In my search view I have overridden the dealloc method, because it seems that the search view isn't getting released:
- (void)dealloc {
self.delegate = nil;
NSLog(#"deallocated search view");
}
This isn't getting called when I pop my view controller. But if I nil the arr variable in the view controller's dealloc I see the log message that the search view has been deleted.
Can anyone explain how is this possible that objects inside arr aren't getting released though the view controller is 100% released since I have "did dealloc" message in its dealloc?

Foundation collection classes such as NSArray, NSDictionary, etc. retain (or have a strong reference to, in ARC-speak) the objects they contain. So as long as the array hasn't been deallocated, the objects it refers to, including your view instance, should still be in memory.
It sounds as though you were leaking the array previously by not setting the property to nil in the controller's dealloc method. When an object declares a property as strong, it's responsible for the nilling the reference in its dealloc implementation to avoid this kind of leak.

Related

One delegate class for multiple NSFetchedResultsController?

I have several NSFetchedResultsControllers throughout my app, and in every view controller, I implement the respective delegate methods. However, instead of copying these delegate methods into every class that implements an NSFetchedResultsController, I thought I would just create a class that implements these delegate methods, and set all fetched results controller's delegate to point to that one class. Here's what I've tried, which doesn't work:
Since the delegate methods need to know which table view they are making changes to, I thought I would just create a separate delegate class for each fetched results controller, and send a pointer to the tableview for that class:
FetchedResultsDelegate *delegate = [[FetchedResultsDelegate alloc] initWithTableView:parentTableView];
self.fetchedResultsController.delegate=delegate;
[delegate release];
However, this causes a BAD_ACCESS crash, so this means that I probably shouldn't be doing what I'm doing above.
How can I create a single delegate class that handles all delegate requests for all my NSFetchedResultsControllers?
Edit: I was able to fix the problem by doing #property (nonatomic, retain) FetchedResultsDelegate *delegate; Is this ok? Some people are saying something about assign rather than retain?
Nothing is retaining your FetchedResultsDelegate as delegate properties are normally declared as assign. e.g. NSFetchedResultsController declares the delegate as
#property(nonatomic, assign) id <NSFetchedResultsControllerDelegate> delegate
Therefore you created the object and destroyed it straight away, but gave the fetchedResultsController a nasty dangling pointer.
To fix this you need a retain on the delegate. So in your UITableViewController class add a new property
// .h
#property (nonatomic, strong) id<NSFetchedResultsControllerDelegate> tableViewDelegate;
// .m
#synthesize tableViewDelegate = _tableViewDelegate;
then when you hook up you delegate just change your code to this
FetchedResultsDelegate *delegate = [[FetchedResultsDelegate alloc] initWithTableView:parentTableView];
self.fetchedResultsController.delegate = delegate;
self.tableViewDelegate = delegate;
[delegate release]; delegate = nil;
Don't forget
Release this new ivar in the dealloc
- (void)dealloc;
{
// ... other releases
[_tableViewDelegate release];
[super dealloc];
}
The use of assign is all about ownership semantics.
In this case your UITableViewController should own the tableView's delegate (e.g. strong/retain) as nothing else is.
The reason that the NSFetchedResultsController uses assign and not retain/strong is because there is a good chance that the object that created it would act as the delegate, which would result in both objects owning each other (both having a retain held on each other), which causes a retain cycle

Set to nil in viewDidUnload, but release in dealloc

I've been reading all day about why views should be set to nil in viewDidUnload and released in dealloc. All the articles keep on repeating the same thing. Yes, I know the behind-the-scene instructions are different, but what are the practical differences?
var = nil
If var is a retained propery, reclaim memory the old object var pointed to.
Set var to nil.
[var release]
Reclaim memory var points to.
var now points to nothing, which is equivalent to nil
To me, both ways of reclaiming memory have the same end result. So why do one over the other? Every book out there tells me to set to nil in viewDidUnload and release in dealloc. Someone should point out the bad things that would happen if a view was released in viewDidUnload and nilled in dealloc.
.h
#import <UIKit/UIKit.h>
#interface DisclosureDetailController : UIViewController {
UILabel* label;
}
#property (nonatomic, retain) IBOutlet UILabel* label;
#end
.m
#import "DisclosureDetailController.h"
#implementation DisclosureDetailController
#synthesize label;
- (void)viewDidUnload {
self.label = nil;
// OR [self.label release];
[super viewDidUnload];
}
- (void)dealloc {
[self.label release];
// OR self.label = nil;
}
First things first, the line
[self.label release];
is absolutely wrong regardless of where you call it. You should never call -release on the results of property access. This is exactly the same as writing [[self label] release], which I hope you can recognize as being wrong.
Your code sample should look like the following:
- (void)viewDidUnload {
self.label = nil;
[super viewDidUnload];
}
- (void)dealloc {
[label release];
[super dealloc];
}
If we look at -viewDidUnload first, it's pretty simple. self.label = nil; is correct. Similarly correct would be [self setLabel:nil];. And while not quite as good, it would also be acceptable to write [label release], label = nil;. This last form isn't as good because it bypasses the setter method, which may be doing more things than simply releasing the property (e.g. it may maintain internal state that cares about the value of the property). It also bypasses KVO notifications.
The real question here is what you do in -dealloc. Many people suggest that it's perfectly fine to say self.label = nil;, and practically speaking, this will work most of the time. The problem is, the rest of the time it will cause subtle bugs. There are two things that calling the setter can do. The first is it can cause side effects in your class if the setter method is implemented manually (even if you're not implementing the setter yourself, a subclass might). The second is it can broadcast KVO notifications. Neither of these things are desired when you're in -dealloc. By releasing the ivar directly, as in [label release];, you avoid both the potential side effects and the KVO notifications.
the practical differences are as follows.
Setting the property to nil by using the property accessor will let the synthesized method take hold of your new nil property after releasing the existing property.
// we will take for granted that you synthesize this property
#property (nonatomic, retain) IBOutlet UILabel* label;
we will use the property accessor and set it to nil.
//This will in actuality set the new value of nil to the label variable after
//releasing the existing label that it had a retain count on.
self.label = nil;
next we will release it directly
//This line on the other hand will merely release the label directly.
//As soon as the label is deallocated you will have a handle to an invalid object.
//(memory space that used to be your label)
[label release];
now we will show a simplified version of the property accessor. (not to be used literally)
//Simply put the following is an pseudo equivalent of the property setter.
[label release]
label = nil;
the main point here being that the property accessor handles releasing the label that it retained. and setting it to whatever you hand it (in this case being nil)
therefore adding the following code
label = nil;
without releasing the retained object would cause a memory leak and you would have a retain count on a label you no longer have a pointer to.
Note:
another thing to take into account.
Any pointer that is nil. will be able
to accept messages. And in return they
will reply with nil. An object that
was released on the other hand, as
soon as that memory is deallocated
your message to it will most likely
throw an error. the outcome is
unpredictable. This is a good reason
for setting your properties to nil.
Not only will it handle the release
for the object that it is holding. but
it will also give you an object you
can safely message to without blowing
up.
A good point #WaltSellers
Accessing a variable -- regardless if its the property accessor or instance variable. -- After it has been released completely. Will result in "Undefined" actions. This means that the access may act ok, or it may ruin other parts of the app, or alternatively it may just blow up real fast and terminate the offending app. Basically setting the variable to nil after releasing will enable you to get past that error.
A separate tip from me
to overcome the misconception of property accessor and instance variable I just #synthesize and tell it to set a variable name.
#synthesize label = _label;
doing this allows me to differentiate self.label from its instance variable. as no longer can you access the label variable directly without the preceding _

When is the proper time to release an object?

I'm still a little confused on this.
I'm creating an object programmatically in Xcode, let's say a UILabel, which is also going to be a class wide property.
When is the proper time to release the object, in the method in which it is created, or in the dealloc method like normal IBOutlet objects?
Thanks.
This depends on whether your property is set to retain the value or not. Usually you want the accessors (generated by #synthesize) to handle the retain/release when the property is set to a new value. You specify such a property like this:
MyController.h
#interface MyController : UIViewController {
UILabel *myLabel;
}
#property (nonatomic, retain) UILabel *myLabel;
#end
You can then use #synthesize to generate the default getters and setters. The default setter for a 'retain' property will release the current value and retain the new value. However, nothing is done for you in dealloc. Meaning, that when the controller is destroyed, your reference to you label will leak since release will not be called. For this reason, you need call release on all your 'retain' properties in dealloc, like this:
MyController.m
#implementation MyController
#synthesize myLabel;
-(void) dealloc {
self.myLabel = nil;
[super dealloc];
}
#end
Notice that in this case, self.myLabel = nil is almost equivalent to calling [myLabel release] since the setter will call release on the existing value and then call retain on the new value. Since the new value is nil, calling [nil retain] has no effect. I prefer to nil instead of releasing since you are also setting the ivar to nil and avoids dangling pointers.
When you create a property like this programmatically as opposed to from Interface Builder, you don't need to mark it with IBOutlet. In the cases where you do create a control using IB, you should nil all of your IBOutlet references in viewDidUnload. This is because your control could be deallocated along with the view if it wasn't retained. Referencing it afterwards will crash the app so it's a good practice to nil them, like this:
- (void)viewDidUnload {
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
self.myIBLabel = nil;
}
Another common mistake that happens when using properties is to omit the 'self' part. If you do not use the self.myIBLabel notation, you are bipassing the getter and setter and working with the ivar directly. This will NOT retain/release the object.
You should release it in the dealloc method, although that depends how you're creating your class property.
If you release it in the method in which you create it, and then use it in some other part of your class (which, since you're making the UILabel a class wide property, I assume you are), you will get a bad access when you try to modify it later on. Note that if you're using a retained property you need to take that into account, in which case you might release the label (because you'll have created it and assigned it to your class property, which will retain it again).
Here's a typical example:
- (void) someMethod {
UILabel *myLabel = [[UILabel alloc] initWithFrame:myFrame];
self.textLabel = myLabel;
[myLabel release];
}
- (void) dealloc {
[textLabel release];
}

Objective-C: Do you have to dealloc property objects before deallocating the parent object?

Let's say I have an object named "foo" with another object named "bar" as property.
When "foo" deallocates, will it automatically remove all references to "bar" so that "bar" deallocates as well? or will "foo" deallocate and "bar" float in memory somewhere? even if all of "bar"'s references are defined in "foo".
thanks in advance.
If the foo object has any retains on or copies of (thanks Dave) bar, for example when you declare the property as either one of these:
#property (nonatomic, retain) NSString *bar;
// Or
#property (nonatomic, copy) NSString *bar;
You'll need to release bar when you deallocate foo:
- (void)dealloc
{
[bar release];
[super dealloc];
}
The system won't free bar's memory space for you until you get rid of all references to it (i.e. reference count goes down to 0), so you'll have to monitor your reference counts and objects yourself.
If you allocate memory you have to release it. So, yes, put a call to [bar release] or self.bar = nil (if you're using synthesized properties and all that) in your dealloc.
See here for an intro to memory management on iOS.
Object A is responsible for releasing any references to other objects (Object B, Object C, etc) when it is deallocated-- this doesn't happen automatically.
This is done in the -dealloc method on the object:
- (void)dealloc
{
[propertyB release];
[propertyC release];
[super dealloc];
}
(or if the properties are read/write and maked as retain, you can substitute [self setPropertyB:nil], etc).
So what will happen is that when all references to Object A go away, it's deallocated, in turn reducing the reference count on properties B and C. If those objects are only owned by Object A, they, too, will end up being deallocated as a result.
(This is true of all iPhone OS development which you've tagged. I assume you're not talking about the garbage-collected environment on the Mac, which has different rules and behavior and does do some things automatically.)
The big reason to be using self.bar = nil would be if bar were a reference to a view that was created within a nib file. In that case, one would include that line in -(void)viewDidUnload, as this will let the system release that object when the view is shuffled out. If the view comes back it will be reloaded through the nib file. This does not, however, obviate one from needing to use 'self.bar = nil or [bar release] in -(void) dealloc

when/where should properties be released once the objects is pushed for display?

I've defined a view controller with an array as one of its properties, and set the array with an allocated and autoreleased array.
After I push the view for display I release it.
By watching the leaks tool I see that every time that I pop the view I suffer from leakage.
I tried to release the properties explicitly, immediately after the push but the app crashes.
looking forward for your suggestions.
The leak is probably because of the array property is set to retain, like so:
#property (nonatomic, retain) NSArray *yourArray;
Your autorelease object is retained on assignment to the yourArray property. Since it is retained, you have to release it in the controller's dealloc method:
- (void) dealloc {
[yourArray release], yourArray = nil;
[super dealloc];
}
HTH.