Why I'm getting memory leaks with xmlTextReaderConstValue? - objective-c

I'm writing my own wrapper class for parsing XML data. Usually I use the Leak Performance Tool to detect suspicios behaviour through forgetting to release allocated memory.
At this time I figured out that the following code (the first line becomes marked by the tool) brings me an enormous memory leak (leaks more the bigger the XML data file becomes).
the following part is used to receive the text inside a Node.
NSString *currentTagValue = [NSString stringWithCString:(char *)xmlTextReaderConstValue(XMLReader) encoding:NSUTF8StringEncoding];
SEL selector = NSSelectorFromString([NSString stringWithFormat:#"set%#:", [currentTag capitalizedString]]);
[currentItem performSelector:selector withObject:currentTagValue];
If I add
[currentTagValue release]
the memory leaks are gone.
This seems strange to me, because I don't allocate memory for the NSString manually. That's why I thought it would be autoreleased.
The whole situation becomes stranger if I compare the upper code example with the part that is responsible for obtaining the node name.
NSString *currentTagName = [NSString stringWithCString:(char *)xmlTextReaderConstName(XMLReader) encoding:NSUTF8StringEncoding];
SEL selector = NSSelectorFromString([NSString stringWithFormat:#"set%#:", [currentTagName capitalizedString]]);
Here I dont't have to add a manual release, everything works fine and I'm getting no memory leak.
I'm not sure if my described problem is a side-effect of the xml...ConstValue function (the working part uses xml...ConstName) or if the reason is the performed selector afterwards.
Thanks for reading, I hope anyone can explain it to me.

Are you using libxml2? I haven't used libxml2 yet, but I googled quickly and found this:
http://xmlsoft.org/html/libxml-xmlreader.html
Function: xmlTextReaderConstValue
Returns: the string or NULL if not
available. The result will be
deallocated on the next Read()
operation.
Compare that with xmlTextReaderConstName
Function: xmlTextReaderConstName
Returns: the local name or NULL if not
available, the string is deallocated
with the reader.
It may be a leak in the lib, or a false alarm as the result seems to be on a delayed release (or something entirely different as I have no firsthand experience to say otherwise). Is the program crashing because of the leak or not? If it is not, maybe it's just a false alarm.
Hope it helps.

Related

NSOperationQueue methods parameter are becoming empty

I am having problem with NSOperationQueue, if I am adding the same operation for 200 times method is behaving as expected.
But if I increase the for loop to 500 times, parameter are becoming empty when queue will start executing the task. Below is the code snippet.
- (void)someMethod:(char *)param1 {
NSBlockOperation *theOp = [NSBlockOperation blockOperationWithBlock: ^{
// use this paramter and do something
}];
[[MyQueueService sharedMyQueueService] operationQueue] addOperation:theOp];
}
This is how I am invoking the above method
for (int index = 1; index < 500; index++) {
MyClass *classInstance = [[MyClass alloc] init];
NSString *parm1 = [NSString stringWithFormat:#"%d", index];
[classInstance someMethod:(char *)[string cStringUsingEncoding:NSUTF8StringEncoding]];
}
Here is becoming empty i.e "", if i run the same method for 500 time, due to this I am unable to perform other operation. Please help me regarding this.
The problem is not with NSOperationQueue. The issue is the use of char *. As the documentation for cStringUsingEncoding says:
The returned C string is guaranteed to be valid only until either the receiver is freed, or until the current memory is emptied, whichever occurs first. You should copy the C string or use getCString:maxLength:encoding: if it needs to store the C string beyond this time.
Bottom line, simple C pointers like char * do not participate in (automatic) reference counting. Your code is using dangling pointers to unmanaged buffer pointers. This is exceedingly dangerous and when not done properly (as in this case), will lead to undefined behavior. The resulting behavior is dictated whether the memory in question happened to be reused for other purposes in the intervening time, which can lead to unpredictable behavior that changes based upon completely unrelated factors (e.g. the loop count or whatever).
You should try running your app with the "address sanitizer" turned on (found in the scheme settings under the "run" settings, on the diagnostics tab) and it will likely report some of these issues. E.g. when I ran your code with address sanitizer, it reported:
==15249==ERROR: AddressSanitizer: heap-use-after-free on address 0x60300003a458 at pc 0x00010ba3bde6 bp 0x70000e837880 sp 0x70000e837028
For more information, see Address Sanitizer documentation or its introductory video.
The easiest solution is going to be to eliminate the char * and instead use the int index value or use an object, such as NSString *.

Error when using "setValue: forKey:" on an NSString's isa pointer then calling [string class]

Heres's what I've got as to error.
libobjc.A.dylib`_objc_trap():
0x14c13f4: pushl %ebp
0x14c13f5: movl %esp, %ebp
0x14c13f7: ud2
So basically I'm trying to understand How NSString works and trying to find a way to change the pointer that points to "real (char *) string" which is said to be a constant.
So, I found there is a pointer called isa which points to (__NSCFConstantString *).
It led me to think that if I change that pointer then I could change the string.
The code I tried was this:
NSString *st3 = [[NSString alloc] initWithString:#"hihi"];
[st3 setValue:#"change" forKey:#"isa"];
And, the result showing that
Before:
After:
It seems changed but it changed every NSString object that has #"hihi" string.
And then what I did was [st3 class] hoping it will give the isa pointer then I got that error message posted on the top.
Could you anyone explain what's going on and why it behaves like this?
And, it there any way to intern (i'm not so sure about the term) like in Java?
Please avoid saying just use "NSMutableString" I'm just trying to figure it out seeing there might be some way to do it.
Just use NSMutableString; mutability is why it exists. ;)
NSString is constant. Not just "wraps a const char *" constant, but "no, really, this thing is immutable and the storage details are entirely opaque to you".
In fact, an __NSCFConstantString isn't even stored on the heap at all; there will be no mallocd chunk of memory you can muck with. Such strings are generated by the compiler and the linker lays them down in a chunk of memory that will be read in at runtime and stored on read-only pages of memory.
But not even the results of [NSString stringWithFormat:#"%d World", 42] will have storage in a chunk of uniquely allocated malloc()d memory just for the string buffer. Most likely (but maybe not -- it is an opaque implementation detail), the mechanism for constructing the formatted string will yield an object whose size will be the minimum instance size for whatever private subclass of NSString (see class clusters) is most appropriate + however many bytes are needed to store the string data itself.
While you could potentially find the actual bytes in memory and muck with them directly, that would be a gross violation of encapsulation and utterly useless in a real program.
Note that the isa you are mucking with is an instance variable of NSObject. It is a pointer to the Class object of the instance. It is not necessarily constant, but you really shouldn't muck with it, either.
For more information on the isa and why you are seeing that particular crash when you stuck an NSString instance into the isa slot, my answer to this question may be helpful (maybe):
objc_msgSend() dispatch table
BTW: Love the question -- while you are going down a path that is completely at odds with OO programming and the Foundation, mucking about with the innards and wildly breaking things is a fantastic way to learn! Don't let the down voters get you...well...down, if they should appear.

Why does this ternary operation causes memory growth

The following line causes memory growth (no releases, only one malloc line in instruments) when testing with mark generation feature of allocations instrument
- (instancetype)initWithTitle:(NSString *)title andImageNamed:(NSString *)imageName andButtonProperties:(NOZSKButtonNodeProperties *)buttonProperties
{
...
textNode.text = buttonProperties.buttonTitleIsUppercase ?
[title uppercaseStringWithLocale:[NSLocale currentLocale] ] : title;
...
}
Here is the code that calls it
NOZSKButtonNodeProperties *props = [self getThreeButtonProps];
...
props.buttonTitleIsUppercase = YES;
...
// this initializer is calling the above initializer by passing nil for imageName arg
NOZSKButtonNode *btn = [[NOZSKButtonNode alloc] initWithTitle:#"Play Again" andButtonProperties:props];
-uppercaseStringWithLocale: makes an upper-case copy of the title string. This requires allocation. And although you didn't show this, I assume textNode both retains its text property and has a wider scope than the -initWithTitle:andImageNamed:andButtonProperties: method and therefore continues to live afterwards.
Without more complete code it is not possible to be sure, but here is a guess in case it helps.
It sounds like you might be chasing a ghost. Are you seeing increasing memory due to this code across different iterations of your run loop?
Explanation: Even with ARC some memory is placed in the "autorelease pool" rather than being immediately deallocated when no longer required. This is an unfortunate legacy of MRC. ARC is able to avoid some uses of the autorelease pool and deallocate memory quicker, but not all uses.
The autorelease pool is typically emptied once per iteration of your main run loop. If you allocate and release a lot of memory in response to a single event, say in a long loop, it can be worth while wrapping the offending code in an #autorelease { ... } block which creates, and empties on exit, a local autorelease pool. This helps keep the peak memory usage down - it doesn't deallocate any more memory overall, it just does it cleanup sooner.
When you altered your code and saw an apparent improvement you may just have reorganised your code in a way more amenable to ARC's optimisations which reduce use of the autorelease pool, and so memory is deallocated sooner.
You only need to be concerned if (a) memory is increasing across multiple events or (b) you are hitting a too high peak memory use. Under ARC (a) should be rare, while (b) requires locating the source and wrapping it in an #autorelease { ... } block.
HTH
textNode.text = buttonProperties.buttonTitleIsUppercase ?
[title uppercaseStringWithLocale:[NSLocale currentLocale] ] : title;
When you write [title uppercaseStringWithLocale:], this method has to create a new instance in order to change the given string to upper case. This is going to increase the memory usage of your program because this new string has to be allocated.
Also, it would have helped to know whether or not you are using ARC, because I don't think this should be an issue with ARC.

Object released with CFRelease causes obvious crash, but only rarely

I have the following method:
+ (NSString*) getMD5HashFromFile:(NSString*)filePath {
CFStringRef md5hash = FileMD5HashCreateWithPath((CFStringRef)filePath, FileHashDefaultChunkSizeForReadingData);
NSString *hashStr = (NSString*)md5hash;
CFRelease(md5hash);
return hashStr;
}
I was getting random crashes on the Simulator, about 1 in 20-30 executions. The fact that this wasn't consistent didn't help me dig deeper before.
Now that I see the code again, it seems obvious that md5hash gets released before being returned, which means the returned object is invalidated. The returned value is used in another method in a consistent way that crashes sometimes, but not always. My question is why this only happens rarely and not always.
Does it have something to do with the mix of Obj-C and C code and the way autorelease pools work?
Note: The bug seems to be fixed by using NSString *hashStr = [NSString stringWithString:(NSString*)md5hash], which makes total sense to me.
Just because a piece of memory is released and deallocated doesn't mean that it's immediately returned to the OS. Your application can hold onto it for an arbitrary period of time based on numerous factors and at several layers. The OS has more important things to do sometimes than reclaim every piece of memory you let go of and might ask for again in half a second. Accessing memory that you've called free() on, but technically own, does not generate a signal. This is why MallocScribble exists. It overwrites memory that you free with trash (0x55) so that it's more obvious when you use freed memory.
Try the following:
char *foo = malloc(100);
strcpy(foo, "stuff");
free(foo);
printf("%s", foo);
Most of the time that'll work fine, despite being completely wrong. Now, edit your Scheme>Diagnostics and Enable Scribble. Re-run and you'll see a bunch of "U" (0x55) indicating that you're reading nonsense. But it still won't crash.
You may be interested in Matt Gallagher's A look at how malloc works on the Mac for a bit more on the topic.
CFRelease argument must not be NULL.
If CFRelease argument is NULL, this will cause a runtime error and
your application will crash
if(md5hash)
CFRelease(md5hash);
+(NSString*) getMD5HashFromFile:(NSString*)filePath {
CFStringRef md5hash = FileMD5HashCreateWithPath((CFStringRef)filePath, FileHashDefaultChunkSizeForReadingData);
NSString *hashStr = [(NSString*)md5hash copy];
CFRelease(md5hash);
return [hashStr autorelease];
}
make sure to retain the returned value in the caller if you need to hang on to it for any length of time.

Debugging unexpected error message - possible memory management problem?

I am trying to debug an application that is throwing up strange (to my untutored eyed) errors. When I try to simply log the count of an array...
NSLog(#"Array has %i items", [[self startingPlayers] count]);
...I sometimes get an error:
-[NSCFString count]: unrecognized selector sent to instance 0x1002af600
or other times
-[NSConcreteNotification count]: unrecognized selector sent to instance 0x1002af600
I am not sending 'count' to any NSString or NSNotification, and this line of code works fine normally.
A Theory...
Although the error varies, the crash happens at predictable times, immediately after I have run through some other code where I'm thinking I might have a memory management issue. Is it possible that the object reference is still pointing to something that is meant to be destroyed? Sorry if my terms are off, but perhaps it's expecting the array at the address it calls 'count' on, but finds another previous object that shouldn't still be there (eg an NSString)? Would this cause the problem?
If so, what is the most efficient way to debug and find out what is that address? Most of my debugging up until now involves inserting NSLogs, so this would be a good opportunity to learn how to use the debugger.
This is a sign that the memory location at which your code is expecting your array to live has either:
Been deallocated and another variable has been allocated in the same place
Been clobbered by some bad code
My bet would be on the first one. You'll want to carefully look at where you are allocating the array and make sure that you're not allowing its retain count to reach zero.
Remember that if you're allocating the array using a convenience method (basically one that starts with array) and not either retaining it or assigning it using dot notation (e.g. self.myArray = [NSArray arrayWith...]) and a property marked retain, it will be freed possibly as soon as the method in which you allocated it returns.
TL;DR is to check where you're assigning the array and make sure you're using something like this:
self.startingPlayers = [NSArray arrayWithObjects:#"first", #"second", nil];
and not like this:
startingPlayers = [NSArray arrayWithObjects:#"first", #"second", nil];
That one's bitten me countless times, including in the middle of a presentation right after I mentioned not to do it.
What does [self startingPlayers] return? Try printing that first:
NSLog("startingPlayers is %#", self.startingPlayers);
Perhaps startingPlayers contains a bad pointer (uninitialized) or a pointer to something that has already been released (and reused for something else).