One delegate class for multiple NSFetchedResultsController? - iphone

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

Related

ARC: Will setting self as a class member's delegate stop self from ever being freed?

Here's a pseudo class to demonstrate:
myView : UIView
- (void) init {
UIScrollView * scroller = [[UIScrollView alloc] init];
scroller.delegate = self;
[myView addSubview:scroller];
return self;
}
Under ARC, do I need to do anything else for memory to be freed correctly when all other references to myView have been removed? Will the reference between the two objects keep them sticking around forever without any intervention?
Does this change depending on whether or not scroller is a class property, or just a local variable declared in the function?
Just trying to find out why I've got multiple instances of some classes sticking around that shouldn't be there - semi related question, is there an easy way to find out why an object stays in memory (eg see all references to this object)?
iOS classes (like UIScrollView and UIWebView) already handle this correctly.
However, if you have your own delegate protocols and delegate properties, you need to make sure they are set to assign and not retain. To do this, wherever you declare a delegate (or whatever kind of protocol) you need to add the __unsafe_unretained tag thing:
#protocol FooBarDelegate {
//...
}
#interface Foo : Bar {
__unsafe_unretained id <FooBarDelegate> delegate;
}
#property (nonatomic, assign) id <FooBarDelegate> delegate;
Does this change depending on whether or not scroller is a class
property, or just a local variable declared in the function?
It doesn't. However keep in mind that adding something as a subview, the parent view will retain it automatically, regardless if you have a property or not.

iOS - Set Model class to be delegate of another Model class

I have a CLLocationManager singleton which implements a protocol, so I can tell another model class (ServerConnection) that an updated location of the user has been found.
In my AppDelegate in the method, didFinishLaunching, I write
ServerConnection* serverConnection = [[ServerConnection alloc] init];
[LocationManager sharedLocationSingleton].delegate = serverConnection;
[[LocationManager sharedLocationSingleton] getUsersLocation];
This doesn't work and the delegate method in my ServerConnection class isn't called. However, if I try having my AppDelegate class be the listener, as in the following line, it works fine.
// self refers to AppDelegate
[LocationManager sharedLocationSingleton].delegate = self;
Here, my AppDelegate implements the required delegate method and the method is called when the user's location is updated, as it should.
Why is my above method failing, where I try to set the delegate to be serverConnection?
Tutorials online usually point to using a UIViewController or the AppDelegate as the "listener", but in my case, I want a separate model class to be the listener. How do I do that?
Below is my LocationManager singleton class with the protocol
#class LocationManager;
#protocol LocationManagerDelegate <NSObject>
#required
-(void)LocationManagerUpdated:(LocationManager*) locationManager
withValue:(CLLocation*) location;
#end
#interface LocationManager : NSObject <CLLocationManagerDelegate>
#property (strong, nonatomic) CLLocationManager* locationManager;
#property (strong, nonatomic) CLLocation* location;
#property (weak, nonatomic) id <LocationManagerDelegate> delegate;
+(LocationManager*)sharedLocationSingleton;
-(void) getUsersLocation;
#end
My header file for Server connection is.
#import <Foundation/Foundation.h>
#import "LocationManager.h"
#interface ServerConnection : NSObject <LocationManagerDelegate>
#end
This works when AppDelegate is set to be the listener, but not my model object ServerConnection. How do I fix this?
Thanks!
There should be no problem in doing what you are trying to do (i.e., having a non-controller class instance to act as a delegate).
This works when AppDelegate is set to be the listener, but not my model object ServerConnection.
Does your ServerConnection class implement the LocationManagerDelegate protocol? (I mean implement as opposed to just declare it in its interface).
Check the LocationManager method in charge for calling the delegate method (LocationManagerUpdated:) and add there a NSLog trace to check that the delegate object is correctly set when you try and send it the message.
EDIT:
ServerConnection* serverConnection = [[ServerConnection alloc] init];
[LocationManager sharedLocationSingleton].delegate = serverConnection;
[[LocationManager sharedLocationSingleton] getUsersLocation];
after you comment, it is clear that the issue stems from instantiating serverConnection in a stack variable and not in a property.
Your approach of making the delegate property a strong property is not correct since it leads to retain cycles. What you need to do is defining a strong serverConnection property in the class that executes the code I pasted above (the app delegate?).
If you don't mind my being rash, if you define the delegate as a strong property, what you are doing is fixing a bug by adding a second bug that hides the first one.
It looks like serverConnection is not retained anywhere and because delegate property is specified as weak, it is released and set to nil.
Check getUsersLocation method and see if delegate is nil at the moment you are trying to call LocationManagerUpdated:withValue:

ARC doesn't release instance variable

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!

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];
}

Is a synthesized property already alloc/init -ed?

If I have a custom NSObject class called ProgramModel, does it get alloc/init -ed when I #property and #synthesize it from another class?
For instance, in a ProgramController class like this
// ProgramController.h
#import "ProgramModel.h"
#interface ProgramController : UIViewController {
ProgramModel *programModel;
}
#property (nonatomic, retain) ProgramModel *programModel;
// ProgramController.m
#import "ProgramController.h"
#implementation ProgramController
#synthesize programModel;
// etc
Do I also need to alloc/init in the initWithNibName or viewDidLoad, or is it already alloc/init-ed because of the property/synthesize?
You need to populate the property manually. The exception is if you have an IBOutlet property that you've connected in a nib file; that will get populated automatically when the nib is loaded.
I find that for view controllers the vast majority of properties are IBOutlets and properties that describe what the view will show, and the latter case is usually set by the object that creates the view controller. That will usually be the case for a view controller that shows a detail view for some object.
If you do have properties that are completely local to the view controller, a common pattern is to write your own getter and setter (rather than using #synthesize) and create the object in the getter if it doesn't exist. This lazy-loading behavior means you can easily free up resources in low-memory conditions, and that you only pay the cost of loading an object when you need it.
// simple lazy-loading getter
-(MyPropertyClass*)propertyName {
if(propertyIvarName == nil) {
propertyIvarName = [[MyPropertyClass alloc] init];
// ... other setup here
}
return propertyIvarName;
}
By default, all instance variables are zero'd out. In the case of objects, that means they're nil. If you want an initial value in the property, you need to put it there during your initializer/viewDidLoad method.
#property only declares the getter/setter methods.
#synthesize only generates the accessors for you.
They are not automatically assigned values, apart from the memory being zeroed. Additionally, you have to set them to nil in -dealloc to avoid a leak.
It also doesn't make sense to "alloc a property". An object property is a pointer. (And think of what happens if you have a linked list class...)
(N.B.: The property attributes also affect the #synthesized method, and the properties are also known to the runtime; see class_copyPropertyList() and friends.)