Objective C: [MyObject alloc] now crashes under iOS SDK 4.1 - iphone

I'm working on an existing, large-ish codebase, and after upgrading the iOS SDK to 4.1 I am now seeing very strange behaviour. The crux of the matter appears to be a particular class that will no longer alloc - it is throwing a bad access in obj_msgSend, and seems to be the Class object on the stack that objc_msgSend doesn't like - although it is not actually NULL.
The original failing line looked like this:-
tileProjection = [[RMFractalTileProjection alloc] initFromProjection:proj tileSideLength:sideLength maxZoom:18];
I deconstructed this to isolate the problem:-
RMFractalTileProjection *p = [RMFractalTileProjection alloc]; // <- this crashes
p = [p initFromProjection:proj tileSideLength:sideLength maxZoom:18];
tileProjection = p;
I then tried this:-
Class c = NSClassFromString(#"RMFractalTileProjection");
assert(c);
NSLog( #"RMFractalTileProjection class(ptr) %p", c ); // <- prints an address OK
NSLog( #"RMFractalTileProjection class(obj) %#", c ); // <- crashes
In the debugger it looks like the Class object is sensible, but NSLog crashes when it tries to print it.
One thing to note: the class in question is declared as below, and I'm not sure if the protocol is causing a problem. Because this particular part is a large chunk of open source code, it is very difficult to remove this protocol requirement to see if that makes a difference.
#interface RMFractalTileProjection : NSObject<RMMercatorToTileProjection>
{
...
}
Any help on this one greatly appreciated - it is a show stopper.
Thanks

This is not really an answer but some ideas to move forward.
The only causes that leap to mind at the moment are memory corruption and some sort of link issue. Perhaps you are linking two versions of the class somehow.
Assuming this is the class, there doesn't look to be anything wrong to make it crash in alloc. There's no +initialize or anything.
Questions I would be asking myself and trying to answer are:
what happens if I rename the class?
what happens if I create a new identical class with a different name?
the pointer that gets passed to obj_msgSend: is it reasonable? does it point to something that looks like a class?
do you ever subclass the class and do you use initialize on the subclass?
is the pointer always the same? If so you can watch what it points to and see if it changes during execution.
what happens if you send self to the class?

OK, finally found this. As Jeremy suggested, this turned out to be a regular memory stomper.
The difficulty I had finding it was that it wasn't the Class object itself that was getting stomped, but the class' metaclass structure - which is a normal Class object but one level up, referenced by the class 'isa' pointer. That's why the class looked OK to me when I inspected it in the debugger - I need to follow the isa pointer and dump memory at one level up to find this. Luckily for me, the class was only a subclass of NSObject - had it been deeply subclassed, this could have been much harder to find. I got my first clue after biting the bullet, reverse-engineering objc_msgSend, working out exactly what was on the stack frame, and following all the pointers. Yep, the hard way :)
Matt Gallaghar's post (and various others I found by following links) were invaluable in helping me through this maze - thanks guys!
Burned a lot of time on this one, but on the up side I learned a hell of a lot about Objective C internals during the past day and a half :)

Thanks for these suggestions JeremyP - it is always good to have fresh suggestions after you've been banging your head against the keyboard all day!
Your suggestion of creating an identical class with the same name appears to have fixed the problem. I have no idea why and I feel I need to understand what's going on here. You're right it sounds like some kind of linker issue, but I still have no idea what could cause such a serious runtime error and not even produce a warning at build time.
Re. the pointer, it does look reasonable, but something inside the class eventually gets dereferenced as a null pointer inside objc_msgSend. Occasionally, after I have changed the code and rebuilt, I get a null pointer instead. This behaviour obviously suggests something nondeterministic like a memory stomp.
I'll post my findings.

Related

Passing a pointer that is part of an object (Cocoa)

OK, I have a custom object (an NSManagedObject subclass, if it matters) and I want to pass a pointer to one of its iVars to a function that I've set up to modify such values. With a normal pointer you'd just prefix it with an ampersand (&) as in the classic NSError &error example, but that can't be done with dot notation. I can't just pass &object.iVar as I'd hoped. Can anyone suggest a simple and elegant way to obtain the pointer of iVar so that I can pass it? I am loath to pass the entire object for reasons of code structure and neatness.
-Ash
Argh, as is almost always the case, I ask a question after an hour of frustrating puzzling then ten minutes later answer it myself. I don't know, maybe asking questions is some kind of therapeutic trigger for answers... shame this isn't a psychology website.
Anyway, my solution was to add a new 'pseudo-getter' method to the object I'm trying to access the pointer from that looks a bit like this:
- (Pointer **)getIVarPointer
{
return &iVar;
}
It's a bit cludgy, but since I only have that one iVar whose pointer I need to obtain it's not too bad. On ther other hand if there is a simpler, more 'official' way of doing this, I'd love to know it!

Setter failing selectively in NSManagedObject

I have a NSManagedObject which I'm trying to instantiate with given values. I access the setters like so:
object.couchID = (NSString *)[dictObject objectForKey:#"_id"];
...and this works fine on my machine, but my partner gets this error when he runs it on his machine:
'-[NSCFString type]: unrecognized selector sent to instance 0x4e465e0'
About 90% of the setters (all formatted in the same way) work on my partner's machine, but a good 10% fail with that error. All of them work on my machine.
We're running the exact same code (according to SVN (yes, I know)), and fetching the same data from the same server, so everything seems like it should work.
We've checked the objects being passed, and they're the same. Commenting out the setter allows the code to get through to the next troublesome setter, but of course we need it to actually work. How else should we troubleshoot? Thanks in advance.
Update 1: Unlocked the Tumbleweed badge for that one... guess it's too sticky to touch? Any thoughts or guesses are welcome. And hey, you could earn 50 points.*
Update 2: the mixed-good-news is that checking out a fresh version from source control results in the same problem on my machine, so a) it's definitely something in the code, and b) I can more actively troubleshoot. Thanks for all your suggestions so far, I'm going to go through them all again.
I ran into something similar at work the other day. I suspect that one of you has a stale .momd file inside the app bundle, and that it's not being replaced when it gets upgraded. I suspect this is a bug in Xcode 4, though I haven't totally verified it yet. If your partner deletes the app completely and then installs the app, does the error go away?
You may need to create a temporary variable whose value is object cast to whatever the actual class is, e.g.
MyClass *c = (MyClass *)object; // if object is in fact a MyClass instance
c.couchID = (NSString *)[dictObject objectForKey:#"_id"];
I have seen cases where the compiler cannot make mental leap and realize that your attribute is the class you know it is. The solution for me in these cases has been to be more explicit. Does this make sense? It's worth a shot at least, no? :-)
if this code fails on your partner's machine:
someManagedObject.couchID = #"some hardcoded string";
seems like you have a dangling pointer: i would check that someManagedObject is properly retained and still a valid object when you try to call the -setCouchID method on it.
I have had nearly the same problem when trying to draw a CATiledLayer with data in NSManagedObjects. What should be a valid object barfs with an "unrecognised selector" exception
It nearly always happens because theres no retain on the object external to the point where you are trying to set or get the property. Being in a separate thread seemed to have a relationship too.
After fruitlessly trying to get round this with [NSManagedObjectContext lock] and retain on the context within the new thread I eventually just threw the contents of my fetch into a mutable set to try and keep a grip on it which seems to work on iOS but not on OS X so well.
So a couple of possibilities
Are you doing this not in the main
thread and does the MOC have a retain
within that thread. Check the docs
for [NSManagedObjectContext lock]. But essentially each thread working with the context needs its own retain on the context.
Try throwing it into a container
while you operate on it. Make it a
bit stickier. Sorry if that sounds
like voodoo but it is.

Is there a way to "find mystery retains" ...?

Recently I was repairing someone's code. There was a big class that would not dealloc. You'd have to hit it with 5 or 6 releases to get it to dealloc.
I carefully looked through the big class and eventually found the various things that needed to be released.
This got me thinking: there just has to be some really easy way to "find" all the retains on an object .. am I right?
So, is there a simple way to "find all the retains" on an object? Is there a button in XCode or Instruments that everyone else knows about?
What do you do when you can't find a mystery retain like that?
So in the iOS universe, if anyone knows the "Show where all the retains came from on this object" button -- thanks!
P.S. Note that there is no leak, and this question is totally unrelated to leaks. The object simply "perfectly correctly" wouldn't release.
Later ..
Truly astounding solution by Fabio:
Fabio has provided an astounding solution to this problem. In nine words, here it is:
-(id)retain
{
NSLog(#"%#", [NSThread callStackSymbols]);
return ([super retain]);
}
That is amazingly useful in many situations and leads to many other useful things. You've probably saved me two man-weeks of work per annum forever, Fabio. Thanks!
BTW if you're just getting to grips with this and struggling with the output, I saw that typically there will be many chunks featuring "UINib instantiateWithOwner:". It looks like those will come first, the significant chunks will follow.
Instruments can show you the call stack for every malloc, release, and retain for any Obj-C object in your app with no code changes required. It works when you're using ARC, which is not the case for the solution from fabio.
It's really useful for finding those mystery retains - e.g. when an object just won't dealloc when it should.
Here's how:
CMD + I (Product / Profile)
When Instruments pops up choose 'Allocations' (NOT Leaks)
Your app should run.
Do whatever causes your mystery retains to happen.
Select the 'Allocation' instrument on the left-hand panel.
Press CMD + 1 or select the circle with the wave in it on the right. In the panel on the lower right, tick the 'Record reference counts' option. This is important, or only mallocs and frees will be recorded.
In the search box on the top-right of the list, type the name of your class (e.g. BCMyObject).
This filters the list of 'Statistics' to show how many instances of your class are currently live. The #Persistent column shows how many instances are live.
Click the row, and then the little arrow -> next to the class name. You'll see the breadcrumbs shows 'Statistics > Allocation Summary > BCMyobject'
This shows you all the instances of said class (and which ones are live).
Select an instance, and click the arrow again (this time by address)
Now you'll see 'Statistics > Allocation Summary > BCMyObject > History: 0xADDRESS' in the breadcrumps.
This'll list every time the object is malloc'd retained or released.
Now in the left panel where the 'Record Reference Counts' option was, press the icon that looks like a bar with boxes connected to it or press CMD + 3.
Select one of the rows and you'll see the complete call stack that led to the call.
Easy! (ish)
Just guessing... but you may overwrite the retain method of the custom class calling super and throwing a nice NSLog to print the call stack.
Update with the actual code from Joe
-(id) retain {
NSLog(#"%#", [NSThread callStackSymbols]);
return ([super retain]);
}
Another important detail is that [NSThread callStackSymbols] returns a NSArray of NSStrings that can be filtered and used for other purposes. For example in complex and dynamic code, to check if a method properly causes another one to fire.
NOTE:
In an ARC environment you will need to first add the -fno-objc-arc to compiler flags to allow you to override retain and call super.
Place a breakpoint on custom class' retain
You could set a symbolic breakpoint on retain and then set it to the retain method of the custom class. The problem here is that retain is a method on NSObject so you will get the choice of all objective-c classes when placing the breakpoint.
In this case it would be better to overwrite the retain method of the custom class with a call to super, so it would not do anything but you could then place a breakpoint in it.
Use a breakpoint action to log the caller
To add a breakpoint action double click on the blue marker. Find the breakpoint in the list and press the + button on the right. Then choose Debugger command and add the GDB command frame 1 in this field, which will show you the caller of the retain. By this you cold log all retains and where they come from. When logging the releases in a similar way you could check what was the extra release.
It is still a bit tedious, but this is the best I can think of.
Instruments and its memory management stuff is your friend. Leaks and Zombies are two of the most valuable tools available. Use them.
Product -> Profile (or Cmd-I)
It is, unfortunately, not easily possible to programmatically determine what "owns" an object, since the idea of "object ownership" is a coding convention (unless you enable garbage collection).
Stack logging is often useful (I usually use a few breakpoints with bt;continue) but that only tells you the function that called retain, not the "bigger picture" (e.g. you might "transfer ownership" with [ivar2 release]; ivar2 = ivar1; ivar1 = nil;). Sometimes it's a UIKit leak so you don't have the source code and you really have to go digging.
If it's not a leak, however, call -release a few times and see where it crashes!
Have you try using "Build & Analyse" in Xcode?
It's great for getting the bottom of objects not being released.

iPhone leak on this line, why?

I am getting a leak on this line and I'm not sure why...
weather.condition = [weather.condition lowercaseString];
weather is a NSMutableArray with a load of NSStrings in? Is there anything obviously wrong with this line or is it a bigger issue?
Thanks
One thing you have to learn about detecting memory leaks, is that leaks doesn't detect the line the leak occurs on per say, it detects where the object that is leaking was retained/copied/created. You need to look elsewhere for the actual leak, posting more code would be helpful. I'll update this answer if you do. Please comment below to indicate you've updated the answer with more code.
I remember i had this problem when i was using stringByReplacingOccurrencesOfString and i had to declare a new string to hold it in, rather than perform it on itself if that makes any sense!:)
If weather.condition is a synthesized retain property, then you could probably get away with that statement without a leak because the synthesized setCondition method will check to see if there is a value assigned to condition, and release it. If you wrote the setCondition method, you are responsible for managing the memory associated with condition.

Memory leak using (void) alloc

I have seen a similar line of code floating about in Apples code:
(void)[[URLRequest alloc] initializeRequestWithValues:postBody url:verifySession httpHeader:nil delegate:self];
URLRequest is my own custom class. I didn't write this and I think the guy that did just grabbed it from Apple's example. To me this should leak and when I test it I'm pretty sure it leaks 16 bytes. Would it? I know how to fix it if it does but wasn't sure as it was taken from Apple's code.
EDIT: The problem was with the SDK, not the above code. See answer below for further details
Thought I might update this as after further testing and the release of iOS4 it has changed.
The above code doesn't leak and the memory footprint of the App returns to normal even after 200 iterations of the code. The leak did occur in iOS3 but was very small, in iOS4 it has completely disappeared both in simulator and device.
Some might wonder why you would want to implement this code but it works and make sense when dealing with lots of different NSURLConnections throughout your code running simultaneously.
Yes. This is a leak, which can easily be fixed by adding an autorelease:
[[[URLRequest alloc] initializeRequestWithValues:postBody url:verifySession httpHeader:nil delegate:self] autorelease];
Perhaps a better fix would be to create a class function that does this:
#interface URLRequest
{
// ...
}
// ...
+ (void) requestWithValues:/* ... */
// ...
#end
Then you could simply use [URLRequest requestWithValues: /* ... */] without invoking alloc.
Not at all sure what this code is supposed to accomplish. It does appear to break every single convention about initialization methods. What's the point of returning a void pointer from an initialization method? The entire point of an initialization method is to return an object. Where in Apple's code examples did you see this?
Having said that, I don't see why it would leak. Since it doesn't return an object there is nothing to leak external to the method. There might be something internally that leaks.
Edit:
It basically does an NSURLConnection.
Because we are submitting a lot of
forms with a lot of different values
we put it in an external class. All
the delegate methods like
didFailWithError: are in NSURLRequest
and connectionDidFinishLoading just
passes the data to its delegate. So it
doesn't really need to return anything
as it is done through a delegate
method.
Yeah, you need to redesign this. At present, this method is just a disaster waiting to happening. If nothing else, everyone else looking at this code will be utterly confused about what you are doing.
If you have no need to retain the object created, then move its allocation and clean up entirely within a method. Change the method name prefix from "initialize" to something like "setup", "configure", "acquire" etc so the name doesn't imply that it creates and returns and object.
If you need a one shot instance of a particular class, use a class method like Michael Aaron Safyan suggested (again without initialize in the name.) The class method should internally initialize an instance, perform the operations needed, return the data to wherever, then dealloc the instance.
That way, you won't have to worry about leaks and everyone else who may read your code (including yourself months down the road) will immediately understand what the code does.