Object created inside scope deallocated even when used outside of scope - objective-c

Update: This was fixed in iOS 6.1 DP3 SDK.
I've tracked down a use-after-deallocated crash when building with ARC using the default release build configuration (debug seems to work fine). The problem occurs when creating an object inside an if-scope with a non-constant condition, assigning it to a variable from outside the scope and then only referencing the variable using Objective-C array or dictionary literals.
Here is the smallest reproducible case I've managed to find:
void test(BOOL arg)
{
id obj = nil;
if (arg) {
obj = [NSObject new];
}
// obj already deallocated here
#[obj];
// but using NSArray works
//[NSArray arrayWithObject:obj];
// #[obj] works if obj is referenced i.e. by NSLog print out
//NSLog(#"%#", obj);
}
int main(int argc, const char * argv[])
{
#autoreleasepool {
test(YES);
}
return 0;
}
When I build and run this with zombie objects enabled I get this error message:
-[NSObject retain]: message sent to deallocated instance 0x100109100
As I commented in the code it works fine if obj is referenced in some other way, like with NSLog or using NSArray instead. Have I misunderstood how objects are released with ARC and scopes or is this a optimization bug in LLVM or Clang?
I'm using Xcode 4.5.2 with clang version 4.1 (tags/Apple/clang-421.11.66) (based on LLVM 3.1svn). I can reproduce it when building for x86 64 bit for iOS simulator and Mac OS X and I'm quite sure the same problem occurs when building for ARM as the issue was first found when running release build on an iPhone.
I have filed and bug report to Apple and created an open radar report.
What, if anything, am I missing?
Update, did some more experiments:
As Gabro pointed out the compiler translates #[] to a [NSArray arrayWithObjects:count:] statement so I did some tests:
// works
id a[] = {obj};
[NSArray arrayWithObjects:a count:1];
// does not work
const id *b = (id[]){obj};
[NSArray arrayWithObjects:b count:1];
// does not work
[NSArray arrayWithObjects:(id[]){obj} count:1];
So my guess is that this happens when combining ARC and anonymous C arrays.

You're not missing anything. It's a compiler bug.

I just tested the following code both building for OSX (x86 64) and iOS Simulator and I cannot reproduce the bug
void test(BOOL arg) {
id obj = nil;
if (arg) {
obj = [NSObject new];
}
#[obj];
NSLog(#"Hi there");
}
int main(int argc, const char * argv[]) {
#autoreleasepool {
test(YES);
}
return 0;
}
The above code simply prints Hi there in the console and returns.
My configuration is the same of yours: XCode 4.5.2 and Apple clang version 4.1 (tags/Apple/clang-421.11.66) (based on LLVM 3.1svn) as a compiler.
EDIT
I also tried to compile from command line (after adding #include <Foundation/Foundation.h>
at the beginning of the above example) using
clang -fobjc-arc -framework Foundation main.m
And the result was again
2012-12-03 12:47:45.647 a.out[39421:707] Hi there
EDIT 2
As pointed out in the comments it is possible to reproduce the bug increasing the optimization level over -O0. Summarizing:
clang -O0 -fobjc-arc -framework Foundation main.m
the program works as expected
clang -O1 -fobjc-arc -framework Foundation main.m
the bug presented in the question shows up. This is true with any optimization level over -O0
It's definitely a bug in the compiler.

Related

Xcode compilation errors with external library (OSX program)

