dispatch_async and retain (non-ARC) - objective-c

In a non-ARC app, I have an NSOperation subclass which is enqueued by a delegate and looks like:
// note that the delegate and element properties are retained
-(id)initWithDelegate:(id<SomeDelegate>)inDelegate element:(SomeElement *)inElement
{
if (self = [super init])
{
[self setDelegate:inDelegate];
[self setElement:inElement];
}
}
-(void)dealloc
{
[_delegate release];
[_element release];
[super dealloc];
}
-(void)main
{
// do stuff
dispatch_async(dispatch_get_main_queue(), ^{
[[self delegate] doSomething:[self element]];
});
}
Since [[self delegate] doSomething:[self element]] will be called after my NSOperation object has likely been deallocated, do I need to make sure to retain "element" in the delegate before adding this operation to the queue? The element object is retained elsewhere in the app but it could potentially be released there. I need to make sure that when my delegate receives it back from the NSOperation, that it is still a valid object.
Just wondering if the act of invoking it in dispatch_async will retain the arguments passed to it. I could of course use an NSInvocation and performSelectorOnMainThread which would retain it.

The queue will retain the Block when it's enqueued, and the Block will retain objects that it captures, such as self. Since self here has a strong reference to its element, the element will be valid at least until after the Block runs.
Note that it's unusual for an object to have a strong reference to its delegate: make sure you don't have an unbroken retain cycle there.

Related

Calling autorelease in a block

There’s a memory management issue with the following code:
dispatch_after(someTime, dispatch_get_main_queue(), ^(void){
[objectA doSomething];
[self doSomethingDifferent];
});
//self’s dealloc:
- (void)dealloc
{
_objectA.delgate = nil; //objectA’s delegate is this object;
[super dealloc]
}
“self” has a reference to objectA but is not retaining objectA. In self’s dealloc it touches objectA. This is a problem if objectA has already been dealloced. I know that by referencing these objects in the block, they will be retained, but I’m not sure there’s anyway to determine in what order they will be released.
One thing that seems to work is this:
dispatch_after(someTime, dispatch_get_main_queue(), ^(void){
[[objectA retain] autorelease];
[objectA doSomething];
[self doSomethingDifferent];
});
It seems that the autorelease pool is then drained after the block releases the objects but I’m not sure that that’s a guarantee so I don’t know if this code is valid.
The class in which that -dealloc method is implemented should hold a zeroing weak reference to _objectA. In that case, you don't have to worry about it. If the object referenced by _objectA was deallocated first, then _objectA will be nil when you access it, which is safe.

Followup to returning nil from a [[class alloc] init]

