Not expected strange behaviour of ARC during deallocating instances - objective-c

I'm refreshing my knowledge in Objective-C world and now I'm testing some ARC with __weak local variables.
I have very simple code with such files GAObject.h
#import <Foundation/Foundation.h>
#interface GAObject : NSObject
+ (instancetype)create;
#end
Implementation of this interface GAObject.h
#import "GAObject.h"
#implementation GAObject
+ (instancetype)create {
return [[GAObject alloc] init];
}
- (void)dealloc {
NSLog(#"GAObject is being deallocated");
}
#end
So there is simple factory method create and I override dealloc method to watch if objects was deallocated when I expecting this. Now the funny part main.m:
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
#import "Learning/GAObject.h"
int main(int argc, char * argv[]) {
#autoreleasepool {
NSLog(#"1");
NSObject *o1 = [[GAObject alloc] init];
NSObject * __weak weakObject = o1; // Line 1
o1 = nil; // o1 should be deallocated because there is no strong references pointing to o1.
NSLog(#"2");
NSObject *o2 = [GAObject create]; // Line 2
o2 = nil; // o2 should be deallocated here too but it is not deallocated. Why?
NSLog(#"3");
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
In the output I see this:
1
GAObject is being deallocated
2
3
But My expecting results should be:
1
GAObject is being deallocated
2
GAObject is being deallocated
3
If I create o2 using factory method then I have this behaviour. If I create o2 like this: [[GAObject alloc] init] then I get expected output. Also I noticed that when I remove line with weakObject I also get expected results. Can somebody explain it?

It's because ARC still respects the Cocoa memory-management naming conventions.
Under those conventions, a method named +create returns a +0 reference. So, in the implementation of the method, ARC has to balance the +1 reference of the alloc/init pair by autoreleasing the reference.
Then, in main(), ARC has to assume it has received a +0 reference from the call to +create. If it needed the reference to survive the current scope, it would retain it, but it doesn't so it doesn't. The second GAObject instance would be deallocated when the autorelease pool is drained, but that will never happen because UIApplicationMain() never returns. If you use two separate autorelease pools, one for the code dealing with GAObjects and another for the call to UIApplicationMain(), I expect you'll get the result you expect.
If ARC did need the reference to survive, it would retain at the assignment to the strong variable and release when that variable is assigned a new value (including nil) or goes out of scope. ARC has a run-time optimization that an autorelease-return in a callee and a retain of the returned value in the caller cancel each other out, such that the object is never put in the autorelease pool. If this were happening, you would get your expected results.
In fact, my expectation is that the compiler initially would emit that retain and release even in your case, but a subsequent pass removes redundant retains and releases. Your example has the release immediately follow the retain, which makes it even more obvious to the compiler that the pair are redundant. Because the retain gets removed, that autorelease optimization doesn't kick in, and a reference to your object really does get put into the autorelease pool.
If your method were named +newGAObject, then the naming conventions would mean it returns a +1 reference and this all changes. (Of course, as it stands, your +create method is just doing the same thing as the built-in +new method, except for the autorelease that ARC has to add. So, you could just change the calling code to use +new and that would also sidestep this issue.)
I don't know why the line with weakObject matters. But, since the behavior you're seeing depends on certain optimizations, anything that could tweak the optimizations can change the outcome.

Related

Swizzling methods, that implicitly return a retained object under ARC

For example, lets consider following code under ARC:
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#implementation NSDate (MyEvilHack)
+ (void)load {
Method originalMethod = class_getInstanceMethod(self, #selector(copyWithZone:));
Method newMethod = class_getInstanceMethod(self, #selector(myCopyWithZone:));
method_exchangeImplementations(originalMethod, newMethod);
}
- (id)myCopyWithZone:(NSZone *)zone {
id result = [self myCopyWithZone:zone];
// do customization
return result;
}
#end
In this code, original copyWithZone: method is implicitly returns a retained object, because it belongs to copy method family. But my myCopyWithZone: is not.
I expect crash, but looks like this code works normally. Of course, I can rename my method to avoid confusion. But I am curious what exactly happens under the hood?
As you know, ARC examines the method name, applies the Cocoa memory management naming conventions, and determines how a method should behave. For a method that it's compiling, it makes the method obey those conventions. For a method that it's calling, it assumes the method obeys those conventions.
(One can override the conventions using function attributes, but ignore that for now.)
When ARC is compiling your -myCopyWithZone:, it determines that such a method should return a +0 reference. When it encounters the call to (apparently) -myCopyWithZone:, it assumes that method returns a +0 reference. Since those match, it should neither retain or release anything. (Well, it may temporarily retain the result, but it must balance that with an autorelease.) As a result, the actual +1 reference returned by the original -copyWithZone: survives to the caller and the caller was expecting a +1 reference, so that's all good.
You could probably cause ARC to screw up by calling a different method (which would not be effectively renamed by swizzling) that returns a +1 reference. If it were to return that and since the current method is expected to return a +0 reference, it would autorelease it. The caller would not retain it because it was expecting a +1 reference. So the object would be prematurely deallocated, likely leading to a crash.

Circular references in Objective-C and clang

Why doesn't the static analyser detect circular references with blocks? I remember it used to do it when I had retains on my delegates instead of assign, pre-blocks introduction. I remember the nice little lines it used to draw on my code (i think...)
If I do this, without using weakSelf, I know I'm going to get a circular reference.
// Note 1: myObject is 'retained' by self.
// Note 2: myObject retains the block for the future
[self.myObject registerBlockOfCodeForFutureExectution:^{
[self doSomething];
}];
Sample Project Exploiting Issue
Now if I know this, and I'm a stupid human, then why doesn't my intelligent computer know this is bad and warn me that I'm being stupid?
There must be a logical reason why it can't detect it, and I want to know what that reason is.
This question is regarding clang and static analysis, please don't suggest how I fix circular references - I know how to do this.
If you use self inside of block it does not automatically mean that you get retain cycle. You get retain cycle only if life-time of block depends on life-time of self object. That may be the case if self has strong reference to myObject or some more complex dependencies are also possible (I assume that it indeed 'saves' block passed to a method, so you already have strong reference there).
So to have retain cycle in your example you need to have two following conditions met (neither of them follows from the code you posted), and compiler needs to be aware of them:
1. Life-time of myObject is tied to self - lets assume that self has strong reference to it
2. saveThisBlockInMyObject: retains block passed to it
I made a small sample that gives compiler warning about capturing self - to address 1st point I declared myObject as a strong property of some class:
#property (strong) MyTestClass* myObj;
...
self.myObj = [MyTestClass new];
For 2nd point I could not find a way to specify that method retains its argument (there're source annotations for returned values, but there's no relevant annotations for method parameters). But you declare block as a strong property of your test class, then compiler is happy to warn you about possible retain cycle:
typedef void (^MyVoidBlock)();
// MyTestClass
#property (nonatomic, copy) MyVoidBlock voidBlock;
self.voidBlock = ^{
[self doSomething]; // Warning!
};
Hope that makes sense :)
The code posted to github does cause a retain cycle.
Current github code:
#interface MyObject ()
#property (nonatomic, copy) dispatch_block_t codeToRunInFuture;
#end
#implementation MyObject
- (void) registerBlockForFuture:(dispatch_block_t)block {
self.codeToRunInFuture = block;
}
// Call in ViewController
self.myObject = [MyObject.alloc init];
[self.myObject registerBlockForFuture:^{
[self runThisInFuture];
}];
I can see where this would be difficult to catch since the Analyzer can not know what block might be and therefore can not tell if there is a self reference either strong or weak. It would have to examine all instances where registerBlockForFuture: is called and the block in each case.
The answer might be to submit a bugreport to Apple.

Using #autoreleasepool in ARC inside a class

I have understood how #autoreleasepool works, I have the newest version of xcode where ARC is supported.So I can use it, but I failt to understand how it works inside a class.
Let's suppose that I have this interface:
#interface AppDelegate : NSObject <NSApplicationDelegate>
{
#private
NSMutableDictionary* dictionary;
}
And that I allocate and initialize the dictionary in init method:
- (id) init
{
self= [super init];
if(self)
{
dictionary=[[NSMutableDictionary alloc]init];
}
return self;
}
In the dealloc method I can't send to dictionary the release message, because I am under ARC.So when I usually allocate memory I do something like that:
#autoreleasepool
{
NSMutableDictionary* dict=[[NSMutableDictionary alloc]init];
< use it>
}
PS: Pardon syntax errors, I have written it directly without compiling.
But in the class, where do I put the "#autoreleasepool" block?
You can place an #autoreleasepool block around any section of code, however you really shouldn't do what I think you're doing.
Autorelease is much less efficient than allowing ARC to add in retain and release calls for you, and it's potentially unsafe. Autorelease puts all of your objects in a "pool" and then when you're out of scope and/or whenever it decides to dump the pool, it "drains" the pool and the objects' retain counts get decremented by one.
The short answer: Leave out the #autorelease blocks completely unless Apple says otherwise in the documentation or the template (for example, main.m will have an #autoreleasepool in it).
This means that your objects could potentially get released before you really wanted them to. #autoreleasepool blocks are more useful for when you have a very tight loop of code that's going to instantiate and then discard a massive amount of objects. For example, a for loop that processes a huge database and allocates string objects and then uses those string objects to fill the properties of instances of a class you've created. In this case, ARC may not release those objects reliably while you're inside the for loop and you may need to create an autorelease pool.
However, ARC not doing the right thing in a tight loop isn't very common. It's really more of a non-ARC concept, where you use an NSAutoreleasePool and you manually drain it.

Use autorelease when setting a retain property using dot syntax?

I see in some sample code that autorelease is used. I am not familiar with the instances when this is required. For example, if I create an annotation object
Header file
#interface someViewController: UIViewController
{
Annotation *annotation;
}
#property (nonatomic, retain) Annotation *annotation;
#end
Implementation file
#implementation someViewController
#synthesize annotation
#end
Question: Is it the correct approach if I initialize my annotation object in the implementation file like this?
self.annotation = [[Annotation alloc] initWithCoordinate:location];
Do I need to set autorelease for this? Or can I just do it the normal way and add the release in the dealloc method?
this is correct:
self.annotation = [[[Annotation alloc] initWithCoordinate:location] autorelease];
because annotation property is declared as a retain property, so assigning to it will increment its retain count.
you will also need, all the same, to release self.annotation in -dealloc.
in short:
init will set retain count to 1;
assigning to self.annotation, will set it to 2;
autorelease will set it back to 1 when the main loop is executed again;
release in dealloc will set the retain count to 0, so that the object will be deallocated);
the best way to think of autorelease is the following, in my opinion: autorelease will "schedule" an "automatic" release for your object at some (near) point in future (typically when the control flow goes back to the main loop, but details are hidden in the hands of Apple).
autorelease is mostly useful in conjunction with init, specifically in the following cases:
when you init a local variable, so that you don't have to release it explicitly before it goes out of scope (the main loop will do that for you);
when you return a pointer to an object you have just created without keeping ownership of it (typical case of the create/make* kind of selectors, the receiver is required to retain it to get ownership);
with properties that retain, when you assign to them an object that they should own uniquely;
with data structures that increment the retain count (NSMutableArray, NSMutableDictionary, etc): you should generally autorelease a newly inited object when you add it to such data structure.
apart from case 2, it is evident that the use of autorelease is meant to improve readability of the code and reduce the potential for errors (meaning that in all of the other cases, you could simply release explicitly your object after the assignment or at the end of the scope).
when using properties, you have always to check whether they are of the retain or assign/copy case; in the first case, assigning a newly inited object to a property generally requires autorelease.
Anyway, I would suggest at least skimming one of the many tutorial on memory management for iOS.
Autorelease is telling the object to release itself before leaving the scope.
Sometimes when you code, you'll encounter something like this
- (void)doSomething
{
if(true)
{
NSString *foo = [[NSString alloc] initWithString:#"foo"];
//Some execution here
[foo release];
}
}
- (void)doSomething
{
if(true)
{
//By doing this is telling to to release foo object before getting out of the scope
//which is similar with above practice
NSString *foo = [[[NSString alloc] initWithString:#"foo"] autorelease];
//Or you can do it this way
NSString *foo = [[NSString alloc] initWithString:#"foo"];
[foo autorelease];
//Some execution carry on, it'll release foo before entering next scope
}
//This is out of the scope
}
Of course, releasing an object doesn't mean deallocating the object.
Sometimes you retain the object so you can still use it outside of its scope.
Judging from your question, if your the object is located within your header file/interface.
You should release it in dealloc method. CMIIW.

What is dealloc in objective C

I want to ask an general question about the objective C. When I write the program of the iPhone application, I always see a function called 'dealloc' in the .m. when will this method be called? do I need to put all the [release] in here good for the application? thank you very much.
// ------------------ updated content -------------------------
NSArray *arr;
NSString *str;
NSMutableArray *mutableArr;
// in the dealloc
// it should have to following
[arr release];
[str release];
[mutableArr release];
the function will be call 3 times?
The dealloc method is called on an object when it's retain count has reached zero. Retain counts are increased by one for each retain call, and reduced once for each release call. The autorelease schedules a future release call when the current NSAutoreleasePool is drained, typically at the end of an event cycle, but you can set up your own NSAutoreleasePools on memory intensive operations. (See the NSAutoreleasePool docs for details.)
What should you put into dealloc? You should put a release for each member object the object of that class retains.
A couple things make this easier. The nil object will quietly ignore any messages sent to it, so [foo release] when foo = nil is not a bug. However, releasing an object twice can cause serious issues. My (hardly unique) solution to this is to explicitly set whatever I just released to nil, whenever I release it. In fact, I put the nil assignment on the same line as the release so I can grep for "release" and find places I missed. Example:
#interface MyClass {
Foo *foo;
Bar *bar;
NSInteger baz;
}
-(void)dealloc;
#end
#implementation MyClass
-(void)dealloc {
[foo release]; foo = nil;
[bar release]; bar = nil;
[super dealloc];
}
#end
I'll assign nil to a variable even when that variable is about to go out of scope or the object is about to go away. Why? If another object of the same class is allocated in the same memory space after I've released this one, it guarantees there will be no dangling pointers the new object might accidentally use and make debugging a nightmare. (See also NSZombieEnabled for debugging help.)
when will this method be called?
It's called when the reference count for that object becomes 0 because all its pointers have been released. The memory taken up by it is deallocated (freed); the object itself is destroyed.
do I need to put all the [release] in here good for the application?
Yes, release all the properties of the object that are still retained.
EDIT: in response to your updated question, dealloc in your custom object is only called once. It will then send these three messages:
[arr release];
[str release];
[mutableArr release];
To each of the three objects. They are entirely different objects, but if you only have one instance of each, then their reference counts all go down to 0 and their dealloc methods are called automatically.
As you surmised, it's called when an object is destroyed. The object should release everything it owns.
In Other words, Dealloc frees/destroys/releases the memory you allocated to your objects.
Allocated objects to memory should be destroyed once not used to avoid memory leaks that might cause your application to crash.
ZaldzBugz