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
Related
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. ;)
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.
arrayWithCapacity is a method defined in NSArray.h and implemented in NSArray.m
When I look at the code that GNUStep provided, I can get that arrayWithCapacity is a normal method that calls initWithCapacity:
+ (id) arrayWithCapacity: (NSUInteger)numItems
{
return AUTORELEASE([[self allocWithZone: NSDefaultMallocZone()]
initWithCapacity: numItems]);
}
And initWithCapacity is a simple method that only does self initialization.
- (id) initWithCapacity: (NSUInteger)numItems
{
self = [self init];
return self;
}
Nothing about memory allocation with number of items executed.
What is the advantage of using the arrayWithCapacity method? Is it better to simply use [[NSArray alloc] init]?
The expectation is that providing an explicit size improves memory allocation as there's no need to adjust the size of the array as items are added. In practice, it is just a hint and there's some evidence that it's not actually used (see this objc.io article on The Foundation Collection Classes).
The methods are actually part of NSMutableArray, not NSArray.
It gives the implementation a hint about how many items you like to store so it can already allocate enough memory up-front instead of growing the memory as-needed, which can be expensive as it might involve allocating, copying from the old memory to the new one, deallocate old memory. And if you add a lot of items this growing might happen a lot, so it's more efficient if the NSMutableArray can allocate the required amount of memory in one go.
Remember that it's just a hint and an implementation may ignore it. In fact, CFArray/CFMutableArray does ignore the capacity when creating a mutable array:
static CFArrayRef __CFArrayInit(CFAllocatorRef allocator, UInt32 flags, CFIndex capacity, const CFArrayCallBacks *callBacks) {
struct __CFArray *memory;
UInt32 size;
...
switch (__CFBitfieldGetValue(flags, 1, 0)) {
case __kCFArrayImmutable:
size += capacity * sizeof(struct __CFArrayBucket);
break;
case __kCFArrayDeque:
case __kCFArrayStorage:
break;
}
...
return (CFArrayRef)memory;
}
CFMutableArrayRef CFArrayCreateMutable(CFAllocatorRef allocator, CFIndex capacity, const CFArrayCallBacks *callBacks) {
...
return (CFMutableArrayRef)__CFArrayInit(allocator, __kCFArrayDeque, capacity, callBacks);
}
According to this article linked by #azsromej, NSMutableArray also seems to ignore the hint.
This applies to NSMutableArray since NSArray does not have the method arrayWithCapacity, this also applies to initWithCapacity:
There is a negligible if any advantage to using arrayWithCapacity from a run-time POV. It can be considered a premature optimization.
It does have a disadvantage or taking some time and/or code to come up with a value that very rarely provides a meaningful time advantage and increases the mental load.
It is also considered a hint and may or not be used, that is an implementation detail Apple can change at any tine.
Note: I have made timing tests with and without the capacity hint and the time saved, if any, was a extremely small percentage.
Also see ridiculousfish for more information on NSArray.
Comment by #bbum: "the docs simply state establishes the initial capacity not that the array has allocated memory to hold all of that capacity. Array's backing store are not linearly allocated chunks of memory and the details change with the size of the array."
If we type
MyObject *obj = [[MyObject alloc] init];
"obj" is a pointer to the memory address.
...When we create an int, we type:
int x = 10;
Why don't we type?
int *x = 10;
The question is, why do we need a pointer to object and not int, float, etc...
Efficiency.
Moving an int from one place to another is easy. Moving an object needs a little bit more work from the CPU. Moving the address of an object is as easy as moving an int.
In plain C, it is common to handle pointers to structs for the same reason. C makes it easy with the -> operator.
There are languages where you can create objects “without a pointer”, on the stack. C++, for example. One great thing about having objects on the stack is that they get automatically deallocated when the scope ends, which helps with memory management. It’s also faster.
One bad thing about having objects on the stack is that they get automatically deallocated when the scope ends and the stack disappears. And since objects are usually longer-lived than local variables, you would have to copy the object’s memory somewhere. It’s entirely possible, but it complicates matters.
And it’s not just the memory lifecycle that’s complicated with stack-based objects. Consider assignment, foo = bar for two object types. If the objects are always pointers (Class*), you just assigned a pointer and got two pointers to the same objects; easy. If foo is stack-based (Class), the assignment semantics starts to get blurry – you could well end with a copy of the original object.
Introducing a rule that all objects are allocated on the heap (“with pointers”) is a great simplification. And as it happens, the speed difference doesn’t matter that much, and the compiler can now also automatically insert code to deallocate heap-based objects after they go out of scope, so it’s generally a win-win situation.
You can have pointer for int, float as well.
Objects are created on heap. To access it, you need the address. Thats why they are of pointer types.
Because that is the nature of an object.
Objective-C is directly derrived from C. That is why objects are referred to as pointers. An int-type variable of the size of an memory address is a pointer.
In the end, an object in memory is not much different from an struct in memory.
However, when working in Objective-C it is advisable to think of these variables as references of objects rather than pointers to an object's memory areas. Think of them like Java does and do not spend much thoughts on how the system manages the references. There are far more important things to think of such as alloc/retain vs. release/autorelease or following the much easier ARC rules respectively.
BTW:
MyObject obj;
That would declare an object, not a pointer. It is not possible in Objective-C (afaik) and certainly not reasonable. But if it was reasonable and possible, that is what the syntax would look like.
int *x;
That does create a pointer to an int. For using it you would have to allocate memory and assign its address to x. Rarerly reasonable in Objective C either but quite useful in standard C.
its the difference between objects being on the stack or the heap.
going int x = 10 x is now on the stack. int *x = 10 is just simply wrong (well most likely not what you want) since that is declaring a pointer to address 10 whatever that may be. you would want int *x = malloc(sizeOf(int)); as CodaFi suggested. that will allocate memory on the heap of the size of an int.
going MyObject *obj = [[Myobject alloc] init]; the compiler behind the scenes is allocating your object to the heap for you, but its basically the same principle
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.