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!
Related
I've created an Xcode project using Swift and a privileged Helper tool using Objective-C. The helper tool works fine within a project which has also been created in Objective-C but it doesn't seem to work within a project created with Swift.
The service itself is being installed. I can see the helper binary within the /Library/PrivilegedHelperTools directory and it's permissions seem to be okay (as well as the user: root). Removing the helper by using launchctl results in re-installing the tool when my project runs (that works as expected) but I can't call any method of the helper tool.
There is neither any exception being thrown nor does any other error occur (at least there seem to be no error as the Console shows nothing as well).
Does anybody know whether this might be an issue with Swift? Because running the same helper tool within another project (written in Objective-C) works well.
I could figure out what the problem was. The helper tool has a main.m wich contains a main() method. I just forgot to fill it with code that creates an instance of my helper class and trigger its listener:
#import <Foundation/Foundation.h>
#import "Helper.h"
int main(int argc, const char * argv[])
{
#autoreleasepool
{
Helper *helper = [[Helper alloc] init];
[helper run];
}
return EXIT_FAILURE;
}
This code causes the Helper instance to run in an infinite loop waiting for incoming connections (from Helper.h):
- (void)run
{
[_listener resume];
[[NSRunLoop currentRunLoop] run];
}
_listener is an instance of NSXPCListener.
So, think the classic beginner C programming right of passage: hello world. But written in Objective-C.
For reasons I prefer not to get into, I don't want to use XCode but rather my new love, Sublime Text.
Can it be done?
Paste the following into Sublime and save the file as main.m
#import <Foundation/Foundation.h>
int main(int argc, char *argv[])
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSLog(#"Hello, World!");
[pool drain];
return 0;
}
Then in the terminal navigate to the file with cd and type:
gcc -framework Foundation main.m -o NAME_OF_YOUR_APP
And run the app by typing:
./NAME_OF_YOUR_APP
I did a bit of digging and found this which lets me build and run entirely within Sublime Text which is what I need. Still testing: https://gist.github.com/chendo/4398077
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.
I'm writing an OS X Service with MacRuby. It upcases the selected text. It mostly works, but… well, here's all of it:
#!/usr/local/bin/macruby
# encoding: UTF-8
framework 'Foundation'
framework 'AppKit'
class KCUpcase
def upcase(pasteboard, userData: s_userdata, error: s_error)
incoming_string = pasteboard.stringForType "public.utf8-plain-text"
outgoing_string = incoming_string.upcase
pasteboard.clearContents
pasteboard.setString(outgoing_string, forType: "public.utf8-plain-text")
end
end
NSLog "Starting…"
NSRegisterServicesProvider(KCUpcase.new, "Upcase")
NSLog "Registered…"
NSRunLoop.currentRunLoop\
.acceptInputForMode(NSDefaultRunLoopMode,
beforeDate:NSDate.dateWithTimeIntervalSinceNow(10.0))
NSLog "Done."
It's just a Foundation tool, not part of an Application.
Now, see the NSRunLoop… line? That doesn't really work. The program exits imediately. I suppose the loop runs once and then exits. Anyhoo, the fact is that it's definititely not waiting 10s for input. So, here's what I did instead:
NSRunLoop.currentRunLoop.runUntilDate NSDate.dateWithTimeIntervalSinceNow(60.0)
And that works, but naturally the program sticks around for 60s, and it's a kludge. So I implemented the whole thing in Objective C (Including KCUpcase, which is not shown). And… it works. With manual memory management. Once I switch to GC (-fobjc-gc-only), it exits imediately same as the MacRuby version.
#import <Foundation/Foundation.h>
#import "KCUpcase.h"
int main (int argc, const char * argv[]) {
NSLog(#"Starting…");
NSRegisterServicesProvider([[KCUpcase alloc] init], #"KCUpcase");
NSLog(#"Registered…");
[[NSRunLoop currentRunLoop]
acceptInputForMode:NSDefaultRunLoopMode
beforeDate:[NSDate dateWithTimeIntervalSinceNow:10.0]];
NSLog(#"Done.");
return 0;
}
But, alas, the fix is easy: because this is a Foundation tool (not an NSApplication), it seems I have to start GC manually by calling objc_startCollectorThread. Here:
#import <objc/objc-auto.h>
// ...
NSLog(#"Starting…");
objc_startCollectorThread();
NSRegisterServicesProvider([[KCUpcase alloc] init], #"KCUpcase");
// ...
Ok, but what's up with MacRuby then? Let's throw it into the mix:
#import <MacRuby/MacRuby.h>
// ...
NSLog(#"Starting…");
objc_startCollectorThread(); // This magic stops working once we add MacRuby
[[MacRuby sharedRuntime] evaluateString: #"$stderr.puts 'hi from macruby'"];
NSRegisterServicesProvider([[KCUpcase alloc] init], #"KCUpcase");
// ...
And, again, it's not waiting in the loop. And, again, ussing the runUntilDate: kludge instead of acceptInputForMode:beforeDate: works:
NSLog(#"Starting…");
[[MacRuby sharedRuntime] evaluateString: #"$stderr.puts 'hi from macruby'"];
NSRegisterServicesProvider([[KCUpcase alloc] init], #"KCUpcase");
NSLog(#"Registered…");
// Hmmm…
[[NSRunLoop currentRunLoop]
runUntilDate:[NSDate dateWithTimeIntervalSinceNow:10.0]];
NSLog(#"Done.");
return 0;
So, I suppose I'm missing something terribly obvious. Please enlighten me.
And by the way, the full MacRuby version of the project is available here (download) with a Rake task that'll build and install it in ~/Library/Services. Then you need to enable its checkbox in Services in the Keyboard Preference Pane (once).
(or git clone git://gist.github.com/537075.git)
Aside: Interestingly, I tried calling NSLog inside the MacRuby string, and it raised NoMethodError. What gives?
It's a bit weird but here is a workaround:
framework 'Foundation'
framework 'AppKit'
class KCUpcase
def upcase(pasteboard, userData: s_userdata, error: s_error)
incoming_string = pasteboard.stringForType "public.utf8-plain-text"
outgoing_string = incoming_string.upcase
pasteboard.clearContents
pasteboard.setString(outgoing_string, forType: "public.utf8-plain-text")
end
end
puts "Starting…"
NSRegisterServicesProvider(KCUpcase.new, "Upcase")
puts "Registered…"
later = NSDate.dateWithTimeIntervalSinceNow(5)
NSRunLoop.currentRunLoop.runUntilDate later
puts "Done"
Basically, you need to define the timestamp before you dispatch the runloop request otherwise the main loop exists before getting the instruction. As you spotted, this is not really a MacRuby bug but still, hopefully that helps.
acceptInputForMode:beforeDate: only runs the loop once. As soon as any input (other than a timer) is processed, it exits. runUntilDate: however continues running the loop until the date is reached.
Consider the following main() method which you would find most iPhone applications:
int main(int argc, char *argv[])
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
int retVal = UIApplicationMain(argc, argv, nil, nil);
[pool release];
return retVal;
}
In every iPhone app that I've run in Simulator with these (including several sample projects provided by Apple), the thread never exits UIApplicationMain() and any remaining code in main() is never executed. Is this expected behavior?
I have verified that statements after UIApplicationMain() never run by stepping through the code with a debugger. When the user stops an application (by hitting the "Home" button, for example), the resulting stack trace shows that [UIApplication _terminateWithStatus:] is eventually called. This function calls your application delegate's applicationWillTerminate: method. Once that finishes, [UIApplication _terminateWithStatus:] seems to kill/exit the thread.
Can someone confirm that this is how main() is supposed to work, or at least confirm the same behavior on their machine?
The original question was: "Why doesn’t an iPhone app’s main() function ever get a chance to finish?"
Short Answer: Because UIApplicationMain() is coded such that it never returns.
After doing several tests in Simulator and on the device, and asking another developer to do the same tests, I have confirmed that UIApplicationMain never returns. When the user terminates an application normally by hitting the Home button, The program ultimately terminates inside an unpublished UIApplication method called _terminateWithStatus. This method calls exit(0).
This behavior matches that of the NSApplicationMain function (which is AppKit/Cocoa version of the UIApplicationMain function). The documentation for NSApplicationMain() clearly states that it will never return.
I have submitted a bug (6600198) to Apple requesting that the official documentation (and Xcode template for main.m) be corrected to state that UIApplicationMain() never returns. While this is not a functional problem, the current template and docs are misleading.
Thanks to everyone for all the input and brainstorming!
Try:
int main(int argc, char *argv[])
{
NSLog(#"Step 0");
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSLog(#"Step 1");
int retVal = UIApplicationMain(argc, argv, nil, nil);
NSLog(#"Step 2");
[pool release];
NSLog(#"Step 3");
return retVal;
}
It may be that the release of the pool is preventing further logging in which case you'd get step 2 but not step 3.
If step 2 isn't being printed, then it's almost certainly something wrong with UIApplicationMain - there's a chance that it doesn't return so put NSLog statements (step 1.1, Step 1.2, ...) at various points within it and run to find the last message logged.
Keep drilling down (Step 1.7.1, 1.7.2, .... 1.7.6.3.2, ...) - eventually, you'll track down the exact line (however deep in the call hierarchy) when the log messages stop being logged and that line will be your culprit (either "turning off" logging or exiting without returning normally).
One further snippet I found on the web:
=====
When you use this line:
int retVal = UIApplicationMain(argc, argv, #"MyApp", #"MyApp");
The first MyApp is your main app delegate class. The second is the class to where SpringBoard sends touch notifications.
Also, if you are using the SDK, and have a main nib defined in the Info.plist, then you can leave the call as:
int retVal = UIApplicationMain(argc, argv, nil, nil);
as all that will be covered when you create your xibs.
=====
Now I don't know enough about iPhone development (specifically xibs) to know what that last bit even means (or if you've set it up correctly) but it sounds like another phase of compilation.
However, my first thought from reading that is that Springboard will call your delegate class when the buttons are pressed to ask you to do something (like shut down gracefully). If it can't ask you (i.e., no delegate), it's probably within its rights to shut you down as it sees fit, such as with [UIApplication _terminateWithStatus:].
In the Windows world, you would probably send off a quit message to the main window but, as I say, iPhone development may be different.
Still, it's an avenue to investigate. I'd be interested in seeing what calls were made to a delegate if you provided one. The code included with the snippet above had this:
#implementation MyApp
- (void) applicationDidFinishLaunching:(id)unused {
rect = [ UIHardware fullScreenApplicationContentRect ];
rect.origin.x = 0.0f;
rect.origin.y = 0.0f;
window = [ [ UIWindow alloc ] initWithContentRect: rect ];
[ window makeKeyAndVisible ];
view = [ [ MyAppView alloc ] initWithFrame: rect ];
[ window setContentView: view ];
}
- (void) dealloc {
[ window release ];
[ view release ];
[ super dealloc ];
}
So maybe a delegate with dealloc() is the secret to getting it to exit back to main(). Why don't you give that a shot? It may get you closer to your goal even if it doesn't solve the core problem.
After [pool release] there is nothing to log to?
trying using fprintf and see what happens
int main(int argc, char *argv[])
{
/*
...
same as above
...
*/
[pool release];
char file_name = "/tmp/log"
FILE *file = fopen(file_name, "w");
fprintf(file_name, "END\n");
}
and tell us what happens
I also thought the easiest way to check was to set a break point right at the return
in gdb do
b main.c:x
where x is the line number of the return statement
After calling the UIApplicationMain function your application launches (establishing a runloop, etc) and all work should then be done outside the context of main (if you need it to run in main, do it before that point). When quitting an application it is generally more efficient to allow the OS to do memory cleanup.
I have that not return experience too. And have set break points to verify exactly like Clint said.
wisequark has a good point.
great topic. makes me feel more comfortable that i am not the only one who has the question.