Okay, I'm NOT new to Objective-C but I encountered a strange bug, that i have never seen before. Maybe I only made a small mistake and you're able to see it.
Within one of my functions I start by creating a new object from a custom view-class in the following way:
[buttonsBackground removeFromSuperview];
self.buttonsBackground = [[[PostCommentButtonsBackground alloc] initWithFrame:self.contentView.bounds] autorelease];
buttonsBackground.delegate = self;
But if I jump with the debugger over this block, the debugger claims that buttonsBackground would be a nil-pointer. But adding a NSLog right afterwards with
NSLog(#"%#",self.buttonsBackground);
still prints the line
<PostCommentButtonsBackground: 0x7bd27a0; frame = (0 0; 320 82); layer = <CALayer: 0x6e688b0>>
which clearly means, that it can't be nil. Does anybody have an idea how this can even be possible?
I am using the LLDB Debugger, not the GDB. The property buttonsBackground is declared as
#property(nonatomic, retain)PostCommentButtonsBackground *buttonsBackground;
so a missing retain isn't the case either.
EDIT: Okay, I just saw, that i'm not the only person with the exact same problem. There's another person with the same problem Debugger lldb says my object is nil when is not ?
I'm just leaving this post open anyway, because in the other post people kept claiming, the developer did a bad job with his memory management. In my post you can actually see, that this is not the case.
LLDB with the new Xcode 4.3 sometimes is totally lost, and using a NSLog instead of LLDB will show you that your initialized object is correctly initialized and not nil.
So, this is really a pain when you're in front of a bug.
Related
I have a problem regarding the memory management when adding objects to a NSMutableArray. Weird thing is that it's all working fine for the first 8 objects I add, but when adding a 9th, the application crashes when retrieving this object.
UploadStatus *status = [[UploadStatus alloc] initWithStatus:[NSString stringWithFormat:#"%#: %d", NSLocalizedString(#"uploadPictureSucceeded", #""), pic_id]
andImageInProgress:nil
imageForSuccess:nil
imageForFailed:nil];
[self.delegate notify:status];
[status release];
This is being done on several places with different texts. But this object contains my status that I display in a UITableView.
In the notify method of the delegate I add the UploadStatus object to the NSMutableArray and I reload the UITableView that shows the objects inside that array.
The first 8 times I add a UploadStatus object to the array and reload the table, it shows correctly. But the 9th time I get the error [CFString retain]: message sent to deallocated instance 0x5c655c0. This error occurs when reloading the table in the cellForRowAtIndexPath method.
Weird thing is that it always shows that the objects inside the NSMutableArray are out of scope like in this screenshot:
Nevertheless if I fetch the item, convert it into the UploadStatus class and get the status from it, it all goes smoothly (for the first 8 objects).
Does anybody have a clue why it goes wrong after adding the 9th UploadStatus object to the NSMutableArray?
Thanks a lot for your help!
The problem is with this code:
[NSString stringWithFormat:#"%#: %d", NSLocalizedString(#"uploadPictureSucceeded", #""), pic_id]
You aren't retaining the string, so it goes away on the next execution of the run loop. You're getting lucky with the first 8. They happen to not get overwritten for some reason, or possibly some other object is retaining them. But the 9th one isn't and you finally see the results of the mistake.
You need for the UploadStatus object to retain that string (and later release it).
I note that you're directly accessing your ivars in this block of code rather than using accessors. This is almost certainly the root of your problem (it is the #1 cause of memory management problems in ObjC). Switch to accessors and most of your memory management problems will go away.
You should also go ahead and run the static analyzer (Build>Analyze). It might shed light. The problem is likely not in the above code; it's somewhere that you're storing something, most likely in an an ivar.
I converted my app to ARC and noticed that an object alloc'ed in one of my view controllers was not being dealloc'ed when that view controller was dealloc'ed. It took a while to figure out why. I have Enable Zombie Objects on for my project while debugging and this turned out to be the cause. Consider the following app logic:
1) Users invokes action in RootViewController that causes a SecondaryViewController to be created and presented via presentModalViewController:animated.
2) SecondaryViewController contains an ActionsController that is an NSObject subclass.
3) ActionsController observes a notification via NSNotificationCenter when it is initialized and stops observing when it is dealloc'ed.
4) User dismisses SecondaryViewController to return to RootViewController.
With Enable Zombie Objects turned off, the above works fine, all objects are deallocated. With Enable Zombie Objects on ActionsController is not deallocated even though SecondaryViewController is deallocated.
This caused problems in my app b/c NSNotificationCenter continues to send notifications to ActionsController and the resulting handlers cause the app to crash.
I created a simple app illustrating this at https://github.com/xjones/XJARCTestApp. Look at the console log with Enable Zombie Objects on/off to verify this.
QUESTION(S)
Is this correct behavior of Enable Zombie Objects?
How should I implement this type of logic to eliminate the issue. I would like to continue using Enable Zombie Objects.
EDIT #1: per Kevin's suggestion I've submitted this to Apple and openradar at http://openradar.appspot.com/10537635.
EDIT #2: clarification on a good answer
First, I'm an experienced iOS developer and I fully understand ARC, zombie objects, etc. If I'm missing something, of course, I appreciate any illumination.
Second, it is true that a workaround for this specific crash is to remove actionsController as an observer when secondaryViewController is deallocated. I have also found that if I explicitly set actionsController = nil when secondaryViewController is dealloc'ed it will be dealloc'ed. Both of these are not great workaround b/c they effectively require you to use ARC but code as if you are not using ARC (e.g. nil iVars explicitly in dealloc). A specific solution also doesn't help identify when this would be an issue in other controllers so developers know deterministically when/how to workaround this issue.
A good answer would explain how to deterministically know that you need to do something special wrt an object when using ARC + NSZombieEnabled so it would solve this specific example and also apply generally to a project as a whole w/o leaving the potential for other similar problems.
It is entirely possible that a good answer doesn't exist as this may be a bug in XCode.
thanks all!
Turns out, I've written some serious nonsense
If zombies worked like I originally wrote, turning on zombies would directly lead to innumerable false positives...
There is some isa-swizzling going on, probably in _objc_rootRelease, so any override of dealloc should still be called with zombies enabled. The only thing that won't happen with zombies is the actual call to object_dispose — at least not by default.
What's funny is that, if you do a little logging, you will actually see that even with ARC enabled, your implementation of dealloc will call through to it's superclass's implementation.
I was actually assuming to not see this at all: since ARC generates these funky .cxx_destruct methods to dispose of any __strong ivars of a class, I was expecting to see this method call dealloc — if it's implemented.
Apparently, setting NSZombieEnabled to YES causes .cxx_destruct to not be called at all — at least that's what happened when I've edited your sample project:
zombies off leads to backtrace and both deallocs, while zombies on yields no backtrace and only one dealloc.
If you're interested, the additional logging is contained in a fork of the sample project — works by just running: there are two shared schemes for zombies on/off.
Original (nonsensical) answer:
This is not a bug, but a feature.
And it has nothing to do with ARC.
NSZombieEnabled basically swizzles dealloc for an implementation which, in turn, isa-swizzles that object's type to _NSZombie — a dummy class that blows up, as soon as you send any message to it. This is expected behavior and — if I'm not entirely mistaken — documented.
This is a bug that has been acknowledged by Apple in Technical Q&A QA1758.
You can workaround on iOS 5 and OS X 10.7 by compiling this code into your app:
#import <objc/runtime.h>
#implementation NSObject (ARCZombie)
+ (void) load
{
const char *NSZombieEnabled = getenv("NSZombieEnabled");
if (NSZombieEnabled && tolower(NSZombieEnabled[0]) == 'y')
{
Method dealloc = class_getInstanceMethod(self, #selector(dealloc));
Method arczombie_dealloc = class_getInstanceMethod(self, #selector(arczombie_dealloc));
method_exchangeImplementations(dealloc, arczombie_dealloc);
}
}
- (void) arczombie_dealloc
{
Class aliveClass = object_getClass(self);
[self arczombie_dealloc];
Class zombieClass = object_getClass(self);
object_setClass(self, aliveClass);
objc_destructInstance(self);
object_setClass(self, zombieClass);
}
#end
You will find more information about this workaround in my blog post Debugging with ARC and Zombies enabled.
Turns out it is an iOS bug. Apple has contacted me and indicated they've fixed this in iOS 6.
to answer the second question you would need to remove the observer from NSNotification - that will keep it from calling the view.
Normally, you would do this in the dealloc but with that zombie issue maybe it's not getting called. Maybe you could put that logic in viewDidUnload?
Because you have open NSZombieEnabled, this let the object not call dealloc, and put the object to a special place. you can close NSZombieEnabled and have try again. And double check if your code have circle retain condition.
I am getting a crash the second time I attempt to add a certain view as a subview. The crash happens here:
-(void)AddAsScrollableSubContext:(UIView*)view {
[pExtendedScrollableSubContextBounds addSubview: view]; //CRASH HERE
pSubScroll.userInteractionEnabled = true;
}
the second time I call...
[mSongContext AddAsScrollableSubContext:pEQRoot];
The flow is something along the lines of
[mSongContext AddAsScrollableSubContext:pEQRoot];
...Load a lot of stuff
...Press a Button
...Unload a lot of stuff
[pEQRoot removeFromSuperview];
...Press a Button
[mSongContext AddAsScrollableSubContext:pEQRoot];
When I get the bad access the callstack looks like the following:
Both objects (pExtendedScrollableSubContextBounds and pEQRoot) appear to be valid. Adding other subview to pExtendedScrollableSubContextBounds works fine and calling other operations on pEQRoot (subview, frame) also work.
I read the in objsend r0 was the object and r1 was the selector so I looked at the memory address for r1 and saw...
This feels like I am trashing memory somewhere around isKindOfClass: but I am not quite sure. Could anyone point me to more info on iOS obj_msgsend? is there a way I can setup a watch point to catch when this memory trash is occurring?
Use NSZombies to fix the problem.
On a slightly unrelated note, there's a rule of thumb - NARC which stands for new, allocate, retain, copy. If a method call includes any of these keywords, then we have ownership of the object and we are then supposed to release the object.
I have a NSMutableArray with a few view elements.
I remove the view from the view hierarchy and then check the retain count of the array, it returns 1. I then send [array release] but the code dumps with EXC_BAD_ACCESS.
I see that there are elements in the array but still the code dumps.
Here is what I found during debugging. The array has all the 100 objects present (count on the array returns 100) and the contents when seen on a debugger returns "Out of Scope" for the elements. Also, since the array is in the view hierarchy, the following code reduces the retain count by two:
for (Liv *view1 in viewArray){
NSLog(#"view count = %d", [view1 retainCount]);
[view1 removeFromSuperview];
NSLog(#"view count = %d", [view1 retainCount]);
}
Do not use retainCount
It is useless for this kind of debugging; you are working with views in the framework's view hierarchy. There could be any number of reasons why the retain count goes up or down by 2, 10, or 42.
From the roundabout evidence posted so far, this appears to be a very straightforward memory management issue.
First, use "build and analyze" to have the llvm static analyzer check your code. Fix any problems it identifies.
Next, how is the array allocated? Where do you store it? Post all of the lines of code that declare or manipulate the array.
Finally, as Paul said, turn on zombies and see what happens.
There is an off chance that this isn't a retain/release issue, but there isn't any evidence to indicate that yet.
Did you retain or alloc the array? If not, you shouldn't be sending it a release.
EXC_BAD_ACCESS means you're sending a message to a object that's already been released. You (or some code somewhere) is releasing it prior to the part of your code where you're sending release (or removeAllObjects).
Basically, if your retains and releases are balanced, you won't get this error. We'll need to see some code before we can offer anything more than generic advice.
Using NSLog() and retainCount isn't the easiest way to debug errors of this nature. Assuming you're on Xcode 3.2.6, then try running your code via Run > Run with Performance Tool > Zombies. That should give you a good pointer as to which part of your code is faulty.
Running a static analysis with clang in both XCode 3.2 and Nikita Zhuk's Analysis Tool I've often come across this pair of warnings:
Method returns an Objective-C object with a +0 retain count (non-owning reference)
Incorrect decrement of the reference count of an object is not owned at this point by the caller
An example of code that may provoke this warning:
UIButton* button = [[UIButton buttonWithType:UIButtonTypeCustom] initWithFrame: CGRectMake(x, y, width, height)];
return button;
I assumed that buttons created this way are autoreleased, just like any other NSObject created with a convenience factory method. So I return it, and the caller can decide whether to retain it or not. What's the problem with that?
Am I obliged to retain and autorelease the object before returning it?
And most importantly, could whatever this warning is warning against ever be the cause of scary release-related crashes?
I realize now that this seems to only occur with UIButtons. Is it due to it being a class cluster?
EDIT: The snipped below shows a minimal case where clang issues these warnings (with the warnings in bold). Both warnings are flagged on the statement creating the object (the buttonWithType: message).
-(UIButton*) ztupidTezt:(UIImage*) img
{
UIButton* bt = [[UIButton buttonWithType:UIButtonTypeCustom]initWithFrame:
1
Method returns an Objective-C object with a +0 retain count (non-owning reference)
2
Incorrect decrement of the reference count of an object is not owned at this point by the caller
CGRectMake(0.0f, 0.0f, img.size.width, img.size.height)];
bt setImage:img forState:UIControlStateNormal];
return bt;
}
Well.... that code makes no sense.
buttonWithType: returns an instance of a UIButton that is already initialized. You shouldn't be calling -initWithFrame: on it.
Call setFrame:.
The bad code is confusing the analyzer.
Secondly, why bother with a third party tool to do the analysis. If you are using Xcode 3.2 on Snow Leopard (you should be -- it is a vastly better version of Xcode than the last release on Leopard), you can just "build and analyze". All of the analysis results will be presented inline with your code quite nicely.
The cause is most likely the use of sending both a buttonWithType: and initWithFrame: message. init* methods perform tasks that should only be done once for a given object. Class methods that create objects also initialize them. The result of your code is repeated initialization. Instead, send the buttonWithType message, then assign to the frame property.
Does the method name that has this code within it have "new" as a prefix? The Clang Static Analyzer follows standard Cocoa naming conventions and assumes that a -newSomething method will return an instance with a retain count of 1. If it's seeing an autoreleased object being returned from such a method, it might present the warning you're seeing.
Old, old, question. I am having the same problem. I think the existing answers successfully explained why the code is wrong, and why the analyzer says "+0 retain count". However it doesn't look like anybody explained why the analyzer says the code is decrementing the retain count. I think i figured out why. It's because of how init methods are allowed to return a different object than you sent the message to. They would release the original object, alloc a new one, and return it. The analyzer is assuming that any init method could do such a thing, although the init method in this example might not.