Next article: Friday Q&A 2011-10-14: What's New in GCD
Previous article: Friday Q&A 2011-09-16: Let's Build Reference Counting
Tags: arc cocoa fridayqna memory objectivec
Since the moment Apple announced it, readers have asked me to write about Automatic Reference Counting, or ARC. Today is the day. I'll talk about Apple's new memory management system, how it works, and how to get the most out of it.
Conceptual
The Clang static analyzer is a really useful tool for finding memory management errors in code. If you're like me, you've looked at the output of the analyzer and thought, "If you can spot the error, why can't you just fix it for me too?"
That, in essence, is what ARC is. The memory management rules are baked into the compiler, but instead of using them to help the programmer find mistakes, it simply inserts the necessary calls on its own.
ARC occupies a middle ground between garbage collection and manual memory management. Like garbage collection, ARC frees the programmer from writing retain
/release
/autorelease
calls. Unlike garbage collection, however, ARC does not deal with retain cycles. Two objects with strong references to each other will never be collected under ARC, even if nothing else refers to them. Because of this, while ARC frees the programmer from dealing with most memory management issues, the programmer still has to avoid or manually break cycles of strong references in the object graph.
When it comes to implementation specifics, there's another key difference between ARC and Apple's implementation of garbage collection: ARC is not an either/or proposition. With Apple's garbage collector, either the entire application runs under GC, or none of it does. This means that all Objective-C code in an application, including all of Apple's frameworks and all third-party libraries you might include, must be GC compatible for you to take advantage of GC. In contrast, ARC coexists peacefully with non-ARC manual memory managed code in the same application. This makes it possible to convert projects piecemeal without the massive compatibility and reliability problems that garbage collection ran into when it was first introduced.
Xcode
ARC is available in Xcode 4.2, currently in beta, and only when compiling with Clang (a.k.a. "Apple LLVM compiler"). The setting is called, obviously enough, "Objective-C Automatic Reference Counting". Turn it on, and off you go.
If you're working on existing code, changing this setting will produce an enormous quantity of errors. ARC not only manages memory for you, but it forbids you from trying to do it yourself. It's illegal to manually send retain
/release
/autorelease
when using ARC. Since normal non-ARC Cocoa code is littered with this stuff, you'll get a lot of errors.
Fortunately, Xcode offers a tool to convert existing code. Select Edit -> Refactor... -> Convert to Objective-C ARC... and Xcode will guide you through converting your code. Although there may be some situations where it needs help figuring out what to do, the process should be largely automatic.
Basic Functionality
Cocoa memory management rules are fairly simple. In short:
- If you
alloc
,new
,copy
, orretain
an object, you must balance that withrelease
orautorelease
. - If you obtain an object from something other than the above, and you need it to stay alive long-term, you must
retain
orcopy
it. This must, of course, be balanced later.
These are highly suitable for automation. If you write this:
Foo *foo = [[Foo alloc] init];
[foo something];
return;
The compiler can see the unbalanced alloc
. The code is thus transformed:
Foo *foo = [[Foo alloc] init];
[foo something];
[foo release];
return;
In reality, the compiler does not insert a message send to release
. Instead, it inserts a call to a special runtime function:
Foo *foo = [[Foo alloc] init];
[foo something];
objc_release(foo);
return;
This allows for some optimization. In the common case where -release
is not overridden, the objc_release
function can bypass Objective-C messaging, resulting in a bit of a speed gain.
This automation can make code safer. Most Cocoa programmers interpret "long-term" in rule #2 to mean objects stored in instance variables and similar places. We don't normally retain and release local temporary objects:
Foo *foo = [self foo];
[foo bar];
[foo baz];
[foo quux];
However, this can be dangerous:
Foo *foo = [self foo];
[foo bar];
[foo baz];
[self setFoo: newFoo];
[foo quux]; // crash
The standard way to work around this is to have the -foo
getter do a retain
/autorelease
before returning the value. This works, but can build up a lot of temporary objects leading to excessive memory usage. ARC, however, will be paranoid and insert extra calls here:
Foo *foo = objc_retainAutoreleasedReturnValue([self foo]);
[foo bar];
[foo baz];
[self setFoo: newFoo];
[foo quux]; // fine
objc_release(foo);
Likewise, even if you write a plain getter, ARC will make it safe as well:
- (Foo *)foo
{
return objc_retainAutoreleaseReturnValue(_foo);
}
But wait, this doesn't solve the problem of excessive temporary objects at all! We're still doing a retain
/autorelease
sequence in the getter, and a retain
/release
combination in the calling code. This is considerably less efficient!
Not to worry. As I mentioned above, ARC emits these special calls instead of plain message sends for the purposes of optimization. In addition to simply making retain
and release
faster, these calls are able to eliminate certain operations altogether.
When objc_retainAutoreleaseReturnValue
runs, it looks on the stack and grabs the return address from its caller. This allows it to see exactly what will happen after it finishes. When compiler optimizations are turned on, the call to objc_retainAutoreleaseReturnValue
will be subject to tail-call optimization, and the return address will point to the call to objc_retainAutoreleasedReturnValue
.
With this crazy return-address examination, the runtime is able to see that it's about to perform some redundant work. It therefore eliminates the autorelease
, and sets a flag that tells the caller to eliminate its retain
. The whole sequence ends up doing a single retain
in the getter and a single release
in the calling code, which is both completely safe and efficient.
Note that this optimization is fully compatible with non-ARC code. In the event that the getter doesn't use ARC, the flag won't be set and the caller will perform a full retain
/release
combination. In the event that the getter uses ARC but the caller does not, the getter will see that it's not returning to code that immediately calls the special runtime function, and will perform a full retain
/autorelease
combination. Some efficiency is lost, but correctness is preserved.
In addition to all of this, ARC also automatically creates or fills out a -dealloc
method for all classes to release their instance variables. It's still possible to manually implement -dealloc
, and it's necessary for classes which manage external resources, but it's no longer necessary (or possible) to manually release instance variables. ARC will even put the [super dealloc]
at the end for you, so you don't have to. Previously, you might have written this:
- (void)dealloc
{
[ivar1 release];
[ivar2 release];
free(buffer);
[super dealloc];
}
Now you can just write this:
- (void)dealloc
{
free(buffer);
}
In the event that your -dealloc
method just releases instance variables, it can simply be eliminated altogether.
Cycles and Weak References
ARC still requires the programmer to manually resolve reference cycles, and the best way to resolve reference cycles is typically to use weak references.
ARC provides zeroing weak references. These are weak references which not only don't keep the referenced object alive, but which also automatically become nil
when the referenced object is destroyed. Zeroing weak references avoid the potential for dangling pointers and the associated crashes and mysterious behavior.
To make a zeroing weak variable, simply prefix its declaration with __weak
. For example, here is a weak instance variable:
@interface Foo : NSObject
{
__weak Bar *_weakBar;
}
Likewise for local variables:
__weak Foo *_weakFoo = [object foo];
You can then use it like any other variable, with the value automatically becoming nil
when appropriate:
[_weakBar doSomethingIfStillAlive];
Note, however, that a __weak
variable can become nil
at almost any time. Memory management is an inherently multithreaded activity, and a weakly referenced object could be destroyed on one thread while another thread is accessing it. Code like this is therefore not valid:
if(_weakBar)
[self mustNotBeNil: _weakBar];
Instead, store the object into a local strong reference and test that:
Bar *bar = _weakBar;
if(bar)
[self mustNotBeNil: bar];
Because bar
is a strong reference here, the object is guaranteed to stay alive (and the variable non-nil
) throughout this code.
ARC's implementation of zeroing weak references requires close coordination between the Objective-C reference counting system and the zeroing weak reference system. This means that any class which overrides retain
and release
can't be the target of a zeroing weak reference. While this is uncommon, some Cocoa classes, like NSWindow
, suffer from this limitation. Fortunately, if you hit one of these cases, you will know it immediately, as your program will crash with a message like this:
objc[2478]: cannot form weak reference to instance (0x10360f000) of class NSWindow
If you really must make a weak reference to classes such as these, you can use the __unsafe_unretained
qualifier in place of __weak
. This creates a weak reference which is not zeroing. You must ensure that you never use such a pointer (preferably by zeroing it out manually) after the object it points to has been destroyed. Be careful, as non-zeroing weak references are playing with fire.
While it's possible to build programs using ARC that run on Mac OS X 10.6 and iOS 4, zeroing weak references are not available on those OSes. All weak references must be __unsafe_unretained
here. Because non-zeroing weak references are so dangerous, this limitation significantly decreases the attractiveness of ARC on those OSes in my view.
Properties
Since properties are so tightly coupled to memory management, it makes sense that ARC would introduce some new behaviors there.
ARC introduces a few new ownership modifiers. Declaring a property as strong
makes that property a strong reference. Declaring it as weak
uses a zeroing weak reference. The unsafe_unretained
modifier uses a non-zeroing weak reference. When @synthesize
is used, the compiler creates an instance variable of the same storage type.
The existing modifiers of assign
, copy
, and retain
still exist and work the same way they did before. Notably, assign
creates a non-zeroing weak reference, so it should be avoided whenever possible.
Aside from the new modifiers, properties work just as they always have.
Blocks
Blocks are Objective-C objects, and as such are also managed by ARC. Blocks have special memory management requirements, and ARC treats them accordingly. Block literals must be copied, not retained, which in practice means that it's best to copy rather than retain blocks everywhere. ARC follows this practice.
Additionally, ARC knows that block literals must be copied if they're used after the current scope returns. Non-ARC code needs to explicitly copy and autorelease returned blocks:
return [[^{
DoSomethingMagical();
} copy] autorelease];
With ARC, this simply becomes:
return ^{ DoSomethingMagical(); };
However, beware! ARC currently does not automatically copy a block literal that's converted to an id
. So while this code is fine:
dispatch_block_t function(void)
{
return ^{ DoSomethingMagical(); };
}
This code is not:
id function(void)
{
return ^{ DoSomethingMagical(); };
}
It's easy to work around by simply copying the block, but it's something to be careful with:
return [^{ DoSomethingMagical(); } copy];
Likewise, you need to explicitly copy blocks that you pass as id
parameters:
[myArray addObject: [^{ DoSomethingMagical(); } copy]];
Fortunately, it appears that this is just an edge case that fell through the cracks and is likely to be fixed soon. There's no problem with throwing in an extra manual copy if you're unsure.
Another significant change with ARC is the behavior of __block
qualified variables. The __block
qualifier allows a block to modify a captured variable:
id x;
__block id y;
void (^block)(void) = ^{
x = [NSString string]; // error
y = [NSString string]; // works
};
Without ARC, __block
also has the side effect of not retaining its contents when it's captured by a block. Blocks will automatically retain and release any object pointers they capture, but __block
pointers are special-cased and act as a weak pointer. It's become a common pattern to rely on this behavior by using __block
to avoid retain cycles.
Under ARC, __block
now retains its contents just like other captured object pointers. Code that uses __block
to avoid retain cycles won't work anymore. Instead, use __weak
as described above.
Toll-Free Bridging
ARC only works on Objective-C types. CoreFoundation types still have to be managed manually by the programmer. Because there is ambiguity about ownership, ARC forbids standard casts between pointers to Objective-C objects and pointers of other types, including pointers to CoreFoundation objects. The following code, which is fairly typical under manual memory management, does not compile with ARC:
id obj = (id)CFDictionaryGetValue(cfDict, key);
In order to make this compile again, you must tell ARC about the ownership semantics involved by using special casting annotations. These annotations are __bridge
, __bridge_retained
, and __bridge_transfer
.
The simplest one to understand is __bridge
. This is a direct conversion with no ownership consequences. ARC receives the value and then manages it normally. This is what we want for the above:
id obj = (__bridge id)CFDictionaryGetValue(cfDict, key);
The other casting annotations transfer ownership to and from the ARC system. These can help ease the pain when bridging back and forth.
Here's an example of using bridging in a situation where the returned object needs to be released.
NSString *value = (NSString *)CFPreferencesCopyAppValue(CFSTR("someKey"), CFSTR("com.company.someapp"));
[self useValue: value];
[value release];
If we move this to ARC by using __bridge
, removing the release
, and otherwise not making any changes, we end up with a leak:
NSString *value = (__bridge NSString *)CFPreferencesCopyAppValue(CFSTR("someKey"), CFSTR("com.company.someapp"));
[self useValue: value];
The Copy
in this code needs to be balanced with a release. ARC will emit a retain
when initializing value
, then balance that with a release
when value
is no longer used. Since nothing balances the original Copy
, that object is leaked.
We can work around this with a little extra code:
CFStringRef valueCF = CFPreferencesCopyAppValue(CFSTR("someKey"), CFSTR("com.company.someapp"));
NSString *value = (__bridge NSString *)valueCF;
CFRelease(valueCF);
[self useValue: value];
This is getting fairly verbose, though. Since the whole point of toll-free bridging is to be as painless as possible, and the whole point of ARC is to remove the need to write memory management code, it would be nice if this could be made more straightforward.
The __bridge_transfer
annotation solves this problem. Rather than simply move a pointer value into ARC, it moves the value and transfers ownership. When __bridge_transfer
is used in a cast, it tells ARC that this object is already retained, and that ARC doesn't need to retain it again. Since ARC takes ownership, it will still release it when it's done. The net result is that everything works the way it's supposed to:
NSString *value = (__bridge_transfer NSString *)CFPreferencesCopyAppValue(CFSTR("someKey"), CFSTR("com.company.someapp"));
[self useValue: value];
Toll-free bridging works both ways. As before, ARC doesn't allow a standard cast to convert from an Objective-C object pointer to a CoreFoundation object pointer. This code won't compile under ARC:
CFStringRef value = (CFStringRef)[self someString];
UseCFStringValue(value);
Adding a __bridge
to the cast makes it compile, but the resulting code is dangerous:
CFStringRef value = (__bridge CFStringRef)[self someString];
UseCFStringValue(value);
Since ARC doesn't manage the lifetime of value
, it will release ownership of the object immediately, before it gets passed to UseCFStringValue
, potentially causing a crash or other misbehavior. By using __bridge_retained
, we can tell ARC to transfer ownership out of the system and into our hands. Since ownership is transferred, we're now responsible for releasing the object when done with it, just like with any other CF code:
CFStringRef value = (__bridge_retained CFStringRef)[self someString];
UseCFStringValue(value);
CFRelease(value);
These cast annotations are useful outside of toll-free bridging as well. Any time you need to store an object pointer in storage that's not managed as an Objective-C object, they smooth the way. There are void *
context pointers found in various places in Cocoa, and a prominent example is sheets. Without ARC:
NSDictionary *contextDict = [NSDictionary dictionary...];
[NSApp beginSheet: sheetWindow
modalForWindow: mainWindow
modalDelegate: self
didEndSelector: @selector(sheetDidEnd:returnCode:contextInfo:)
contextInfo: [contextDict retain]];
- (void)sheetDidEnd: (NSWindow *)sheet returnCode: (NSInteger)code contextInfo: (void *)contextInfo
{
NSDictionary *contextDict = [(id)contextInfo autorelease];
if(code == NSRunStoppedResponse)
...
}
As before, this fails under ARC, because normal casts between object and non-object pointers are not allowed. However, using the cast modifiers, we can not only get ARC to allow it, but also get ARC to do the requisite memory management for us:
NSDictionary *contextDict = [NSDictionary dictionary...];
[NSApp beginSheet: sheetWindow
modalForWindow: mainWindow
modalDelegate: self
didEndSelector: @selector(sheetDidEnd:returnCode:contextInfo:)
contextInfo: (__bridge_retained void *)contextDict];
- (void)sheetDidEnd: (NSWindow *)sheet returnCode: (NSInteger)code contextInfo: (void *)contextInfo
{
NSDictionary *contextDict = (__bridge_transfer NSDictionary *)contextInfo;
if(code == NSRunStoppedResponse)
...
}
To summarize:
__bridge
simply transfers a pointer between ARC and non-ARC with no transfer of ownership.__bridge_transfer
moves a non-Objective-C pointer to Objective-C and also transfers ownership, such that ARC will release the value for you.__bridge_retained
moves an Objective-C pointer to a non-Objective-C pointer and also transfers ownership, such that you, the programmer, are responsible for later callingCFRelease
or otherwise releasing ownership of the object.
Structs
Under ARC, structs and Objective-C object pointers pretty much don't mix. The problem is that there is no good way for the compiler to know when a particular struct is destroyed or copied, and thus no good place for the compiler to insert the necessary retain
and release
calls. Because it's such a difficult problem, and because putting object pointers in structs is so unusual anyway, ARC just gives up on the whole proposition. If you want to put an Objective-C object pointer in a struct, you must qualify it with __unsafe_unretained
, and deal with all of the problems and danger that this implies.
Because it's so uncommon to put Objective-C pointers into structs, it's likely that this won't be a problem for your code. If it is, your best bet is to change the struct into a lightweight Objective-C class instead. ARC will start managing memory for you and the problem goes away.
Further Reading
While Apple's official documentation on ARC is still under wraps while Xcode 4.2 is in beta, a great deal of information about the system is available on the Clang site here: http://clang.llvm.org/docs/AutomaticReferenceCounting.html
Conclusion
Automatic reference counting substantially reduces the burden on the programmer for dealing with memory management. ARC is not a full garbage collector. It cannot detect retain cycles, and these must be dealt with and broken by the programmer. It still takes a lot of the grunt work out of writing Cocoa code, and the zeroing weak references that it provides are a powerful tool for dealing with cycles.
Things get trickier when it comes to CoreFoundation objects and toll-free bridging. ARC limits itself to dealing with Objective-C, so the programmer still needs to manage the CoreFoundation side manually. When converting between Objective-C and CoreFoundation pointers, the special __bridge
cast modifiers need to be used to inform ARC about the memory management semantics of the conversion.
That wraps up today's exploration of Apple's latest programming language technology. Come back in two weeks for another fine word salad. Until then, keep watching the skies and sending in your suggestions for topics.
Comments:
Also, how would you override weak/strong synthesized properties with ARC?
I used to write @property(assign) BOOL flag; but now it sounds like assign should be avoided. But "unsafe-unretained' doesn't sound like it would be problematic with, say, an NSInteger ivar. Do I understand this correctly?
However, the language around ARC makes it sound like Apple is planning to phase out GC in favor of ARC, so even though GC has advantages, these are probably outweighed by the fact that ARC code will have better longevity and portability.
Marc: Copy properties are fine, and are implicitly strong. Retain properties are also strong, but it's preferred that you explicitly use
strong
instead. For overriding, all you need to do is make sure that the backing storage has the correct semantics. If you write your own accessors for a strong property, a normal ivar will do. For a weak property, a __weak
ivar will do it.
Iain: ARC only affects object pointers, so for primitives you can simply carry on as usual.
CFBridgingRetain()
CFBridgingRelease()
In the places where you meant
objc_autoreleaseReturnValue
you wrote objc_retainAutoreleaseReturnValue
.
Also, Dave++. If you use CFBridgingRetain and CFBridgingRelease, then the CF ref count looks balanced. For example,
NSString *str = CFBridgingRelease(CFStringCreate(...));
I find the semantics much easier to keep track of, and it's efficient. CFBridgingRelease is basically transfer one ref count from CF to objc, and the stack's strong, so in effect that is "don't generate any code, but make stuff look balanced".
ken: I'm pretty sure I want objc_retainAutoreleaseReturnValue, since an ivar accessor needs to do a retain/autorelease combo.
Regarding the efficiency of the bridging functions, I thought they were just doing the same thing as the casts, so they should be the same, efficiency-wise.
So, what's your take on ARC: a good thing, a meh thing, 'good riddance GC', none of the above?... :-)
Taken entirely on its own, ARC is great. It gets rid of a ton of grunt work at a minimum of overhead and complexity. It's a perfect case where the compiler can step in and take a bunch of boring stuff away from the programmer.
Taken in the larger context, where all of this work on ARC fairly heavily implies that Apple has given up on garbage collection, I'm sad. The ObjC GC has a lot of problems, but I was hopeful that it could be made better with time.
Also, in the nice graphical way:
http://dl.dropbox.com/u/27714647/screen.png
You can read about it (and other things about ARC) in the presentation here
http://dl.dropbox.com/u/27714647/ITJam2011%20ARC.key
Foo *foo = objc_retainAutoreleasedReturnValue([self foo]);
this one should be
objc_autoreleaseReturnValue
.
- (Foo *)foo
{
return objc_retainAutoreleaseReturnValue(_foo);
}
They have to be paired.
objc_autoreleaseReturnValue
just autoreleases the value passed to it, as you would expect. This is incorrect to do to an ivar returned from a basic accessor, and will lead to an over-release and likely subsequent crash.
objc_retainAutoreleaseReturnValue
does a retain and an autorelease, which is precisely what is needed here.
objc_retainAutoreleasedReturnValue
can be paired with either of the above. Which one of the above is needed depends entirely on the semantics of the called method. If the called method allocs (or retains or copies) something and then needs to return it to the caller, it will use objc_autoreleaseReturnValue
. If the called method is returning a value which it has not alloced (or retained or copied), like an ivar, then it will use objc_retainAutoreleaseReturnValue
to do the retain/autorelease combination necessary for that case.-[TestClass foo]:
0000000100000dd0 pushq %rbp
0000000100000dd1 movq %rsp,%rbp
0000000100000dd4 movq 0x0000040d(%rip),%rax
0000000100000ddb movq (%rdi,%rax),%rdi
0000000100000ddf popq %rbp
0000000100000de0 jmp 0x100000e7a ; symbol stub for: _objc_retainAutoreleaseReturnValue
0000000100000de5 nopl %cs:0x00000000(%rax,%rax)
I'm rather sad that the GC seems to be dying, I wonder if that means macruby's future suddenly turned dark :( Would so love to use that on iOS...
Grzegorz Adam Hankiewicz: MAZeroingWeakRef can be used fine in ARC projects as long as those particular files are compiled as non-ARC. MAZWR is useful if you're using ARC but targeting 10.6/iOS4 where official ZWRs aren't available. If you're targeting 10.7/iOS5, then MAZWR doesn't offer any real advantage over the built-in stuff, and in fact it will use the built-in stuff when available.
(The one potential advantage of MAZWR is that it can work with classes like NSWindow which don't work with the ARC version. Right now it doesn't, but in theory it could fall back to the dynamic subclass craziness for those classes. I'm not sure if this is worthwhile to build.)
I think I have some unlearning to do though, since this sentence:
made my spine crawl just a little bit :-p
scott
[block copy]
where it's not necessary, when using ARC.
Bar *bar = _weakBar;
if(bar)
[self mustNotBeNil: bar];
Because bar is a strong reference here, the object is guaranteed to stay alive (and the variable non-nil) throughout this code.
What exactly do you mean with "the object is guaranteed to stay alive"? I understand that bar will never he zero'ed by ARC, but why can't the referenced object be released in another thread? It's a simple assignement operation, what exactly makes it a strong reference?
__strong
. So, another way to write that would have been:
__strong Bar *bar = _weakBar;
if(bar)
[self mustNotBeNil: bar];
[foo newsFeedWithObject:bar]
will make ARC think you are returning a retained object since it has the 'new' prefix.According to the WWDC presentation, ARC is aware of camel case and will know that -newsFeedWithObject: is not a method returning ownership. I don't have iOS developer status and so can't test this in the current 4.2 beta, however.
One question, though:
How would one silence the warning on -[NSObject performSelector:withObject:] without resorting to objc_msgSend()?
#pragma clang diagnostic push
#pragma clang diagnostic ignored "<warning you want to disable>"
// Code that issues the warning
#pragma clang diagnostic pop
Keep in mind that this is not something you should do to generally kill warnings. Warnings are absolutely there for a reason, and you should take great care before you disable one.
In general, it is likely a good idea to move away from using -performSelector* calls to arbitrary selectors if possible – using blocks or trying to switch to a set of known selectors is generally preferable. Yes, this means that you may want to rethink your target-action pattern based code. At the same time, it is sometimes the correct pattern so don't be afraid to use it if you really think it is correct.
I was posting from the phone — which actually ate my more verbose first draft :-(
@Mike When using performSelector* with a non-constant selector under ARC, you get a compiler warning that this may cause a leak — the exact warning Xcode gives you is:
warning: performSelector may cause a leak because its selector is unknown [-Warc-performSelector-leaks,3]
(void)[target performSelector:selector withObject:self];
^
FILENAME:LINE:COLUMN: note: used here [3]
(void)[target performSelector:selector withObject:self];
^
1 warning generated.
.
(Which I now see how to get rid of, for the general case...)
My particular case was a category on NSTimer — one that holds a weak reference to the actual timer-target through indirection.
So the #pragma solution @David mentioned would definitely be an option — although much uglier than resorting to plain old objc_msgSend().
I hoped using one of the CLANG macros like NS_RETURNS_NOT_RETAINED might be an option, but they seem to only be available on method declaration and not inline, at the calling site.
Therefore, I tried defining and explicitly using the following protocol:
@protocol WeakTimerTarget <NSObject>
- (id)performSelector:(SEL)selector withObject:(id)object NS_RETURNS_NOT_RETAINED;
@end
But that didn't do the trick, either.
Mike, is that your understanding as well?
So yes, you should use the same amount of caution as dealing with both. However, that is most likely not the amount of caution that you're used to.
As you noticed earlier zeroing weak references won't work under iOS 4 and I must use __unsafe_unretained when declaring delegates, right?
Thus this will make working with delegates in before-ARC style? Just without automatic zeroing of weak reference?
However, I keep getting this error: "-fobjc-arc is not supported with fragile abi" on my project. What are the potential causes for this error? My framework uses Oracle's instant client c++ libraries so is objective-c++ as well as 32 bit only as the 64 bit instant client doesn't work on Lion yet. Does arc require 64 bit?
Ray: Yes, I believe ARC requires 64-bit, so you're probably out of luck. Note that the order of object destruction is something you should try to avoid relying upon anyway. Order is not guaranteed with ARC either, unless one of the objects has a strong reference to the other.
If you don't want the awful hackiness of that approach, you'll basically need to code like you would without ARC when it comes to weak references. Use __unsafe_unretained and be really paranoid about manually zeroing out weak references and not messaging stale pointers.
(I'm also sad that Apple seems to be ditching GC. Coding in GC one has less to worry about, though no doubt runtime performance will be somewhat better under ARC.)
As changing the fundamental memory management model of a large codebase is not be be done lightly, I'm initially only interested in making ARC-friendly changes that are still valid in the GC case.
Of the top of my head, my (rare) NSAllocateCollectable usage can change to malloc/free, and still work in both cases.
Do the new strong/weak keywords work in GC?
What does NSMakeCollectable do under ARC? What does CFBridgingRelease do under GC?
Replacing NSAllocateCollectable will work as long as you remember to implement both
dealloc
and finalize
to free the stuff, in cases where it's tied to the lifetime of an object.
I believe weak properties already worked under GC before ARC. The
__weak
qualifier definitely works. I'm not sure if strong works, but if not, retain is equivalent.
NSMakCollectable should work under ARC the same as it does under non-ARC non-GC, which is to say that it doesn't do anything. CFBridgingRelease is an inline function so you can see how it works. Under non-ARC it just does a standard CFMakeCollectable/autorelease combination.
What if I need to avoid retain cycles and allow the block to change a variable – with ARC?
Is the __weak qualifier enough or is it necessary to use __block __weak?
I am trying to update my cancellable NSBlockOperation which looked like this without ARC:
__block NSBlockOperation *b = [NSBlockOperation blockOperationWithBlock:^{
...
If (! [b isCancelled]) ...
}];
1 __strong NSDictionary* myObject = [[NSDictionary alloc] initWithObjectsAndKeys:@"hello 1", @"message", nil];
2 __weak NSDictionary* weakMyObject = myObject;
3
4 NSLog(@"Message before: %@", [weakMyObject objectForKey:@"message"]);
5
6 myObject = [[NSDictionary alloc] initWithObjectsAndKeys:@"hello 2", @"message", nil];
7
8 NSLog(@"Message after: %@", [weakMyObject objectForKey:@"message"]);
In line 6 myObject points to a new NSDictionary, so I suppose that weakMyObject have to be nil in line 8 but it isn't. What is wrong in my head? When weakMyObject became null.
another question: there is a way yo see the generated ARC's code?
The best way to see the generated code is to disassemble your binary. All of the inserted calls to objc_retain and friends will be there.
have you idea when and where the weakMyObject became nil?
Before a test, I've supposed that in line 8 weakMyObject must be nil!
One thing I note, which you haven't mentioned and I was astonished about is how amazingly well ARC works with C++ objects. No, ARC wont manage your C++ objects, but you can use Objective C object pointers in your C++ objects (or structs, although that turns them into objects) and ARC will manage them so that they are released when the object is destructed.
I know you're not font of C++, but this is actually quite amazingly well done, and certainly worth a note near where you talk about C structs.
See http://tinyurl.com/Objective-C-ARC for some details - I've verified it all works as expected.
(The link is http://www.informit.com/articles/article.aspx?p=1745876&;seqNum=3 but I couldn't get the comment system to encode the link correctly after the ampersand)
objc[2478]: cannot form weak reference to instance (0x10360f000) of class MyClass
It seems like any time you try to form a weak reference to an object after you reach the object's destructor, you can trigger this error. No override of retain or release needed, as your FAQ stated.
Explanation:
I have some code that stores weak references as the keys to a mutable dictionary (Think NSMapTable, but pre-iOS 6 so NSMapTable isn't available). When my objects are no longer needed in the dictionary, I want to remove the keys and their associated objects.
I added some cleanup code to -dealloc and discovered something: My cleanup code tries to recreate the wrapped object to remove it from the dictionary, but it blows up with the same error above.
I can fix it if I switch to __unsafe_unretained references, but unsafe pointers drive me crazy. I could also let the references go to zero and then clean them up later by looking for zeros, but this seemed less tidy. I've since solved it but figured somebody else might run into this problem.
In case it's not clear, this is a simplified version of what I've got for my wrapper object:
@interface WeakValue : NSObject
@property (weak, nonatomic) id nonretainedObject;
@end
@implementation WeakValue
@synthesize nonretainedObject;
- (BOOL)isEqual:(WeakValue *)object { return [self.nonretainedObject isEqual:object.nonretainedObject; }
@end
And then a dealloc method that eventually calls a tiny block of code:
WeakValue *key = [[WeakValue alloc] init];
key.nonretainedObject = self; // It blows up at runtime here with "cannot form weak reference to instance" error
[dictionaryWithWeakValueKeys removeObjectForKey:key];
I think the only safe way to implement a weak dictionary key like this would be to use object identity rather than isEqual:, and have the value object cache its hash when it's created. You could then remove it by having a specialization that used __unsafe_unretained (completely safe as long as you don't message it).
Another, perhaps better, way would be to write your own dictionary class (e.g. starting with MAMutableDictionary from https://github.com/mikeash/MACollections) and having it use weak references directly, with the ability to locate and remove zeroed ones.
if(_weakBar)
[self mustNotBeNil: _weakBar];
From:
http://clang.llvm.org/docs/AutomaticReferenceCounting.html#runtime.objc_loadWeak
(Accessed 9/11/2012)
Precondition: object is a valid pointer which either contains a null pointer or has been registered as a __weak object.
If object is registered as a __weak object, and the last value stored into object has not yet been deallocated or begun deallocation, retains and autoreleases that value and returns it. Otherwise returns null. Equivalent to the following code:
id objc_loadWeak(id *object) {
return objc_autorelease(objc_loadWeakRetained(object));
}
Must be atomic with respect to calls to objc_storeWeak on object.
Rationale: loading weak references would be inherently prone to race conditions without the retain.
This code:
Bar *bar = _weakBar;
if(bar)
[self mustNotBeNil: bar];
however should result in a call to:
Precondition: object is a valid pointer which either contains a null pointer or has been registered as a __weak object.
If object is registered as a __weak object, and the last value stored into object has not yet been deallocated or begun deallocation, retains that value and returns it. Otherwise returns null.
Must be atomic with respect to calls to objc_storeWeak on object.
Via disassembling a quick test app in lldb, I confirmed that these are the calls being generated in both cases.
Thus, both versions of your code are safe, but for different reasons.
objc_loadWeakRetained
followed immediately by a release. If you're relying on Clang generating a call to objc_loadWeak
without any guarantee that that's what it must do, you're leaving your code open to breaking in future compiler releases.For __weak objects, the current pointee is retained and then released at the end of the current full-expression. This must execute atomically with respect to assignments and to the final release of the pointee.
Thus, you're guaranteed to have a valid pointer for the rest of your scope. (You're correct that the autoreleased nature of objc_loadWeak is not necessary to achieve the above specification. A hidden strong variable could suffice.)
http://clang.llvm.org/docs/AutomaticReferenceCounting.html#ownership.semantics
I'd never heard the term "full expression" in this context before, but we all know, or should know, what a sequence point is.
Your quoted documentation says that, in the code we're discussing, the loaded value from _weakBar will be released as soon as the if statement decides which branch to take. That the compiler is more lenient is entirely chance.
Also, Apple's got your back:
// ...
MyViewController * __weak weakMyController = myController;
myController.completionHandler = ^(NSInteger result) {
MyViewController *strongMyController = weakMyController;
if (strongMyController) {
// ...
[strongMyController dismissViewControllerAnimated:YES completion:nil];
// ...
}
else {
// Probably nothing...
}
};
http://developer.apple.com/library/mac/#releasenotes/ObjectiveC/RN-TransitioningToARC/Introduction/Introduction.html#//apple_ref/doc/uid/TP40011226-CH1-SW9
How do blocks work in ARC?
Blocks “just work” when you pass blocks up the stack in ARC mode, such as in a return. You don’t have to call Block Copy any more. You still need to use [^{} copy] when passing “down” the stack into arrayWithObjects: and other methods that do a retain.
But you say that you don't need to copy unless it is being cast to id. So was it that Apple didn't want to get into the real reason you need copy (because arrayWithObjects doesn't know it is a block) or is it true that you need to use [block copy] anytime you hold onto a block (vs when you return it up up the stack)?
However, beware! ARC currently does not automatically copy a block literal that's converted to an id.
This seems to have been fixed by Apple by SDK 8.0, would you please verify this and note it on your article?
Thanks.
Comments RSS feed for this page
Add your thoughts, post a comment:
Spam and off-topic posts will be deleted without notice. Culprits may be publicly humiliated at my sole discretion.