NSNumber* n = [[NSNumber alloc] initWithInt:100];
NSNumber* n1 = n;
In the code above, why is the value of n's retainCount set to 2? In the second line of the code, I didn't use retain to increase the number of retainCount.
I found a strange situation. Actually the retainCount depends on the initial number:
NSNumber *n = [[NSNumber alloc] initWithInt:100];
// n has a retainCount of 1
NSNumber *n2 = [[NSNumber alloc] initWithInt:11];
// n has a retainCount of 2
Stop. Just stop. Never look at the retainCount of an object. Ever. It should never have been API and available. You're asking for pain.
There's too much going on for retainCount to be meaningful.
Based on this link here, it's possible that there's some optimization going on under the covers for common NSNumbers (which may not happen in all implementations hence a possible reason why #dizy's retainCount is 1).
Basically, because NSNumbers are non-mutable, the underlying code is free to give you a second copy of the same number which would explain why the retain count is two.
What is the address of n and n1? I suspect they're the same.
NSNumber* n = [[NSNumber alloc] initWithInt:100];
NSLog(#"Count of n : %i",[n retainCount]);
NSNumber* n1 = n;
NSLog(#"Count of n : %i",[n retainCount]);
NSLog(#"Count of n1: %i",[n1 retainCount]);
NSLog(#"Address of n : %p", n);
NSLog(#"Address of n1: %p", n1);
Based on your update, that link I gave you is almost certainly the issue. Someone ran a test and found out that the NSNumbers from 0 to 12 will give you duplicates of those already created (they may in fact be created by the framework even before a user requests them). Others above 12 seemed to give a retain count of 1. Quoting:
From the little bit of examination I've been able to do, it looks as if you will get "shared" versions of integer NSNumbers for values in the range [0-12]. Anything larger than 12 gets you a unique instance even if the values are equal. Why twelve? No clue. I don't even know if that's a hard number or circumstantial.
Try it with 11, 12 and 13 - I think you'll find 13 is the first to give you a non-shared NSNumber.
Retain counts are an implementation detail. They can be kindasorta useful in debugging, sometimes, but in general you should not care about them. All you should care about is that you're following the memory management rules.
For an example of why looking at retain counts is unreliable, this is a perfectly legal class that obeys the API contract and will behave correctly in all circumstances:
#implementation CrazyClass
- (id)retain {
for(int i=0; i<100; i++) {
[super retain];
}
}
- (void)release {
for(int i=0; i<100; i++) {
[super release];
}
}
#end
…but if you inspected its retain count, you'd think you had an "issue."
This precise case doesn't happen too often in practice, but it illustrates why looking at retain counts is useless for telling if something is wrong. Objects do get retained behind the scenes by code outside of your control. NSNumber, for example, will sometimes cache instances. Objects get autoreleased, which isn't reflected in the retain count. Lots of things can happen that will confuse the retain count. Some classes might not even keep their retain counts where you can see them.
If you suspect you have a leak, you should check with the real debugging tools meant for that purpose, not by poking at retain counts. And for code you're writing, you should primarily be concerned with following the guidelines I linked above.
You should never rely on the retainCount of an object. You should only use it as a debugging aid, never for normal control flow.
Why? Because it doesn't take into account autoreleases. If an object is retained and subequently autoreleased, its retainCount will increment, but as far as you're concerned, its real retain count hasn't been changed. The only way to get an object's real retain count is to also count how many times it's been added to any of the autorelease pools in the autorelease pool chain, and trying to do so is asking for trouble.
In this case, the retainCount is 2 because somewhere inside alloc or initWithInt:, the object is being retained and autoreleased. But you shouldn't need to know or care about that, it's an implementation detail.
I think you have something else going on...
NSNumber* n = [[NSNumber alloc] initWithInt:100];
NSNumber* n1 = n;
NSLog(#"n = %i",[n retainCount]);
Result is 1
There is however a fly in this ointment. I've seen crashes due to retain count overflows from NSNumber instances containing small integers. In large systems that run for a very long time you can exceed the max int and get an Internal Consistancy error.
NSInternalInconsistencyException: NSIncrementExtraRefCount() asked to increment too far for <NSIntNumber: 0x56531f7969f0> - 0
Related
This question already has answers here:
When to use -retainCount?
(11 answers)
Closed 9 years ago.
I have following code:
- (IBAction)HeyCount:(UIButton *)sender {
NSString* strr = [[NSString alloc] initWithString:#"hi there"];
self.string = #"789";
ohYeah = #"456";
NSLog(#"Retain Count of ohYeah:[%d] with String:[%ld]",[ohYeah retainCount],(long)[ohYeah integerValue]);
NSLog(#"Retain Count of strr:[%d] with String:[%ld]",[strr retainCount],(long)[strr integerValue]);
}
And the out put of the above code is:
Retain Count of ohYeah:[-1] with String:[456]
Retain Count of strr:[-1] with String:[0]
Declaration of ohYeah is in .h file
NSString * ohYeah;
I'm not using ARC. Can anyone of you explain why retain count of both strings is -1 and accessing an object with retain count -1 should not be crash?
I guess the compiler is clever and creates string literals from your given code. Since those reside in their own memory space and are never released they get a retain count of UINT_MAX. UINT_MAX printed with %d will result in -1. Use %u for unsigned integers.
You shouldn't look to closely at retainCount.
There are objects like constant strings that don't take part in the retain/release mechanism. For example, #"456" is such a constant string. You can release or retain it as much as you like, nothing will happen.
There are other objects like #123 that are not even objects in a 64 bit system. They behave like objects, but no memory is ever allocated for them.
In both cases, the retain count won't give any sensible result. Which is why it is very, very rare that you should ever look at the retain count.
And then there are methods like "copy" which sometimes copy an object, sometimes just retain the original object. So if you have an object with a retain count of 100, and you make a copy, that copy might have a retain count of 1 or 101.
Note that retainCount is declared like this in NSObject.h:
- (NSUInteger)retainCount OBJC_ARC_UNAVAILABLE;
You should use %u to display it on 32 bits architecture and %lu on 64 bits.
But you shouldn't use directly retainCount, use it in your code means that you've a problem of architecture, objective-c (ARC or Manual Reference Counting) give a complete set of mechanism to manage memory and objects life cycle properly.
Here is retaincount code.
#import <Foundation/Foundation.h>
int main (int argc, const char * argv[])
{
#autoreleasepool {
NSNumber *number = [[NSNumber alloc]initWithInt:10];
NSMutableArray *array = [[NSMutableArray alloc]initWithCapacity:0];
NSLog(#"retain count : %d",[number retainCount]);
[array addObject:number];
NSLog(#"retain count : %d",[number retainCount]);
[number release];
NSLog(#"retain count : %d",[number retainCount]);
[number release];
NSLog(#"retain count : %d",[number retainCount]);
[number release];
NSLog(#"retain count : %d",[number retainCount]);
}
return 0;
}
My expected answer is
retain count : 1
retain count : 2
retain count : 1
retain count : 0
and then error
but actually running result is as following.
[Switching to process 6363 thread 0x0]
2011-12-01 19:39:53.843 nsnumber[6363:707] retain count : -1
2011-12-01 19:39:53.846 nsnumber[6363:707] retain count : -1
2011-12-01 19:39:53.847 nsnumber[6363:707] retain count : -1
2011-12-01 19:39:53.847 nsnumber[6363:707] retain count : -1
2011-12-01 19:39:53.848 nsnumber[6363:707] retain count : -1
I can't understand this result.
Why is this the result will come?
Don't trust the value of retain count. Especially as you are using ARC! Follow the rules. That's all.
NSNumber returns singleton instances for certain numbers (I believe integers 1 to 12, or something).
You shouldn't ever rely on the value of retainCount. Any number of things could be happening throughout your code or framework code that can change the retain count.
About retainCount
This method is typically of no value in debugging memory management
issues. Because any number of framework objects may have retained an
object in order to hold references to it, while at the same time
autorelease pools may be holding any number of deferred releases on an
object, it is very unlikely that you can get useful information from
this method.
See also
http://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Protocols/NSObject_Protocol/Reference/NSObject.html#//apple_ref/occ/intfm/NSObject/retainCount
When to use -retainCount?
The return type of retainCount is NSUInteger; that's an unsigned integer. The string format specifier %d is for a signed integer.
Cocoa uses NSUIntegerMax to represent the retain counts of immortal objects -- objects which will never be released. For performance reasons, it caches and reuses NSNumber objects representing small integers -- this NSNumber that you've created is apparently one of those, and is immortal.
When you interpret the maximum unsigned integer value as if it were signed (under two's complement arithmetic), it appears to be -1.
To see the "true" value, you should use the specifier %lu, as indicated by the chart I linked to above.
However, you generally shouldn't rely on retainCount to give you any useful information. This is documented:
Because any number of framework objects may have retained an object in order to hold references to it, while at the same time autorelease pools may be holding any number of deferred releases on an object, it is very unlikely that you can get useful information from this method.
as beryllium already noted.
This is a bit of a silly question, but if I want to add an object to an array I can do it with both NSMutableArray and NSArray, which should I use?
NSMutableArray * array1;
[array1 addObject:obj];
NSArray * array2;
array2 = [array2 arrayByAddingObject:obj];
Use NSMutableArray, that is what it is there for. If I was looking at code and I saw NSArray I would expect it's collection to stay constant forever, whereas if I see NSMuteableArray I know that the collection is destined to change.
It might not sound like much right now, but as your project grows and as you spend more time on it you will see the value of this eventually.
NSMutableArray is not threadsafe, while NSArray is. This could be a huge problem if you're multithreading.
NSMutableArray and NSArray both are build on CFArray, performance/complexity should be same. The access time for a value in the array is guaranteed to be at
worst O(lg N) for any implementation, current and future, but will
often be O(1) (constant time). Linear search operations similarly
have a worst case complexity of O(N*lg N), though typically the
bounds will be tighter, and so on. Insertion or deletion operations
will typically be linear in the number of values in the array, but
may be O(N*lg N) clearly in the worst case in some implementations.
When deciding which is best to use:
NSMutableArray is primarily used for when you are building collections and you want to modify them. Think of it as dynamic.
NSArray is used for read only inform and either:
used to populate an NSMutableArray, to perform modifications
used to temporarily store data that is not meant to be edited
What you are actually doing here:
NSArray * array2;
array2 = [array2 arrayByAddingObject:obj];
is you are creating a new NSArray and changing the pointer to the location of the new array you created.
You are leaking memory this way, because it is not cleaning up the old Array before you add a new object.
if you still want to do this you will need to clean up like the following:
NSArray *oldArray;
NSArray *newArray;
newArray = [oldArray arrayByAddingObject:obj];
[oldArray release];
But the best practice is to do the following:
NSMutableArray *mutableArray;
// Initialisation etc
[mutableArray addObject:obj];
An NSArray object manages an immutable array—that is, after you have created the array, you cannot add, remove, or replace objects. You can, however, modify individual elements themselves (if they support modification). The mutability of the collection does not affect the mutability of the objects inside the collection. You should use an immutable array if the array rarely changes, or changes wholesale.
An NSMutableArray object manages a mutable array, which allows the addition and deletion of entries, allocating memory as needed. For example, given an NSMutableArray object that contains just a single dog object, you can add another dog, or a cat, or any other object. You can also, as with an NSArray object, change the dog’s name—and in general, anything that you can do with an NSArray object you can do with an NSMutableArray object. You should use a mutable array if the array changes incrementally or is very large—as large collections take more time to initialize.
Even the Q and the answer are very old, someone has to correct it.
What does "better" mean? Better what? Your Q leaks of information what the problem is and it is highly opinion-based. However, it is not closed.
If you are talking about performance, you can measure it yourself. But remember Donald Knuth: "Premature optimization is the root of all evil".
If I take your Q seriously, "better" can mean runtime performance, memory footprint, or architecture. For the first two topics it is easy to check yourself. So no answer is needed.
On an architectural point of view, things become more complicated.
First of all I have to mention, that having an instance of NSArray does not mean, that it is immutable. This is, because in Cocoa the mutable variants of collections are subclasses of the immutable variants. Therefore an instance of NSMutableArray is an instance of NSArray, but obviously mutable.
One can say that this was no good idea, especially when thinking about Barbara and Jeanette and there is a relation to the circle-ellipse problem, which is not easy to solve. However, it is as it is.
So only the docs can give you the information, whether a returned instance is immutable or not. Or you do a runtime check. For this reason, some people always do a -copy on every mutable collection.
However, mutability is another root of all evil. Therefore: If it is possible, always create an instance of NSArray as final result. Write that in your docs, if you return that instance from a method (esp. getter) or not, so everyone can rely on immutability or not. This prevents unexpected changes "behind the scene". This is important, not 0.000000000003 sec runtime or 130 bytes of memory.
This test gives the best answer:
Method 1:
NSTimeInterval start = [NSDate timeIntervalSinceReferenceDate];
NSMutableArray *mutableItems = [[NSMutableArray alloc] initWithCapacity:1000];
for (int i = 0; i < 10000; i++) {
[mutableItems addObject:[NSDate date]];
}
NSTimeInterval end = [NSDate timeIntervalSinceReferenceDate];
NSLog(#"elapsed time = %g", (end - start) * 1000.0);
Method 2:
...
NSArray *items = [[[NSArray alloc] init] autorelease];
or (int i = 0; i < 10000; i++) {
items = [items arrayByAddingObject:[NSDate date]];
}
...
Output:
Method 1: elapsed time = 0.011135 seconds.
Method 2: elapsed time = 9.712520 seconds.
I'm new to objective C, I have a NSMutableArray with 3 objects in it, then I try to print the retainCount of the array. Why the final retainCount return -1? Thanks
NSLog(#"myArray has retain count of %d", [myArray retainCount]);
[myArray release];
NSLog(#"myArray has retain count of %d", [myArray retainCount]);
Result from console:
2010-10-17 11:58:06.407 TestRetainCount [527:a0f] myArray has retain count of 1
2010-10-17 11:58:06.407 TestRetainCount [527:a0f] myArray has retain count of -1
After an object has been deallocated (which may happen after a release), you can no longer rely on its data being intact. You're trying to trust the retain count after it has become invalid.
On a general note, don't use the retain count. Ever. Use the rules in the memory management programming guide, and you'll always get the reference counting correct.
Graham Lee answered the question specific to your example.
not specific to your example, but to your question (subject):
UINT_MAX is often used to denote an object which uses no reference counting (e.g., is never deallocated, such as a singleton), or a custom reference counting implementation.
Can't rely on an accurate retainCount because of the timing autoreleased objects. That said, count your alloc/init, new, retains, etc... and match with corresponding release.
Can someone explain why the last line prints out -1? It happens when copy is called on a NSMutableString, I expect the returnCount of strFour to be 1, since an immutable copy should be returned.
NSMutableString *str =[NSMutableString stringWithString:#"hi"];
NSLog(#"new instantiated variable has a retain cout:");
NSLog(#"%d",[str retainCount]); //1, str is a pointer, its value is a memory address
NSMutableString *strFour =[str copy]; //receiver str is mutable, copy returns an immutable
NSLog(#"%d",[strFour retainCount]); ////strFour s retain count should be 1, but it had a retain count of -1
Thanks a lot.
Never bother looking at the retain count of an object. It is always meaningless to do so. The retain count is likely affected by optimisations “under the hood”. These optimisations rely on the fact that your code follows the Cocoa Memory Management Guidelines. Just worry about sticking to those guidelines and don't bother looking directly at the retain count.
One reason why it could be (-1) because the string “hi” may be cached somewhere and your copy is referring to the cached string. Keep in mind that the retain count is actually an unsigned integer. The documentation dor -retainCount says that for objects that never get released, the retain count should be UINT_MAX (which when printed as a signed decimal will come out as “-1”).