Register FAQ / Rules Forum Spy Search Today's Posts Mark Forums Read
Go Back   MacRumors Forums > Apple Systems and Services > Programming > Mac Programming

Reply
 
Thread Tools Search this Thread Display Modes
Old May 8, 2012, 12:30 AM   #1
Marimuthu
macrumors member
 
Join Date: Oct 2010
Unloading NSBundle causing application crash.

Dear All,

I am a developer working on an application developed on Lion using Xcode 4.3.2.

One of the modules of the app is to load an NSBundle dynamically and use the same to query some information.
I find that when I try to unload the bundle, the application crashes a few seconds later and the calls tack in crash report is as provided below.

I believe this type of error happens when user tries to release the same object twice. It tried running the NSZombieEnabled technique along with Xcode but to no avail.

Code:
Exception Type:  EXC_BAD_ACCESS (SIGSEGV)
Exception Codes: KERN_INVALID_ADDRESS at 0x000000000396d310

VM Regions Near 0x396d310:
    mapped file            000000000395a000-0000000003960000 [   24K] r--/rwx SM=COW  /Applications/########.app/Contents/Resources/information.png
--> 
    MALLOC metadata        0000000003974000-0000000003975000 [    4K] r--/rwx SM=ZER  

Application Specific Information:
objc[717]: garbage collection is OFF

Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
0   com.apple.CoreFoundation      	0x9c120fc1 CFRelease + 49
1   com.apple.CoreFoundation      	0x9c1698e6 -[__NSArrayI dealloc] + 246
2   libobjc.A.dylib               	0x9367754e _objc_rootRelease + 47
3   libobjc.A.dylib               	0x93678c58 (anonymous namespace)::AutoreleasePoolPage::pop(void*) + 404
4   com.apple.CoreFoundation      	0x9c14be05 _CFAutoreleasePoolPop + 53
5   com.apple.Foundation          	0x95e4476e -[NSAutoreleasePool drain] + 131
6   com.apple.AppKit              	0x9c370cbd -[NSApplication run] + 791
7   com.apple.AppKit              	0x9c601c59 NSApplicationMain + 1054
8   com.App.MyApp   	0x0000323d start + 53
In my application which is multithreaded, I load the bundle in a different thread (not the main thread) and unload it in a different thread (this too is not the main thread). Could this possibly be resulting in the crash? Should the bundle load and unload operation be performed on the same thread?

Can you guys also please let me know as to what info you guys can extract from the call stack above?

Thanks & Regards.
Marimuthu is offline   0 Reply With Quote
Old May 8, 2012, 01:50 AM   #2
Sydde
macrumors 68000
 
Sydde's Avatar
 
Join Date: Aug 2009
The documentation says that before you unload a NSBundle, you must make sure that there are no objects in existence that reference any classes defined by the bundle, since the bundle contains the code for those classes. Since the crash is occurring on the main thread, while draining its autorelease pool, this may be what is happening. Make sure your secondary and tertiary threads can keep track of any such resources and either dispose of them properly or wait until they have been disposed of before unloading the bundle. If this is what is happening, you could consider translating what you get from the bundle into a different class before passing it to the main thread.
__________________
You got to be a spirit. You can't be no ghost.

Last edited by Sydde; May 8, 2012 at 02:07 AM.
Sydde is offline   0 Reply With Quote
Old May 8, 2012, 03:12 AM   #3
Marimuthu
Thread Starter
macrumors member
 
Join Date: Oct 2010
Hi Sydde,

Thanks for the reply.

I am pretty sure that any object that is created is also being released within the bundle before the bundle is unloaded. I have run the Leak test using Instruments tool to plug all the memory leaks that were present in the code.

Is it possible that trying to release an object twice could result in such kind of a crash? (though Zombie did not show any such instance).
Marimuthu is offline   0 Reply With Quote
Old May 8, 2012, 11:38 AM   #4
Sydde
macrumors 68000
 
Sydde's Avatar
 
