Lifetime of a class and blocks - objective-c

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.

Related

In Objective-C++, for testing, how can I make a class nil at runtime?

Here's the situation:
In async functions, I capture a weakSelf.
__block auto weakSelf = self;
Then, inside the block, I capture a strongSelf of that weakSelf.
[_someIvar someAsyncMethod:^{
__strong auto strongSelf = weakSelf;
}];
But, if strongSelf is nil, I do some error handling and reporting.
if (!strongSelf) {
_NotifyDelegate(someDeallocationError); // C
}
The whole thing:
__block auto weakSelf = self;
[_someIvar someAsyncMethod:^{
__strong auto strongSelf = weakSelf;
if (!strongSelf) {
// How can I trigger this line?
_NotifyDelegate(someDeallocationError); // C
}
}
}];
This is all legacy code, and I'm adding unit tests using OCMock. How can I make strongSelf nil at runtime and trigger that delegate notification?
You haven't specified any class names as far as I can see, but assuming you have:
#interface SomeClass
…
#end
…
#interface IvarClass
- (void) someAsyncMethod:(void(^)(void))block;
#end
…
#implementation SomeClass
{
IvarClass* _someIvar;
}
…
- (void)someMethod
{
__block auto weakSelf = self;
[_someIvar someAsyncMethod:^{
__strong auto strongSelf = weakSelf;
if (!strongSelf) {
// How can I trigger this line?
_NotifyDelegate(someDeallocationError); // C
}
}];
}
#end
In that case, I would subclass IvarClass with a StubIvarClassDelayedAsync where you override - (void) someAsyncMethod: so that the block is simply stored in an ivar. You then implement another method on it along the lines of - (void)completeAsyncMethod which calls the block.
You will need to use an instance of this StubIvarClassDelayedAsync as your _someIvar in your test - either you can inject this directly via the existing API on SomeClass, otherwise you may may need to subclass SomeClass to change wherever the production instance is created.
Putting it all together in the test method: Within an #autoreleasepool block, create an instance of your SomeClass (or its stub subclass), prepare it with an instance of StubIvarClassDelayedAsync, then call someMethod. This in turn should cause your overridden version of someAsyncMethod: to be called, which stores the block reference. (You can assert that the method was called for good measure.) nil out the SomeClass pointer in your test and close the pool block to remove any last references. You may want to assert that dealloc is called on your SomeClass instance, which you can easily do if using a dummy subclass. Make sure to retain a pointer to your StubIvarClassDelayedAsync instance however. Finally, call completeAsyncMethod on that stub instance, which should cause the block to take the error handling branch.
I'd be more specific about a code example if you provided a little more than just unnamed fragments, if you've got code preceding or following these fragments within the respective methods, then that may need to be taken into account or refactored first. Real names would also be useful.

Bizarre object lifetimes in ARC: how to force precise object lifetimes?

