Thread safe lazy initialization on iOS - ios

I have a view controller that I want to lazily initialize, and once initialized, use the same copy when possible (I don't use a singleton since I do want to remove it from memory eventually), I use the getter to do so, my code look like this:
#property (retain) UIViewController *myController
...
#synthesize myController = _myController;
...
- (UIViewController *)myController
{
if (!_myController) { // Evaluation
_myController = [[MyViewController alloc] init]; // Object Creation
}
return _myController;
}
This works, but it's not thread safe, and if more than one thread evaluate to true before the object is created, I'll have a memory leak. One solution I've tried is to #synchronized the code, but I'm not sure the correct way to do it.
This appears to work, (lockForMyController is a simple NSString) but it makes this section of code a lot slower:
- (UIViewController *)myController
{
#synchronized(self.lockForMyController){
if (!_myController) {
_myController = [[MyViewController alloc] init];
}
}
return _myController;
}
I was wondering if there is some other way to achieve a lazy initialized, thread safe, property?

This solution works
Note that this solution only works if myController is accessed on a background thread the first time. It will deadlock if called on the main thread.
You want to use gcd. The key is serialize the creation of the object, so that regardless of the threads starting the block, it will always only be created exactly once.
- (UIViewController *)myController
if (_myController == nil) {
dispatch_sync(dispatch_get_main_queue(), ^ { if (_myController == nil) _myController = [[MyViewController alloc] init]; });
}
return _myController;
}
Here, even if multiple threads execute the block, the execution of the block is serialized onto the main thread and only one MyViewController can ever be created.
You won't see a performance hit here unless the object is nil.
Since the property is implicitly atomic, that means that in the setter the value will be autoreleased. This should make it suitable for mingling with your custom getting, since it will autorelease any value changes to _myController.
http://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjectiveC/Chapters/ocProperties.html#//apple_ref/doc/uid/TP30001163-CH17-SW2
However, you still may get into a race condition where you are setting the value on one thread but accessing it on another. Any time you set the value, you probably want to make sure and do something like this:
dispatch_sync(dispatch_get_main_queue(), ^ { self.myController = {newValueOrNil} });
This will make sure to serialize your setter methods calls without having to reinvent the wheel for atomic setters, which is very hard to get right.
This solution does not work
You want to use gcd.
http://developer.apple.com/library/ios/documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html#//apple_ref/c/func/dispatch_once
See this post about singletons. I know you don't want a singleton, but this demonstrates how to use the method. You can easily adapt it.
Create singleton using GCD's dispatch_once in Objective C

Related

Lazy instantiation

Well I'm just confused when the lazy instantiation should be used.
I understand the basic concept of lazy instantiation though.
" I understand that all properties start out as nil in Objective-C and that sending a message to nil does nothing, therefore you must initialize using [[Class alloc] init]; before sending a message to a newly created property. "(Lazy instantiation in Objective-C/ iPhone development)
m.file:
#property (strong, nonatomic) NSMutableArray *cards;
- (NSMutableArray *)cards
{
if (!_cards) _cards = [[NSMutableArray alloc] init];
return _cards;
}
- (void)addCard:(Card *)card atTop:(BOOL)atTop
{
if (atTop) {
[self.cards insertObject:card atIndex:0];
} else {
[self.cards addObject:card];
} }
Well, what I really don't get is when I'm supposed to use this type of instantiation?
Mostly I see the code like this:
h.file:
#interface Card : NSObject
#property (strong, nonatomic) NSString *contents;
m.file:
if([card.contents isEqualToString:self.contents]){
score = 1;
}
*This might be a stupid question but I'm really confused. I'm new here, Thanks.
There is no reason to use Lazy Instantiation/Lazy Initialization if you find it confusing; simply initialize your instance variables/properties in the class init methods and don't worry about it.
As the object is created as a side-effect of calling the getter method, it's not immediately obvious that it is being created at all, so one alternative, which would also mean you can use the default compiler-generate getter method, is to explicitly check for it in addCard:
- (void)addCard:(Card *)card
atTop:(BOOL)atTop
{
if (!self.cards)
self.cards = [NSMutableArray new];
if (atTop) {
[self.cards insertObject:card atIndex:0];
} else {
[self.cards addObject:card];
}
}
(and removing the user-supplied getter method)
However the net-effect is the same as the code you posted, with the exception that self.cards will return nil until addCard is called, however I doubt this will cause a problem.
When using dot notation to access your instance variables, you are calling your getter method for that given property. Therefore, by using dot notation and lazy instantiation, your getter will always assert that a property is not nil before you send it a message. Therefore, code such as
[self.cards insertObject:card atIndex:0];
will actually call the getter at self.cards; if you use dot notation on your objects and program the getters accordingly, you will always ensure that your instance variables are allocated and initialized, while simultaneously cleaning up your init method for code that is much more important.
Lazy instantiation is a common practice among Objective-C programmers; I suggest getting into the flow of the convention.
EDIT: thanks for Raphael mentioning this in a comment previously.
Lazy instantiation is a performance enhancement in certain types of scenarios. One example would be a class that has a very expensive user facing UI string.
If you create many of instances of that class but only a very small subset of those instances will be shown in your UI, you waste a lot of CPU resources creating a very expensive UI string that rarely will be used.