I have a simple "Hello world" type program in Xcode and I'm trying to use an external library in that, but I'm getting compilation errors. The library is the Magtek edynamo macOS sdk:
https://www.magtek.com/Content/SoftwarePackages/1000004036.zip
(parent page is https://www.magtek.com/support/edynamo?tab=software; download is the macOS SCRA SDK)
This is my code:
#import "MTSCRA.h"
int main (int argc, const char * argv[])
{
MTSCRA* mtSCRALib = [[MTSCRA alloc] init];
return 0;
}
So basically I'm just importing the library and trying to instantiate one of its classes. Xcode doesn't show any inline compile errors anywhere, but when I try to build and run, it results in a variety of undefined symbol errors such as this:
Undefined symbols for architecture x86_64:
"_NSApplicationWillTerminateNotification", referenced from:
-[HIDManager init] in libMTSCRAOSX.a(HIDManager.o)
and warnings such as this:
ld: warning: object file (/path/HelloWorld/libMTSCRAOSX.a(MTSCRA.o)) was built for newer OSX version (10.12) than being linked (10.11)
(If I comment out the MTSCRA* mtSCRALib = [[MTSCRA alloc] init]; line, it runs fine.)
These are the steps I took to include the library in my Xcode project:
I dropped the MTSCRA.h and libMTSCRAOSX.a into my project directory.
From Build phases -> Link binary, I added libMTSCRAOSX.a.
Build Settings -> Library search paths is set to "$(inherited)" and "$(PROJECT_DIR)/HelloWorld"
This is my environment:
Macbook Pro 64-bit
OSX El Capitan 10.11.5
Xcode V8.2.1
Tried all the google solutions; nothing worked. Any help...can anyone else successfully import and use this library?
Solved this. Not sure if these are rookie mistakes, but I had two issues:
1) I also needed to include the library /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.‌​platform/Developer/S‌​DKs/MacOSX.sdk/usr/l‌​ib/libstdc++.6.0.9.t‌​bd
2) I also had to import #import <Cocoa/Cocoa.h>. So:
#import "MTSCRA.h"
#import <Cocoa/Cocoa.h>
int main (int argc, const char * argv[])
{
MTSCRA* mtSCRALib = [[MTSCRA alloc] init];
return 0;
}

Gnustep with Arc and Gui fails INCOMPATIBLE ABIs

