I'm working on converting a document-based application from Garbage Collection (it ran fine under 10.6) to Automatic Reference Counting (trying to get it to compile and run for 10.12). I'm getting a consistent EXC_BAD_ACCESS when the last window is closed. Nothing is flagged by the static analyser.
I used Instruments to look for Zombies, and indeed there appears to be a release message sent to a deallocated object. Here is the trace:
# Event ∆ RefCt Timestamp Responsible Library/Responsible Caller
173 Release -1 3 00:05.226.677 Foundation __48-[NSFileAccessArbiterProxy removeFilePresenter:]_block_invoke
174 Release -1 2 00:05.226.679 Foundation -[NSFilePresenterXPCMessenger invalidate]
175 Retain +1 3 00:05.226.823 Foundation -[NSBlockOperation initWithBlock:]
176 Retain +1 4 00:05.226.858 AppKit -[NSDocument close]
177 Release -1 3 00:05.227.350 Foundation -[NSFilePresenterXPCMessenger dealloc]
Retain/Release (2) 00:05.227.484 AppKit -[NSDocumentController removeDocument:]
180 Release -1 2 00:05.227.485 AppKit -[NSDocumentController removeDocument:]
Retain/Release (2) 00:05.227.496 AppKit -[NSUIActivityManager addProvider:toUserActivity:withSetter:]
183 Autorelease 00:05.227.499 AppKit -[NSWindowController _windowDidClose]
184 Release -1 1 00:05.228.172 Foundation -[NSAutoreleasePool drain]
185 Release -1 0 00:05.228.184 Foundation -[NSBlockOperation dealloc]
186 Zombie -1 00:05.242.579 AppKit -[NSApplication(NSWindowCache) _checkForTerminateAfterLastWindowClosed:saveWindows:]
The difficult I am having in diagnosing this problem is that I can see the message being sent to the Zombie, but because I do not release any objects (it is all done by ARC), I'm guessing there is an implicit release going on somewhere. But I'm not sure where to look.
When in the debugger, lldb complains of a crash in main.m at:
return NSApplicationMain(argc, (const char **) argv);
Any help would be greatly appreciated. Thank you.
After doing more research, the problem is still unsolved, but I have a clue:
Thank you for your response. Yes, I am at this point assuming it is a memory management issue. Is there way to track down the culprit? It doesn't seem like using Zombies in Instruments is helping: it points to a problem, but does not help me target the problem. That is, the offending line according to Instruments appears to be "-[NSApplication(NSWindowCache) _checkForTerminateAfterLastWindowClosed:saveWindows:]"; but I never issued that line. I have made what might be a little progress towards figuring things out.
The structure I am using in the document is:
NSDocumentController: default, not subclassed
NSDocument: subclassed, MyDocument; also the delegate of window
NSWindowController: default, not subclassed
NSWindow: MyDocument.xib, MainMenu.xib
I tried setting the delegate of the open window to nil as follows:
-(void)windowWillClose:(NSNotification *)notification
{
windowOpen = YES;
NSArray *windowControllers = [self windowControllers];
NSWindowController *currentWindowController = [windowControllers firstObject];
NSWindow *windowForCurrentController = [currentWindowController window];
NSDocument *w = [windowForCurrentController delegate];
[windowForCurrentController setDelegate:nil];
}
This causes the same crash.
Then, I thought perhaps the currentWindowController (or the application) was messaging a deallocated window. So I tried adding the line (at the end of the method above):
[currentWindowController setWindow:nil];
This gets rid of the crashes, but introduces new problems (such as when trying to load new files, etc.). But I'm wondering if this is a clue to help solve the overall problem.
The problem was that the app delegate was set to being MyDocument (the NSDocument subclass). The problem was solved by creating a standard AppDelegate class (NSObject, implementing the NSApplicationDelegate protocol), creating a corresponding NSObject in the XIB and setting it to type AppDelegate, and then making AppDelegate the delegate for MyDocument.
I seems that the source of the problem was that the MyDocument object was being deallocated, but it was still the delegate of the NSWindow subclass. The NSWindow then sent a dealloc message to the delegate, but it was already deallocated, causing a crash.
Here is the chain that solved it (long but instructive).
Related
Getting this weird crash after updating to Mojave.
Not doing anything special, just creating an NSWindow and calling orderFrontRegardless
Always worked fine before.
1 libsystem_platform.dylib 0x00007fff6610ab5d _sigtramp + 29
2 ??? 0x0000000000000000 0x0 + 0
3 CoreFoundation 0x00007fff39b00bb6 __CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER__ + 12
4 CoreFoundation 0x00007fff39b00b30 ___CFXRegistrationPost_block_invoke + 63
5 CoreFoundation 0x00007fff39b00a9a _CFXRegistrationPost + 404
6 CoreFoundation 0x00007fff39b08f48 ___CFXNotificationPost_block_invoke + 87
7 CoreFoundation 0x00007fff39a71994 -[_CFXNotificationRegistrar find:object:observer:enumerator:] + 1642
8 CoreFoundation 0x00007fff39a70d47 _CFXNotificationPost + 732
9 Foundation 0x00007fff3bdab217 -[NSNotificationCenter postNotificationName:object:userInfo:] + 66
10 AppKit 0x00007fff3720538b -[NSWindow _setFrameCommon:display:stashSize:] + 3090
11 AppKit 0x00007fff37204766 -[NSWindow _setFrame:display:allowImplicitAnimation:stashSize:] + 192
12 AppKit 0x00007fff3720469f -[NSWindow setFrame:display:] + 51
13 AppKit 0x00007fff3727aca9 -[NSWindow _reallyDoOrderWindowAboveOrBelow:relativeTo:findKey:forCounter:force:isModal:] + 1336
14 AppKit 0x00007fff372792a0 -[NSWindow _doOrderWindow:relativeTo:findKey:forCounter:force:isModal:] + 283
15 AppKit 0x00007fff37a0dce9 -[NSWindow orderFrontRegardless] + 40
Code (it's a console app):
NSWindow *window = [[NSWindow alloc] initWithContentRect:windowRect
styleMask:windowStyle
backing:NSBackingStoreBuffered
defer:NO];
// Since Snow Leopard, programs without application bundles and Info.plist
// files don't get a menubar and can't be brought to the front unless the
// presentation option is changed
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
[NSApp activateIgnoringOtherApps:YES];
[window makeKeyAndOrderFront:nil];
How do you initialize the app? Do you have NSApplication initialized before using AppKit?
Something like these steps should be necessary in main.m:
#autoreleasepool {
NSApplication* application = NSApplication.sharedApplication;
AppDelegate* delegate = [[AppDelegate alloc] init];
application.delegate = delegate;
[application run];
}
Also your delegate might be getting deallocated, since NSApp holds a weak reference to it.
You indicated that you were dereferencing an uninitialized pointer. But, I don't have enough information from the report you posted to know if this was (perhaps by luck) null, or just garbage memory. I'll assume that at some point you crashed with an EXC_BAD_ACCESS (signal equivalent is SIGBUS or SIGSEGV, depending).
The critical bit of information here was that you had a signal handler installed.
Signal handlers generally (but not always) run on the crashing thread using the same stack. The kernel injects the handler using that _sigtramp function. Upon signal delivery, current stack state contained the information you needed to track down the bad memory access. But, your signal handler was invoked instead. So it ran, mutating the stack as it did.
Then, your signal handler completed somehow. It is possible to configure a signal handler using sigaction such that the process state is restored to the moment just before the crashing event. I'm not sure how your signal handler was configured. But, ultimately, I'm going to assume that the process was allowed to exit.
At this point, Apple's ReportCrash would have been triggered, and would capture backtraces for all threads in whatever state your signal handler left them. This is critical, because that's not necessarily the crashing state.
Adding complexity, backtrace_symbols_fd is not at all safe to use from a signal handler. Async safety is challenging, and running code from a signal handler is highly difficult to get right. There are very few things you can do safely. I'm pretty sure, additionally, that backtrace_symbols_fd allocates memory. So, if your crash was in the memory allocator somewhere, and it sounds like it was, you were definitely at risk for a deadlock. Judging by the backtrace, it seems like that's exactly what might have happened. Check out man sigaction for some details.
Worse, unwinding the stack over a signal handler frame is particularly challenging because of the magic the kernel does to run your handler. That's why that ??? frame is in there.
A summary:
Without a signal handler installed, Apple's ReportCrash would have produced a correct (and likely helpful) backtrace for the crashing thread.
The stack trace you've included isn't great, but it's hard to know exactly why.
It appears that backtrace_symbols_fd didn't do a good job of unwinding, possibly due to it being inappropriate to use from a signal handler, possibly because it isn't backed by a good-enough stack unwinding mechanism for this situation. But, without more information, it's difficult for me to know. I am surprised the the top frame was _sigtramp, though. That doesn't make a lot of sense. It makes me think something might have been going wrong in the signal handler itself. It is possible to crash a second time in your handler.
Apple's backtraces (generated by ReportCrash, backtrace_symbols_fd, or NSThread's callStackReturnAddresses, for example) can definitely be trusted, provided you're careful to use them in safe contexts.
Turns out I had a serious memory bug in a completely different place not even mentioned in the backtrace.
I was dereferencing an uninitialised pointer.
This is the second time it happens.
Do no trust Apple's backtraces when debugging memory errors.
Even with libgmalloc.
I am receiving a EXC_BAD_ACCESS when calling setFrame on NSWindow. Unfortunately the exception occurs in AppKit where I can't debug it, in [NSWindow _setFrameCommon:display:stashSize:] with an invalid address of 0x40dedeadbec0 (the same every time).
The call in my code that causes the exception is in a subclass of NSWindow:
[super setFrame:frameRect display:flag];
Both frameRect and display are valid values, and self is a valid instance of my NSWindow subclass.
How should I go about debugging this error?
You need start debuging with enabling NSZombieEnabled. Fast of all NSWindow was released somewhere by some reasons. For example you have releaseWhenClose flag with YES value.
Error address which you provided seems like a Hexspeak
I am having an interesting memory management problem with an iOS app.
I have a property which when release using [self.propertyName release]; will cause the property to be released twice and cause a bad memory access exception on an iPad 1/iOS 4 device (in the dealloc of the property).
When the this code is changed to [_propertyName release]; (accessing the local variable instead of the property), this will cause a leak on an iPad 3/iOS 5.
Of course, I can stop this from happening by checking the OS version, but I would like to understand what I am doing wrong.
Some more details:
The property is a subclass of UIViewController, as is the object who owns the property. I am adding the property's UIView as a subview. I have heard this is not good practice, but we are doing it elsewhere without the memory problems.
The test cases are:
iPad 1, iOS 4.2 - Needs to release using the ivar [_propertyName release]
iPad 2, iOS 5.0 - Needs to release using the property [self.propertyName release]
iPad 3, iOS 5.1 - Needs to release using the property [self.propertyName release]
Adding another viewController's view as a subview is bound to cause strange issues, when you release the other viewController it's view will still be retained by your main view but it's viewController will have disappeared.
That said self.propertyName = nil is the best way to release properties, just make sure you have removed the subview from your main view first.
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'm creating one Cocoa application for myself and I found a problem. I have two NSTextFields and they're connected to each other as nextKeyViews. When I run this app with memory leaks detection tool and tab through those 2 textboxes for a while, enter some text etc., I start to leak memory. It shows me that the AppKit library is responsible, the leaked objects are NSCFStrings and the responsible frames are [NSEvent charactersIgnoringModifiers] and [NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:]. I know this is quite a brief and incomplete description, but does anyone have any ideas what could be the problem?
Also, I don't use GC, so I release my instance variables in the controllers dealloc. What about the outlets? Since IBOutlet is just a mark for Interface Builder and doesn't actually mean anything, should I release them too?
What about the outlets? Since IBOutlet is just a mark for Interface Builder and doesn't actually mean anything, should I release them too?
Your declaration of the IBOutlet tells you how to manage it... If you declare it as retained and then #synthesize it, the process of loading the nib will retain the outlet. Therefore you must release it.
Andiih coined the mnemonic NARC
NARC: "New Alloc Retain Copy". If you
are not doing any of those things, you
don't need to release.
The corollary is also true.. if you do any of those, you are responsible for releasing the object at the appropriate time.