As follow-up of sorts to Is returning nil from a [[class alloc] init] considered good practice?, there's a case that I haven't seen any discussed much: what to do with an init that fails some preconditions before it can call the next init?
Example, suppose in this initWithStuff: method being passed nil or in general having no value to pass to initWithValue: is an absolute failure and we definitely want to return nil.
- (id)initWithStuff:(Stuff *)inStuff {
if (!inStuff || ![inStuff hasValidValue])
{
// can't proceed to call initWithValue: because we have no value
// so do what?
return nil;
}
NSInteger value = [inStuff integerValue];
return [super initWithValue:value];
}
Perhaps a clearer example is if the designated initializer method we wrap takes an object pointer and throws an exception if its passed nil. We definitely need to short-circuit that init call that would cause an exception.
My guess: init by any means possible, and only then release self before returning nil. If necessary, call bare init or any other initializer that will work to finish putting self into a known state before releasing it.
// can't proceed to call super's initWithValue: because we have no value
// so do what? do this:
self = [super init]; // or initWithValue:0
[self release];
return nil;
And if there were no such initializer that will work without valid data, I guess one would need to construct some valid, dummy data. Or complain to its author and until then just return nil and live with the leak :^)
Also, how does ARC affect the situation?
My guess: still finish init by any means possible, then just return nil. You'd think setting self might be redundant, but in some cases it's not. In any case, it but it needs to be there to silence a compiler warning.
// can't proceed to call super's initWithValue: because we have no value
// so do what? do this:
self = [super init]; // finish init so ARC can release it having no strong references
return nil;
Are my guesses wrong in any way?
Ideally, if a precondition fails, you don't call [super init…]. You just release self (if not using ARC) and return nil:
- (id)initWithStuff:(Stuff *)stuff {
if (!stuff || ![stuff isValid]) {
[self release]; // if not using ARC
return nil;
}
if (self = [super init]) {
// initialization here
}
return self;
}
The release takes care of deallocating self under MRC. Under ARC, the compiler will insert the release for you.
However, there is a potential problem with this approach. When you release self (or when ARC releases it for you), the system will send the dealloc message to the object. And your dealloc method will call [super dealloc]. You could suppress the [super dealloc] under MRC, but you can't avoid it with ARC.
So the danger is that your superclass might assume that one of its instance variables has been initialized, and rely on that initialized value in its dealloc. For example, suppose this is the superclass:
#interface SomeSuperclass : NSObject
#end
#implementation SomeSuperclass {
CFMutableBagRef bag;
}
- (id)init {
if (self = [super init]) {
bag = CFBagCreateMutable(NULL, 0, &kCFTypeBagCallBacks);
}
return self;
}
- (void)dealloc {
CFRelease(bag);
}
#end
The problem here is that CFRelease requires its argument to not be nil. So this will crash during deallocation if you don't call [super init] in your subclass.
Given this problem, I have to change my initial recommendation. If you know that your superclass's dealloc doesn't have this sort of problem (because, for example, it checks pointers before dereferencing them or passing them to CFRelease), then you can safely not call [super init].
If you don't know that your superclass's dealloc is safe, then my recommendation is that you move your preconditions out of init and into a class factory method.
In other words, don't treat alloc/init as part of your class's public interface. Provide a class method for creating instances:
// The class factory method. Declare this in your header file. This is how you
// or any user of this class should create instances.
+ (id)myObjectWithStuff:(Stuff *)stuff {
if (!stuff || ![stuff isValid])
return nil;
// self here is the class object, so it's appropriate to send `alloc` to it.
// You don't want to hardcode the class name here because that would break
// subclassing.
return [[self alloc] initWithStuff:stuff];
}
// This is now considered a private method. You should not declare it in your
// header file, though in Objective-C you can't prevent the user from calling it
// if he's determined to.
- (id)initWithStuff:(Stuff *)stuff {
// Precondition was already checked in myObjectWithStuff:.
if (self = [super init]) {
// initialization here...
}
return self;
}

objective-c broken pointer with callback woes

I'm having trouble with a broken pointer that's pointing to garbage after an object has been released. objectA is the delegate for a callback from another object, objectB.
objectA is being allocated and released quite often (in my application its a menu UI object). Every time objectA is being initialised it initialises objectB and begins an asynchronous operation and then calls back to objectA via the id delegate property.
How can I stop my pointer: id delegate from breaking ?
ObjA.m
-(void)dealloc{
[super dealloc];
}
+(id)init{
ObjA *objectA = [[ObjA alloc]init];
return objectA;
}
-(id)init{
if (self == [super init]){
ObjB *objectB = [ObjB initialiseWithDelegate:self];
}
return self;
}
-(void)callback:(id)arg{
//This callback is called from an asynchronous routine from objectB
}
-(void)terminate{
//This is called when I want to remove/release objectA
//Other logical termination code here
[self release];
}
ObjB.m
#synthesize delegate;
+(id)initialiseWithDelegate:(id)delegate{
ObjB *objectB = [[ObjB alloc]init];
[objectB setDelegate:delegate];
return objectB;
}
-(id)init{
if (self == [super init]){
[self beginAsynchronousOperation];
}
return self;
}
-(void)beginAsynchronousOperation{
//Do asynchronous stuff here
}
-(void)finishedAsynchronousOperation{
//Called when asynch operation is complete
if (self.delegate) [self.delegate callback:someargument]; //EXC error. Pointer broke
}
The short answer here is that you nil out objectB's delegate property when you dealloc objectA. Because delegates are assigned, and not retained (explicitly to prevent retain cycles), as you have seen, the delegate reference can be left hanging when the "owning" object goes away. Typically objectA will be holding a retained reference to objectB, and during objectA's dealloc, it will first set objectB's delegate to nil, and then release objectB. This will prevent the crash. Of course this assumes (as is typical) that you don't need to do anything with that async completion. It also assumes that objectB can safely be released by objectA when it is in the middle of an async operation. This is usually true of (say) animations, but if you're building your own tasks, you might need to be careful of the lifetimes here.
Some notes on this code snippet that might be helpful:
Your objectA isn't actually holding a reference to objectB once it's created. This means you can't nil out the delegate. You should keep the reference to objectB when it's created so you can do this.
You are leaking objectB. ObjectA creates it (alloc, init), but then drops the reference, so even when it's done, no one seems to be responsible for releasing it. Again, holding it so you can release it will fix this too.
Your -terminate on objectA is an anti-pattern-- an object should never (with only one exception: a failure inside init) be calling -release on self. An object should be released by its owner, which is whoever originally created it.
Your pattern of if (self == [super init]) is normally written with one equals sign, meaning that you're both assigning self to the result as well as checking it for nil. This is a Cocoa historical oddity, and probably makes no difference here, but worth pointing out.

