Why do these ARC objects behave inconsistently? - objective-c

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.

Related

Objective C - ARC, memory management and performSelectorInBackground?

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.

Release method not recognized at runtime (non Cocoa)

I'm a newbie to Objective-C but have extensive experience in C and C++. The first thing I have noticed is that there is a real void in basic tutorials out there as all assume you are developing for the iPhone or Mac and using Cocoa. I'm not using Cocoa or Gnustep. To the point:
As a simple example to get started I'm trying to wrap the C's File I/O functionality. My code starts as
File.h
#include <objc/Object.h>
#include <stdio.h>
#interface File:Object
{
FILE *pFile;
char *path;
}
#property FILE *pFile;
#property char *path;
- (void)new;
- (void)OpenReadText:(const char*)var1;
- (void)release;
#end
And File.m
#include "File.h"
#implementation File
#synthesize pFile, path;
- (void)new
{
self = [super init];
}
- (void)release
{
fclose(pFile);
[super release];
}
- (void)OpenReadText:(char*)var1
{
path = var1;
pFile = fopen(path,"r");
}
#end
Then main.m
#include <stdio.h>
#import <objc/Object.h>
#include "File.h"
int main(void) {
File *Fileobj = [File new];
[Fileobj OpenReadText:"File.h"];
[Fileobj release];
}
The compiler gives me a warning that my object "may not respond to '-release'". Then when running the program is results in a runtime error: "does not recognize release. This application has requested the Runtime to terminate" .. and so on.
I'm guessing I'm making a simple newbie error, but where? Or perhaps there is something missing? I'm hoping someone can point me in the right direction here. Thanks.
If this qst has been asked already then a reference would do too. I did try to find a reference but no luck.
FOLLOW UP:
changed release method to
- (void)release
{
fclose(pFile);
[super free];
}
and it appeared to work. Apparently free is recognized in object.h.
As others have said it is unusual to use Objective-C without the Foundation frameworks. However, the Object class should implement release, retain etc. The Object class included (but not used) in Apple's Objective-C Runtime certainly contains these basic methods.
Assuming your Object class does contain these basic methods there are a couple of problems with your class as implemented.
First, you have created a new instance method which simply calls [super init]. The new method by convention is a class method which is shorthand for calling alloc and init to create and initialise an object. new is defined in Apple's Object class. It is implemented as:
+ (id)new
{
id newObject = (*_alloc)((Class)self, 0);
Class metaClass = self->isa;
if (class_getVersion(metaClass) > 1)
return [newObject init];
else
return newObject;
}
Note that this method is a class method, signified by the + instead of the -. GNUStep implements new as follows:
+ new
{
return [[self alloc] init];
}
The idiomatic way to use new would be:
File *obj = [File new];
This is in fact what you have done, however, this is calling the class method new not your instance method new.
If you wanted to call your new method you'd have to call:
File *obj = [[File alloc] new];
but as others have stated you'd need to return your object. Removing your new method would have no effect on your implementation as it isn't currently being called.
Secondly, you have placed your call to fclose in your overriden release method. This is wrong, certainly in Apple's implementation of Object anyway, GNUstep appears to be different. release could get called multiple times on a single instance of an object. retain and release are used to increment/decrement the objects retain count. Only when the retain count reaches zero should the file handle be closed. Normally, within Foundation you'd place the call to fclose in a dealloc method. dealloc is Objective-C's destructor method. The dealloc should look something like:
- (void)dealloc
{
fclose(pFile);
[super dealloc];
}
However, dealloc doesn't appear to be implemented in either Apple's or GNUstep's Object class. There is, as you point out in your question a free method which seems to be a destructor.
It would appear that replacing the above dealloc method with an equivalent free method would work as a destructor, e.g.:
- (void)free
{
fclose(pFile);
[super free];
}
Apple's implementation of Object contains retain and release methods but the GNUstep implementation does not. Neither implementation contains a dealloc method.
The implementations of Object.m and NSObject.m for Apple and GNUstep can be found at the following locations:
Apple Object.m: http://opensource.apple.com/source/objc4/objc4-532.2/runtime/Object.m
GNUstep Object.m: https://github.com/gnustep/gnustep-libobjc/blob/master/Object.m
Apple NSObject.mm: http://opensource.apple.com/source/objc4/objc4-532.2/runtime/NSObject.mm
GNUstep NSObject.m: https://github.com/gnustep/gnustep-base/blob/master/Source/NSObject.m
Is release defined on class Object? If it is not, then your call to
[super release];
will not work. (In cocoa, release is a member of NSObject; your Object class may or may not have it, and in fact the retain/release reference counting might not be there at all.)
You should confirm that your base class includes all methods called via super.
As #xlc0212 pointed out, the reference counting style of memory management is included in NSObject.
NSObject is a part of CoreFoundation library for Cocoa, CocoaTouch and GnuStep. I would say you need to link to CoreFoundation.
One book that I've read and focuses on pure Objective-C (not necessarily Cocoa) is "Programming in Objective-C 2.0" by Steven G Kochan.

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.

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.