Weird bad_access with dispatch_async

I'm getting a really weird bad access error while using dispatch async. I managed to reduce it down to this segment of code in my program.
-(void)buttonTapped:(id)sender {
__block NSArray*foo = nil;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
//Foo was initially declared here but then moved it outside.
foo = [self someMethod];
[foo retain]; // bad access here. Why ?
dispatch_async(dispatch_get_main_queue(),0) {
// doesnt matter what happens here
}); });
}
-(id)someMethod
{
return [self secondMethod];
}
-(id)secondMethod
{
// was initially returning an autoreleased object from here. Changed it
// to eliminate that as source of the error.
id newThing = [[NSObject alloc] init];
return newThing;
}
The code didnt initially look like this but this is how it is right now . Including allocating a dummy NSObject .
How is it possible for foo to get released in between calls inside a dispatch async ? I dont understand how this is possible. I know its difficult to suggest whats going from just this but any debugging suggestions would be helpful. I tried turning on NSZombies but I dont get any Zombies.
You ask:
How is it possible for foo to get released in between calls inside a dispatch_async?
It shouldn't, unless someMethod or secondMethod are, themselves, doing something asynchronously which might allow the autorelease pool to be drained in the interim.
I tried turning on NSZombies but I dont get any Zombies.
If you've got zombies turned on and you're not getting a zombie, then I suspect the problem rests elsewhere. Frankly, I suspect that the root of the problem was eliminated in your process of simplifying the sample code for the purposes of the question:
A few other observations/clarifications:
You declared foo to be a NSArray, but then you're returning NSObject. I'll assume you meant it to be NSObject throughout.
You have a line of code that says:
dispatch_async(dispatch_get_main_queue(),0) {
I'll just assume that was a typo and that you intended:
dispatch_async(dispatch_get_main_queue(), ^{
The foo variable should definitely be inside the dispatch_async block. It doesn't really make sense to have a __block variable for something (a) you don't reference outside of that block for a block; and (b) for a block you're dispatching asynchronously.
The secondMethod should return an autorelease object, as you apparently originally had it. (Or you'd probably want to change secondMethod and someMethod to start with new in their names to avoid confusion and make life easier for yourself when you eventually move to ARC.)
If you retain the foo object, you'll want to also add the appropriate release. In fact, your original code sample returns a +1 object, and then retain it again, bumping it to +2, so you'd need two release calls.
Anyway, correcting for these various issues, I end up with the following, which does not generate an exception:
- (IBAction)buttonTapped:(id)sender
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
NSObject *foo = [self someMethod];
[foo retain]; // no bad access here
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"foo = %#", foo);
[foo release];
});
});
}
- (NSObject *)someMethod
{
return [self secondMethod];
}
- (NSObject *)secondMethod
{
return [[[NSObject alloc] init] autorelease];
}
Furthermore, I would suggest, especially when using manual retain and release (MRR), that you run it through the static analyzer ("Analyze" on the Xcode "Product" menu) and make sure you have a clean bill of health. (It would have pointed out some of the issues I mentioned.) It's not perfect, but it's remarkably good at identifying issues.
But, in short, the above code is fine, and if you're still getting an exception, update your question with working code that reproduces the exception.

How to force release on iOS

