2

I created an empty iOS app on Xcode 4.4.1, and did the following:

NSNumber *n1 = @1;
NSNumber *n2 = @2;
NSNumber *n3 = @3;
NSNumber *n100 = @100;

NSString *s = @"haha";
NSArray *a = @[n1, s];
NSDictionary *d = @{ @"ha" : @1, @3 : @"hello" };

NSLog(@"retain count of @1 is %i", [n1 retainCount]);
NSLog(@"retain count of @2 is %i", [n2 retainCount]);
NSLog(@"retain count of @3 is %i", [n3 retainCount]);
NSLog(@"retain count of @100 is %i", [n100 retainCount]);

NSLog(@"retain count of @\"haha\" is %i", [s retainCount]);

NSLog(@"retain count of array literal is %i", [a retainCount]);
NSLog(@"retain count of dictionary literal is %i", [d retainCount]);

and the result is:

retain count of @1 is 10
retain count of @2 is 4
retain count of @3 is 5
retain count of @100 is 1
retain count of @"haha" is -1
retain count of array literal is 1
retain count of dictionary literal is 1

so the retain count of array literal and dictionary literal is 1, and string literal is said to exist for the whole app's running, so that's why it is -1 (probably meaning MAX unsigned int), but the retain count of @1 actually come out as 7, 8, and 10 at different times. Is there a rule to it? I found that I can do [n1 retain] and [n1 release] as well, and it will increase and decrease the retain count accordingly.

Jeremy L
  • 3,770
  • 6
  • 41
  • 62
  • 3
    http://whentouseretaincount.com –  Sep 14 '12 at 04:44
  • Dont use retainCount. Check here for example http://stackoverflow.com/questions/11507537/how-to-print-the-retain-count-of-an-object or use search! – Mario Sep 14 '12 at 04:44
  • i am not saying that I use retainCount to do programming. I am using here for experimenting and verifying the number should be what it is – Jeremy L Sep 14 '12 at 04:54
  • @Jeremy L One, that's not a reason for downvoting a correct answer, two, you can't use retainCount even for that. –  Sep 14 '12 at 05:15
  • 1
    @JeremyL just for extra emphasis, what H2CO3 says is important, **you cannot use retainCount to verify that the number is what it should be**. And the reason is very simple: you do not (and cannot) know what it should be. – Analog File Sep 14 '12 at 08:25
  • so you are saying, "you cannot know what it should be, because you cannot know what it should be." so what's the use of `retainCount` at all? Actually, for most cases, they do show a value of what it should be. – Jeremy L Sep 14 '12 at 17:23
  • Exactly, what's the use of `retainCount` at all? Nobody knows. – BoltClock Sep 15 '12 at 13:25
  • Honestly, this looks like a small bug (specifically a missed optimization) to me. Small integer NSNumbers are generally going to be in the number cache on iOS, so they should probably ignore retain and release. Though if iOS gets tagged pointers like OSX has, it won't matter anymore :) – Catfish_Man Jan 14 '13 at 22:33

2 Answers2

4

I don't know.

Neither does anybody on StackOverflow exactly.

Why?

Because it's only Apple's engineers who know it. Foundation is simple only in its interface - the underlying implementation is a mess. It's intermixed with the Objective-C runtime, optimized hard for a lot of possible or likely circumstances, and if something is mentioned in the docs as a thing that is not to be relied upon, that is to be taken seriously.

- retainCount is one of them. It's not there to get the actual reference count of a particual instance of a class - its absolute value is meaningless. Only relative changes are meaningful in the case of this method. The use of autorelease pools and automatic reference counting (though not applicable here) add another level of ambiguity.

That's why.

Oh, and yes, do check out http://whentouseretaincount.com

  • 1
    +1, and don't forget a new level meaninglessness brought by ARC – Carl Veazey Sep 14 '12 at 05:19
  • @Carl thanks! Yes, that's also a valid reason along with autorelease pools - I updated my answer. –  Sep 14 '12 at 05:24
  • 1
    +1 - should never read and make decisions based on retain count. Follow the memory rules with retain and release and rely on it to behave per the rules. – bryanmac Sep 14 '12 at 05:27
  • so your answer is that, every single user on Stackoverflow came over and looked and the question and nobody knows. – Jeremy L Sep 14 '12 at 05:33
  • @Jeremy L no, I didn't say they came and looked and didn't know that. I said nobody except Apple can know it. –  Sep 14 '12 at 05:38
0

I think this points to the reason why: every time @1 is encountered, it is something like [[[NSNumber alloc] initWithInt:1] autorelease], and it appears that it returns the same object. So when the following is done:

NSMutableArray *arr = [[NSMutableArray alloc] init];
for (int i = 0; i < 300; i++) {
    [arr addObject:@1];
}

the retain count actually becomes 610. And it is the same if the following is done:

NSMutableArray *arr = [[NSMutableArray alloc] init];
for (int i = 0; i < 300; i++) {
    [arr addObject:[[[NSNumber alloc] initWithInt:1] autorelease]];
} 

(Update: it is 610, because the retain count is increased by 1 by the usage of @1, and another time when it is retained by the array, so it is 600 times total). It suggests that any usage of @1 any where will increase the retain count, and this NSNumber object is probably placed in the autorelease pool the same equal number of times.

Jeremy L
  • 3,770
  • 6
  • 41
  • 62
  • 1
    It's not even just `@1` but `[NSNumber numberWithInt:1]` and so on. This is the more interesting result; the retain count values are just a very imprecise indicator (and possibly in other situations downright misleading) of the optimizations going on underneath. To feed your curiosity, use the debugger and profilers to understand what's happening because using `retainCount` isn't just bad engineering, it's bad science too. – Carl Veazey Sep 14 '12 at 05:49
  • at least it gives you a clue as to what might be going on, so I don't know how the conclusion of "even experimenting and verifying with it, it is bad" can be reached. This is an interesting case that alloc supposedly returns a different object every time, but with the line above, alloc and init actually returns the same object every time – Jeremy L Sep 14 '12 at 05:55
  • 1
    It provided a hunch, so I'm not saying it's not completely useless, and I would never say using anything to generate inspiration is "bad". But it can't be reliably used to reason about or model your program because it's essentially an implementation quirk. – Carl Veazey Sep 14 '12 at 06:12
  • as I said many times, I never intended to use it for regular programming. I don't know why people see `retainCount` and freak out automatically – Jeremy L Sep 14 '12 at 14:09
  • 3
    The "freak out" is because, ultimately, it is a waste of time; whatever clues `retainCount` reveals, there is always an alternative clue that is generally as easy to get and is far more indicative of what is really going on. In this case, if you had printed the pointers of the objects out `NSLog(@"%p", obj);`, you would have seen that the objects were singletons and, thus, the retain/release behavior will be undefined. – bbum Sep 14 '12 at 16:02
  • 1
    it does lead you to looking deeper, doesn't it. Of course, some people may consider looking any deeper to be "a waste of time" – Jeremy L Sep 14 '12 at 16:49
  • 1
    Yeah -- that it catalyzed your exploration certainly does mean that it has some value! Didn't mean to imply otherwise. The point, though, is that if you want to explore retain/release/alloc goop, there are much better tools like printing the address of the object to discover singletons and using the Allocations instrument with "track reference counts" enabled to see both the retain count history of the object *and the backtrace of all retain/release calls*. – bbum Sep 14 '12 at 17:19