I have a problem with ARC. After few hours of debugging I gave up and decided to check here.
Basically I have a MainViewController, which creates instance of PreviewsView.
In PreviewsView I have delegate and dataSource properties:
#property (nonatomic, unsafe_unretained) IBOutlet id<PreviewsDataSource> dataSource;
#property (nonatomic, unsafe_unretained) IBOutlet id<PreviewsDelegate> delegate;
I create instance of PreviewsView in MainViewController, set self as delegate and dataSource, add it to view and save as an instance variable:
- (void)addPreviews {
previewsView = [[PreviewsView alloc] initWithFrame:CGRectMake(0, 75, 1024, 480)];
previewsView.dataSource = self;
previewsView.delegate = self;
[self.view addSubview:previewsView];
}
Then when I remove MainViewController from navigation controller ACR deallocates is, BUT previewsView is still exist (WHY???) and actually running the method which triggers [self.delegate doSomeStuff]. As MainViewController already deallocates - it throws EXC_BAD_ACCESS.
So basically:
Instance of MainViewController owns instance of PreviewsView (previewsView)
Instance of MainViewController assigned as delegate and dataSource in PreviewsView
After deallocation instance of MainViewController, previewsView is still alive and running.
Any ideas why??
Thanks.
you need to set your delegates to nil, before releasing MainViewController like so
previewsView.delegate = nil;
previewsView.dataSource = nil;
Preferable call this code in dealloc method of you MainViewController -> you still can use it also with ARC like this
- (void) dealloc
{
previewsView.dataSource = nil;
previewsView.delegate = nil;
[super dealloc];
}
You should keep in mind that the protocols !assumes! your instance of previewsView is still "alive" ( notifications works the same way ) and try to send messages to your instance whether it's dead or alive . So when MainViewController is realeased, also previewsView is, but the delegates are still "alive". Therefore you need to undelegate them by setting them to nil;)
unsafe_unretained does not nil an ivar. __weak does.
Does the MainViewController member previewsView retain it? In viewDidUnload or after you have finished with previewsView, set it to nil, ie: previewsView = nil.
Generally all IBOutlets or subviews held as ivars should be __weak. If you don't need the previewsView after adding it as a subview you can remove the ivar.
Hope this helps!
Related
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
On this tutorial, the author has these declarations:
on .h
UIViewController *presentingViewController;
...
#property (retain) UIViewController *presentingViewController;
on .m
#synthesize presentingViewController;
at some point in code, inside a block, he does:
self.presentingViewController = viewController;
and then
[presentingViewController dismissModalViewControllerAnimated:NO];
I find this very strange. If is is assigning the viewController to self.presentingViewController, should't it be calling
[self.presentingViewController dismissModalViewControllerAnimated:NO];
?
I have changed his code to
.h
#property (retain) UIViewController *presentingViewController;
.m
#synthesize presentingViewController = _presentingViewController;
and what I do is:
self.presentingViewController = viewController;
and then
[self.presentingViewController dismissModalViewControllerAnimated:NO];
the problem is that, self.presentingViewController is nil at this line, even being declared as retain and never being released.
any clues?
thanks
Did you remove the instance variable UIViewController *presentingViewController when you added the instance variable UIViewController *_presentingViewController ? If not i would put money on you accidentally using the wrong one at some point.
Stepping through.. originally you had an instance variable
UIViewController *presentingViewController;
And from the property syntactic sugar you have the two accessor methods for setting and getting the variable
- (UIViewController *)presentingViewController;
- (void)setPresentingViewController:(UIViewController *)val;
at some point in code, inside a block, he does:
[self setPresentingViewController: viewController]; // uses setter
and then
[presentingViewController dismissModalViewControllerAnimated:NO]; // uses instance variable
which is fine, but you think it should be
UIViewController *localPresentingViewController = [self presentingViewController]; // uses getter
[localPresentingViewController dismissModalViewControllerAnimated:NO];
which is also fine but somewhat unnecessary.
Then you added a new instance variable:-
#synthesize presentingViewController = _presentingViewController;
so now you have
UIViewController *presentingViewController;
UIViewController *_presentingViewController;
// These now get / set _presentingViewController
- (UIViewController *)presentingViewController;
- (void)setPresentingViewController:(UIViewController *)val;
But almost certainly, somewhere you are confusing the two ivars (or you aren't aware you have two ivars) presentingViewController / _presentingViewController and still have a reference to the presentingViewController ivar, leading you to think your property is nil.
You are right that it should be:
[self.presentingViewController dismissModalViewControllerAnimated:NO];
However, reinstate this:
#synthesize presentingViewController;
And remove the instance decoration of a similar looking variable:
UIViewController *presentingViewController;
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];
}
my code broke somewhere along the way, and crashes when using the navigation bar buttons.
Error message:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UIView newMemoViewController:didAddMemo:]: unrecognized selector sent to instance 0x5b55a60'
When debugging, the program does run the cancel method, and throws an exception at the #synthesize line. However, I cannot see anything wrong with it.
The symptoms are identical, so I am including the relevant code only for the Cancel button:
NewMemoViewController.h
#import <UIKit/UIKit.h>
#protocol NewMemoDelegate;
#class AKVoiceMemo;
#interface NewMemoViewController : UIViewController {
#private
AKVoiceMemo *voiceMemo;
id <NewMemoDelegate> delegate;
}
#property (nonatomic, retain) AKVoiceMemo *voiceMemo;
#property (nonatomic, assign) id <NewMemoDelegate> delegate;
#end
#protocol NewMemoDelegate <NSObject>
- (void)newMemoViewController:(NewMemoViewController *)newMemoViewController didAddMemo:(AKVoiceMemo *)voiceMemo;
#end
NewMemoViewController.m
#import "NewMemoViewController.h"
#synthesize delegate;
- (void)viewDidLoad {
UIBarButtonItem *cancelButtonItem = [[UIBarButtonItem alloc] initWithTitle:#"Cancel" style:UIBarButtonItemStyleBordered target:self action:#selector(cancel)];
self.navigationItem.leftBarButtonItem = cancelButtonItem;
[cancelButtonItem release];
}
- (void)cancel {
[self.delegate newMemoViewController:self didAddMemo:nil];
}
Your help would be appreciated.
Edit: the delegate is the RootViewController:
- (void)newMemoViewController:(NewMemoViewController *)newMemoViewController didAddMemo:(AKVoiceMemo *)voiceMemo {
if (voiceMemo){
// Show the note in a new view controller
// TODO: Implement this
}
[self dismissModalViewControllerAnimated:YES];
}
You're probably setting the delegate of NewMemoViewController to a UIView object instead of an object that implements the NewMemoDelegate protocol.
The error message is telling you that a newMemoViewController:didAddMemo: message was sent to a UIView object and the UIView object didn't know what to do with it. Since your cancel method calls newMemoViewController:didAddMemo: on the delegate, it is the delegate which is the UIView object that doesn't recognize the newMemoViewController:didAddMemo: message. In other words, your delegate is a UIView and it doesn't implement the NewMemoDelegate protocol.
If you are correctly setting the delegate, then #jtbandes makes a great point: The delegate is probably being released and a UIView object is taking over the same memory location, thus "becoming" the delegate by accident. You're doing the right thing by using the assign attribute for your delegate; that's fairly standard Cocoa practice. However, you do need to make sure that the delegate is retained by another object, and that object needs to make sure that the delegate sticks around as long as NewMemoViewController needs it to.
I'm guessing you've over-released the delegate. I notice you have #property (assign) ... delegate;. This means that whenever you set the delegate, that object must be retained by something else as well.
The other possibility is the delegate is actually a UIView, but I'm guessing it's the other case.
I have a tableview(being IBOutlet) and tableviewController in my ViewController
what I do is
//.... allocation for tableviewController
self.tableview.delegate = tableviewController;
//now this increases the retain count of tableviewController...
So in deallocation do I need to set the tableview delegate to nil...like
self.tableview.delegate = nil;
or
self.tableview = nil; // is sufficient to make sure that the retain count of tableviewController get decreased by 1.
The tableView already realeases its delegate in its dealloc method, so you should be ok without having to explicitly set the delegate to nil.
The table view does not retain its delegate:
#property(nonatomic, assign) id<UITableViewDelegate> delegate
The reason is that retaining would likely cause a retain cycle. See Avoiding retain cycles rule #3: "Connection" objects should not retain their target.
To keep the delegate alive, you must maintain a reference to it yourself somewhere.