Join Date: Aug 2009
Here is what I see: on the main thread, an object named ArrayI is causing the problem when the autorelease pool is drained. Find out how you are handling this object and what it contains, then see what you can do to properly synchronize its lifespan.

If you create an autoreleased object, add it to an array, then release the object, it will be deallocated while it is still in the array when the pool is drained, causing a crash like this when the array is released.
__________________
You got to be a spirit. You can't be no ghost.
Sydde is offline   0 Reply With Quote
Old May 8, 2012, 11:52 AM   #5
gnasher729
macrumors G5
 
gnasher729's Avatar
 
Join Date: Nov 2005
Quote:
Originally Posted by Sydde View Post
Here is what I see: on the main thread, an object named ArrayI is causing the problem when the autorelease pool is drained. Find out how you are handling this object and what it contains, then see what you can do to properly synchronize its lifespan.

If you create an autoreleased object, add it to an array, then release the object, it will be deallocated while it is still in the array when the pool is drained, causing a crash like this when the array is released.
The array is probably created with [NSArray arrayWithObject:anObject]
gnasher729 is offline   0 Reply With Quote
Old May 8, 2012, 12:21 PM   #6
Catfish_Man
macrumors 68030
 
Catfish_Man's Avatar
 
Join Date: Sep 2001
Location: Portland, OR
Send a message via AIM to Catfish_Man
Quote:
Originally Posted by Marimuthu View Post
Dear All,

I am a developer working on an application developed on Lion using Xcode 4.3.2.

One of the modules of the app is to load an NSBundle dynamically and use the same to query some information.
I find that when I try to unload the bundle, the application crashes a few seconds later and the calls tack in crash report is as provided below.

I believe this type of error happens when user tries to release the same object twice. It tried running the NSZombieEnabled technique along with Xcode but to no avail.

Code:
Exception Type:  EXC_BAD_ACCESS (SIGSEGV)
Exception Codes: KERN_INVALID_ADDRESS at 0x000000000396d310

VM Regions Near 0x396d310:
    mapped file            000000000395a000-0000000003960000 [   24K] r--/rwx SM=COW  /Applications/########.app/Contents/Resources/information.png
--> 
    MALLOC metadata        0000000003974000-0000000003975000 [    4K] r--/rwx SM=ZER  

Application Specific Information:
objc[717]: garbage collection is OFF

Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
0   com.apple.CoreFoundation      	0x9c120fc1 CFRelease + 49
1   com.apple.CoreFoundation      	0x9c1698e6 -[__NSArrayI dealloc] + 246
2   libobjc.A.dylib               	0x9367754e _objc_rootRelease + 47
3   libobjc.A.dylib               	0x93678c58 (anonymous namespace)::AutoreleasePoolPage::pop(void*) + 404
4   com.apple.CoreFoundation      	0x9c14be05 _CFAutoreleasePoolPop + 53
5   com.apple.Foundation          	0x95e4476e -[NSAutoreleasePool drain] + 131
6   com.apple.AppKit              	0x9c370cbd -[NSApplication run] + 791
7   com.apple.AppKit              	0x9c601c59 NSApplicationMain + 1054
8   com.App.MyApp   	0x0000323d start + 53
In my application which is multithreaded, I load the bundle in a different thread (not the main thread) and unload it in a different thread (this too is not the main thread). Could this possibly be resulting in the crash? Should the bundle load and unload operation be performed on the same thread?

Can you guys also please let me know as to what info you guys can extract from the call stack above?

Thanks & Regards.

I would *strongly* recommend never unloading bundles. Just don't do it, it's incredibly difficult to do safely.

Specifically, if you ever use a constant NSString (@"foo"), and pass that to any other part of your program, you're pretty much doomed. Calling -copy will not actually create a copy of the string, so even if you try to guard yourself that way, it won't work. When the bundle is unloaded, the binary that the constant string is in goes with it, and anything that uses that string in the future will crash.
Catfish_Man is offline   0 Reply With Quote
Old May 8, 2012, 04:16 PM   #7
Sydde
macrumors 68000
 
