weak property (delegate) still causing retain cycle - objective-c

This is bizarre. I have what I think is a typical delegate property declaration:
#property (weak) id<BTSAudioStreamerDelegate> delegate;
If I assign to it from another object:
btsAudioStreamer.delegate = self;
It is retaining the delegate! I have an NSLog in dealloc and it doesn't get called. If I comment out the delegate assignment line, it does. Also the first object plays audio, and if it doesn't dealloc, I end up with two audio streams playing at the same time. Not good! How can I debug this?
EDIT: I should mention that I am setting btsAudioStreamer.delegate = nil before allocing any new objects.

Related

Message sent to deallocated instance using ARC

I wouldn't normally ask questions like this, but I really cant get my head around it. I have a class 'GetTableInfoFromDatabase', which connects to my database and downloads some JSON. This works great from the first screen of my tab-bar application. I can call the getNewData method as much as I want to effectively refresh the data.
My problem arises when I try and create an instance of the 'GetTableInfoFromDatabase' class and call the very same method from another tab. I get the following error:
*** -[GetTableInfoFromDatabase respondsToSelector:]: message sent to deallocated instance 0x1d89e830
The funny thing is, i'm using ARC. The culprit (in my opinion) is ASIHTTPRequest. I have had to enable -fno-objc-arc to get the project to compile. This library is used in the GetTableInfoFromDatabase class.
Here is the class:
- (void) getEventDataWithSender:(id)sender{
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:#"-------.com/getdata.php"]];
[request setDelegate:self];
NSLog(#"Running!");
[request startAsynchronous];
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
self.managedObjectContext = appDelegate.managedObjectContext;
}
And this is how i'm calling it:
GetTableInfoFromDatabase *getInfo = [[GetTableInfoFromDatabase alloc]init];
[getInfo getEventDataWithSender:self];
I've even changed the order of the tabs around, so the first tab to be displayed purely just calls this method, nothing else. Not even before the 'GetTableInfoFromDatabase' has been previously initialised by the class that initialised it first last time. Still crashes.
Has anyone got any ideas? This is so frustrating!
You need to assign that variable to a property if you plan on exposing it to other view controllers. ARC will, and should, immediately deallocate getInfo after this code executes:
GetTableInfoFromDatabase *getInfo = [[GetTableInfoFromDatabase alloc]init];
[getInfo getEventDataWithSender:self];
So if this line is included in say viewDidLoad: and nothing else refers to getInfo in that method, it will be immediately released. Why, because you haven't told the compiler that it should retain it.
So in the view controller that's exposing this class, on whatever tab it might be a child of... you would do something like this:
ViewController.h
#class GetTableInfoFromDatabase; // forward declaration
#interface ViewController : UIViewController
#property (strong, nonatomic) GetTableInfoFromDatabase *getInfo;
ViewController.m
#implementation ViewController
#synthesize getInfo = _getInfo;
- (void) viewDidLoad {
[super viewDidLoad];
self.getInfo = [[GetTableInfoFromDatabase alloc]init]; // assign your value to a property
[self.getInfo getEventDataWithSender:self];
}
So when you declare your property as Strong in your header, it will maintain a strong reference to it. #Synthesize getInfo = _getInfo means that it will create a getter and setter for self.getInfo around an instance variable named _getInfo. If you didn't want to expose it as a property, just an instance variable... you could do this:
ViewController.h
#class GetTableInfoFromDatabase; // forward declaration
#interface ViewController : UIViewController {
GetTableInfoFromDatabase _getInfo;
}
ViewController.m
#implementation ViewController
- (void) viewDidLoad {
[super viewDidLoad];
_getInfo = [[GetTableInfoFromDatabase alloc]init]; // assign your value to a property
[_getInfo getEventDataWithSender:self];
}
By default, the compiler will maintain a strong reference to that instance variable unless otherwise specified. You can have weak references as well, and all of those options are pretty well documented. So with ARC, or plain old memory management in general, you need to make an instance variable or property if you want it to hang around for a while.
Honestly... all ARC is doing for you is keeping you from having to call retain and release. Before ARC, setting that property would look like this:
GetTableInfoFromDatabase getInfo = [[GetTableInfoFromDatabase alloc]init];
self.getInfo = getInfo;
[getInfo release];
Now with ARC, the compiler just writes that code for you ;) Hope this helps!
[self.getInfo getEventDataWithSender:self];
Your GetTableInfoFromDatabase object is being deallocated, almost certainly because nothing is holding a strong reference to it. In your code above, getInfo is a local variable, so I would expect it to be released very shortly after this code, unless you are storing it somewhere.
Almost certainly, your dealloc in GetTableInfoFromDatabase does not clear the request's delegate. You should be holding the request in an ivar of GetTableInfoFromDatabase so that it can remove itself as delegate when it is deallocated.
As a side note, avoid prefacing ObjC methods with get. That has a special meaning in KVC (it means that the first parameter is supposed to be updated by reference). Typically the kind of method you have here would be prefaced with "fetch."

