I'm puzzling over how multithreading can work with delegates.
The main thread has an object "A", which has created an object "B". Object "A" is the delegate for object "B". Object "B" uses a thread to run the code.
When object "B" wants to notify the delegate, it does:
[[self delegate] performSelectorOnMainThread:#selector(didFinish:) withObject:self waitUntilDone:[NSThread isMainThread]];
The "delegate" property is an assign, atomic #property. Hence it would appear that the generated getter will do [[delegate retain] autorelease], according to objective c manual.
The dealloc method for "A" is:
- (void)dealloc
{
[b setDelegate:nil];
[b release];
[super dealloc];
}
This would appear to lead to the possible situation where the threads run like this:
Main thread: call [A dealloc] (due to a call to [a release])
Other thread: b calls [A retain] (due to the call to [self delegate])
Main thread: calls [b setDelegate:nil]
Other thread: calls performSelectorOnMainThread
At step 2, it would seem that retain cannot succeed, as dealloc is already committed to - is this race condition? What happens if you call retain on an object that is in the process of being dealloced? Can it actually occur?
If it is a race condition, how do multi-threaded objects with delegates usually avoid it?
(This arose from a slightly similar but simpler question/answer I previously asked, how to handle setDelegate with multiple threads.)
Update
It is a race conditon, as the accepted answer proves.
The solution to my original problem is to avoid this case all together, I've updated How to handle setDelegate: when using multipe threads to show this.
I don't think there's a lock on dealloc versus retain/release. The following example has a dealloc method with a sleep() in it (does anyone know if sleep() breaks locks? I don't think it does, but you never know). A better example might be to repeatedly instantiate/destroy instances of A and B until you get a situation like the one mentioned here, without the sleep().
View controller, in my case, but could be anything:
-(void)septhreadRetainDel
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSLog(#"[thread#2] sleep(1.f);");
sleep(1.f);
NSLog(#"[thread#2] [b retainDelegate];");
[b retainDelegate];
NSLog(#"[thread#2] sleep(2.f);");
sleep(2.f);
NSLog(#"[thread#2] [b release];");
[b release];
[pool release];
}
- (void)viewDidLoad {
NSLog(#"-(void)viewDidLoad:");
[super viewDidLoad];
NSLog(#"a = [[A alloc] init];");
a = [[A alloc] init];
NSLog(#"[a autorelease];");
[a autorelease];
NSLog(#"b = [[B alloc] init];");
b = [[B alloc] init];
NSLog(#"b.delegate = a;");
b.delegate = a;
NSLog(#"[NSThread detachNewThreadSelector:#selector(septhreadRetainDel) toTarget:self withObject:nil];");
[NSThread detachNewThreadSelector:#selector(septhreadRetainDel) toTarget:self withObject:nil];
}
A:
#import "A.h"
#implementation A
-(void)dealloc
{
NSLog(#"A: dealloc; zzz for 2s");
sleep(2.f);
NSLog(#"A: dealloc; waking up in time for my demise!");
[super dealloc];
}
-(id)retain
{
NSLog(#"A retain (%d++>%d)", self.retainCount, self.retainCount+1);
return [super retain];
}
-(void)release
{
NSLog(#"A release (%d-->%d)", self.retainCount, self.retainCount-1);
[super release];
}
#end
B (.h):
#import "A.h"
#interface B : NSObject {
A *delegate;
}
-(void) retainDelegate;
#property (nonatomic, assign) A *delegate;
#end
B (.m):
#import "B.h"
#implementation B
#synthesize delegate;
-(void)retainDelegate
{
NSLog(#"B:: -(void)retainDelegate (delegate currently has %d retain count):", delegate.retainCount);
NSLog(#"B:: [delegate retain];");
[delegate retain];
}
-(void)releaseDelegate
{
NSLog(#"B releases delegate");
[delegate release];
delegate = nil;
}
-(void)dealloc
{
NSLog(#"B dealloc; closing shop");
[self releaseDelegate];
[super dealloc];
}
-(id)retain
{
NSLog(#"B retain (%d++>%d)", self.retainCount, self.retainCount+1);
return [super retain];
}
-(void)release
{
NSLog(#"B release (%d-->%d)", self.retainCount, self.retainCount-1);
[super release];
}
#end
The program ends up crashing with EXC_BAD_ACCESS at B's releaseDelegate method. The following is the output from the NSLogs:
2010-07-10 11:49:27.044 race[832:207] -(void)viewDidLoad:
2010-07-10 11:49:27.050 race[832:207] a = [[A alloc] init];
2010-07-10 11:49:27.053 race[832:207] [a autorelease];
2010-07-10 11:49:27.056 race[832:207] b = [[B alloc] init];
2010-07-10 11:49:27.058 race[832:207] b.delegate = a;
2010-07-10 11:49:27.061 race[832:207] [NSThread detachNewThreadSelector:#selector(septhreadRetainDel) toTarget:self withObject:nil];
2010-07-10 11:49:27.064 race[832:4703] [thread#2] sleep(1.f);
2010-07-10 11:49:27.082 race[832:207] A release (1-->0)
2010-07-10 11:49:27.089 race[832:207] A: dealloc; zzz for 2s
2010-07-10 11:49:28.066 race[832:4703] [thread#2] [b retainDelegate];
2010-07-10 11:49:28.072 race[832:4703] B:: -(void)retainDelegate (delegate currently has 1 retain count):
2010-07-10 11:49:28.076 race[832:4703] B:: [delegate retain];
2010-07-10 11:49:28.079 race[832:4703] A retain (1++>2)
2010-07-10 11:49:28.081 race[832:4703] [thread#2] sleep(2.f);
2010-07-10 11:49:29.092 race[832:207] A: dealloc; waking up in time for my demise!
2010-07-10 11:49:30.084 race[832:4703] [thread#2] [b release];
2010-07-10 11:49:30.089 race[832:4703] B release (1-->0)
2010-07-10 11:49:30.094 race[832:4703] B dealloc; closing shop
2010-07-10 11:49:30.097 race[832:4703] B releases delegate
Program received signal: “EXC_BAD_ACCESS”.
Once -dealloc is called, retain counts are no longer of import. The object will be destroyed (this is probably obvious, though I wonder what would happen if you checked self's retainCount and DID NOT call [super dealloc] if the object had retains... insane idea). Now if we modify the -dealloc for A to set B's delegate to nil first, the program works but only because we're nil'ing delegate in B in releaseDelegate.
I don't know if that answers your question, really, but presuming sleep()'s are not somehow breaking thread locks, the exact same behavior should happen when dealloc is called right before a retain.
This is as close to a wild guess as I'm ever going to give on stack overflow, but here goes:
I think that -dealloc is synchronized to the same lock as -retain and -release, which would be crazy to not be atomic. This lock is not gotten magically in dealloc, as clearly that is filled with you're own code, but rather in release, it just holds the same lock while it does its dealloc. (this may be one of the reason's you're not supposed to call dealloc directly)
Now in object B, [self delegate] calls object A's retain, which is, if I'm right, atomic in respect to dealloc and release, and will either occur before -[A dealloc] because it will occur before -[A release], or will happen after -[A dealloc], depending on its timing.
In the first case, where -[A retain] happens before -[A release], the outcome is obvious: object A will not be deallocated until the following -[A autorelease] from the same accessor, and object B will call the delegate method on the still-around object A.
The second case is much trickier, and from this point forth, we shall be leaving the firm foundation of fact and journeying together through the murky marshes of memory into thickets of wildest guesswork. I believe that in the second case, -[A dealloc] attempts to set the delegate of object B (as said before, while the other thread is waiting to acquire the lock on its delegate) to nil. However, with an atomic property, A would then have to acquire the lock B had acquired and was using while waiting for the lock A used for retain/release/dealloc, which is obviously in use.
I think therefore this would cause a deadlock, although again, I am entirely unsure, and this answer is largely based on guesses about what is locked and when. Once again, my only viable solution (but don't stop looking, there must be a better way) is to retain the delegate, at least while the second thread is running, and prevent it from being deallocated in the first place.
Related
In my class dealloc method I have
- (void) dealloc
{
[searchField release];
[super dealloc];
}
Where searchField is defined in the class variables.
#interface SearchCell : UITableViewCell
{
UISearchBar *searchField;
id delegate;
}
The class is used with the following way:
if (indexPath.section == 0)
{
SearchCell *mycell = [[SearchCell alloc] init];
[cell setDelegate:self];
return [mycell autorelease];
}
searchField is created here:
- (id) init
{
self = [super initWithFrame:CGRectZero];
[self create];
return self;
}
- (void) create
{
searchField = [[UISearchBar alloc] initWithFrame:CGRectZero];
searchField.autocorrectionType = UITextAutocorrectionTypeNo;
[self addSubview:searchField];
}
Do I need to use [searchField release]; in my dealloc? The application crashes with message: "*[UISearchBar respondsToSelector:]: message sent to deallocated instance *".
No, don't release it there. Since you return it with "autorelease", the next run through your event loop will automatically decrease the retain count.
When you finally hit your "dealloc" method, the retain count is already 0 and your app will raise an exception.
You need to be aware of object ownership rules. In manual retain & release environment (as opposed to ARC) the rules for Objective-C objects can be remembered with the acronym NARC.
If you have a reference to an object that you created fresh using new or alloc, or any object that you called retain on, or any object you created by calling copy or mutableCopy on another, or any object created by any methods that start with any of these names (e.g. newInstance), then
you own that object and before you are deallocated you must release it.
But, if you didn't do any of those things, then you do not own that object, and must not release it. source
In your case - you don't show the code where you set the value searchField, but from the crash I'd imagine you never take ownership of the object. Show the code where you assign to searchField and we'll all know for sure.
EDIT: You do take ownership by creating it with alloc. You need to keep the release there in dealloc. Is it being released from somewhere else? Or is the cell itself possibly getting overreleased?
Please take a look at the following code.
XmlManipulatorObject=[[xmlManipulator alloc] init];
self.QuestionMutableArray=[[XmlManipulatorObject ReadXml] init];
self.dictionary=[[NSMutableDictionary alloc]init];
self.dictionary=[QuestionMutableArray objectAtIndex:reloader];
in my dealloc method i need to release all the objects i am using above.What should be the way?
I tried with the following but having BAD_ACCESS :
-(void)deallocAll
{
[self.dictionary release];
[self.XmlManipulatorObject release];
[self.QuestionMutableArray release];
}
and the properties are as below :
#property(nonatomic,retain)NSMutableDictionary *dictionary;
#property(nonatomic,retain)NSMutableArray *QuestionMutableArray;
#property(nonatomic,retain)xmlManipulator *XmlManipulatorObject;
Ok you have a number of things wrong/ bad ideas happening here:
Variable and method names should start with a lowercase letter. This helps with readability and avoids confusion with class names. So XmlManipulatorObject should be xmlManipulatorObject, ReadXml should be readXml, etc.
When you create an instance of a class, you either should autorelease it or manually release it. For example:
self.dictionary=[[[NSMutableDictionary alloc] init] autorelease];
or
NSMutableDictionary *myDic = [[NSMutableDictionary alloc]init];
self.dictionary=myDic;
[myDic release];
From the above it also appears that you don't know about the way that properties handle retains and releases. Do some reading about properties.
Sometimes it is far better to release your objects immediately after your done using them rather than at the dealloc method.
Also you should probably use the default dealloc method.
Also since you never called the dealloc super method you wont be releasing any memory.
-(void)deallocAll
{
[self dealloc];
[self.dictionary release];
[self.XmlManipulatorObject release];
[self.QuestionMutableArray release];
}
I would even go as far to change this from deallocAll to just dealloc.
drekka is right. You should note the below point too,
If you are using synthesized properties, set your variable to nil instead of releasing it.
self.yourObject = nil;
This will send the setter method 'nil' as a parameter, and your object will be released by the setter method.
i dont know what is the purpose of deallocAll. Well the best way i learned so far for that case:
NSObject *ob = [[NSObject alloc] init];
self.myObject = ob;
[ob release];
And in dealloc
-(void)deallocAll{
[super dealloc];
[myObject release];
}
#implementation TestClass
- (id) init
{
string = [[NSString alloc] init];
return self;
}
- (void) release
{
[string release];
}
#end
#implementation MainScreen
- (void) addItems
{
[myMutableArray addObject:[[[TestClass alloc] init] autorelease]];
}
- (void) release
{
[myMutableArray release];
}
My Question is when we release the myMutableArray, will it call the release method of TestClass or not?
No, that's not how Cocoa memory management works. When you add the object to the array, the array expresses that it owns the object by sending the -retain message. The array keeps this ownership until it itself disappears (i.e. is deallocated), then it no longer needs the object and sends it -release to relinquish ownership. The array does not need to retain or release the objects every time it is retained or released.
To summarise: objects retain other objects when they need to take ownership of them, and release them when they no longer need that ownership.
This points to your memory management of the string ivar being incorrect. You correctly take ownership of a zero-length string in -init (where I'm using "correctly" in a very loose sense), but then every time your object is released it releases the string. Consider:
TestClass *obj = [[TestClass alloc] init];
[obj retain];
[obj release];
[obj release];
the above is likely to crash (and if it doesn't, you're very unlucky). You should release the string in -dealloc, when your object finally doesn't need it any longer. If you change the object referred to by the ivar, you also have to change the ownership.
Never ever override -release! What you want to override is -dealloc! And in there you have to call [super dealloc]
Please read http://developer.apple.com/library/mac/documentation/cocoa/conceptual/memorymgmt/MemoryMgmt.pdf
And to your question, if your array gets deallocated, every object in the array is sent a release message.
You should be overwriting the - (void) dealloc method, not the -(void) release one; and always call [super dealloc] at the end.
Yes, releasing an NSMutableArray releases the individual objects too.
just as
[myMutableArray addObject:[[[TestClass alloc] init] autorelease]];
will increment retainCount to the TestClass instance
so will release decrement the retainCount for all objects in the array
note that dealloc is the method to implement in your custom class, it is called when your object is released.
One of my custom class has NSThread* as member variable.
I'm letting the thread exit by setting a variable(isNeedToExit).
Then call a release on the thread object [myThread release];
I intended the thread to exit first and release call gets called.
But on second thought, release call might get called before the thread notices the boolean value changed and exit.
(Because the thread is not constantly looking at the value to see if it needs to exit)
#property (nonatomic, retain) NSThread* myThread;
- (void) dealloc
{
...
[myThread release];
myThread = nil;
...
[super dealloc];
}
- (id) initMyClass
{
if(self = [super init] )
{
...
NSThread* aThread = [[NSThread alloc] initWithTarget: self selector:#selector(theThreadMain) object: nil];
self.myThread = aThread;
[aThread release];
[self.myThread start];
..
}
return self;
}
- (void) theThreadMain
{
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
// Add your sources or timers to the run loop and do any other setup.
NSRunLoop *runloop = [NSRunLoop currentRunLoop];
[runloop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
do
{
// Start the run loop but return after each source is handled.
SInt32 result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 10, YES);
}
while (self.isNeedToExit == false);
[pool release];
SYSLOG(LOG_DEBUG, "thread exiting");
}
I thought of moving [myThread release] call (now at the class's dealloc) to last line of theThreadMain..
Not sure if this is the correct way to stop a thread when it is a member variable of another class.
Thank you
Actually, your initial assumption was more correct. Your current implementation, however, will not work because dealloc will never be called.
Your class is retained by NSThread in initWithTarget:selector:object and will not be released until the thread finishes executing. Therefore, your class instance will not be deallocated as long as the thread is executing.
One way to solve the problem is to add a "cancel" method to your class that stops the thread. E.g.:
- (void)cancel
{
isNeedToExit = YES;
[myThread release];
myThread = nil;
}
That will cause the thread to stop and will allow your class to be deallocated (once the thread stops.) You need to ensure that all users of your class know that "cancel" needs to be called before the class is released. E.g.:
[myObject cancel]; // Stop the thread
[myObject release];
myObject = nil;
A more traditional solution would be to simply subclass NSThread.
I am using Apple's MyGizmoClass Singleton class for program-wide "session variables" and loving it! However, when I run "Build and Analyze" it gives weird results. Maybe my usage is wrong (it works, but it may be working due to a flakey side effect). Here is an example.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
int ct = 0;
MyGizmoClass *myGizmoClass= [MyGizmoClass sharedManager];
ct = [[myGizmoClass searchResultsForResortLocation] count];
[myGizmoClass release];
NSLog(#"ct: %d",ct);
return ct;
}
or
- (void)viewWillAppear:(BOOL)animated {
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
//self.navigationItem.rightBarButtonItem = self.editButtonItem;
NSMutableString *which_resort = [[NSMutableString alloc] init];
NSMutableString *category_code = [[NSMutableString alloc] init];
MyGizmoClass *myGizmoClass= [MyGizmoClass sharedManager];
...
which_resort = [self which_resort_location_are_we_in];
...
[myGizmoClass setWhich_resort:which_resort];
int useDebugMode = [myGizmoClass useDebugMode];
...
[myGizmoClass release];
[which_resort release];
[category_code release];
[super viewWillAppear:animated];
}
Again, this usage may be WAY off, but I thought each method I used a value from the singleton I had to do:
MyGizmoClass *myGizmoClass= [MyGizmoClass sharedManager];
and
[myGizmoClass release];
BUT i am getting these errors during Analyze:
/Users/jgobble/Documents/ProgramName/Classes/ResortsListViewController.m:495:2
Incorrect decrement of the reference
count of an object is not owned at
this point by the caller
/Users/jgobble/Documents/ProgramName/Classes/ResortsListViewController.m:493:30
Method returns an Objective-C object
with a +0 retain count (non-owning
reference)
/Users/jgobble/Documents/ProgramName/Classes/ResortsListViewController.m:495:2
Incorrect decrement of the reference
count of an object is not owned at
this point by the caller
Now, please take this into account: I am calling this:
MyGizmoClass *myGizmoClass= [MyGizmoClass sharedManager];
at the beginning of EVERY method need a "session variable" and then calling:
[myGizmoClass release];
at the end of that method PRIOR to returning a result (if i return a result from that function.
Is this not the way I should do it?
This is the ONLY thing that the analyzer (thank goodness) is reporting wrong with the program. I do not know if i should ignore it. I do not know if I am doing the calls in the right place.
Here is another question I am worried about: Does this work or do the subsequent calls to *myGizmoClass mess anything up?
-(void) function_a {
MyGizmoClass *myGizmoClass= [MyGizmoClass sharedManager];
[myGizmoClass setC:1];
int result_b = [self function_b];
printf("Addition result is: %d", result_b);
[myGizmoClass release]
}
-(int) function_b {
MyGizmoClass *myGizmoClass= [MyGizmoClass sharedManager];
int b = 0;
b = b + [myGizmoClass c];
[myGizmoClass release]
return b;
}
(i have not tested the code above)
In other words there anything wrong with calling MyGizmoClass *myGizmoClass= [MyGizmoClass sharedManager]; from function_b when you have not released MyGizmoClass *myGizmoClass= [MyGizmoClass sharedManager]; from function_a?
The problem is the way that you are using a singleton. The general idea is that you don't retain or release any singleton objects, because there is only one instance that hangs around, and the Apple implementation of singletons are coded in such a way that you can't. This brings about some issues that Peter Hosey blogged about with a better implementation of a Cocoa singleton.
The warnings that the Analyzer logs are simply stating that you "shouldn't" retain or release the singleton object; by virtue of the fact that you call [MyGizmoClass sharedManager] you shouldn't be releasing anyway (following the Cocoa memory management guidelines).
You should not be releasing myGizmoClass. When [MyGizmoClass sharedManager] is called, the returned object is not being retained. Since it's a shared object, its only owner is its class other object is responsible for releasing it unless they first retain it.
Take a look at this short guide from Apple on using singleton objects in your Objective-C code.