I'm new to ARC but understand how it works and I'm trying it out. I'm on iOS so memory is a severe concern.
I have a MyObject class which contains lots of big data. I want to release it, and load a new set of data.
MyObject *object;
object = [[MyObject alloc] initWithData:folder1]; // load data from folder1
// later...
object = [[MyObject alloc] initWithData:folder2]; // load data from folder2
This works fine without leaks, and I'm guessing the ARC inserts a [object release] before the new assignment. My problem is the data inside 'object' is released after the new set is allocated, and I run out of memory. What I really want to be able to do is:
object = nil;
<function to pop the pool, wait till everything is deallocated>
object = [MyObject alloc] initWithData:folder2]; // load data from folder2
but I'm not sure how to do that. I could run the new allocation on a performselector afterdelay, but it feels like I'm shooting in the dark and a bit of hack. There's probably a proper way to do this?
P.S I've tried searching for an answer, but all results are about memory leaks and how to make sure variables go out of scope and set variables to nil etc. My issue isn't about that, it's more of a timing thing.
UPDATE
Thanks for the answers, I'd already tried
object = nil;
object = [MyObject alloc] initWithData:folder2];
and it hadn't worked. I wasn't sure whether it was supposed to or not. Now I understand that it is supposed to work, but I must have something else holding on to it for that fraction of a second. I have NSLogs in all of my init/dealloc methods, and I can see first all the inits of the new instances of classes (of MyObject's ivars) being called, and then almost immediately after (within a few ms), the dealloc of MyObject, followed by the deallocs of its ivars.
I also tried the #autorelease but the same thing happens.
I've searched throughout the project and pasted all the code which I think may be relevant to this.
#interface AppDelegate : UIResponder <UIApplicationDelegate>;
#property PBSoundSession *soundSession;
#end
//--------------------------------------------------------------
#implementation AppDelegate
// onTimer fired at 60Hz
-(void)onTimer:(NSTimer *) theTimer {
[oscReceiver readIncoming]; // check incoming OSC messages
// then do a bunch of stuff with _soundSession;
}
#end
//--------------------------------------------------------------
#implementation OscReceiver
-(void)readIncoming {
AppDelegate *appDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
// parse all incoming messages
if(bLoadNewSoundBank) {
NSString *newFolder = parseNewFolder();
appDelegate.soundSession = nil;
appDelegate.soundSession = [MyObject alloc] initWithData:newFolder];
}
}
#end
//--------------------------------------------------------------
#implementation GuiController
// onTimer fired at 10Hz
-(void)onTimer:(NSTimer *) theTimer {
PBSoundSession *soundSession = appDelegate.soundSession;
// update gui with received values
}
#end
I thought it might be the 'soundSession' local variable in the GuiController::onTimer holding onto the old appDelegate.soundSession for the duration of that method, but to my surprise commenting out all of the GUI code (in fact disabling the timer), made no difference.
Is there a way of finding out at that point who is still holding onto my appDelegate.soundSession? I placed a breakpoint where I set it to nil, but couldn't find any useful information. I tried Instruments in Allocation template, but couldn't find anything useful there either (probably because I don't know where to look).
This is what my allocations track looks like, you can see the memory is all deallocated a bit too late!
.
This might not be an an ARC problem. What you could be seeing is your autorelease pool not draining soon enough—your MyObject is getting released, but the data it loaded is getting held onto by the pool because of some internal -retain/-autorelease pair. Try wrapping your -initWithData: calls in an #autoreleasepool block, like this:
#autoreleasepool {
object = [[MyObject alloc] initWithData:folder1];
// do things
}
// later…
#autoreleasepool {
object = [[MyObject alloc] initWitData:folder2];
// do other things
}
Setting the object to nil immediately before setting it to something else as Gabriele suggests might cause the compiler to insert the appropriate release before the second -alloc/-initWithData:, but it might be smart enough to do that already—if that doesn’t work, it’s most likely the autorelease-pool thing.
There is no delay when draining an #autoreleasepool {...}; the objects in the pool have release invoked immediately. If an object survives that, it is because there is either a strong reference elsewhere or because the object was autoreleased into the next pool out.
If you do:
a = [[Foo alloc] initBigThing];
a = nil;
a = [[Foo alloc] initBigThing];
The first instance of Foo will be released prior to the allocation of the second
With one big caveat; if any of the code paths that a is invoked upon happen to retain/autorelease it, then it'll stick around until the pool is drained. Surrounding it in #autoreleasepool{ ... }; should do the trick.
Note that the compiler will sometimes emit retain/autorelease sequences in non-optimized builds that are eliminated in optimized builds.
A bit more general answer, I found how you can force release an object:
#import <objc/message.h>
// ---
while ([[object valueForKey:#"retainCount"] integerValue] > 1) {
objc_msgSend(object, NSSelectorFromString(#"release"));
}
objc_msgSend(object, NSSelectorFromString(#"release"));
But you shouldn't do this because ARC will probably release the object later and this will cause a crash. This method should be only used in debug!

How do I access the dealloc method in a class category?

I need to perform an action in the dealloc method of a category. I've tried swizzling but that doesn't work (nor is it a great idea).
In case anyone asks, the answer is no, I can't use a subclass, this is specifically for a category.
I want to perform an action on delay using [NSTimer scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:] or [self performSelector:withObject:afterDelay:] and cancel it on dealloc.
The first issue is that NSTimer retains the target, which I don't want. [self performSelector:withObject:afterDelay:] doesn't retain, but I need to be able to call [NSObject cancelPreviousPerformRequestsWithTarget:selector:object:] in the dealloc method or we get a crash.
Any suggestions how to do this on a category?
I still think it would be better to subclass your class and not mess with the runtime, but if you are definitely sure you need to do it in a category, I have an option in mind for you. It still messes with the runtime, but is safer than swizzling I think.
Consider writing a helper class, say calling it DeallocHook which can be attached to any NSObject and perform an action when this NSObject gets deallocated. Then you can do something like this:
// Instead of directly messing with your class -dealloc method, attach
// the hook to your instance and do the cleanup in the callback
[DeallocHook attachTo: yourObject
callback: ^{ [NSObject cancelPrevious... /* your code here */ ]; }];
You can implement the DeallocHook using objc_setAssociatedObject:
#interface DeallocHook : NSObject
#property (copy, nonatomic) dispatch_block_t callback;
+ (id) attachTo: (id) target callback: (dispatch_block_t) block;
#end
Implementation would be something like this:
#import "DeallocHook.h"
#import <objc/runtime.h>
// Address of a static global var can be used as a key
static void *kDeallocHookAssociation = &kDeallocHookAssociation;
#implementation DeallocHook
+ (id) attachTo: (id) target callback: (dispatch_block_t) block
{
DeallocHook *hook = [[DeallocHook alloc] initWithCallback: block];
// The trick is that associations are released when your target
// object gets deallocated, so our DeallocHook object will get
// deallocated right after your object
objc_setAssociatedObject(target, kDeallocHookAssociation, hook, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
return hook;
}
- (id) initWithCallback: (dispatch_block_t) block
{
self = [super init];
if (self != nil)
{
// Here we just copy the callback for later
self.callback = block;
}
return self;
}
- (void) dealloc
{
// And we place our callback within the -dealloc method
// of your helper class.
if (self.callback != nil)
dispatch_async(dispatch_get_main_queue(), self.callback);
}
#end
See Apple's documentation on Objective-C runtime for more info about the associative references (although I'd say the docs are not very detailed regarding this subject).
I've not tested this thoroughly, but it seemed to work. Just thought I'd give you another direction to look into.
I just stumbled on a solution to this that I haven't seen before, and seems to work...
I have a category that--as one often does--needs some state variables, so I use objc_setAssociatedObject, like this:
Memento *m = [[[Memento alloc] init] autorelease];
objc_setAssociatedObject(self, kMementoTagKey, m, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
And, I needed to know when the instances my category extending were being dealloced. In my case it's because I set observers on self, and have to remove those observers at some point, otherwise I get the NSKVODeallocateBreak leak warnings, which could lead to bad stuff.
Suddenly it dawned on me, since my associated objects were being retained (because of using OBJC_ASSOCIATION_RETAIN_NONATOMIC), they must be being released also, and therefore being dealloced...in fact I had implemented a dealloc method in the simple storage class I had created for storing my state values. And, I postulated: my associated objects must be dealloced before my category's instances are! So, I can have my associated objects notify their owners when they realize they are being dealloced! Since I already had my retained associated objects, I just had to add an owner property (which is not specified as retain!), set the owner, and then call some method on the owner in the associated object's dealloc method.
Here's a modified part of my category's .m file, with the relevant bits:
#import <objc/runtime.h> // So we can use objc_setAssociatedObject, etc.
#import "TargetClass+Category.h"
#interface TargetClass_CategoryMemento : NSObject
{
GLfloat *_coef;
}
#property (nonatomic) GLfloat *coef;
#property (nonatomic, assign) id owner;
#end
#implementation TargetClass_CategoryMemento
-(id)init {
if (self=[super init]) {
_coef = (GLfloat *)malloc(sizeof(GLfloat) * 15);
}
return self;
};
-(void)dealloc {
free(_coef);
if (_owner != nil
&& [_owner respondsToSelector:#selector(associatedObjectReportsDealloc)]) {
[_owner associatedObjectReportsDealloc];
}
[super dealloc];
}
#end
#implementation TargetClass (Category)
static NSString *kMementoTagKey = #"TargetClass+Category_MementoTagKey";
-(TargetClass_CategoryMemento *)TargetClass_CategoryGetMemento
{
TargetClass_CategoryMemento *m = objc_getAssociatedObject(self, kMementoTagKey);
if (m) {
return m;
}
// else
m = [[[TargetClass_CategoryMemento alloc] init] autorelease];
m.owner = self; // so we can let the owner know when we dealloc!
objc_setAssociatedObject(self, kMementoTagKey, m, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
return m;
}
-(void) doStuff
{
CCSprite_BlurableMemento *m = [self CCSprite_BlurableGetMemento];
// do stuff you needed a category for, and store state values in m
}
-(void) associatedObjectReportsDealloc
{
NSLog(#"My associated object is being dealloced!");
// do stuff you need to do when your category instances are dealloced!
}
#end
The pattern here I learned somewhere (probably on S.O.) uses a factory method to get or create a memento object. Now it sets the owner on the memento, and the memento's dealloc method calls back to let the owner know it's being dealloced
CAVEATS:
Obviously, you have to have your associated object set with OBJC_ASSOCIATION_RETAIN_NONATOMIC, or it won't be retained and released for you automatically.
This becomes trickier if your memento/state associated object gets dealloced under other circumstances than the owner being dealloced...but you can probably train one object or the other to ignore that event.
The owner property can't be declared as retain, or you'll truly create a strong reference loop and neither object will ever qualify to be dealloced!
I don't know that it's documented that OBJC_ASSOCIATION_RETAIN_NONATOMIC associated objects are necessarily released before the owner is completely dealloced, but it seems to happen that way and almost must be the case, intuitively at least.
I don't know if associatedObjectReportsDealloc will be called before or after the TargetClass's dealloc method--this could be important! If it runs afterwards, if you try to access member objects of the TargetClass you will crash! And my guess is that it's afterwards.
This is a little messy, because you're double-linking your objects, which requires you to be very careful to keep those references straight. But, it doesn't involve swizzling, or other interference with the runtime--this just relies on a certain behavior of the runtime. Seems like a handy solution if you already have an associated object. In some cases it might be worth creating one just to catch your own deallocs!
Your proposed solution unfortunately won't work: because NSTimer retains its target, the target will never run its dealloc until the timer has been invalidated. The target's retain count will always be hovering at 1 or above, waiting for the timer to release it. You have to get to the timer before dealloc. (Pre-ARC, you could override retain and release and destroy the timer, although that's really not a good solution.)
NSThread also has this problem, and the solution is simple: a bit of redesigning separates the controller of the thread from the "model". The object which creates and owns the thread, or timer in this case, should not also be the target of the timer. Then, instead of the retain cycle you currently have (timer owns object which owns timer), you have a nice straight line: controller owns timer which owns target. Outside objects only need to interact with the controller: when it is deallocated, it can shut down the timer without you having to play games with overriding dealloc or other memory management methods.
That's the best way to handle this. In the case that you can't do that for some reason -- you're talking about category overrides, so apparently you don't have the code for the class which is the target of the timer (but you can still probably make a controller even in that case) -- you can use weak references. Unfortunately I don't know any way to make an NSTimer take a weak reference to its target, but GCD will give you a fair approximation via dispatch_after(). Get a weak reference to the target and use that exclusively in the Block you pass. The Block will not retain the object through the weak reference (the way NSTimer would), and the weak reference will of course be nil if the object has been deallocated before the Block runs, so you can safely write whatever message sends you like.

Hooking end of ARC dealloc

Given the following simple implementation:
#implementation RTUDeallocLogger
-(void)dealloc
{
NSLog(#"deallocated");
}
#end
we run the following code under ARC:
#implementation RTURunner
{
NSArray* arr;
}
-(void)run{
arr = [NSArray
arrayWithObjects:[[RTUDeallocLogger alloc]init],
[[RTUDeallocLogger alloc]init],
[[RTUDeallocLogger alloc]init],
nil];
NSLog(#"nulling arr");
arr = NULL;
NSLog(#"finished nulling");
}
#end
we get the following log output:
nulling arr
finished nulling
deallocated
deallocated
deallocated
I'd like to perform an action after all the deallocations have finished. Is this possible?
The aim of this question is really to understand a little more about the mechanics of ARC, in particular, at what point ARC triggers these deallocations, and whether or not this can ever happen synchronously when I drop references.
-dealloc is always synchronous, and occurs when the last strong reference is removed. In the case of your code though, +arrayWithObjects: is likely (if compiled at -O0 at least) putting the array in the autorelease pool, so the last strong reference is removed when the pool drains, not when you set the variable to NULL (you should use nil for ObjC objects, btw).
You can likely avoid having the object in the autorelease pool by using alloc/init to create, and you may (implementation detail, bla bla) be able to avoid it by compiling with optimizations turned on. You can also use #autoreleasepool { } to introduce an inner pool and bound the lifetime that way.
If I were an engineer from Apple I'd probably argue that your problem is probably your design. There are almost no reasons you'd want effectively to act by watching dealloc rather than having dealloc itself act.
[a huge edit follows: weak properties don't go through the normal property mechanisms, so they aren't KVO compliant, including for internal implicit KVO as originally proposed]
That said, what you can do is bind the lifetime of two objects together via object associations and use the dealloc of the latter as a call-out on the dealloc of the former.
So, e.g.
#import <objc/runtime.h>
#interface DeallocNotifier;
- (id)initWithObject:(id)object target:(id)target action:(SEL)action;
#end
#implementation DeallocNotifier
- (id)initWithObject:(id)object target:(id)target action:(SEL)action
{
... blah ...
// we'll use a static int even though we'll never access by this key again
// to definitely ensure no potential collisions from lazy patterns
static int anyOldKeyWellNeverUseAgain;
objc_setAssociatedObject(object, &anyOldKeyWellNeverUseAgain, self, OBJC_ASSOCIATION_RETAIN);
... blah ...
}
- (void)dealloc
{
[_target performSelector:_action];
}
#end
-(void)run{
arr = ...
[[DeallocNotifier alloc]
initWithObject:arr target:self action:#selector(arrayDidDealloc)];
/* you may not even need *arr in this case; I'm unclear as
to why you have an instance variable for something you don't
want to keep, so I guess it'll depend on your code */
} // end of run
- (void)arrayDidDealloc
{
NSLog(#"array was deallocated");
}
I've assumed you're able to tie the lifecycle of all the objects you're interested in to that of a single container; otherwise you could associate the notifier to all relevant objects.
The array has definitely gone by the time you get arrayDidDealloc.
at what point ARC triggers these deallocations
ARC inserts allocations/deallocations into your code based on static analysis. You can see where it does this by looking at the assembly of your source -- go to Product -> Generate Output in Xcode.
whether or not this can ever happen synchronously when I drop references
Retain/release/autorelease is always synchronous.
Your code
arr = [NSArray arrayWithObjects:[[RTUDeallocLogger alloc] init],
[[RTUDeallocLogger alloc] init],
[[RTUDeallocLogger alloc] init],
nil];
will be implicitly placing the objects into an autorelease pool. After the object is allocated, you don't want it retained (because the NSArray will do the retain once it receives the object), but you can't release it immediately, otherwise it will never make it to the NSArray alive. This is the purpose of autorelease - to cover the case where the object would otherwise be in limbo between two owners.
The retain count at alloc time is 1, then it's retained by the autoreleasepool and released by you, so the retain count remains 1. Then, it's retained by the NSArray, so the retain count becomes 2.
Later, the NSArray is released and so the retain count returns to 1, and the objects are finally cleaned up when the autorelease pool gets its chance to run.
You can make the autorelease act faster by nesting another pool - by wrapping your NSArray creation with an #autorelease{} clause.

Resources