Delegates and retain cycles?

Edit: I'm really sorry. I edited the confusing errors I made in my post.
I have these ivars declared in WhereamiViewController.h:
CLLocationManager *locationManager;
IBOutlet MKMapView *worldView;
IBOutlet UITextField *locationTitleField;
The author writes that since WhereamiViewController owns locationManager and locationManager's delegate is the WhereamiViewController, locationManager delegate must be set to nil in the WhereamiViewController's dealloc method because the delegate is assigned instead of weak. In the .xib file worldView and locationTitleField are set to delegate the File's Owner, but why don't those two delegates need to be set to nil when both of them are also assign instead of weak?
PS: It's using ARC
locationManager must be set to nil in the WhereamiViewController's dealloc method
The CLLocationManager does not retain its delegate. Even if it did setting the locationManager to nil in dealloc will do nothing to break a retain cycle because a retain cycle would result in dealloc never being called. There needs to be some other event that breaks the retain cycle such as dismissing/popping the view controller.
but why don't those two need to be set to nil?
If t is documented that the class does not retain the delegate then you do not have to worry about a retain cycle. Sometimes the documentation comes from just looking at the header file and looking for assign rather than strong or retain. CLLocationManager does not retain its delegate so you do not have to assign locationManager to nil. If, however, the locationManager may still receive events after your class is deallocated you should set its delegate to nil in the dealloc method to prevent callbacks after your class is deallocated.
- (void)dealloc
{
//Prevent callbacks after dealloc
//Useful if locationManager is a singleton or used elsewhere
locationManager.delegate = nil;
[locationManager release]; //If not ARC
[super dealloc];//If not ARC
}
Well you need to set it to nil simply as a precautionary measure. Did I Confuse you? Let me explain.
The setting to nil has really nothing to do with retain release cycle, it is simply to avoid locationManager sending delegate call to your controller. For instance, if locationManager is updating location meanwhile your controller is released, the locationManager still having the delegate reference set to your view controller, will call the delegate with location parameters.
But since you controller has been deallocated, the call will result in bad memory access.
However, if you set it to nil, exception will not be thrown since manipulatin of nil pointers has no affect.
locationManager must be set to nil in the WhereamiViewController's dealloc method.
That doesn't do anything.
If you are using manual reference counting, it should be released in WhereamiViewController's dealloc (because WhereamiViewController owns it). If you have a property that wraps the locationManager instance variable, you can set that property to nil in dealloc to achieve the same effect, as long as the property is a retain property. However, using properties in dealloc is generally discouraged by Apple.
If you re using ARC, the compiler will do all that for you.
What you should be doing in WhereamiViewController's dealloc, is setting the location manager's delegate to nil because, if the location manager survives beyond the dealloc, you do not want it sending delegate messages to the non existent WhereamiViewController.
Again, with ARC if the delegate of CLLocationManager is a weak reference, the nilling is done for you.
but why don't those two need to be set to nil?
They don't, but the same reasoning applies to their delegates, when their delegates get deallocated.

Why does assigning with a strong property work, but not with a weak one?

