This question already has answers here:
NSString retain count -1
(3 answers)
Closed 7 years ago.
I am doing some simple testing of retaincount, all I get is -1.
For example,
NSString *s1 = [[NSString alloc] init];
NSLog(#"s1 Retain Count = %d",[s1 retainCount]);
NSString *s2 = [NSString string];
NSLog(#"s2 Retain Count = %d",[s2 retainCount]);
I am wondering if there is anyway to get the internal retaincount?
NSLog(#"s1 Retain Count = %lu",(unsigned long)[s1 retainCount]);
If you'd used the correct format specifier — a warning should tell you to do so — you'd see
retaincoundt[11169:438727] s1 Retain Count = 18446744073709551615
retaincoundt[11169:438727] s2 Retain Count = 18446744073709551615
or 264 - 1 -> NSUIntegerMax on 64 bit systems. You are looking on a constant object. The compiler had identified room for optimisation by pointing to the same ever present constant immutable object.
Things like these may happen or not at any time. You can't and shouldn't rely on retainCount.
Take a look at the Apple Doc's..
Declaration OBJECTIVE-C
- (NSUInteger)retainCount Return Value The receiver’s reference count.
Special Considerations This method is 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.
To understand the fundamental rules of memory management that you must
abide by, read Memory Management Policy. To diagnose memory management
problems, use a suitable tool:
The Clang Static analyzer can typically find memory management
problems even before you run your program.
The Object Alloc instrument in the Instruments application (see
Instruments User Guide) can track object allocation and destruction.
Related
I found a memory leak for NSString with objc_setAssociatedObject
Test Code:
int i = 0;
while (YES) {
#autoreleasepool {
NSString *string = [[NSString alloc] initWithFormat:#"%d", i];
// Comment this line. Then the memory leak is gone.
objc_setAssociatedObject(string, "key", [[NSObject alloc] init], OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
i++;
}
NSObject can't release. Not sure for NSString.
If I comment the code objc_setAssociatedObject(string, "key", [[NSObject alloc] init], OBJC_ASSOCIATION_RETAIN_NONATOMIC);. There is no memory leak.
Does anyone know what happened?
Maybe related to Tagged Pointer Strings
Tagger Pointer Strings are actually invalid 64 bit pointers where the content is stored within the pointer itself
this helps prevent unnecessary memory allocation
They are invalid pointers in the sense that they don't point to any real memory value
"""Objects are aligned in memory, such that their address is always at least a multiple of the pointer size, and in practice typically a multiple of 16. Object pointers are stored as a full 64-bit integer, but this alignment means that some of the bits will always be zero.
Tagged pointers take advantage of this fact to give special meaning to object pointers where those bits are not zero. In Apple's 64-bit Objective-C implementation, object pointers with the least significant bit set to one (which is to say, odd numbers) are considered tagged pointers. Instead of doing the standard isa dereference to figure out the class, the next three bits are considered as an index into a tagged class table. This index is used to look up the class of the tagged pointer. The remaining 60 bits are then left up to the tagged class to use as they please."""
This is a beautiful hack.
I think for NSTaggedPointerString teh associated objects might be getting cleared in the next runloop
I made a test for NSString.
int a = 10;
NSString *str1 = #"str";
NSString *str2 = [NSString stringWithFormat:#"str"];
NSString *str3 = #"str";
NSString *str4 = #"str2";
NSLog(#"%p %p | 3 %p | 4 %p | a %p", str1,str2,str3,str4,&a);
result is
0x1286287c8 0xa000000007274733 | 3 0x1286287c8 | 4 0x1286287e8 | a 0x7ffeeb215b14
this test placed in iPhone 6s(11.1) Simulator. So what's the place of str1 in memory? is it Stack or Heap?
Note that your curious value, 0xa000000007274733, is odd - that is the least significant bit is set. Object pointers are usually even - see the other ones you've displayed.
An odd-valued pointer is an indicator that it might be one of Apple's tagged pointers, where instead of allocating an object the data is stored directly in the pointer itself.
The Unicode for r is 0x72, s & t follow. So it looks like you have the Unicode for the characters in there.
Mike Ash did a good article on tagged strings back in 2015, though your value doesn't seem to match the scheme he discovered. However I'd go with this being a tagged pointer of some kind.
HTH
Addendum
I see you edited the question to ask whether the literal is in the stack or heap, and that made be realise you were asking about str1, oops. The literal will be in a "data segment", in the same area of memory as the code.
To see this try allocating a general object (an NSObject will do, but not an NSString which tend to get special treatment) to see where a heap object goes, and taking the address of a C function to see where the code goes. You'll probably find str1 is closest to the latter.
So what's the place of str1 in memory? is it Stack or Heap?
A string -- an Objective-C object -- can never be on the stack *. Stack based ObjC objects have never been supported.
An Objective-C object can be in one of three places, though. Well, two, really, and the third is weird.
An object can be on the heap. It is, effectively, in a hunk of memory that was malloc()d (though not always malloc(), but that is an implementation detail) and the first 4 (32 bit) or 8 (64 bit) bytes will be a pointer to the class of the object and, possibly, some metadata encoded into said pointer (i.e. don't just grab the isa and think you've got a valid class).
An object may be in the readonly memory that is mapped into memory by dyld. These objects are actually created by the linker and are a part of the executable. NSString constants fall into this category. NSArray and NSDictionary constants do not; they are still dynamically allocated as per (1) above. Note one oddity; because they are not allocated, they do not follow the string 16 byte (typically) aligned allocation patterns of (1). In fact, they may be 2 byte aligned (they won't likely be odd, but that's another implementation detail that may change).
They may be a tagged pointer. If the lowest order bit is set-- an odd address-- that means "tagged pointer". But that, too, is an implementation detail that can-- and has-- changed. With a tagged pointer, there is no allocation at all. The "pointer" isn't a pointer at all. The pointer is the data; for an NSNumber, the value and type of the contained number is encoded in the object "pointer".
The one exception is a Block. They can be in the heap, in the memory mapped data segment loaded by the executable, or on the stack.
However, that's another implementation detail. Only, in this case, you are exposed to it. While the compiler, under ARC, actually makes it largely fully automatic at this time, there are still a couple of cases where you need to copy a block to ensure it moves to the heap before the declarative scope disappears.
However, since the class of a Block isn't exposed nor is there any means of manually allocating Blocks, I'm going to stick with Objective-C objects never being on the stack. ;)
I am trying to reproduce the huge memory footprint in the for loop.For example, in a for loop like below, if there is not a autorelease pool in it. It will cause a peak memory footprint.
for(int i=0; i< 500000; i++){
NSNumber *test = [NSNumber numberWithInt:i];
}
Because the expression [NSNumber numberWithInt:i] return a autorelease object and if no autorelease is in the for loop, all the autoreleased objects will be released once, this will cause a peak memory footprint.
But as I tested in the Xcode6, this result was not reproduced. I used this totalSize += malloc_size((__bridge const void *) test); to calculate the total size of all the objects, but I got totalSize = 0 result.
I can reproduce the huge memory footprint with the code like [NSString stringWithFormat:#"%d ", i];.
Can some one explain this question, thanks in advance.
Small NSNumbers (those that fit in 60 bit) are stored as tagged pointers. It's the actual 64-bit pointer value that carries the numeric value, so there's no heap allocation going on. This is done transparently for you by the Obj-C runtime.
More on this topic: https://mikeash.com/pyblog/friday-qa-2012-07-27-lets-build-tagged-pointers.html
Short NSString's are stored as tagged pointers as well. See Mike Ash's excellent article on tagged pointer strings: https://mikeash.com/pyblog/friday-qa-2015-07-31-tagged-pointer-strings.html
In the for loop you pasted, the test variable is created and immediately destroyed.
It belongs to the for's scope and since nothing else references it from outside, it will be automatically deallocated by ARC at the end of the loop.
That's why your memory footprint doesn't grow up.
I have an instant variable "obj1" in MyClass.h.
#property (nonatomic, retain) NSMutableArray *arrObj1;
and in MyClass.m viewDidLoad method, i have allocated another object "obj2" and assign that to obj1.
- (void)viewDidLoad {
[super viewDidLoad];
NSMutableArray *arrObj2 = [[NSMutableArray alloc] initWithObjects:#"a",#"b",#"c", nil];
NSLog(#"arrObj2 count %lu",(unsigned long)[arrObj2 retainCount]);
self.arrObj1 = arrObj2;
NSLog(#"arrObj1 count %lu",(unsigned long)[self.arrObj1 retainCount]);
NSLog(#"arrObj2 count %lu",(unsigned long)[arrObj2 retainCount]);
}
Here is the output
2015-08-19 11:06:14.461 MyClass[1812:24133] arrObj2 count 1
2015-08-19 11:06:16.332 MyClass[1812:24133] arrObj1 count 2
2015-08-19 11:06:17.327 MyClass[1812:24133] arrObj2 count 2
I am not getting how arrObj2 get retain Count value 2.
please explain me.
Thank you
A more relevant question to your problem is: Why do arrObj and arrObj2 seem to share properties? And the answer is they're two references to the same object. That means the same array is referenced strongly in the same place; that is, arrObj1 and arrObj2 are actually the same array. That's why you see a retainCount of 2, though you shouldn't use retainCount for this. (More on that later.) Compare the addresses of the arrays in the debugger instead; they will be the same. If you add an object to one array, it'll be added to "the other."
You can fix that by using copy on the property declaration instead, or by changing self.arrObj1 = arrObj2 to self.arrObj1 = [arrObj2 mutableCopy] . (Edit: I'm not actually sure changing the property type to copy will help here, since it's a mutable type. It would work if it was just an NSArray, though.)
I hope that answers your question, but now I want to explain why it's the wrong question. Please don't take any offence, because learning this is important and not easy. :)
Think in terms of relationship type, not retain/release
You shouldn't be thinking in terms of retain counts at all, but in terms of strong and weak relationship: A strong relationship expresses an ownership interest in the object, a weak relationship doesn't and will be converted to nil shortly after all ownership interests in the object are removed. Also, note that the retain property type is deprecated: it should be strong in ARC code.
Don't use retainCount.
Don't use retainCount. Or, if you prefer, When to use Retain Count:
Never.
Then there's Apple's official documentation, which begins:
This method is of no value in debugging memory management issues.
Or there's this from bbum, which I consider even better. He goes into great detail about why retainCount is useless, concluding with:
Bottom line: the only time the absolute retainCount can be conclusively used for analytic purposes is if you have the backtrace of every retain and release that contributed to the current retain count’s value (there is another use case, documented at the end). If you have that, then you don’t need the retain count and, fortunately, Instruments is already generally quite adept at producing a per-object inventory of retains and releases for you.
Basically, it's value is not useful. It will return (in some cases) the number of times an object has been retained without releasing. It does not (and can not) take the number of times it has been autoreleased into account. And it will not always return the "correct" value even without considering autorelease, since some objects can never be deallocated from RAM.
in Objective-C an object is a reference type.
The retain count is increased the first time when the object was allocated.
When the object is assigned to arrObj1 the object is not copied when the property is declared as retain or strong so arrObj1 and arrObj2 contain the pointer to the same object. The implicit setter of the property arrObj1 increases the retain counter a second time.
Memory management is the programming discipline of managing the life cycles of objects and freeing them when they are no longer needed.
Managing object memory is a matter of performance; if an application doesn’t free unneeded objects, its memory footprint grows and performance suffers.
Memory management in a Cocoa application that doesn’t use garbage collection is based on a reference counting model. When you create or copy an object, its retain count is 1.
Thereafter other objects may express an ownership interest in your object, which increments its retain count.
The owners of an object may also relinquish their ownership interest in it, which decrements the retain count. When the retain count becomes zero, the object is deallocated (destroyed).
Check this image.
https://developer.apple.com/library/ios/documentation/General/Conceptual/DevPedia-CocoaCore/Art/memory_management.jpg
Hope this help.
After the statement
self.arrObj1 = arrObj2;
the variable arrObj2 and the property self.arrObj1 point to the very same object.
If they point to the same object, and arrObj2 has a reference count of 2, what reference count do you expect self.arrObj1 to have?
Reason why retain count is coming as 2 is because you have used retain for NSMutableArray *arrObj1, so in effect
when you allocate arrObj2 retain count become 1,
the when you assign it to arrObj1 as its having a retain property, count increase - becoming 2.
arrObj2 is same arrObj1 as you have not copied it (as Steven Fisher in above answer pointed out) both has same retain count.
If you make arrObj1 to assign property then count will be remained as 1.
Simply put, the object pointed to by arrObj2 is the same object pointed to by arrObj1. Instruments shows us that this object gets +1 retain count when you called initWithObjects:
And that it got +2 retain count when you called setArrObj1 (i.e. you used the self.arrObj1 = ... syntax):
For demonstrations using Instruments' "Allocations" tool for tracking memory usage, see WWDC 2013 video Fixing Memory Issues and WWDC 2012 video iOS App Performance: Memory. Bottom line, while it will take some practice to efficiently find the correct objects in the "Allocations" tool, this "Record reference counts" feature is invaluable in finding where the strong references to the objects in question were established.
I'm sure you've seen it, but I'd also refer you to the Advanced Memory Management Programming Guide.
When you alloc a new memory area (I call it M) and assign it to obj2, the retainCount of M is 1.
When you do: self.obj1 = obj2.
Because self.obj1 is a retain property, so M will be retain +1, so its retainCount = 2 now.
Cause obj1 & obj2 are references to M, so their retainCount are 2.
Let try [obj1 release] and get retainCount of obj2 again to check.
Hope it helps.
I want to print retain count of NSString in AppDelegate class in didFinishLaunchingWithOptions method
NSString *str = [[NSString alloc] init];
NSLog(#"str retain count %d",[str retainCount]);
but it always gives -1 instead of +1....why ???
NSUIntegerMax means the returned object is immortal. This used to be in the docs, but has since been removed (because -retainCount is very discouraged).
In MRC, some people would actually override -retainCount to prevent their singletons from ever being deallocated.
In this case, it is a logical optimization for [[NSString alloc] init] to return a constant/immortal, rather than creating an empty string for each request. Of course, you should not rely on this detail/behavior in your program.
Apple docs say:
Special Considerations
This method is 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.
So you shouldn't count on its correctness.
Also, -1 is actually largest unsigned integer, not negative value. Retain count returns NSUInteger, so you should use %u instead of %d.
Generally you should no longer use the -retainCount method, as pointed out by others' answers. The reason is, a lot of retain/release work is done inside the NSString class depending on how you create it. When you create an NSString you could actually be creating one of many NSString subclasses (the id return type of the initialisers is not specific).
All NSConstantStrings (those created by #"") are not releasable - they exist for the duration of the program (as specified by both gcc and clang). Thus, Apple have arbitrarily set its retainCount to be the highest possible unsigned integer (or -1 if it is read as signed, as it is here) because it makes no sense for a constant object, alive for the duration of the program, to have a retain count.
When you create an empty string in the way you have, it is likely Apple just automatically point you to the constant #"" in memory as it takes up less space at runtime. As your object is now an NSConstantString, it is implemented to return a retain count of -1.
EDIT: Incidentally, as NSConstantString is a subclass of NSString it must implement the methods that NSString implements, including -retainCount. This is why you can actually call the -retainCount method on a constant string object (and why Apple make it return a special value). This inheritance relationship can be found near the bottom of the NSString.h header.