2

A simple program:

-(void)doSomething {

 NSLog(@"self rc=%d", [self retainCount]);

 [self performSelector:@selector(doMe:) withObject:nil afterDelay:0 inModes:[NSArray arrayWithObject:NSDefaultRunLoopMode]];

 NSLog(@"self rc=%d", [self retainCount]);
}

-(void)doMe:(id)object {

 NSLog(@"i'm done");

 NSLog(@"self rc=%d", [self retainCount]);

}

Output:

self rc=1

self rc=2

i'm done

self rc=2

Why does the retain count increment to and stay at 2?

nacho4d
  • 43,720
  • 45
  • 157
  • 240
Ruug
  • 23
  • 1
  • 3

2 Answers2

13

From the -[NSObject performSelector:withObject:afterDelay:inModes:] documentation:

2013/11/01 Answer

The docs appear to be updated, as expected, now they don't say the target object is retained however they still mention an scheduled timer in the runloop.

If an NSTimer is used then the object must be retained by somebody or there would be no guarantee the selector can be performed since no-one could assure the object would be still alive. If not using ARC's weak it will crash. In my opinion, Apple edited its documentation to not mention implementation details, although I think it is pretty obvious.

Above document does NOT mention the word retain at all however, it does NOT mean the target is is not retained.

This is what I tried in the debugger in the simulator iOS7.0:

(lldb) p (int)[self retainCount]
(int) $1 = 8
(lldb) expr (void)[self performSelector:@selector(description) withObject:nil afterDelay:1.0]
(lldb) p (int)[self retainCount]
(int) $2 = 9
(lldb) expr (void)[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(description) object:nil]
(lldb) p (int)[self retainCount]
(int) $3 = 8

Conclusion: the object is retained, but it is not something we should care about. Mister solved :p


Previous Answer: (still valid)

This method retains the receiver and the anArgument parameter until after the selector is performed.

Because if the object is not retained, the object might be released before it gets executed and your app will crash.

Is the same logic when you set and try to fire a method using NSTimers. When the timer is actually fired if the target object does not exist anymore (is released) your app will crash. So performSelector:withObject:afterDelay:... and its friends are here to make our life a little bit easier because it makes sure the app won't crash when the timer is fired ;)

Hope it helps

nacho4d
  • 43,720
  • 45
  • 157
  • 240
  • Thanks for the doc pointer. That answers the question and makes sense. – Ruug Dec 11 '10 at 23:03
  • Hi, the docs you link to DO NOT AGREE with your statement. There are a bunch of "performSelector" variations which are part of NSObject, and the docs on them say nothing about retaining the receiver or the object. Only the NSRunloop method performSelector is documented to retain both the target and the object. BEWARE!!! – Motti Shneor Oct 23 '13 at 09:07
  • @MottiShneor he mentioned explicitly that messages which involves NSSTimers. remaining bunch of performSelector does not retain because they dont involves timer's – Kunal Balani Oct 29 '13 at 21:15
  • Btw , the document is updated and seems to be that object is no longer retained. – Kunal Balani Oct 29 '13 at 21:16
  • Thanks for your lines. Can you please post a link to the updated document? can't see it. However, who ever told us that performSelector:withObject:afterDelay variants use NSTimer? Even if they do - there should have been a note about object and target retaining in the docs of performSelector:withObject:afterDelay. – Motti Shneor Oct 31 '13 at 06:27
4

You seem to be wrongly believing that the NSLog in doMe: will execute before the second one in doSomething:. That's wrong. The performSelector:afterDelay:… methods still schedule the message with the runloop even if the delay is 0, so it will execute on the next runloop iteration.

Besides that, there's pretty much no good reason to be looking at an object's retain count — and if you do look, don't trust what you see. The result of the retainCount method is confusing at best and outright deceptive at worst — for one obvious and extremely common example, the retain count does not reflect autoreleases.

Chuck
  • 234,037
  • 30
  • 302
  • 389
  • 1
    Thanks for the advice about not looking at retain count. I guess it can be instructive too look at it in controlled examples, but perhaps not as a debugging tool. – Ruug Dec 11 '10 at 23:05
  • I partially agree with Chuck, using retainCount might be confusing if you compare it with a value you have calculated yourself because you don't know if the object will be retained or not. But, I think it is a good way to check the object's life cycle. If object's retainCount at time A is X then I do something with the object (its retain count might change) then I FINISH what I was doing, then at that time (time B) its retainCount should go back to X again. Like in this case, before performSelector... and after doMe: retainCount is, I don't know exactly what, but they are the same, for sure. – nacho4d Dec 12 '10 at 03:45
  • @nacho4d: I don't know what to say besides you're just plain wrong. Autorelease alone causes the "then I FINISH what I was doing, then at that time … its retainCount should go back to X again" description to be inaccurate. If the object is autoreleased instead of released, its retain count will still be elevated for a while. It even says in the docs that the `retainCount` method is unlikely to yield useful information: http://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Protocols/NSObject_Protocol/Reference/NSObject.html#//apple_ref/occ/intfm/NSObject/retainCount – Chuck Dec 12 '10 at 05:16