I am experiencing a bizarre behavior with object lifetimes in ARC. I have narrowed it down to this example:
//---------------------------------------------------------------------------------
#interface MyObject : NSObject
#end
#implementation MyObject
-(id)init
{
self = [super init];
if(!self) return nil;
NSLog(#" MyObject init %p", self);
return self;
}
-(void)dealloc
{
NSLog(#" MyObject dealloc %p", self);
}
#end
//---------------------------------------------------------------------------------
#implementation TLAppDelegate
-(MyObject *)createMyObject:(NSString *)unusedArg
{
return [MyObject new];
}
-(void)someOperation
{
NSLog(#" Entering someOperation");
MyObject* x = [self createMyObject:#"some message"];
NSLog(#" Exiting someOperation; x should be deallocated right after this...");
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
NSLog(#"entering applicationDidFinishLaunching");
[self someOperation];
[self someOperation];
NSLog(#"exiting applicationDidFinishLaunching");
}
#end
Summary of code: applicationDidFinishLaunching calls someOperation twice in a row. someOperation creates a local object, which
I would expect is deallocated when someOperation returns.
Thus here is the output I would expect:
entering applicationDidFinishLaunching
Entering someOperation
MyObject init 0x600000012cd0
Exiting someOperation; x should be deallocated right after this...
MyObject dealloc 0x600000012cd0
Entering someOperation
MyObject init 0x600000012cb0
Exiting someOperation; x should be deallocated right after this...
MyObject dealloc 0x600000012cb0
exiting applicationDidFinishLaunching
But here's the output I actually get:
entering applicationDidFinishLaunching
Entering someOperation
MyObject init 0x600000012cd0 <-- this object is retained until the end of the output!
Exiting someOperation; x should be deallocated right after this...
Entering someOperation
MyObject init 0x600000012cb0
Exiting someOperation; x should be deallocated right after this...
MyObject dealloc 0x600000012cb0
exiting applicationDidFinishLaunching
MyObject dealloc 0x600000012cd0
Why is the first object retained all the way until applicationDidFinishLaunching returns?
As far as I can tell, no MyObject instance should ever live outside of someOperation.
It's not a leak, because it's deallocated on the next scope.
It feels almost like the compiler inlines someOperation, merging scope with the caller. But this is also not true as
(correct me if I'm wrong), the compiler cannot inline objective-C methods.
For my real project, this is causing problems in multiple areas. First in our logging class we keep track of some scope, but because of this behavior, the following log:
Generating some list {
Calculating item #1 {
}
Calculating item #2 {
}
Calculating item #3 {
}
}
Turns into this, which is meaningless:
Generating some list {
Calculating item #1 {
Calculating item #2 {
Calculating item #3 {
}
}
}
}
Also, it means we are holding on to way too much memory for no good reason.
Is there a way to make this behavior more predictable?
Note that this behavior does not change if I use __attribute__((objc_precise_lifetime)); it makes no change to the observed behavior.
MyObject wouldn't be released at the end of someOperation because the object that createMyObject returns is retained (when created) and autoreleased (when returned).
So while someOperation subsequently assigns that object to x and retains and releases it as you'd expect, there's still that autoreleased reference from createMyObject that won't be cleared until the autorelease pool is drained (which normally happens at the end of every run loop).
If, instead of getting MyObject from createMyObject, you instantiated it directly like:
-(void)someOperation{
NSLog(#" Entering someOperation");
MyObject* x = [[MyObject alloc] init];
NSLog(#" Exiting someOperation; x should be deallocated right after this...");
}
there won't be that autoreleased reference hanging and everything should be deallocated immediately as you expect.
Update
Martin R brings up a good point about ARC and naming conventions. By default, an object returned by a method is retained/autoreleased by ARC (if it weren't retained, it'd be dealloc'd immediately. If it weren't autoreleased, it'd leak).
There are a handful of method that, according to Cocoa naming conventions, are expected to return a "retained" object — that is, an non-autoreleased object with a +1 retain count. For these specific methods, whose names start with alloc…, copy…, init…, mutableCopy…, or new…, ARC will return a retained object. Everything else returns an autoreleased one.

Does calling of a block with a variable guarantee that its lifecycle will be retained?

I have some typedef:
typedef void (^myBlock)(SomeObject);
And I have some object
#interface SomeObject : NSObject
#end
// Method of some arbitrary class
- (void) someMethod1 {
SomeObject *someObject = [[SomeObject alloc] init];
myBlock block = ^(SomeObject obj){
// When _block(someObject)_ will be called inside someQueue -
// Is it guaranteed that someObject will be alive, retained inside me?
// Do something complex and involving (or not) obj ...
}
dispatch_async(someQueue, ^{
// Some bunch of code - after which we are sure that
// by the next line someMethod1 will run out, so its scope is lost
block(someObject);
});
}
The question is put inside the block variable's block: is it guaranteed that someObject object we pass to the block block inside someQueue queue will be alive and retained inside the block block?
This question is a bit more complex variation of the question I've just asked: Does a passing of an object to a block guarantee that its lifecycle will be retained?.
the block and all it will use is retained until after the block is executed.. it is captured
I believe that what you're asking is the same of the previous question.The only difference here is that you aren't calling a method inside the block, but you are calling a block.
Maybe my answer wasn't enough clear: everything is retained inside a block, even a block is retained.
dispatch_async(someQueue, ^{
// Some bunch of code - after which we are sure that
// by the next line someMethod1 will run out, so its scope is lost
block(someObject);
});
Inside the block you call someObject, so someObject is captured.Also the block is captured.

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.

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.