For the most part with ARC (Automatic Reference Counting), we don't need to think about memory management at all with Objective-C objects. It is not permitted to create NSAutoreleasePools anymore, however there is a new syntax:
#autoreleasepool {
…
}
My question is, why would I ever need this when I'm not supposed to be manually releasing/autoreleasing ?
EDIT: To sum up what I got out of all the anwers and comments succinctly:
New Syntax:
#autoreleasepool { … } is new syntax for
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
…
[pool drain];
More importantly:
ARC uses autorelease as well as release.
It needs an auto release pool in place to do so.
ARC doesn't create the auto release pool for you. However:
The main thread of every Cocoa app already has an autorelease pool in it.
There are two occasions when you might want to make use of #autoreleasepool:
When you are in a secondary thread and there is no auto release pool, you must make your own to prevent leaks, such as myRunLoop(…) { #autoreleasepool { … } return success; }.
When you wish to create a more local pool, as #mattjgalloway has shown in his answer.
ARC doesn't get rid of retains, releases and autoreleases, it just adds in the required ones for you. So there are still calls to retain, there are still calls to release, there are still calls to autorelease and there are still auto release pools.
One of the other changes they made with the new Clang 3.0 compiler and ARC is that they replaced NSAutoReleasePool with the #autoreleasepool compiler directive. NSAutoReleasePool was always a bit of a special "object" anyway and they made it so that the syntax of using one is not confused with an object so that it's generally a bit more simple.
So basically, you need #autoreleasepool because there are still auto release pools to worry about. You just don't need to worry about adding in autorelease calls.
An example of using an auto release pool:
- (void)useALoadOfNumbers {
for (int j = 0; j < 10000; ++j) {
#autoreleasepool {
for (int i = 0; i < 10000; ++i) {
NSNumber *number = [NSNumber numberWithInt:(i+j)];
NSLog(#"number = %p", number);
}
}
}
}
A hugely contrived example, sure, but if you didn't have the #autoreleasepool inside the outer for-loop then you'd be releasing 100000000 objects later on rather than 10000 each time round the outer for-loop.
Update:
Also see this answer - https://stackoverflow.com/a/7950636/1068248 - for why #autoreleasepool is nothing to do with ARC.
Update:
I took a look into the internals of what's going on here and wrote it up on my blog. If you take a look there then you will see exactly what ARC is doing and how the new style #autoreleasepool and how it introduces a scope is used by the compiler to infer information about what retains, releases & autoreleases are required.
#autoreleasepool doesn't autorelease anything. It creates an autorelease pool, so that when the end of block is reached, any objects that were autoreleased by ARC while the block was active will be sent release messages. Apple's Advanced Memory Management Programming Guide explains it thus:
At the end of the autorelease pool block, objects that received an autorelease message within the block are sent a release message—an object receives a release message for each time it was sent an autorelease message within the block.
People often misunderstand ARC for some kind of garbage collection or the like. The truth is that, after some time people at Apple (thanks to llvm and clang projects) realized that Objective-C's memory administration (all the retains and releases, etc.) can be fully automatized at compile time. This is, just by reading the code, even before it is run! :)
In order to do so there is only one condition: We MUST follow the rules, otherwise the compiler would not be able to automate the process at compile time. So, to ensure that we never break the rules, we are not allowed to explicitly write release, retain, etc. Those calls are Automatically injected into our code by the compiler. Hence internally we still have autoreleases, retain, release, etc. It is just we don't need to write them anymore.
The A of ARC is automatic at compile time, which is much better than at run time like garbage collection.
We still have #autoreleasepool{...} because having it does not break any of the rules, we are free create/drain our pool anytime we need it :).
Autorelease pools are required for returning newly created objects from a method. E.g. consider this piece of code:
- (NSString *)messageOfTheDay {
return [[NSString alloc] initWithFormat:#"Hello %#!", self.username];
}
The string created in the method will have a retain count of one. Now who shall balance that retain count with a release?
The method itself? Not possible, it has to return the created object, so it must not release it prior to returning.
The caller of the method? The caller does not expect to retrieve an object that needs releasing, the method name does not imply that a new object is created, it only says that an object is returned and this returned object may be a new one requiring a release but it may as well be an existing one that doesn't. What the method does return may even depend on some internal state, so the the caller cannot know if it has to release that object and it shouldn't have to care.
If the caller had to always release all returned object by convention, then every object not newly created would always have to be retained prior to returning it from a method and it would have to be released by the caller once it goes out of scope, unless it is returned again. This would be highly inefficient in many cases as one can completely avoid altering retain counts in many cases if the caller will not always release the returned object.
That's why there are autorelease pools, so the first method will in fact become
- (NSString *)messageOfTheDay {
NSString * res = [[NSString alloc] initWithFormat:#"Hello %#!", self.username];
return [res autorelease];
}
Calling autorelease on an object adds it to the autorelease pool, but what does that really mean, adding an object to the autorelease pool? Well, it means telling your system "I want you to to release that object for me but at some later time, not now; it has a retain count that needs to be balanced by a release otherwise memory will leak but I cannot do that myself right now, as I need the object to stay alive beyond my current scope and my caller won't do it for me either, it has no knowledge that this needs to be done. So add it to your pool and once you clean up that pool, also clean up my object for me."
With ARC the compiler decides for you when to retain an object, when to release an object and when to add it to an autorelease pool but it still requires the presence of autorelease pools to be able to return newly created objects from methods without leaking memory. Apple has just made some nifty optimizations to the generated code which will sometimes eliminate autorelease pools during runtime. These optimizations require that both, the caller and the callee are using ARC (remember mixing ARC and non-ARC is legal and also officially supported) and if that is actually the case can only be known at runtime.
Consider this ARC Code:
// Callee
- (SomeObject *)getSomeObject {
return [[SomeObject alloc] init];
}
// Caller
SomeObject * obj = [self getSomeObject];
[obj doStuff];
The code that the system generates, can either behave like the following code (that is the safe version that allows you to freely mix ARC and non-ARC code):
// Callee
- (SomeObject *)getSomeObject {
return [[[SomeObject alloc] init] autorelease];
}
// Caller
SomeObject * obj = [[self getSomeObject] retain];
[obj doStuff];
[obj release];
(Note the retain/release in the caller is just a defensive safety retain, it's not strictly required, the code would be perfectly correct without it)
Or it can behave like this code, in case that both are detected to use ARC at runtime:
// Callee
- (SomeObject *)getSomeObject {
return [[SomeObject alloc] init];
}
// Caller
SomeObject * obj = [self getSomeObject];
[obj doStuff];
[obj release];
As you can see, Apple eliminates the atuorelease, thus also the delayed object release when the pool is destroyed, as well as the safety retain. To learn more about how that is possible and what's really going on behind the scenes, check out this blog post.
Now to the actual question: Why would one use #autoreleasepool?
For most developers, there's only one reason left today for using this construct in their code and that is to keep the memory footprint small where applicable. E.g. consider this loop:
for (int i = 0; i < 1000000; i++) {
// ... code ...
TempObject * to = [TempObject tempObjectForData:...];
// ... do something with to ...
}
Assume that every call to tempObjectForData may create a new TempObject that is returned autorelease. The for-loop will create one million of these temp objects which are all collected in the current autoreleasepool and only once that pool is destroyed, all the temp objects are destroyed as well. Until that happens, you have one million of these temp objects in memory.
If you write the code like this instead:
for (int i = 0; i < 1000000; i++) #autoreleasepool {
// ... code ...
TempObject * to = [TempObject tempObjectForData:...];
// ... do something with to ...
}
Then a new pool is created every time the for-loop runs and is destroyed at the end of each loop iteration. That way at most one temp object is hanging around in memory at any time despite the loop running one million times.
In the past you often had to also manage autoreleasepools yourself when managing threads (e.g. using NSThread) as only the main thread automatically has an autorelease pool for a Cocoa/UIKit app. Yet this is pretty much legacy today as today you probably wouldn't use threads to begin with. You'd use GCD DispatchQueue's or NSOperationQueue's and these two both do manage a top level autorelease pool for you, created before running a block/task and destroyed once done with it.
It's because you still need to provide the compiler with hints about when it is safe for autoreleased objects to go out of scope.
Quoted from https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmAutoreleasePools.html:
Autorelease Pool Blocks and Threads
Each thread in a Cocoa application maintains its own stack of
autorelease pool blocks. If you are writing a Foundation-only program
or if you detach a thread, you need to create your own autorelease
pool block.
If your application or thread is long-lived and potentially generates
a lot of autoreleased objects, you should use autorelease pool blocks
(like AppKit and UIKit do on the main thread); otherwise, autoreleased
objects accumulate and your memory footprint grows. If your detached
thread does not make Cocoa calls, you do not need to use an
autorelease pool block.
Note: If you create secondary threads using the POSIX thread APIs
instead of NSThread, you cannot use Cocoa unless Cocoa is in
multithreading mode. Cocoa enters multithreading mode only after
detaching its first NSThread object. To use Cocoa on secondary POSIX
threads, your application must first detach at least one NSThread
object, which can immediately exit. You can test whether Cocoa is in
multithreading mode with the NSThread class method isMultiThreaded.
...
In Automatic Reference Counting, or ARC, the system uses the same
reference counting system as MRR, but it insertsthe appropriate memory
management method callsfor you at compile-time. You are strongly
encouraged to use ARC for new projects. If you use ARC, there is
typically no need to understand the underlying implementation
described in this document, although it may in some situations be
helpful. For more about ARC, see Transitioning to ARC Release Notes.
TL;DR
Why is #autoreleasepool still needed with ARC?
#autoreleasepool is used by Objective-C and Swift to work with autorelese inside
When you work with pure Swift and allocate Swift objects - ARC handles it
But if you decide call/use Foundation/Legacy Objective-C code(NSData, Data) which uses autorelese inside then #autoreleasepool in a rescue
//Swift
let imageData = try! Data(contentsOf: url)
//Data init uses Objective-C code with [NSData dataWithContentsOfURL] which uses `autorelese`
Long answer
MRC, ARC, GC
Manual Reference Counting(MRC) or Manual Retain-Release(MRR) as a developer you are responsible for counting references on objects manually
Automatic Reference Counting(ARC) was introduced in iOS v5.0 and OS X Mountain Lion with xCode v4.2
Garbage Collection(GC) was available for Mac OS and was deprecated in OS X Mountain Lion. Must Move to ARC
Reference count in MRC and ARC
//MRC
NSLog(#"Retain Count: %d", [variable retainCount]);
//ARC
NSLog(#"Retain Count: %ld", CFGetRetainCount((__bridge CFTypeRef) variable));
Every object in heap has an integer value which indicates how many references are pointed out on it. When it equals to 0 object is deallocated by system
Allocating object
Working with Reference count
Deallocating object. deinit is called when retainCount == 0
MRC
A *a1 = [[A alloc] init]; //this A object retainCount = 1
A *a2 = a1;
[a2 retain]; //this A object retainCount = 2
// a1, a2 -> object in heap with retainCount
Correct way to release an object:
release If only this - dangling pointer. Because it still can point on the object in heap and it is possible to send a message
= nil If only this - memory leak. deinit will not be called
A *a = [[A alloc] init]; //++retainCount = 1
[a release]; //--retainCount = 0
a = nil; //guarantees that even somebody else has a reference to the object, and we try to send some message thought variable `a` this message will be just skipped
Working with Reference count(Object owner rules):
(0 -> 1) alloc, new, copy, mutableCopy
(+1) retain You are able to own an object as many times as you need(you can call retain several times)
(-1) release If you an owner you must release it. If you release more than retainCount it will be 0
(-1) autorelease Adds an object, which should be released, to autorelease pool. This pool will be processed at the end of RunLoop iteration cycle(it means when all tasks will be finished on the stack)[About] and after that release will be applied for all objects in the pool
(-1) #autoreleasepool Forces process an autorelease pool at the end of block. It is used when you deal with autorelease in a loop and want to clear resources ASAP. If you don't do it your memory footprint will be constantly increasing
autorelease is used in method calls when you allocate a new object there and return it
- (B *)foo {
B *b1 = [[B alloc] init]; //retainCount = 1
//fix - correct way - add it to fix wrong way
//[b1 autorelease];
//wrong way(without fix)
return b;
}
- (void)testFoo {
B *b2 = [a foo];
[b2 retain]; //retainCount = 2
//some logic
[b2 release]; //retainCount = 1
//Memory Leak
}
#autoreleasepool example
- (void)testFoo {
for(i=0; i<100; i++) {
B *b2 = [a foo];
//process b2
}
}
ARC
One of biggest advantage of ARC is that it automatically insert retain, release, autorelease under the hood in Compile Time and as developer you should not take care of it anymore
Enable/Disable ARC
//enable
-fobjc-arc
//disable
-fno-objc-arc
Variants from more to less priority
//1. local file - most priority
Build Phases -> Compile Sources -> Compiler Flags(Select files -> Enter)
//2. global
Build Settings -> Other C Flags(OTHER_CFLAGS)
//3. global
Build Settings -> Objective-C Automatic Reference Counting(CLANG_ENABLE_OBJC_ARC)
Check if ARC is enabled/disabled
Preprocessor __has_feature function is used
__has_feature(objc_arc)
Compile time
// error if ARC is Off. Force to enable ARC
#if ! __has_feature(objc_arc)
#error Please enable ARC for this file
#endif
//or
// error if ARC is On. Force to disable ARC
#if __has_feature(objc_arc)
#error Please disable ARC for this file
#endif
Runtime
#if __has_feature(objc_arc)
// ARC is On
NSLog(#"ARC on");
#else
// ARC is Off
NSLog(#"ARC off");
#endif
Reverse engineering(for Objective-C)
//ARC is enabled
otool -I -v <binary_path> | grep "<mrc_message>"
//e.g.
otool -I -v "/Users/alex/ARC_experiments.app/ARC_experiments" | grep "_objc_release"
//result
0x00000001000080e0 748 _objc_release
//<mrc_message>
_objc_retain
_objc_release
_objc_autoreleaseReturnValue
_objc_retainAutoreleaseReturnValue
_objc_retainAutoreleasedReturnValue
_objc_storeStrong
Tool to Migrate Objective-C MRC to ARC
ARC generates errors where you should manually remove retain, release, autorelease and others issues
Edit -> Convert -> To Objective-C ARC...
New Xcode with MRC
If you enable MRC you get next errors(warnings)(but the build will be successful)
//release/retain/autorelease/retainCount
'release' is unavailable: not available in automatic reference counting mode
ARC forbids explicit message send of 'release'
There seems to be a lot of confusion on this topic (and at least 80 people who probably are now confused about this and think they need to sprinkle #autoreleasepool around their code).
If a project (including its dependencies) exclusively uses ARC, then #autoreleasepool never needs to be used and will do nothing useful. ARC will handle releasing objects at the correct time. For example:
#interface Testing: NSObject
+ (void) test;
#end
#implementation Testing
- (void) dealloc { NSLog(#"dealloc"); }
+ (void) test
{
while(true) NSLog(#"p = %p", [Testing new]);
}
#end
displays:
p = 0x17696f80
dealloc
p = 0x17570a90
dealloc
Each Testing object is deallocated as soon as the value goes out of scope, without waiting for an autorelease pool to be exited. (The same thing happens with the NSNumber example; this just lets us observe the dealloc.) ARC does not use autorelease.
The reason #autoreleasepool is still allowed is for mixed ARC and non-ARC projects, which haven't yet completely transitioned to ARC.
If you call into non-ARC code, it may return an autoreleased object. In that case, the above loop would leak, since the current autorelease pool will never be exited. That's where you'd want to put an #autoreleasepool around the code block.
But if you've completely made the ARC transition, then forget about autoreleasepool.
Related
I'm struggling with one issue related to the cocoa foundation memory management. Basically I have a project, written in Rust and I'm using cocoa-rs and objc-rs to interact with Objective-C. I'm familiar with memory management in CoreFoundation and CocoaFoundation (I've already read the corresponding articles in documentation). I did not have any memory issues when I worked with CoreFoundation functions, but I'm having a lot of problems when I use CocoaFoundation related stuff, it seems that getting any objects from CocoaFoundation leaks memory.
Here is the simplified version of one of the functions which leads to a memory memory:
pub fn enumerate_apps()-> Vec<Rc<AppInfo>> {
let mut apps_list = Vec::new();
unsafe {
let shared_workspace: *mut Object = msg_send![class("NSWorkspace"), sharedWorkspace];
let running_apps: *mut Object = msg_send![shared_workspace, runningApplications];
let apps_count = msg_send![running_apps, count];
for i in 0..apps_count {
let app: *mut Object = msg_send![running_apps, objectAtIndex:i];
// Those ones are not used at the moment, but I actually need them,
// I just removed all business logic to keep the example simple and compilable
// to demonstrate the problem.
let bundle_url: *mut Object = msg_send![app, bundleURL];
let app_bundle: *mut Object = msg_send![class("NSBundle"), bundleWithURL:bundle_url];
let info_dict: *mut Object = msg_send![app_bundle, infoDictionary];
apps_list.push(Rc::new(AppInfo {
pid: msg_send![app, processIdentifier],
}));
}
}
apps_list
}
I tried to call this function inside a loop to make the memory leak visible:
fn main() {
loop {
for i in 0..200 {
enumerate_apps();
}
std::thread::sleep(std::time::Duration::from_millis(5000));
}
}
When I run the application, I can see that it consumes more and more memory over time.
My question is: why? What is the correct way to manage memory in a such FFI code? If I run the same code in XCode, using the plain Objective-C, it works fine and seems that it does not leak memory. Well, the reason why the memory is not leaked in XCode is that ARC is enabled by default. To my knowledge ARC is not enabled when we work with Objective-C from Rust in a such manner, so basically it means that we have to manage memory ourselves. Commenting 3 lines containing bundle_url, app_bundle, info_dict creates an illusion that the memory leak is gone (without commenting them, the process leaks a couple megabytes of memory every 2 seconds), but actually the memory is still leaked, but not that fast.
What I tried:
I tried to create an NSAutoreleasePool in the beginning of the function and call autorelease() for bundle_url and app_bundle upon creation. Did not help, the memory it still leaked.
I tried to call release() manually on bundle_url and app_bundle, did not have any effect.
Even tried to call dealloc() on them (which I believe is a wrong way), which also did not help to solve my problem.
Am I doing something wrong? Or is it a bug in objc-rs (I guess this is very unlikely, but who knows)?
Since Objective-C ARC is not implemented in objc-rs/cocoa-rs, you need to follow the memory management rule, in particular for this question: You must not relinquish ownership of an object you do not own. That is you should not call autorelease(), release() or dealloc() on any of the returned objects.
What you should do is to create an NSAutoreleasePool inside the function, and don't touch anything else. The pool will free all those objects when it is released.
pub fn enumerate_apps()-> Vec<Rc<AppInfo>> {
let mut apps_list = Vec::new();
unsafe {
let autoreleasePool: *mut Object = msg_send![class("NSAutoreleasePool"), new];
// ...
// all code unchanged
// ...
msg_send![autoreleasePool, release];
}
apps_list
}
Why calling autorelease()/release()/dealloc() on bundle_url/app_bundle/info_dict cannot decrease memory? Because it is not just these objects leaking memory. The biggest consumption is the running_apps object.
And why explicitly calling autorelease()/release()/dealloc() is wrong? Let's review the ObjC memory management rules, and compare it with normal Rust code (I assume you know how the Rc<T> type works):
You own any object you create — You create an object using a method whose name begins with “alloc”, “new”, “copy”, or “mutableCopy”
You could think of it like:
// Objective-C code:
NSMutableString* s = [NSMutableString new];
NSMutableString* t = [s mutableCopy];
// Similar to this in Rust:
let s: Rc<NSMutableString> = Rc::new(NSMutableString::new());
let t: Rc<NSMutableString> = Rc::new(s.mutableCopy());
Your code never called any method begins with "alloc", "new", "copy" or "mutableCopy", so you don't own any of them. This naming convention is followed by all ObjC APIs.
You can take ownership of an object using retain.
This is similar to having an object a: Rc<T>, and then you get a new reference by calling b = Rc::clone(&a). Now b also "owns" the original object through reference-counting:
// Objective-C code:
NSMutableString* u = [t retain];
// Similar to this in Rust:
let u: Rc<NSMutableString> = Rc::clone(&u);
But you never called retain, so still you don't own any objects.
When you no longer need it, you must relinquish ownership of an object you own — You relinquish ownership of an object by sending it a release message or an autorelease message.
In terms of Rust, sending a -release message is equivalent to dropping the Rc object.
// Objective-C code:
[u release];
// Similar to this in Rust:
drop(u);
-autorelease transfers the ownership to the autorelease pool. The most recently allocated NSAutoreleasePool will be found, the ownership of the object be moved into that pool, and we only keep a borrowed reference(*).
// Objective-C code:
NSMutableString* v = [t autorelease];
// Similar to this in Rust:
let pool: &NSAutoreleasePool = find_top_autorelease_pool()?;
let v: &NSMutableString = pool.add_object(t);
// `t` is passed-by-value, so `pool` now owns `t`.
// `pool` returns a borrowed reference,
// so that we can still access the memory pointed to by `t`,
// but we no longer own it.
You must not relinquish ownership of an object you do not own.
That is you can never drop the memory through a borrowed reference. Well in Rust it is impossible but Objective-C doesn't have a borrow checker.
Additionally, calling -dealloc is like explicitly invoking the destructor via drop(*s) in Rust. This bypasses the reference-counting mechanism and is explicitly discouraged.
Let's recap:
None of the methods you called (sharedWorkspace/runningApplications/objectAtIndex:/bundleURL/bundleWithURL:/infoDictionary) begins with alloc/new/copy/mutableCopy.
You never called -retain.
That means everything you've got are borrowed, by rules 1 and 2.
That means you should never call release() or autorelease(), by rule 4.
Calling -release or -autorelease on objects you don't own result in double-free. This may cause SEGFAULT, or no-op, or any undefined behavior.
Why the program leaks like a sieve if we don't provide an NSAutoreleasePool? The runningApplications/bundleWithURL: methods do allocate objects, but adhering to the Cocoa memory management rule, they call -autorelease internally to ensure you don't get owned objects. But if we don't allocate any pools, -autorelease can transfer the ownership to nowhere, i.e. those auto-released objects become unowned by anyone, and nobody has the ownership to free them, and thus leaked.
(*): The analogy is not perfect, because you could get a new ownership with [[x autorelease] retain]. But this detail doesn't matter here.
When ARC enabled, following code causes the obj retained twice for the block by calling objc_retain() and objc_retainBlock().(So retainCount becomes 3 after the block definition.)
I checked that obj is not in autorelease pool and there are two objc_release() calls for obj at the end of the method. (I know counting retains does not make sense but i am checking the objc_retain() calls, not barely checking retain counts.)
What is the rationale behind this?
-(void)myMethod
{
NSObject *obj = [[NSObject alloc] init];
NSLog(#"obj %# retaincount %ld", obj, CFGetRetainCount((__bridge CFTypeRef)obj));
void (^myBlock)(void) = ^()
{
NSLog(#"obj %# retaincount %ld", obj, CFGetRetainCount((__bridge CFTypeRef)obj));
};
NSLog(#"obj %# retaincount %ld", obj, CFGetRetainCount((__bridge CFTypeRef)obj));
myBlock();
}
Isn't it enough to retain the obj just once for capturing it by the block?
Note: Also, when i remove the myBlock variable and the call for it, so just with ^() {...} definition and that definition could never be called, obj is still retained with objc_retain() which seems weird. I am using XCode 4.3.2.
www.whentouseretaincount.com -- i.e. don't use retainCount as it'll cause nothing but confusion. Even for learning the innards.
If you want to know how and when blocks retain objects, you should look at the compiler, runtime, and blocks runtime sources. All are available.
A block may or may not retain an object. Specifically, a block may only retain an object when the block is copied (as long as the object reference isn't __weak).
Similarly, but orthogonally, ARC's retain/release behavior changes based on the optimization level of the compiler. In particular, DEBUG (-O0) code tends to have lots of retain/release. -Os (optimized) code will have many of these optimized out.
Please read this: http://whentouseretaincount.com/
You shouldn't use retain count, the apple developers who wrote it advise against it, a lot of smart people advise against it and i'm telling you not to use it.
As for retain cycles its good practice to have a weak property outside of your block scope as such:
__weak typeof(obj) weakObj = obj;
void (^block)(void) = ^{
[weakObj method];
};
Based on Bavarious's answer to this SO question, I am led to believe that #autoreleasepool is now an Objective-C language feature if you build with LLVM/clang.
This being the case, how does one rewrite the following code to use an #autoreleasepool instead of an NSAutoreleasePool in a non-ARC environment?
[NSAutoreleasePool addObject:anObject]; // (*)
Background: I essentially want to write a custom implementation of -autorelease that does not in any way interact with the NSAutoreleasePool class:
#autoreleasepool {
SomeCls *obj = [[SomeCls alloc] init];
[obj autorelease]; // Does not go through an NSAutoreleasePool object
// ...
}
#autoreleasepool { } is a new language feature meant to obviate the need to drain the current pool at every exit point of a function. Now, instead of having to write:
void f(void) {
//Make a new pool
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
//Every inner scope that exits prematurely requires a drain
if (life_is_good) {
for (int i = 0; i < 1000; i++) {
NSObject *obj = [[[NSObject alloc]init]autorelease];
//breaks out of the loop, and the function, so drain the pool
if (life_is_bad) {
[pool drain];
return;
}
}
//Life gets bad below here, so return
[pool drain];
return;
}
//end of function requires drain.
[pool drain];
}
You can wrap the entire function in an #autoreleasepool { } directive and the compiler will insert the proper drains where the function returns:
void f(void) {
//Make a new pool
#autoreleasepool {
if (life_is_good) {
for (int i = 0; i < 1000; i++) {
NSObject *obj = [[[NSObject alloc]init]autorelease];
//breaks out of the loop, and the function, so drain the pool
if (life_is_bad) {
//[pool drain]; auto-drain
return;
}
}
//[pool drain]; auto-drain
return;
}
//[pool drain]; auto-drain
}
}
I have no idea why you would want to forgo the automatic pool mechanism, as it handles a huge amount of work for you (page management, per-thread pool management, releasing objects in a timely yet efficient manner), but it shouldn't be that much of a hassle to get a primitive NSAutoreleasePool up and running. Any autoreleasepool would require a page manager (malloc() and realloc()), pointer storage (a simple array), and some way to detect whether or not the current scope in which it is being used has exited (-drain). A simple pool could just be a thread-safe singleton, but be wary of how many objects you try to insert and remove at one time with that implementation. You just might segfault if you try to allocate too many hot pages for too many objects.
Based on Bavarious's answer to this SO question, I am led to believe that #autoreleasepool is now an Objective-C language feature if you build with LLVM/clang.
Correct.
This being the case, how does one rewrite the following code to use an #autoreleasepool instead of an NSAutoreleasePool in a non-ARC environment?
[NSAutoreleasePool addObject:anObject]; // (*)
Doc: Normally you don’t invoke this method directly—you send autorelease to object instead.
#autoreleasepool {
id anObject = ...;
[anObject autorelease];
}
Background: I essentially want to write a custom implementation of -autorelease that does not in any way interact with the NSAutoreleasePool class:
You can't even subclass NSAutoreleasePool any longer (in a meaningful way). Unless you never pass the object to other APIs which are free to autorelease, your objects are going to end up in the current autorelease pool. The mechanics of this today is not even object based.
If this is impossible, I would be open to workaround suggestions not involving NSAutoreleasePool.
Not certain what problem you are trying to solve, but you can extend lifetimes using:
NSMutableArray * fauxAutoreleasePool = NSMutableArray.new;
id anObject = ...;
[fauxAutoreleasePool addObject:anObject];
...
[fauxAutoreleasePool removeAllObjects]; // << explicitly drain, which also happens when the array is destroyed
Of course, you could write your own implementation. Why you would write your own is still a mystery, and not a good idea for production code (#autoreleasepool works well).
Though I have already marked an answer as accepted, and offered a bounty at the time, I have since found the true answer I was searching for and realised my question wasn't very clear - primarily because I didn't know what to ask.
How to interact with #autoreleasepool:
When the compiler comes across an #autoreleasepool { } block, it automatically inserts two function calls. At the opening of the block it inserts the C function call:
void *_objc_autoreleasePoolPush(void);
At the close of the #autoreleasepool block (including when a return or break is encountered within - but NOT when an exception is thrown according to the LLVM docs) the compiler inserts the C function call:
_objc_autoreleasePoolPop(void *ctxt); // ctxt is Apple's label for the param
I believe the void *ctxt parameter is an indicator of where the current #autoreleasepool started (really only useful in Apple's implementation - see here). In any case I found it can easily be ignored in custom implementations.
The -autorelease selector:
Essentially the methodology is this:
Create (if outermost block) or mark (if inner block) an autorelease pool object (in C or C++) in _objc_autoreleasePoolPush(). It seems easiest to me to use a Stack data structure for this but YMMV.
Ensure this object is in scope within the -autorelease selector, or you have a fail-safe way of accessing it.
Add the object to the autorelease pool object (in whatever way that may be) inside -autorelease.
In _objc_autoreleasePoolPop(void *), send the -release message to every item in the autorelease pool object until the marker set in _objc_autoreleasePoolPush() is reached (or you reach the bottom of the pool). Then do any additional required cleanup.
Final notes on threading:
Objective-C #autoreleasepools are supposed to be thread-local. That is, each thread has its own (if it requires one). As a result, _objc_autoreleasePoolPush() must have some way of determining whether the current thread already has an active autorelease pool and create the object if it does not.
Similarly, this is why, when creating a thread, the thread must first open an #autoreleasepool { } block before it does anything else (in Objective-C), and the final thing it must do is close that #autoreleasepool block (implicitly through break or return or with explicit fall-through).
Three final observations:
If you wish to implement your own autorelease pool object, you will need some form of thread-local storage to store it.
That last point about threading is why every Objective-C program must begin with an #autoreleasepool { } block in main().
This is why Apple ask you to use the NSThread class and GCD rather than DIY threading - they take care of all of this for you!
At the end of the #autoreleasepool block, the autoreleased objects are sent a release message.
If you want to reimplement the autorelease method, you may not want to directly send release messages to your NSObjectsubclass. If so, reimplementing release with some special behavior could do your business.
Background: I essentially want to write a custom implementation of
-autorelease that does not in any way interact with the NSAutoreleasePool class:
What makes you think that -autorelease interacts with NSAutoreleasePool?
As long as you are running on iOS 5+ or Mac OS X 10.7+, both the #autoreleasepool and -[NSObject autorelease] will use the runtime autorelease pool management functions, and have nothing to do with NSAutoreleasePool at all (and NSAutoreleasePool on those OS versions is simply a wrapper around the runtime functions).
I am trying to get the hang of retain / release. I get that they are a matched set. But I don't know when I have to retain references.
-(void)sampleMethod:(RandomClass *) obj {
[obj retain];
// Do stuff to object...
[obj release];
}
Is it necessary to retain (and thus release) obj?
I am worried about obj going away. Does it follow that you must (if) retain reference parameters as soon as possible in the function? What about the space of time between the functions call and the first instruction of the function?
Thanks!
Short answer; use ARC.
Joe's answer is more or less correct. Until it isn't.
In general, there is no need to retain arguments or return values from other methods. However, the resulting code only works by coincidence and convention, not by algorithmic analysis.
Consider:
NSString *foo = [aMutableArray objectAtIndex: 5];
[aMutableArray removeObjectAtindex: 5];
[someTextField setTextValue: foo];
BOOM!
Your code just crashed. Maybe (it won't crash if foo happens to be a constant string or happens to have been retained by something else or happens to have been retain/autoreleased somewhere else).
Technically, that should be:
NSString *foo = [aMutableArray objectAtIndex: 5];
[foo retain];
[aMutableArray removeObjectAtindex: 5];
[someTextField setTextValue: foo];
[foo release];
That is, foo should be retained the moment it comes into scope and released the moment it is no longer used in a scope. Or you could [[foo retain] autorelease];, but autorelease pressure can be a problem (it generally isn't, but it can be).
ARC does this kind of analysis and would ensure that foo is retained as shown above when necessary.
You do not have to worry about the object being passed going away so there is no need to retain it. Proper memory management* ensures that the object will live for the duration of your method because it will be within the same thread as the caller, therefore the autorelease pool for that thread should not be drained and the caller can not release the object until your method has returned. This even holds true with methods such as performSelectorInBackground because that will retain the argument.
*Proper memory management - This means each thread that uses auto released objects gets it own autorelease pool that is drained within the same context it is created and objects passed across threads are properly retained.
after having spent few months in trying to master the syntax and rules, I am going deeper in memory management rules.
One thing I do not understand and causing me confusion is how one creates objects.
Based on what stated in apple memory management guide, the following is a valid approach:
– (NSArray *)sprockets {
NSArray *array = [NSArray arrayWithObjects:mainSprocket,auxiliarySprocket, nil];
return array;
}
because I am not causing any memory leaks.
The reason why is that it's not using alloc for creating array and therefore sprockets is not the owner.
However I am wondering now what's inside arrayWithObjects.
Because it happens that in my apps I often have factory for creating custom objects using something similiar to:
return [[MyObject alloc] initWithParameter:(id)params]; // possible leak
If I want to change with a static method like:
return [MyObject initWithParameter:(id)params];
what could be in initWithParameter for adhere to memory rules ? And what if MyObject extends some other object ? I also find out that method naming rules are important to properly advise programmer, what are this rule ?
Also could you point out a web link where this is explained (I am not yet good in finding docs on apple web site).
thanks
This is the page you're looking for: Memory Management Rules. It all comes down to adhering to the rules of ownership for an object.
If you create an object using alloc then you own that object and must release it. For example:
NSString* str = [[NSString alloc] init];
//do something with your str object
[str release];
str = nil; //don't forget to set it to nil, it's still
//pointing to the (now unallocated) block of memory
If you create an object using a factory method, for example:
NSString* str = [NSString stringWithString:#"blah"];
Then what is happening here is that it is creating the string object using alloc for you and then returning the object but first adding it to the local autorelease pool. To use the example in your question.
return [[MyObject alloc] initWithParameter:(id)params]; // possible leak
return [[[MyObject alloc] initWithParameter:(id)params] autorelease]; //now autoreleased -- no leak
Here is the page on Autorelease Pools. Apple say it better than me.
An autorelease pool is an instance of NSAutoreleasePool that “contains” other objects that have received an autorelease message; when the autorelease pool is deallocated it sends a release message to each of those objects. An object can be put into an autorelease pool several times, and receives a release message for each time it was put into the pool. Thus, sending autorelease instead of release to an object extends the lifetime of that object at least until the pool itself is released (the object may survive longer if it is retained in the interim).
Method naming rules are important as it gives a strong indication as to whether the method returns an autoreleased object. In this example I would name the method:
[MyObject myObjectWithParameter:(id)param]
Hope this has helped.
It is standard practice for alloc and creation methods to return a retained object. Anything that starts with init, create, or the object name will clue other programmers into the purpose of the method.
Even if you create with alloc, if the purpose of your method is to pass on ownership, you should not release the object.