Does dispatching to a queue that's owned by an object retain that object? [duplicate] - objective-c-blocks

This question already has an answer here:
Is self retained within this Objective-C block?
(1 answer)
Closed 9 years ago.
Suppose I have a simple call dispatch_async(self.queue, ^{ /* Empty */ }), where self.queue is a queue created previously.
Does self get retained by the Block in this case, given that there is no self reference inside the block, but only as a parameter for dispatch_async()?

Ok, so apple docs states that
The queue is retained by the system until the block has run to
completion.
So the queue will be retained by the system until the block completes the execution, but the self won't be retained in this case.
Thanks to #Wain for pointing out my mistake in the previous version of this answer.

No, self will not get retained if the block has neither (a) any explicit references to self; nor (b) any implicit references to self that are generated by referencing any instance variables of self. The presence of self.queue in the invocation of dispatch_async will not cause it to be retained. It's what inside the block that matters.
This is quite easy to demonstrate. Imagine a view controller whose implementation looks like:
#interface SecondViewController ()
#property (nonatomic, strong) dispatch_queue_t queue;
#end
#implementation SecondViewController
- (void)dealloc
{
NSLog(#"%s", __FUNCTION__);
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.queue = dispatch_queue_create("com.stackoverflow.17306228", 0);
void (^block)(void) = ^(void){
sleep(10);
NSLog(#"%s", __FUNCTION__);
};
dispatch_async(self.queue, block);
}
#end
If you push to this view controller's scene, and then immediately press the "back" button to pop it off, you'll see the view controller immediately be deallocated, and the block will continue to execute. But if you add a class instance variable or property to the block, you'll see the view controller retained until after the block completes.

Related

Calling getter for dispatch_queue_t property causes crash

I have a private serial queue declared as a property and I am running into a very strange situation.
If I dispatch_async the property, it will crash (EXC_BAD_ACCESS (code=EXC_i386_GPFLT)). After some debugging, I found out that it's because the getter is called. If getter is not called, the crash won't happen. Also, it always crashes the second time self.queue is called. See second example below.
It's as though the first synthesized getter call has somehow caused the ivar to be over-released.
This is targeting iOS 9 and above so I am not checking OS_OBJECT_USE_OBJC.
Example 1) This doesn't work:
#interface Test ()
#property (nonatomic, strong) dispatch_queue_t initQueue;
#end
- (instancetype)init {
self = [super init];
if (self) {
_initQueue = dispatch_queue_create("com.test.initQueue", DISPATCH_QUEUE_SERIAL);
}
return self;
}
- (void)onCompletion:(void (^)())completion {
// Crashes here - EXC_BAD_ACCESS (code=EXC_i386_GPFLT)
// the second time self.queue is accessed - either by subsequent call into
// this method, or by adding NSLog(#"%#", self.queue) before this line.
dispatch_async(self.initQueue, ^{
...
});
}
Example 2) This also doesn't work:
#interface Test ()
#property (nonatomic, strong) dispatch_queue_t initQueue;
#end
- (instancetype)init {
self = [super init];
if (self) {
_initQueue = dispatch_queue_create("com.test.initQueue", DISPATCH_QUEUE_SERIAL);
}
return self;
}
- (void)onCompletion:(void (^)())completion {
NSLog(#"%#", self.initQueue);
// Crashes below - EXC_BAD_INSTRUCTION (code=EXC_i386_INVOP, subcode=0x0)
NSLog(#"%#", self.initQueue);
}
Example 3) It works if I stay away from using the getter:
#interface Test ()
#property (nonatomic, strong) dispatch_queue_t initQueue;
#end
- (instancetype)init {
self = [super init];
if (self) {
_initQueue = dispatch_queue_create("com.test.initQueue", DISPATCH_QUEUE_SERIAL);
}
return self;
}
- (void)onCompletion:(void (^)())completion {
// Works fine
dispatch_async(_initQueue, ^{
...
});
}
Example 4) It also works if I supply the getter:
#interface Test ()
#property (nonatomic, strong) dispatch_queue_t initQueue;
#end
- (instancetype)init {
self = [super init];
if (self) {
_initQueue = dispatch_queue_create("com.test.initQueue", DISPATCH_QUEUE_SERIAL);
}
return self;
}
- (dispatch_queue_t)initQueue {
return _initQueue;
}
- (void)onCompletion:(void (^)())completion {
// Works fine
dispatch_async(self.initQueue, ^{
...
});
}
Example 5) It will also work if I use ivar for queue instead of property or self.initQueue is assigned the main queue instead.
What's the reason for this behavior?
Other open source library is using property for dispatch_queue_t along with the getter and they have no issue at all. Example: https://github.com/rs/SDWebImage/blob/7e0964f8d90dcd80d535c52dd9f6d5fa7432052b/SDWebImage/SDImageCache.m#L57
Per your comments you originally named the property initQueue, this in turn created a method called initQueue which fell afoul of the ARC Method family rules. Those rules indicate that ARC will automatically annotate any method beginning with new or init as NS_RETURNS_RETAINED.
Methods in the init family implicitly consume their self parameter and return a retained object. Neither of these properties can be altered through attributes.
This in turn means that callers of the method are supposed to be safe to assume that they are taking ownership of the returned value and do not need to increment the retain value. As a result when you attempted to use the property ARC did not increase the reference count as was expected but ARC still left a release call at the end of the method. This resulted in your property value being released prior to when your class was dealloced.
It is possible to override this behavior using attributes in some cases. However I would suggest just being aware of method families, as they can have a nice performance impact on your application particularly for factory methods.
Other pitfalls to be aware of:
Methods in the alloc, copy, mutableCopy, and new families — that is, methods in all the currently-defined families except init — implicitly return a retained object as if they were annotated with the ns_returns_retained attribute. This can be overridden by annotating the method with either of the ns_returns_autoreleased or ns_returns_not_retained attributes.
A side note on this as well:
It is undefined behavior for a program to cause two or more calls to init methods on the same object, except that each init method invocation may perform at most one delegate init call.
Sadly the compiler doesn't seem to warn about that one.