Sydde's Avatar
 
Join Date: Aug 2009
Quote:
Originally Posted by gnasher729 View Post
The array is probably created with [NSArray arrayWithObject:anObject]
Most likely, but that means the array is either being created on the main thread or the main thread is adding it to its autorelease pool without first retaining it. The latter case would exhibit random crashes, because the other thread would release the array at some unknown time, possibly making it invalid when the main thread wants to use it.

If I were faced with a situation like this and absolutely did have to unload the bundle, I think I might use -performSelectorOnMainThread:withObject:waitUntilDone:YES to make sure everything was properly cleaned up first (in which case, the main thread's method should be autorelease pool bracketed).
__________________
You got to be a spirit. You can't be no ghost.
Sydde is offline   0 Reply With Quote
Old May 9, 2012, 02:16 AM   #8
Marimuthu
Thread Starter
macrumors member
 
Join Date: Oct 2010
Thanks all for providing valuable guidance.

Quote:
Originally Posted by Catfish_Man View Post
I would *strongly* recommend never unloading bundles. Just don't do it, it's incredibly difficult to do safely.
In my application which detects an inserted Mobile broadband/data device where each card is represented by an unique bundle (only one data card can be used hence only 1 device bundle will be loaded at any given time) I dynamically load the bundle corresponding to the device inserted into the machine and unload the bundle when the user plugs out the device. If the user inserts the device again, I again load the same bundle and unload the bundle when device is removed.

In such a scenario, will not calling unload on the bundle (but directly calling release on the bundle object) not cause any kind of an unexpected behaviour (like memory leaks etc) as in my case, the same bundle might needed to be loaded multiple number of times during the same application instance.
Marimuthu is offline   0 Reply With Quote
Old May 9, 2012, 12:01 PM   #9
Catfish_Man
macrumors 68030
 
Catfish_Man's Avatar
 
Join Date: Sep 2001
Location: Portland, OR
Send a message via AIM to Catfish_Man
Quote:
Originally Posted by Marimuthu View Post
Thanks all for providing valuable guidance.



In my application which detects an inserted Mobile broadband/data device where each card is represented by an unique bundle (only one data card can be used hence only 1 device bundle will be loaded at any given time) I dynamically load the bundle corresponding to the device inserted into the machine and unload the bundle when the user plugs out the device. If the user inserts the device again, I again load the same bundle and unload the bundle when device is removed.

In such a scenario, will not calling unload on the bundle (but directly calling release on the bundle object) not cause any kind of an unexpected behaviour (like memory leaks etc) as in my case, the same bundle might needed to be loaded multiple number of times during the same application instance.
Memory leaks are not the issue I was concerned about; I'm concerned about crashes. In order to do this safely you will need to audit *all constant string usage in all the bundles*, or compile them with the flag to not use constant strings. I don't think you understand how much pain you're signing up for by using bundle unloading.
Catfish_Man is offline   0 Reply With Quote
Old May 9, 2012, 01:38 PM   #10
Sydde
macrumors 68000
 
Sydde's Avatar
 
Join Date: Aug 2009
The two questions I would have are: are the bundles built by someone else (or might they be), and does the reason for unloading them have something to do with conflicting class definitions? If each bundle, as it should, defines unique classes/subclasses, there really is no need to unload them, they do not take up that much space. And if there is a potential class conflict in code that you have written, you really ought to fix that.
__________________
You got to be a spirit. You can't be no ghost.
Sydde is offline   0 Reply With Quote
Old Jun 20, 2012, 01:35 AM   #11
Marimuthu
Thread Starter
macrumors member
 
Join Date: Oct 2010
Hi Sydde,

Sorry for the delay in replying.

Quote:
Originally Posted by Sydde View Post
The two questions I would have are: are the bundles built by someone else (or might they be)
No. The bundles is build and owned by me.

Quote:
Originally Posted by Sydde View Post
does the reason for unloading them have something to do with conflicting class definitions?
Each bundle is giving a different class name. I dont think there is any conflicting class definition.
Marimuthu is offline   0 Reply With Quote
Old Jun 25, 2012, 02:09 AM   #12
Marimuthu
Thread Starter
macrumors member
 
Join Date: Oct 2010
After slogging for many days, finally figured out the code that was causing when the unload was called on the bundle.

Code:
//infoDict is an NSDictionary with some objects in it.

//line 1
NSArray *arrayKey = [NSArray arrayWithObjects: @"Key1", @"Key2", @"Key3", nil];
//line 2
NSArray *arrayValues = [infoDict objectsForKeys:arrayKey notFoundMarker:@""];
//line 3
NSString *valueOfKey1 = [arrayValues objectAtIndex:0];
When I commented code in line 1 ,2 and 3 and accessed the value for Key1 using NSDictionary directly, I found that there was no crash.

From this I could conclude that using an NSArray as illustrated above (atleast for me) in an Bundle was causing an crash.

Can you guys from your experience in Cocoa programming figure out as to what is wrong above for it to cause a crash when the bundle was unloaded?
Marimuthu is offline   0 Reply With Quote
Old Jun 25, 2012, 07:44 AM   #13
gnasher729
macrumors G5
 
gnasher729's Avatar
 
Join Date: Nov 2005
Quote:
Originally Posted by Marimuthu View Post
After slogging for many days, finally figured out the code that was causing when the unload was called on the bundle.

Code:
//infoDict is an NSDictionary with some objects in it.

//line 1
NSArray *arrayKey = [NSArray arrayWithObjects: @"Key1", @"Key2", @"Key3", nil];
//line 2
NSArray *arrayValues = [infoDict objectsForKeys:arrayKey notFoundMarker:@""];
//line 3
NSString *valueOfKey1 = [arrayValues objectAtIndex:0];
When I commented code in line 1 ,2 and 3 and accessed the value for Key1 using NSDictionary directly, I found that there was no crash.

From this I could conclude that using an NSArray as illustrated above (atleast for me) in an Bundle was causing an crash.

Can you guys from your experience in Cocoa programming figure out as to what is wrong above for it to cause a crash when the bundle was unloaded?
objectAtIndex:0 will crash if the array contains no objects. In many cases nil objects are harmless (sending any message to them does nothing), but asking for an object at a non-existing index isn't. That's possibly the difference. You might just sprinkle a few NSLog statements in your code. Like printing the contents of arrayKey, infoDict, arrayValues.
gnasher729 is offline   0 Reply With Quote
Old Jun 25, 2012, 09:58 AM   #14
Sydde
macrumors 68000
 
Sydde's Avatar
 
Join Date: Aug 2009
Quote:
Originally Posted by Marimuthu View Post
Can you guys from your experience in Cocoa programming figure out as to what is wrong above for it to cause a crash when the bundle was unloaded?
Seems kind of unimportant. You decrufted your code and it works correctly now, time to move on.

arrayValues clearly must have contained a reference to a constant string that existed within the bundle. The bundle was unloaded before the main thread cycled its runloop, causing the string constant to evaporate as a valid object. Then, when the main thread drained its autorelease pool, it tried to release an invalid object, causing the crash.

If you were to bracket the troublesome code with an autorelease pool that was drained at a time when the bundle was certain to still be present, the crash would not happen. Any time you have a crash related to autoreleasing (as indicated by your crash report above), squeezing a pool bracket in around the problem code can help you locate the problem. But making your code cleaner as you have is usually a better choice.
__________________
You got to be a spirit. You can't be no ghost.
Sydde is offline   0 Reply With Quote

Reply
MacRumors Forums > Apple Systems and Services > Programming > Mac Programming

Thread Tools Search this Thread
Search this Thread:

Advanced Search
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -5. The time now is 07:14 AM.

Mac Rumors | Mac | iPhone | iPhone Game Reviews | iPhone Apps

Mobile Version | Fixed | Fluid | Fluid HD
Copyright 2002-2013, MacRumors.com, LLC