Basically, I followed the instructions here: http://wiki.gnustep.org/index.php/GNUstep_under_Ubuntu_Linux
With the extra step of building the gui and back.
If I build and run a non-gui program, all works well. Only when I add something that uses the gui, like the following:
// Created by Tobias Lensing on 2/22/13.
#import <Foundation/Foundation.h>
#import <AppKit/AppKit.h>
#import <dispatch/dispatch.h>
int main(int argc, const char * argv[])
{
#autoreleasepool {
int multiplier = 7;
int (^myBlock)(int) = ^(int num) {
return num * multiplier;
};
NSLog(#"%d", myBlock(3));
dispatch_queue_t queue = dispatch_queue_create(NULL, NULL);
dispatch_sync(queue, ^{
printf("Hello, world from a dispatch queue!\n");
});
// dispatch_release(queue); // ARC takes care of this
}
#autoreleasepool {
[NSApplication sharedApplication];
NSRunAlertPanel(#"Test", #"Wow it works!", #"OK", nil, nil);
}
return 0;
}
I compile this with the following:
clang `gnustep-config --objc-flags` `gnustep-config --objc-libs` -fobjc-arc -fobjc-runtime=gnustep -fblocks -lobjc -ldispatch -lgnustep-gui test.m
It compiles and links with no errors.
When I run it, however, it spits out the following ugly-gram:
Hello, world from a dispatch queue!
Objective-C ABI Error: Loading modules from incompatible ABI's while loading .GSBackend.m
a.out: /home/lloyd/projects/ThirdParty/BuildGnuStep/libobjc2/loader.c:53: void __objc_exec_class(struct objc_module_abi_8 *): Assertion `objc_check_abi_verion(module)' failed.
Aborted (core dumped)
I have assured myself that there is no other version of libobjc (this is on a virtual machine so I can go back redo my steps).
Commenting out the following:
// [NSApplication sharedApplication];
// NSRunAlertPanel(#"Test", #"Wow it works!", #"OK", nil, nil);
and everything compiles and runs, aside from the GUI obviously.
How can I have two ABI's when I build everything from scratch? Do I need to configure the GUI differently? I've been puzzling over this for a couple of weeks.
Sorry, too long for a comment:
Well I am not sure of you exact problem but you have 3 ABI'a to consider rather than just the normal 2 (gnustep, fragile, not-fragile)... I am not an absolute expert on the area, but I believe you can run gnustep against the newer apple non-fragile ABI... so it is possible that isn't the ABI you have, but it is the one that gets selected with: gnustep-config --objc-libs, you could try omitting that...
I always used gnustep-make, but I haven't done much gnustep for a long time, and don't know if that is still preferred.
I did find an interesting thread: http://comments.gmane.org/gmane.comp.lib.gnustep.general/38698
Okay, after much messing around I discovered that, surprise, I was building everything wrong.
I would go into the long, deep, painful process, but this website actually has a nice set up, scripts, and everything.
Sadly I didn't find this website before asking my question. Now I have Objective C, ARC, blocks and GNUstep!

Running a project build in iOS5/xcode 4.2 on iOS simulator 4.2/xcode3.2.5

I had created a project using base sdk 5.0 in xcode 4.2. While creating the project, I had not checked all 3 options that are displayed, when we create the new project on iOS 5.0, namely. storyboard, use ARC and include unit tests. After writing my code, I want to run this on xcode 3.2.5/ iOS 4.2, but it gives an error because iOS 5 uses #autoreleasepool instead of NSAutoreleasePool. How will I run it on iOS4.2
How about using the old main.m?
#import <UIKit/UIKit.h>
int main(int argc, char *argv[])
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
int retVal = UIApplicationMain(argc, argv, nil, nil);
[pool release];
return retVal;
}
Another thing. "storyboards" and "ARC" are only supported in iOS 5. So this will be a problem too if you want to run this in iOS4.
summary:
if u want backward compatibility write your program on the lowest version you want to support and it will be fine. use old main.m, retain instead of strong and no storyboards or ARC and it works on iOS >= 4.

iPad testing of PhoneGap application - issues in main method

So I was attempting to test a PhoneGap application I've been working on, and had some issues with the test on my iPad. I have the following as the main method for this application:
//
// main.m
// elog
//
// Created by Ben Potter on 9/08/11.
// Copyright Arden Anglican School 2011. All rights reserved.
//
#import <UIKit/UIKit.h>
int main(int argc, char *argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
int retVal = UIApplicationMain(argc, argv, nil, #"AppDelegate");
[pool release];
return retVal;
}
It all works fine until you close the app completely and then reopen it, upon which it freezes. And I have no idea why.
I am running ios5 on my ipad with XCode 4.2
Finally, these are the errors which come up, thanks for the help!
Direct link here
Your application is using Automatic Reference Counting (which is new) and Phonegap doesn't support it yet. Go to your project's build settings and turn Automatic Reference Counting to off.
This is how I solved the "NSAutoReleasePool" error in XCode 4.3 and with PhoneGap 1.5.
Go to "YourApplicationName" in the Project Navigator.
Select "YourApplicationName" under Project.
Go to Build Settings.
Make sure to toggle "All" and "Combined".
Find the section "Apple LLVM compiler 3.1 - Language".
Scroll down and you will find "Objective-C Automatic Reference Counting".
Change it from Yes to No.
Try to build your project again and you should be fine!
So, just to clarify for the visually-inclined, it took me a few minutes to find the correct option since it's only mentioned in the comments of another answer. I had to find the CLANG_ENABLE_OBJC_ARC build flag and switch it to NO. You'll find it under the Build settings in the User-Defined section (very bottom for me):
I was also able to get past the runtime error by going into main.m and commenting out the NSAutoreleasePool setup code like so:
//NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
int retVal = UIApplicationMain(argc, argv, nil, #"AppDelegate");
//[pool release];
return retVal;
However, I'm not sure what other effects that might have down the line. It seems that at present, while working with PhoneGap, it's probably best to stick with manual ref counting until PhoneGap properly supports ARC.
And just for Google, the error that led me here was "NSAutoreleasePool is unavailable" as I don't see that as text in the original post.
If you don't want to disable ARC, then the following will fix the errors in Xcode 4.2.
AppDelegate.m
// self.window = [[[UIWindow alloc] initWithFrame:screenBounds]autorelease];
self.window = [[UIWindow alloc] initWithFrame:screenBounds];
// self.viewController = [[[MainViewController alloc] init] autorelease];
self.viewController = [[MainViewController alloc] init];
(void) dealloc
{
// [super dealloc];
}
main.m
int main(int argc, char *argv[]) {
// NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
// int retVal = UIApplicationMain(argc, argv, nil, #"AppDelegate");
// [pool release];
// return retVal;
#autoreleasepool {
NSLog (#"Programming is fun!");
}
return 0;
}
Disclaimer:
Not sure how this affects the rest of the PhoneGap lib an plugins though. However, after implementing these changes, the template PhoneGap project runs on the simulator, but terminates instantly. This was just a starting point.
Better Alternative:
I suggest you disable Automatic Reference Counting (ARC) under Build Settings until PhoneGap supports it. This thread on on PhoneGap forum hints that ARC may be supported in PhoneGap 1.6.
Prior to cordova 2.1.0 ARC is not supported, you are not supposed tick the box below (when you are still creating your project):
However, this is the exact code you need:
#import <UIKit/UIKit.h>
int main(int argc, char *argv[]) {
#autoreleasepool {
int retVal = UIApplicationMain(argc, argv, nil, #"AppDelegate");
return retVal;
}
}
If you upgrade to cordova 2.1.0 in Xcode 4.5.1 you can go to:
Edit
Refactor
The choose between Objective ARC and Modern Objective-C syntax.

Symbol not found: _OBJC_CLASS_$_Article

I am working on an extension to Vienna to add the ability for third parties to write Objective-C plugins, but I am getting some runtime linker issues only when running in 64-bit mode (everything appears to work fine in 32-bit mode). My plugin, SynkPlugin, is loaded by the following code in Vienna.app:
NSArray * bundlePaths = [NSBundle pathsForResourcesOfType:#"bundle" inDirectory:[[Preferences standardPreferences] pluginsFolder]];
NSEnumerator * enumerator = [bundlePaths objectEnumerator];
NSString * bundlePath;
NSMutableArray * plugins = [NSMutableArray array];
while ( (bundlePath = [enumerator nextObject]) != nil )
{
NSBundle * pluginBundle = [NSBundle bundleWithPath:bundlePath];
Class principalClass = [pluginBundle principalClass];
id <ViennaPlugin, NSObject> plugin = [[principalClass alloc] init];
[plugins addObject:plugin];
[plugin release];
NSLog(#"Loaded plugin %# [main class: %#]", bundlePath, principalClass);
}
And in the console output, I get the following error message:
2010-07-09 08:55:40.128 Vienna[74065:a0f] Error loading /Users/dcrosta/Library/Application Support/Vienna/PlugIns/SynkPlugin.bundle/Contents/MacOS/SynkPlugin: dlopen(/Users/dcrosta/Library/Application Support/Vienna/PlugIns/SynkPlugin.bundle/Contents/MacOS/SynkPlugin, 265): Symbol not found: _OBJC_CLASS_$_Article
Referenced from: /Users/dcrosta/Library/Application Support/Vienna/PlugIns/SynkPlugin.bundle/Contents/MacOS/SynkPlugin
Expected in: flat namespace
in /Users/dcrosta/Library/Application Support/Vienna/PlugIns/SynkPlugin.bundle/Contents/MacOS/SynkPlugin
This error only happens when running in 64-bit mode, not 32-bit mode. Both Vienna and SynkPlugin are compiled with the "standard 32/64 universal" settings, and SynkPlugin has the additional linker flag "-undefined dynamic_lookup", which, it is my understanding, allows it to link to classes found in Vienna.app without having to compile code for those classes into its own binary.
The other references to this error on Stack Overflow have to do with UIKit differences between iPhone/iPod Touch and iPad -- in those cases, the frameworks are actually missing the classes on iPhone/iPod Touch. In my case, I'm certain that Vienna.app has the Article class within it somewhere, since it is built from identical code to the 32-bit version.
Has anyone seen an error like this before? Have any suggestions on where to look for more information? Thanks.
In the build settings of the Vienna target, under GCC 4.2 - Code Generation uncheck Symbols Hidden by Default (GCC_SYMBOLS_PRIVATE_EXTERN).