In Objective-C under MRC if an object gets de-alloced do objects it created get de-allocated as well?

This may look like a dup of this, but I don't think that answers my question as it is about associated objects, not objects that were created by and whose only pointer resides within an object.
Let's say I had this example in MRC mode.
// In h file
#interface MyViewController : UIViewController {
NSObject* myNsObject;
}
// In m file
-(void) viewDidLoad() {
myNsObject = [[NSObject alloc] init]; // I'm never going to release myNsObject
}
I'm smart enough to release myViewController correctly. It's reference count goes to zero and it is de-allocated. But I never released myNsObject, so it had been hanging around with a reference count of 1. So would a release, and therefore de-alloc, automatically get done on myNsObject? Or would myNsObject get leaked in that case?
The proper memory management here is to release myNsObject in the dealloc method of the view controller:
- (void)dealloc {
[myNsObject release];
[super dealloc];
}
If you create something then you are responsible for releasing it (under MRC).
Failure to do this results in memory leaks.

Lifetime of a class and blocks

When a class created by a UIViewController serves as completion block to another class, how does the memory management lifecycle work when UIViewController gets a dealloc call?
Let's say UIViewController inheriting class instantiates a class, ControllerMediatorClass.
ControllerMediatorClass in turn invokes another class, ClassThatDealsWithNetworking, whose work takes some time to complete.
If the UIViewController gets its dealloc before the ClassThatDealsWithNetworking finishes up, when would the classes under its ownership get cleaned up?
Would the instance of 'ControllerMediatorClass' not get deallocated immediately when MyUIViewController sets it to nil because ControllerMediatorClass still serves as a completion block to the instance of ClassThatDealsWithNetworking?
MyUIViewController:
#property (nonatomic, strong) ControllerMediatorClass *mediatorClass;
- (IBAction)userTappedSomething
{
[mediatorClass makeANetworkCall];
}
- (void)dealloc
{
self.mediatorClass = nil;
}
ControllerMediatorClass:
- (void)makeANetworkCall
{
ClassThatDealsWithNetworking *networkCommand;
[networkCommand execute:^(NSDictionary *data)
{
// handling completion that
} error:^(MyError *error)
{
// handling completion
}
];
}
(using ARC)
Would the instance of 'ControllerMediatorClass' not get deallocated immediately when MyUIViewController sets it to nil because ControllerMediatorClass still serves as a completion block to the instance of ClassThatDealsWithNetworking?
Yes. Because blocks automatically capture objects that it uses and retain them as long as the block is retained.
So, as the [ClassThatDealsWithNetworking execute:] method will probably retain the completion block that is passed to it, then execute the network call in the background, then call the block once done and release the block, when the block is retained every variable that is used in this block is retained too. And will be released when the block is released.
So imagine this pseudo-code for your ClassThatDealsWithNetworking class:
typedef void(^NetworkingCompletionBlock)(NSDictionary* data)
#interface ClassThatDealsWithNetworking
#property(nonatomic, copy) NetworkingCompletionBlock completionBlock;
-(void)execute:(NetworkingCompletionBlock)block;
#end
#implementation ClassThatDealsWithNetworking
-(void)execute:(NetworkingCompletionBlock)block {
// make a copy of the block passed as a parameter
// so that we keep the completionBlock around inside
// the ClassThatDealsWithNetworking instance
// until the network request has finished
// ==> THIS WILL implicitly RETAIN every object used in the completionBlock
self.completionBlock = block;
...
// Then perform the network request
[NSURLConnection connectionWithRequest:... delegate:self];
...
}
-(void)connection:(NSURLConnection*)cxn didFailWithError:(NSError*)error
{
// call the completion block here
self.completionBlock(nil,error);
// then release the completionBlock
// ==> THIS WILL RELEASE every object previously implicitly retained by the completionBlock
self.completionBlock = nil;
}
-(void)connectionDidFinishLoading:(NSURLConnection*)cxn
{
NSDictionary* data = ...
// call the completion block here
self.completionBlock(data,nil);
// then release the completionBlock
// ==> THIS WILL RELEASE every object previously implicitly retained by the completionBlock
self.completionBlock = nil;
}
#end
Then if you do this:
[networkCommand execute:^(NSDictionary *data)
{
self.someProperty = data;
} error:^(MyError *error)
{
NSLog(#"%#", error);
self.someProperty : nil;
}];
Then self (your ControllerMediatorClass in your example) WILL BE implicitly RETAINED by the block itself, as long as the block exists, because your reference self in the body of the block somewhere. So the compiler knows that it will need it when the block is executed and retain it. And it will be implicitly released when the block is released.
This ensures that all the objects you use in the body of your block will still exist when the block is executed, avoiding to crash.
Note that this may lead to retain cycle if you are not careful. For example, if you forget to self self.completionBlock = nil in the ClassThatDealsWithNetworking class (in the delegate methods or in the dealloc method), the block will never be released by the ClassThatDealsWithNetworking instance, and will keep retaining self.
For more information, read the Blocks Programming Guide in the Apple documentation.

Will An Associated Object Be Released Automatically?

Note: This other question seems relevant but it's not: When does an associated object get released?
I'm adding a second description to a UIView instance as follows:
- (void) setSecondDescription:(UIView*)view description2:(NSString*)description2 {
objc_setAssociatedObject (view,&key,description2,OBJC_ASSOCIATION_RETAIN);
}
- (NSString*) secondDescription:(UIView*)view {
return (id)objc_getAssociatedObject(view, &key);
}
If the UIView deallocs, will the associated description 2 get dealloced? Is there any way to get this to happen automatically?
If you want to actually see the description of the entire dealloc timeline, look at WWDC 2011, Session 322, 36:22. However, here's the basic rundown (I wanted to remember it, so this is an actual comment in a piece of my code).
Note, that the associated objects are released at the end of the life cycle.
// General Information
// We take advantage of the documented Deallocation Timeline (WWDC 2011, Session 322, 36:22).
// 1. -release to zero
// * Object is now deallocating and will die.
// * New __weak references are not allowed, and will get nil.
// * [self dealloc] is called
// 2. Subclass -dealloc
// * bottom-most subclass -dealloc is called
// * Non-ARC code manually releases iVars
// * Walk the super-class chain calling -dealloc
// 3. NSObject -dealloc
// * Simply calls the ObjC runtime object_dispose()
// 4. object_dispose()
// * Call destructors for C++ iVars
// * Call -release for ARC iVars
// * Erase associated references
// * Erase __weak references
// * Call free()
Yes. When an object is dealloc'd, any associated objects (that use the RETAIN or COPY association types) are automatically released.
In short, yes - when the owning object is released then retained associated objects are released. See the first section of Apple's documentation
Section 4 in Jody Hagins' answer says "Erase associated references", which doesn't explicitly imply that the references are released. So I used the following piece of code (note WITHOUT ARC) to test this.
#interface AssociatedObjectHelper : NSObject
#end
#implementation AssociatedObjectHelper
- (void) dealloc
{
NSLog(#"In %s", __FUNCTION__);
[super dealloc];
}
#end
#implementation AppDelegate
...
- (void) testReleaseAssociatedObject
{
static const NSString *key = #"testKey123";
NSObject *ob = [NSObject new];
AssociatedObjectHelper *assocOb = [AssociatedObjectHelper new];
objc_setAssociatedObject(ob, key, assocOb, OBJC_ASSOCIATION_RETAIN);
[assocOb release];
[ob release];
}
Invoking above code does indeed end up calling -[AssociatedObjectHelper dealloc], with the following stack-trace:
#0 0x000000010000528f in -[AssociatedObjectHelper dealloc]
#1 0x00007fff8a0bb89c in objc_object::sidetable_release(bool) ()
#2 0x00007fff8a0a537f in _object_remove_assocations ()
#3 0x00007fff8a0a1644 in objc_destructInstance ()
#4 0x00007fff8a0a1595 in object_dispose ()
#5 0x00007fff8a0bb89c in objc_object::sidetable_release(bool) ()
#6 0x000000010000e9b6 in -[AppDelegate testReleaseAssociatedObject]
Tested on Xcode 7.0.1

How do I ensure a controller is retained until after it completes processing when converting to ARC?

I use the following pattern in my app and am transitioning to ARC. Basically, an object retains an instance of a controller and releases that controller when it is notified through a delegate protocol that it has finished. I don't use an iVar/property b/c startProcess can be called N times to process N things.
Example below:
// start a process in a controller
- (void)startProcess
{
MyController *controller = [[MyController alloc] init];
// set the delegate, the delegate is defined as (nonatomic, assign)
controller.delegate = self;
[controller start];
}
// when the delegate is notified, release the controller
- (void)myControllerDidFinish:(MyController):controller
{
// do something with results
[controller release];
}
When the above implementation is converted to ARC, the controller is no longer retained after startProcess concludes so the processing doesn't occur and the delegate message is never received.
QUESTION: When converting my project to use ARC, how would the above implementation be modified to work correctly w/o creating iVars in the object instantiating the controller? There is a similar example in Apple's documentation to transition to ARC but it involves using blocks. I'd rather not replace the delegate protocol with completion blocks.
EDIT: added comment in code re how delegate is defined
EDIT: clarified first para to explain why an iVar/property to hold the controller won't work
Why not just create an NSMutableArray instance variable, pendingControllers, and adding your controller there? Since arrays retain their members, your code would look like this:
// start a process in a controller
- (void)startProcess
{
MyController *controller = [[MyController alloc] init];
// set the delegate, the delegate is defined as (nonatomic, assign)
controller.delegate = self;
[controller start];
if (pendingControllers == nil) {
pendingControllers = [[NSMutableArray alloc] init];
}
[pendingControllers addObject:controller];
[controller release];
}
// when the delegate is notified, release the controller
- (void)myControllerDidFinish:(MyController):controller
{
// do something with results
[pendingControllers removeObject:controller];
if ([pendingControllers count] == 0) {
// if ARC is enabled, remove the call to -release.
[pendingControllers release], pendingControllers = nil;
}
}
This avoids the problem. Completion blocks are the right answer, and they’re what Apple is using going forward, but this method will work for now.
Usually, it is the controller's responsibility to retain itself while it completes a task. If your controller runs a task on a background thread, then it should automatically be retained by the instance of NSThread. If data is being fetched over the network using NSURLConnection, the controller should be retained as the delegate.
If you are not doing a task like this, you can use synthetic circular retains to retain the controller while the task is being carried out. This can be done by creating an object, I will call it ObjectRetainer, that simply has a __strong id property. When the controller begins its task, it should have a __strong ObjectRetainer instance variable that gets set to a new ObjectRetainer that retains the controller. This way, the controller is retaining an ObjectRetainer that is retaining the controller, thus preventing either one from being deallocated.
When the controller completes its task and has called all necessary delegate methods, it should set the ObjectRetainer instance variable to nil. This will release the ObjectRetainer, that in turn will release the controller.
The ObjectRetainer interface might look something like this:
#interface ObjectRetainer : NSObject {
__strong id object;
}
#property (nonatomic, strong) __strong id object;
#end
You should declare an ivar in the controller's header: __strong ObjectRetainer _retainer. Then, in the controller's start method:
- (void)start {
...
_retainer = [[ObjectRetainer alloc] init];
_retainer.object = self;
}
When the controller is done, simply set _retainer to nil:
- (void)performBackgroundTask {
....
[delegate myControllerDidFinish:self];
_retainer = nil;
}
more simply: make the controller a data member... #synthesized method will do the magic.