Under Automatic Reference Counting (ARC), where do I put my free() statements?

In cocoa, ARC frees you of having to worry about retain, release, autorelease, etc. It also prohibits calling [super dealloc]. A -(void) dealloc method is allowed, but I'm not sure if/when it's called.
I get how this is all great for objects, etc., but where do I put the free() that matches the malloc() I did in -(id) init ?
Example:
#implementation SomeObject
- (id) initWithSize: (Vertex3Di) theSize
{
self = [super init];
if (self)
{
size = theSize;
filled = malloc(size.x * size.y * size.z);
if (filled == nil)
{
//* TODO: handle error
self = nil;
}
}
return self;
}
- (void) dealloc // does this ever get called? If so, at the normal time, like I expect?
{
if (filled)
free(filled); // is this the right way to do this?
// [super dealloc]; // This is certainly not allowed in ARC!
}
You are right, you have to implement dealloc and call free inside of it. dealloc will be called when the object is deallocated as before ARC. Also, you can't call [super dealloc]; as this will be done automatically.
Finally, note that you can use NSData to allocate the memory for filled:
self.filledData = [NSMutableData dataWithLength:size.x * size.y * size.z];
self.filled = [self.filledData mutableBytes];
When you do this, you don't have to explicitly free the memory as it will be done automatically when the object and consequently filledData are deallocated.
Yes, you put it in -dealloc just like you do under MRR. The only difference with -dealloc is you must not call [super dealloc]. Other than that, it's exactly the same, and will get called when the object is fully released.
As an aside, free() will accept the NULL pointer and do nothing, so you don't actually need that conditional in -dealloc. You could just say
- (void)dealloc {
free(filled);
}

Calling dealloc in init?

I am writing a framework and I have an object with a custom init method:
#implementation OSDatabase
#synthesize database;
// MEM
- (void)dealloc {
sqlite3_close(database);
[super dealloc];
}
// INIT
- (id)initWithDatabasePath:(NSString *)path error:(NSError **)error {
if (self = [super init]) {
if (!sqlite3_open_v2([path UTF8String], &database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL)) {
error = [NSError errorWithDomain:#"OSDatabaseErrorDomain" code:1 userInfo:nil];
[self dealloc];
return nil;
}
}
return self;
}
#end
Is it safe to call dealloc inside of an init method if an error occoured? I'm not sure about this and memory management is one of the most important things in my life.
Thanks.
Is it safe to call dealloc inside of an init method if an error occoured?
No. send -release like you would everywhere else. Even in -init you can't guarantee that the current retain count is 1.
Why must you never ever send -dealloc except [super dealloc] in -dealloc? The reason is that you cannot ever guarantee that something else also has a reference to your object, even in your object's -init because the [super init] might choose to retain the object.
If the object returned by [super init] has a retain count of 1, sending -release will have the same effect as sending -dealloc. If it has a retain count of more than 1, something else thinks it owns the object and deallocing it would leave it with an invalid pointer, so -release is still the right thing to do.
Also, this:
while([self retainCount] != 0){[self release];}
would result in an infinite loop and is a terrible idea for a number of reasons. The retain counts of objects never go down to 0. -release looks something like this:
- (id)release
{
if (retainCount == 1)
{
[self dealloc];
}
else
{
retainCount--;
}
}
so the loop will decrement the retain count to 1 and then continually call dealloc forever or until the heap corruption it has caused leads to a seg fault.
Apart from never reaching zero, the retain may be UINT_MAX (for example in string or numeric literals) which colloquially means "this object must never be deallocated". If the retain count is UINT_MAX, -release won't decrement it.
Per docs you should never call dealloc directly - only [super dealloc] method in your custom dealloc.
I think calling release instead should do what expected (at least if you use init method only in standard alloc-init pattern).
As Vladimir stated you should never call dealloc directly. when retain count of an object reaches 0, Cocoa automatically calls dealloc.