I have a property declared in my .h file as
#property (weak, nonatomic) UIPickerView *levelPicker;
which is synthezised in my implementation file as:
#synthesize levelPicker = _levelPicker;
I then have a code block in the same implementation file which does the following:
if (self.levelPicker == nil) {
self.levelPicker = [[UIPickerView alloc] initWithFrame:CGRectZero];
self.levelPicker.delegate = self;
self.levelPicker.dataSource = self;
}
textField.inputView = self.levelPicker;
In this case, self._levelPicker is not set to the new UIPickerView. I.e. the self.levelPicker = blah assignment doesn't work.
However, if I change the property declaration to:
#property (strong, nonatomic) UIPickerView *levelPicker;
then everything works as expected and _levelPicker is set to the newly allocated UIPickerView.
Can someone please tell me why this is the case? I thought I was coming to an understanding of how references work, but I guess I still have more to learn. I read some of the other related SO posts, but it's still not entirely clear to me.
As #Inazfiger says, your objects need at least one strong (retaining) reference, otherwise they won't be retained.
In this case, you're assigning the picker view to a UITextField's inputView property. The text field will retain the picker view (I know this because the inputView property on UITextField is declared with the modifiers "readwrite, retain"), but only once you've made the assignment. So if you want to stick with a weak reference, you need to rearrange your code slightly - something like this:
// Declare a temporary UIPickerView reference. By default, this is
// a strong reference - so tempPicker will be retained until this
// variable goes out of scope.
UIPickerView *tempPicker = [[UIPickerView alloc] initWithFrame:frame];
// Configure the picker
tempPicker.delegate = self;
tempPicker.dataSource = self;
// Assign the picker view to the text field's inputView property. This
// will increase the picker's retain count. Now it'll no longer be
// released when tempPicker goes out of scope.
textField.inputView = tempPicker;
// Finally, assign the same object to self.levelPicker - it won't
// go out of scope as long as it remains assigned to textField's
// inputView property, and textField itself remains retained.
self.levelPicker = tempPicker;
Well, the short answer is that the assignment actually does work.
However, since it is a weak reference, it isn't retained since there is no (other) strong reference to your picker and it is automatically set to nil.
There has to be at least one strong reference to any object otherwise it isn't retained, which in this case there isn't.
For more information, see "ARC Introduces New Lifetime Qualifiers" in Apple's "Transitioning to ARC Release Notes".
Ray Wenderlich created a great tutorial on this here.
The "strong" qualifier creates an owner relationship that stops the object being deallocated, which is equivalent what you would do in the non-ARC world previously:
#property(retain) NSObject *obj;
While "weak" qualifier doesn't create the owner relationship so the object will be deallocated as you would do before:
#property(assign) NSObject *obj;
In your case, you need the first relationship because you need your instance variable (_levelPicker) to hold on to the newly created UIPickerView instance. The weak assignment you did actually work but it was deallocated shortly after.

Retain/assign memory management

I'm trying to understand memory management on iOS. I created this interface:
#interface Player : NSObject {
PlayerType pType;
PlayerWeapon pWeapon;
}
#property(nonatomic, readwrite, retain) pType;
#property(nonatomic, readwrite, retain) pWeapon;
#end
and this in the implementation file:
#synthesize pType;
#synthesize pWeapon;
In the header file, I use the retain property because pType and pWeapon are not standard C structs. From what I understand, if they were C structs, I would use assign instead. Since I've used retain, does that mean this class retains the object or whichever class instantiates it? For example, if I do this in another class:
Player *player = [[Player alloc] init];
Does this new class have to call [player release] or will the object automatically be released?
A good, general rule is that whatever you alloc/init or copy you have "created" and own, therefore you will have to release it. So yes, the object that owns player will need to release it when it is done using it. This applies if the Player object is created just for a local scope within a method, or if it is an ivar.
Remember though, that if you ever decide to create an autoreleased Player object, you will need to retain the object either through the property dot syntax or an actual retain message to keep the Player object from being autoreleased after the local method has finished executing.
// Retaining an autoreleased object
self.player=[Player playerWithName: #"George"];
or
player=[[Player playerWithName: #"George"] retain];
Good luck
When you make a property "retained" the compiler generated setter method takes responsibility of making sure objects are release and retained correctly. This property essentially handles the work of releasing the previous object it was referencing and retains (takes ownership) of the object that was assigned. You will also need to add the following code in the implementation file to release these objects when the Player object is released:
- (void) dealloc
{
[pType release];
[pWeapon release];
[super dealloc];
}
This means that even though the internal properties are "retained," when a "Player" object is allocated, you will still have to release it at some point.
The caller of [[Player alloc] init] is responsible for sending the new Player object a release message. The Player object's properties don't affect that responsibility.

how to release "delegate" memories in objective-c?

For my project,am creating delegate class. When i assign obj.delegate = self, [self retainCount] get increased by one. So that assigned object having retain count is 2. how should release delegate object and assigned object retaincount is 1?
Regards
Srini
It's the normal convention that delegates are not retained. This is mainly because the usual pattern is that the owner of the object is often also its delegate and if the delegate were retained, you'd get a retain cycle.
If you are using a property, declare it like this:
#property (assign) DelegateType delegate; // replace "DelegateType" with whatever type you need
And remove the line in -dealloc that releases the delegate.
If the accessors are synthesised, you are now done. If not, make the accessors assign accessors e.g.
-(DelegateType) delegate
{
return delegate;
}
-(void) setDelegate: (DelegateType) newValue
{
delegate = newValue;
}
In general you shouldn't be retaining delegates. The usual pattern is just to assign them. Otherwise, as you note, you'll get all kinds of problems with release cycles and so forth.
How are you defining the accessor for the delegate
#property (nonatomic, retain) Whatever *delegate;
or
#property (nonatomic, assign) Whatever *delegate;
if it is the former then the retain count will be incremented which is not what you want to be doing with a delegate. It's the responsibility of the creator to keep hold of the delegate. You are only being told about it, and should not retain it. Its only the ability of Obj C to send messages to nil without failing that means you should not be checking the reference before use too.