Objective C - ARC, memory management and performSelectorInBackground? - objective-c

Does ARC no longer require #autoreleasepool on methods invoked in a background thread? The following code suppose to cause a memory leak unless doStuff is wrapped with an #autorelease pool, but when I run instruments it shows that User gets allocated and it gets deallocated at the end of the runloop.
- (IBAction)buttonClicked:(id)sender {
[self performSelectorInBackground:#selector(doStuff) withObject:nil];
}
- (void)doStuff {
User *user = [[User alloc] init];
NSLog(#"%#", user);
}

While there might be hints to an existing autorelease pool in NSThread's implementation there is no such guarantee the documentation. In contrary, the documentation explicitly states otherwise:
performSelectorInBackground:withObject:
The method represented by aSelector must set up the thread environment just as you would for any other new thread in your program.
Threading Programming Guide
If your application uses the managed memory model [MRC and ARC, as opposed to Garbage Collection], creating an autorelease pool should be the first thing you do in your thread entry routine.
Conclusion: While in some specific scenarios there might be an autorelease pool in place it's not advisable to rely on this fact. It's undocumented behavior and can change with every release of the OS or other circumstances. It should generally be avoided in shipping code.

I used the following test app:
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController
#end
#import "ViewController.h"
#implementation ViewController
- (void)viewDidLoad{
[super viewDidLoad];
[self performSelectorInBackground:#selector(doStuff) withObject:nil];
}
- (void)doStuff {
NSMutableArray *ar = [NSMutableArray array];
for (int i=0; i<1000000; i++) {
ar[i] = [NSString stringWithFormat:#"%i", i];
}
}
#end
Profiling it with the Allocation instrument, I get the following result:
In the 8th line of the call tree, an autorelease pool is used, although I did not set up one in my code. Also, the memory allocated by the background thread seems to be released.
It thus looks as if the background thread has an autorelease pool installed.
EDIT (see comment below):
This does not mean, however, that it is not necessary to install autorelease pools for threads, since the behavior shown above is not documented, as far as I know.

Actually, that code is supposed to leak memory (under MRC) if you don't add an autorelease (which is pretty much just asking for trouble). But I'm going to answer your question as you asked it anyways.
ARC is supposed to eliminate the need for any sort of memory management (except for blocks and a lot of other gotchas, but whatever). But there are still autoreleased objects. #autoreleasepool is particularly useful in tight loops, etc. There are still pools to drain, you just don't have to add autorelease to objects yourself.

Related

Why do these ARC objects behave inconsistently?

Here's an Objective-C program for OS X that uses ARC - you can build it with cc -fobjc-arc -o objc_arc_test objc_arc_test.m or something. It creates two pairs of two objects, one using alloc/init and one using a factory function (the sort of thing you'd use autorelease for in pre-ARC code), all outside any autorelease pool, printing init and dealloc messages as it goes.
#import <Foundation/NSObject.h>
#include <stdio.h>
#if !__has_feature(objc_arc)
#error
#endif
#interface TestClass:NSObject {
int value;
}
-(TestClass *)initWithValue:(int)value;
-(void)dealloc;
+(TestClass *)testClassWithValue:(int)value;
#end
#implementation TestClass
-(TestClass *)initWithValue:(int)value_ {
if((self=[super init]))
self->value=value_;
printf("init: self=%p value=%d\n",self,self->value);
return self;
}
-(void)dealloc {
printf("dealloc: self=%p value=%d\n",self,self->value);
}
+(TestClass *)testClassWithValue:(int)value {
return [[TestClass alloc] initWithValue:value];
}
#end
static void f() {
TestClass *c5=[TestClass testClassWithValue:5];
TestClass *c6=[[TestClass alloc] initWithValue:6];
}
static void f2() {
TestClass *c7=[TestClass testClassWithValue:7];
TestClass *c8=[[TestClass alloc] initWithValue:8];
}
int main() {
f();
f2();
}
I'd expected to get init messages for 4 objects, and dealloc messages for 2, since ARC will ensure the alloc+init'd objects are destroyed and, on account of the lack of autorelease pool, will leave the other ones alone.
But what I get instead is init messages for 4 objects, and dealloc messages for 3:
init: self=0x7fea20500690 value=5
init: self=0x7fea205006f0 value=6
dealloc: self=0x7fea205006f0 value=6
init: self=0x7fea205006f0 value=7
init: self=0x7fea20500700 value=8
dealloc: self=0x7fea20500700 value=8
dealloc: self=0x7fea205006f0 value=7
I don't understand this behaviour! I'd expect the value=5 and value=7 objects to behave the same.
Why is it doing this?
(OS X 10.11.6; Xcode 8 - Apple LLVM version 8.0.0 (clang-800.0.38), Target: x86_64-apple-darwin15.6.0, Thread model: posix)
Since I believe OS X 10.9, there is an autorelease pool automatically created at the top level, even if you don't make one (this got rid of the historic "object autoreleased without an autorelease pool, just leaking" warning).
That said, that's not particularly relevant to this situation. ARC doesn't promise that anything will ever be autoreleased. It's free to use explicit releases when it can prove you won't use the object later. ARC is only obligated to ensure that objects with strong references are not destroyed, and that objects with no strong references are destroyed. Autoreleasepool is an implementation detail that ARC is free to use or not use at its discretion. As an optimization, ARC often avoids the autorelease pool in places that we would have used it manually in the past.
It's worth also noting that dealloc is never actually promised. The program is free to terminate without running it (this is a massive optimization which is why ObjC programs can terminate dramatically faster than C++ programs). In this case it happens to, but if you're relying on dealloc to run or not run, you're probably misusing it.

Detected autorelease objects

I using Test Driven Development in Objective-C for iOS and Mac OS X development, and I want to be able to write tests that can verify that objects I create with class factory methods return autorelease objects.
How can someone write a test that verifies a provided object is autorelease?
In short, you can't. There is no way to know the autorelease state of an object.
In some cases, you can infer whether an object was placed in an autorelease pool. The idea is declaring a pointer to an object, instantiating it within an #autoreleasepool block, and then verifying that it had dealloc called after the end of the block.
Through whatever combination of swizzling or overriding dealloc you choose, you must first provide a way to verify that dealloc has been called. I wrote an NSObject category with the following interface and implementation, that provides a deallocationDelegate property that will receive a message of handleDeallocation: when the object is deallocated.
#interface NSObject (FunTimes)
#property (nonatomic, assign) id deallocationDelegate;
#end
#implementation NSObject (FunTimes)
+ (void)load
{
Class klass = [NSObject class];
SEL originalSelector = #selector(dealloc);
Method originalMethod = class_getInstanceMethod(klass, originalSelector);
SEL replacementSelector = #selector(funDealloc);
Method replacementMethod = class_getInstanceMethod(klass, replacementSelector);
if(class_addMethod(klass, originalSelector, method_getImplementation(replacementMethod), method_getTypeEncoding(replacementMethod)))
{
class_replaceMethod(klass, replacementSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
}
else
{
method_exchangeImplementations(originalMethod, replacementMethod);
}
}
- (void)funDealloc
{
if (self.deallocationDelegate)
[self.deallocationDelegate performSelector:#selector(handleDeallocation:) withObject:self];
[self funDealloc];
}
static char myKey;
- (void)setDeallocationDelegate:(id)deallocationDelegate
{
objc_setAssociatedObject(self, &myKey, deallocationDelegate, OBJC_ASSOCIATION_ASSIGN);
}
- (id)deallocationDelegate
{
return objc_getAssociatedObject(self, &myKey);
}
#end
I ran some test code in my application delegate just to see if it works. I declared an NSMutableArray instance designed to hold NSValue instances derived from the pointers of objects calling -handleDeallocation, which I implement as shown:
- (void)handleDeallocation:(id)toDie
{
NSValue *pointerValue = [NSValue valueWithPointer:toDie];
[self.deallocatedPointerValues addObject:pointerValue];
}
Now, here's a snippet of what I ran. SomeClass is an NSObject subclass with no additional properties or methods.
self.deallocatedPointerValues = [NSMutableArray array];
SomeClass *arsc = nil;
#autoreleasepool {
arsc = [[[SomeClass alloc] init] autorelease];
arsc.deallocationDelegate = self;
NSValue *prePointerValue = [NSValue valueWithPointer:arsc];
BOOL preDeallocated = [self.deallocatedPointerValues containsObject:prePointerValue];
NSLog(#"PreDeallocated should be no is %d",preDeallocated);
}
NSValue *postPointerValue = [NSValue valueWithPointer:arsc];
BOOL postDeallocated = [self.deallocatedPointerValues containsObject:postPointerValue];
NSLog(#"Post deallocated should be yes is %d",postDeallocated);
In this case, it can be verified that the object pointed to by arsc (which stands for auto released SomeClass) has been deallocated due to ending the #autoreleasepool block.
There are several significant limitations to the this approach. One, this cannot work when other messages of retain may be sent to your object that is returned from your factory method. Also, and this should go without saying, swizzling dealloc should only be done in experimental settings, and I think some would argue that it shouldn't be swizzled in testing (obviously it shouldn't be swizzled in production!). Finally, and more significantly, this doesn't work well with Foundation objects such as NSString that have been optimized in ways that it's not always clear whether you are creating a new instance or not. So this is most appropriate, if at all, for your own custom objects.
As a final word, I don't think it's practical to do this really. I felt it was more work than it was worth and is so narrowly applicable as the make spending time learning instruments better to be a far better investment when it comes to memory management. And, of course, with ARC's ascendency, this approach is archaic from the start. Regardless, if you do have need to write such tests, and can work around the limitations here, feel free to adapt this code. I'd be curious to see how it pans out in an actual testing environment.
I commend your dedication to TDD. But memory management is an area where you simply have to follow well-established conventions: "When returning an object, it needs to take care of its own lifetime." My unit tests catch me when I accidentally over-release something, but they won't catch a leak. For that, I rely first on Analyze, then on running the Leaks instrument.

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.

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

How do I dispose of my own sharedInstance in Objective-C?

If I have a class that has a sharedInstance of itself (singleton pattern) or a shared instance of another class, how do I make sure that shared instance is released properly when my program exits? Better yet, can you point me to some documentation about this?
sample class (with all other methods removed for clarity):
#interface Foo : NSObject {
}
+ (Foo*)sharedInstance;
#end
the .m file:
static Foo* SharedInstance;
#implementation Foo
+ (Foo*)sharedInstance
{
if (!SharedInstance)
SharedInstance = [[Foo alloc] init]; // possible memory leak?
return SharedInstance;
}
#end
In the above code, when can I release SharedInstance?
Unless there are resources beyond the shared instance that need to be released (e.g. files, notifications at cleanup etc.), you can just let the memory for sharedInstance be cleaned up at application exit. Even files and network sockets etc. will be cleaned up by the OS at application exit, so you could get away not cleaning up those as well.
If you do have clean-up actions that need to be taken before application exit, you'll have to add a cleanup or somesuch method to your shared instance and a static method to release the shared instance.
Usually there is no need to clean up anything on exit as Barry pointed out, but what you can do is setting an NSApplication delegate that implements
- (void)applicationWillTerminate:(NSNotification *)aNotification
This method is then called right before your application is going to quit. In this method you can call some static method of your shared instance classes to clean up the internally cached shared instance.
However, something like a memory leak beyond application quit doesn't exist. If your application terminates, all memory it ever owned is given back to the system no matter how it was allocated. If that was not the case and your application was going to crash, the memory it used before the crash would be ultimately lost to the system and that is not acceptable. On a multi-tasking OS, an application that crashes must not have any negative effects on the rest of the system!
Whatever you do, don't autorelease a non-retained shared instance, because it will be free'd, and you will be screwed. Do it the way you are currently doing, but in applicationWillTerminate: put something like this:
BackgroundFactory * factory = [self sharedFactory];
// prevent an analyzer annoyance
NSValue * value = [NSValue valueWithPointer:factory];
id fac = (id)[value pointerValue];
[fac release];
Note that sharedFactory is the method returning a shared instance. All of this code should go into a +freeSharedInstance method.
It is better to do it like this
+ (Foo*)sharedInstance
{
if (!SharedInstance)
SharedInstance = [[[Foo alloc] init] autorelease];
return SharedInstance;
}
#end
Class B which uses the shared instance can retain and release it.