Debugging unexpected error message - possible memory management problem? - objective-c

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).

Related

AXUIElementRef and ARC - Deallocated instances and __bridge vs __bridge_transfer

I get this error on an NSMutableArray:
-[Not A Type release]: message sent to deallocated instance 0x1006e29c0
It happens on this line:
[_array removeAllObjects];
Now, I understand what the error means but not why it happens in this case.
I've added an NSLog right before the above line like so:
NSLog(#"%#", [_array class]);
And this executes normally and with the correct behavior, this is the log:
2013-03-13 11:19:27.366 App[66921:303] __NSArrayM
2013-03-13 11:19:27.367 App[66921:303] *** -[Not A Type release]: message sent to deallocated instance 0x1006e29c0
So right before removing it doesn't seem to be deallocated..
Even if I remove the removeAllObjects call and replace it with the below line, I still get the same error.
_array = [[NSMutableArray alloc] init];
All I'm doing elsewhere in the code is calling [_array addObject:...]
_array is a strong property:
#property (strong) NSMutableArray *array;
The array is first initialized in the init method.
EDIT: I'm adding AXUIElementRefs to this array like so:
[_array addObject:(__bridge_transfer id)elementRef];
I thought the __bridge_transfer brings it over to ARC and I wouldn't need to manage any of that?
What could be the problem?
UPDATE:
Here is a sample project: http://users.telenet.be/prullen/AXMemory.zip
This is a working project as I'm using __bridge and not __bridge_transfer.
This project contains a simple loop that does the same thing over and over again. This is to demonstrate that with just __bridge, the AXUIElementRefs never get released. They stay in memory. That's what I see in the Instruments profiler too. You can also see the memory usage increasing every few seconds via activity monitor.
The AXUIElementRefs were obtained via AXUIElementCopyMultipleAttributeValues - so I would assume that I would have ownership of them? Nevertheless, changing to __bridge_transfer results in the errors above.
If anyone could take a look at this and let me know what I'm doing wrong, that would be much appreciated.
I get an array of properties via the following statement: (line 60).
AXUIElementCopyMultipleAttributeValues(frontWindowRef, attributes,0,&attributeValues);
Line 110 I assign the AXUIElement (looping over the children array):
element = CFArrayGetValueAtIndex(childrenArrayRef, i);
Line 112 is where I add the AXUIElementRef to the array:
[_array addObject:(__bridge id)(element)];
Line 36 is where I empty it if it's over 500 elements:
if ([_array count] > 500) {
[_array removeAllObjects];
}
You can't just assume that __bridge_transfer "brings it over to ARC and [you] wouldn't need to manage any of that". You have to understand what rights and responsibilities you had for elementRef at the time (which we can't tell from what you've written).
I find it's much easier to understand the bridge casts if you use the corresponding CFBridgingRetain() and CFBridgingRelease() functions. __bridge_transfer is the same as CFBridgingRelease(). So, the question is: do you own elementRef at the time? Is it appropriate for you to release it?
Consider, would the following have been correct?
[_array addObject:(__bridge id)elementRef];
CFRelease(elementRef);
If not, then neither is __bridge_transfer.
From the nature of the error you're getting, I strongly suspect you were not entitled to release elementRef, so you were not entitled to tell ARC that it owns it and must release it. You have caused the AXUIElementRef to be over-released. You should just have used a __bridge cast – no "transfer" and no CF…Release().

ARC and __unsafe_unretained

I think I have a pretty good understanding of ARC and the proper use cases for selecting an appropriate lifetime qualifiers (__strong, __weak, __unsafe_unretained, and __autoreleasing). However, in my testing, I've found one example that doesn't make sense to me.
As I understand it, both __weak and __unsafe_unretained do not add a retain count. Therefore, if there are no other __strong pointers to the object, it is instantly deallocated (with immutable strings being an exception to this rule). The only difference in this process is that __weak pointers are set to nil, and __unsafe_unretained pointers are left alone.
If I create a __weak pointer to a simple, custom object (composed of one NSString property), I see the expected (null) value when trying to access a property:
Test * __weak myTest = [[Test alloc] init];
myTest.myVal = #"Hi!";
NSLog(#"Value: %#", myTest.myVal); // Prints Value: (null)
Similarly, I would expect the __unsafe_unretained lifetime qualifier to cause a crash, due to the resulting dangling pointer. However, it doesn't. In this next test, I see the actual value:
Test * __unsafe_unretained myTest = [[Test alloc] init];
myTest.myVal = #"Hi!";
NSLog(#"Value: %#", myTest.myVal); // Prints Value: Hi!
Why doesn't the __unsafe_unretained object become deallocated?
[EDIT]: The object is being deallocated... if I try to substitute lines 2 - 3 with NSLog(#"%#", myTest); the app crashes (and an overridden dealloc in Test is being called immediately after the first line). I know that immutable strings will continue to be available even with __unsafe_unretained, and that a direct pointer to the NSString would work. I am just surprised that I could set a property on a deallocated object (line 2), and that it could later be dereferenced from a pointer to the deallocated object it belonged to (line 3)! If anyone could explain that, it would definitely answer my question.
I am just surprised that I could set a property on a deallocated object (line 2), and that it could later be dereferenced from a pointer to the deallocated object it belonged to (line 3)! If anyone could explain that, it would definitely answer my question.
When the object is deallocated it is not zeroed. As you have a pointer to the deallocated object and the property value is stored at some offset to that pointer it is possible that storing and retrieving that property value will succeed after deallocation, it is also quite possible that everything will blow up for some reason or other.
That your code works is quite fragile, try debugging it with "Show Disassembly While Debugging" and stepping through, you'll probably hit an access violation, or take down Xcode itself...
You should never be surprised that strange things happen in C, Objective-C, C++ or any of the family; instead reserve your surprise for so few strange things happening!
Because the constant string in objc is a constant pointer to a heap address and the address is still valid.
edited after comment:
Maybe because the memory at the test objects address hasn't been overwritten and still contains that object? Speculating....
You can see when Test is deallocated by implementing its -dealloc method and adding some simple logging.
However, even if Test is deallocated immediately, the memory it occupied in RAM may remain unchanged at the time you call myVal.
#"hi!" produces a static global constant string instance that is, effectively, a singleton. Thus, it'll never be deallocated because it wasn't really allocated in the first place (at least, it really isn't a normal heap allocation).
Anytime you want to explore object lifespan issues, always use a subclass of NSObject both to guarantee behavior and to make it easy to drop in logging hooks by overriding behavior.
Nothing strange there…
You need to have at least 1 strong reference to object to keep it alive.
Test * anTest = [[Test alloc] init];
Test * __weak myTest = anTest;
myTest.myVal = #"Hi!";
NSLog(#"Value: %#", myTest.myVal); // Prints Value: (Hi)

Re-initialize NSMutableArray as NSMutableArray

I was having a problem with my app throwing an exception when calling removeObjectAtIndex on an NSMutableArray, saying that myLocationsArray was declared immutable. All other manipulation on that array was fine, it was most definitely declared correctly etc etc but somewhere in my app it was getting set as immutable. After investigating for a while trying to find where it was getting set immutable, I decided screw it and just redeclared the variable as such:
myLocationsArray = [[NSMutableArray alloc] initWithArray:[defaults
objectForKey:MYLOCATIONSARRAY_KEY]];
right before the removeObjectAtIndex call.
However I know this has got to be badwrong, I'm calling alloc/init twice on the same variable. However it's the only thing that has worked. Is there any way to remind this variable that it is an NSMutableArray without introducing memory leaks like I am?
NSUserDefaults returns immutable copy of your array. It doesn't matter whether you put NSArray or NSMutableArray in it, it always give you immutable copy back.
So, do this to get a mutable copy that you can work with
myLocationsArray = [[NSMutableArray alloc] initWithArray:[[[defaults objectForKey:MYLOCATIONSARRAY_KEY] mutableCopy] autorelease]];
or just this
myLocationsArray = [[defaults objectForKey:MYLOCATIONSARRAY_KEY] mutableCopy];
I would suggest to set a breakpoint on the line where your program is throwing an exception (the one containing removeObjectAtIndex) and inspect with the debugger the real type of the array. If you go with you mouse over the array name, a popup menu will display giving you all the information you need about the pointed object.
What I expect is that you find out this way that the object is an NSArray (vs. NSMutableArray) and then trace back to the point where you initialized it in the first place.
It looks like you're working with NSUserDefaults. All objects you get out of NSUserDefaults are always immutable, regardless of what you stored into it. NSUserDefaults doesn't keep a reference to the specific object you set into it, it keeps the data. It's effectively making a copy. When you get something out of NSUserDefaults, it makes a new (immutable) object from the data it has stored and gives that to you.
Unsurprisingly, you can't change what's stored in NSUserDefaults by mutating what you (think you) stored in it. You can only change what's stored by replacing what you previously stored by storing something anew.
The declaration should not matter; your error is a run-time error. It sounds like your myLocationsArray variable has been assigned an immutable array (NSArray) though whether it is being re-assigned somewhere or was always immutable is impossible to say from your code fragment.

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.

Why I'm getting memory leaks with xmlTextReaderConstValue?

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.