I was trying out a sample thread program from google and i am getting a runtime exception.
Is there any website that gives an example of how to use runloops along with threads.
I need to set two events and spawn a thread and to do another function parallely.
// Runner.m
#import "Runner.h"
#implementation Runner
- (void)rumMe:(id)ignored {
NSLog(#"Running with threads!!");
}
#end
// Runner.h
#interface Runner : NSObject
-(void)rumMe:(id)ignored;
#end
// Thread1.m
#import <Foundation/Foundation.h>
#import "Runner.h"
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
Runner* runner = [Runner new];
[NSThread detachNewThreadSelector:#selector(runMe:) toTarget:runner withObject:nil];
[pool drain];
return 0;
}
Runtime Exception:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '***
-[NSThread initWithTarget:selector:object:]: target does not implement selector (***
-[Runner runMe:])'
First part: you had a typo
// method declaration
rumMe: with an _m_
// call
runMe: with an _n_
Second part: your main function is returning and causing the program to exit before you have given the thread a chance to do anything. In this simple simple example, you could simply
sleep(2);
right after the call to detachNewThreadSelector:
In more complex cases, you might need to make a call to CFRunLoopRun(); on the main thread, or take other action to keep the second thread alive.
You have made a typo. The method in Runner is defined as rumMe, but in the main program you use runMe.
Related
I want to execute my method when any application is closing. My code is:
#interface FO: NSObject
- (void)applicationKilled:(NSNotification*)notification;
- (void)appDidLaunch:(NSNotification*)notification;
#end
#implementation FO
- (void)applicationKilled:(NSNotification*)notification {
NSLog(#"success");
}
- (void)appDidLaunch:(NSNotification*)notification {
NSLog(#"app info: %#", [notification userInfo]);
}
#end
#implementation Main:NSObject
FO fo;
NSString * filePath = "...MyPath";
NSString * application = "..MyApplication";
int main(int argc, const char * argv[]) {
fo = [[FO alloc]init];
[Main MyMethod];
while(1==1) {
...some code;
}
return 0;
}
+(void) MyMethod {
center = [[NSWorkspace sharedWorkspace] notificationCenter];
[center addObserver:fo selector:#selector(appDidLaunch:) name:NSWorkspaceDidLaunchApplicationNotification object:nil];
[center addObserver:fo selector:#selector(applicationKilled:) name:NSWorkspaceDidTerminateApplicationNotification
object:nil];
[[NSWorkspace sharedWorkspace] openFile:filePath withApplication:application]; }
#end
However, appDidLaunch method is not firing, even if i'll open another application in finder. Also applicationKilled method is never firing.
When i'm executing following code
[center postNotificationName:NSWorkspaceDidLaunchApplicationNotification
object:self];
appDidLaunch method is firing OK. Where can be a problem? Should this methods be fired every time when some application is opened or closed?
CRD is on the right track. You absolutely must have a runloop to receive this notification. For example:
#implementation Main : NSObject
- (void)applicationDidFinishLaunching:(NSApplication *)app {
[Main MyMethod];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// ... The rest of your program ...
});
}
#end
int main(int argc, const char * argv[]) {
#autoreleasepool {
MyDelegate *delegate = [Main new];
[[NSApplication sharedApplication] setDelegate:delegate];
[NSApp run];
}
return 0;
}
I've put "the rest of your program" into a dispatch_async because you must not block the main thread. The usual way that Mac apps work is not with a big while (YES) loop. The usual way is by registering for various events and then waiting from them to happen. That's what the run loop is for. But if you have to manage your own loop (you generally shouldn't, but if you must), then you need to move it off of the main queue.
Assuming you are using ARC and also guessing as the information you give seems to be incomplete:
In your updated question you show fo declared as a local variable of MyMethod. The method addObserver:selector:name:object: does not keep a strong reference to the observer. After MyMethod returns the local fo object will be reclaimed, you now have no observer to call methods on.
However, while the above would explain why your code doesn't work it wouldn't explain why your app does not crash - and you don't report that it crashes. Running the code you give above causes the app to crash. So it appears that you've missed some information out or at least not reported the crash.
Guess Two
You have no run loop.
Many parts of the framework rely on there being a run loop which dispatches incoming events to appropriate handlers - just type "run loop" into Xcode's help. If you create a standard application using Xcode's "Cocoa Application" template the run loop is created for you by the code in main.m.
Events produced by OS X when applications start and stop are dispatched by the run loop to framework handlers which produce the corresponding notifications. Without a run loop these system events will not be handled, so no notifications.
You have:
int main(int argc, const char * argv[])
{
fo = [[FO alloc]init];
[Main MyMethod];
while(1==1)
{
...some code;
}
return 0;
}
so unless "...some code" creates a run loop the system events will not be handled.
Write your project using the standard "Cocoa Application" template and, for example, put your call to setup the notification handlers in applicationDidFinishLaunching:.
HTH
I need some help in Xcode. I am getting the error Thread 1: SIGABRT and it is pointing to the main.m file. I set up the exception breakpoint and it still stops in the main.m file.
#import <UIKit/UIKit.h>
#import <QuartzCore/QuartzCore.h>
#import "AppDelegate.h"
int main(int argc, char *argv[])
{
#autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
That is the code that I have in my main.m file. Can someone please help me with this error.
The full error is:
2013-09-07 23:41:05.440 save the jewel 5[86090:c07] -[game pause:]: unrecognized selector sent to instance 0x845ece0
2013-09-07 23:41:09.460 save the jewel 5[86090:c07] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[game pause:]: unrecognized selector sent to instance 0x845ece0'
*** First throw call stack:
(0x1693012 0x13a0e7e 0x171e4bd 0x1682bbc 0x168294e 0x13b4705 0x2e82c0 0x2e8258 0x3a9021 0x3a957f 0x3a86e8 0x317cef 0x317f02 0x2f5d4a 0x2e7698 0x26f5df9 0x26f5ad0 0x1608bf5 0x1608962 0x1639bb6 0x1638f44 0x1638e1b 0x26f47e3 0x26f4668 0x2e4ffc 0x23c2 0x22f5)
libc++abi.dylib: terminate called throwing an exception
In my game.m file I have:
-(void)pauseLayer:(CALayer*)layer{
CFTimeInterval pausedTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil];
layer.speed = 0.0;
layer.timeOffset = pausedTime;
}
-(void)resumeLayer:(CALayer*)layer{
CFTimeInterval pausedTime = [layer timeOffset];
layer.speed = 1.0;
layer.timeOffset = 0.0;
layer.beginTime = 0.0;
CFTimeInterval timeSincePause = [layer convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime;
layer.beginTime = timeSincePause;
}
- (IBAction)pause:(id)sender {
[self pauseLayer:self.view.layer];
}
- (IBAction)resume:(id)sender {
[self resumeLayer:self.view.layer];
}
In my game.h file I have:
- (IBAction)pause:(id)sender;
- (IBAction)resume:(id)sender;
-(void)pauseLayer:(CALayer*)layer;
-(void)resumeLayer:(CALayer*)layer;
I need some help.
-[game pause:]: unrecognized selector sent to instance 0x845ece0
This means that you have an instance of the game class (which, btw, that class should be Game, not game, classes are capitalized) and something is calling the method pause: on that class, but there is no pause: method.
Given that the class is called Game (fixed) and pausing seems like a reasonable thing to do to a game, it is unlikely that this is an over-release problem. pause: seems like it is something that you might have a button hooked up to in interface builder.
Did you, perchance, rename the pause: method to something else (pauseGame:?) and not fix the connection in IB?
And that pause: implementation is inside the #implementation Game scope? If so, trying cleaning the project and rebuilding as there may be a dependency issue. If not, then that is your problem.
If the build from clean thing doesn't work, then there is some detail missing. Since building from clean didn't work, you'll have to post more details. Show the declaration of the class, at least.
I wanted to learn more about threading in objective-c, so I made a little test program that would just loop and output what iteration of the loop it is on.
However, the output I am getting is not what I am expecting. I have an idea as to why, but first here is my code:
main.m
#import <Foundation/Foundation.h>
#import "Car.h"
int main(int argc, const char * argv[])
{
#autoreleasepool {
Car* myCar = [[Car new] autorelease];
[myCar performSelectorInBackground:#selector(LoopAndSay) withObject:nil];
for(int i = 0; i < 100; i++ )
{
NSLog(#"Main loop on %i", i);
}
}
return 0;
}
Car.m
#import "Car.h"
#implementation Car
#synthesize name, model;
-(void) LoopAndSay {
for(int i = 0; i < 100; i++)
{
NSLog(#"Looping for the %i time", i);
}
}
#end
Now, if I run it as is the background loop will sometimes not complete(stopping between iterations 94 and 97). Additionally, if I switch my code around so that the background loop is not called until after the main thread loop then it will not run any iterations.
Is this because the main thread is finished and does not want to wait for the background thread to run to completion? If this is the case, is there a way to force the program to continue running until both the main thread and any background threads are complete?
This is almost certainly because you exit the scope of the autorelease pool before the thread completes your main function returns before the background thread completes. Try adding sleep(3) or something before its end and see if your background thread completes.
On Mac OS X, a process terminates when all its "foreground" threads have completed. peformSelectorInBackground: only creates a background thread, so once main returns, you're left with no foreground thread and your process is terminated.
This is because all threads created with performSelectorInBackground:... and other Objective-C APIs are detached, so that the program can terminate without waiting for them to finish. From the Threading Programming Guide:
At application exit time, detached threads can be terminated immediately but joinable threads cannot. [...] If you do want to create joinable threads, the only way to do so is using POSIX threads...
In this case, your main thread loop happens to finish a bit sooner than the background thread, so the background thread loop doesn't run through to the end. Instead of using POSIX threads (which is much less convenient), you could also use NSOperationQueue, which easily allows you to wait for all operations in a queue to finish.
here is my code template:
int main(int argc, char *argv[]) {
// create an autorelease pool
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
// make sure the application singleton has been instantiated
NSApplication * application = [NSApplication sharedApplication];
// instantiate our application delegate
AppDelegate * applicationDelegate =
[[[AppDelegate alloc] init] autorelease];
// assign our delegate to the NSApplication
[application setDelegate:applicationDelegate];
// call the run method of our application
[application run];
// drain the autorelease pool
[pool drain];
// execution never gets here ..
return 0;
}
"[pool drain]" followd by "return 0" why never get executed.
but, i found another one of gnustep official examples, it does the same thing.
int
main(int argc, char **argv, char** env)
{
id pool = [NSAutoreleasePool new];
NSApplication *theApp;
#if LIB_FOUNDATION_LIBRARY
[NSProcessInfo initializeWithArguments:argv count:argc environment:env];
#endif
theApp = [NSApplication sharedApplication];
[theApp setDelegate: [browserController new]];
[theApp run];
[pool release];
return 0;
}
to prove it "[theApp run]" never get back,i have done the practice with adding an infinite loop immediately after " [theApp run]".but it never get executed. why did the example from GNUSTEP official do such this?
Are you certain that [pool drain] is actually getting called either? [application run] won't return unless [NSApp stop] is called (which is rare). If the more common [NSApp terminate] is called, as the docs say:
Do not bother to put final cleanup code in your application’s main()
function—it will never be executed. If cleanup is necessary, perform
that cleanup in the delegate’s applicationWillTerminate: method.
Once you hand the application over to run, you generally aren't going to get it back.
So, I am using [NSThread detachNewThreadSelector] to spawn a new thread and I am getting "autoreleased with no pool in place " errors in the console. I know this can happen if you fail to create an auto release pool, but the thing is, I am creating one. I use similar code in other parts of the same app and do NOT get these errors.
Here is the relevant code:
- (void) startThread:(NSString*)strURL
{
// start new thread to load image
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[NSThread detachNewThreadSelector:#selector(loadImageFromURL:) toTarget:self withObject:strURL];
[pool release];
}
- (void) loadImageFromURL:(NSString*)strURL
{
NSNumber* nn = [NSNumber numberWithInt:self.tag];
NSLog(#"loadURL: Tag number == %i", [nn intValue]);
// other code here actually does the work
}
Now, there was more code in loadImageFromURL which actually does the work (of loading an image from a remote server) - but the problem manifests itself without that code, so I've removed it (just so you don't think I have a pointless thread which does nothing!). I left in just one line of code which demonstrates the problem - it creates an autoreleased NSNumber object.
When this code runs, it reports this to the console:
__NSAutoreleaseNoPool(): Object 0x535c0e0 of class NSCFNumber autoreleased with no pool in place - just leaking
Of course, the real code creates many other AR objects and all of them get reported as well.
Would be grateful for any tips or pointers which might help!
Thanks!
When you create a new thread, you need to also create a new autorelease pool for it. In your case, that looks as simple as adding:
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
at the beginning of loadImageFromURL: and
[pool drain];
at the end.
You probably don't need or want the pool you're creating in startThread:. Check out the Threading Programming Guide, particularly the "Writing Your Thread Entry Routine" section.
On your code, - (void) startThread:(NSString*)strURL is running in the main thread, while - (void) loadImageFromURL:(NSString*)strURL is running on the background thread you are detaching.
The main thread already has a NSAutoreleasePool, so the one you are creating in startThread: is probably unneeded. However, the background thread will not create a NSAutoreleasePool, so you'd need to create it yourself.
In your code, that would look like:
- (void) startThread:(NSString*)strURL
{
// start new thread to load image
[NSThread detachNewThreadSelector:#selector(loadImageFromURL:) toTarget:self withObject:strURL];
}
- (void) loadImageFromURL:(NSString*)strURL
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSNumber* nn = [NSNumber numberWithInt:self.tag];
NSLog(#"loadURL: Tag number == %i", [nn intValue]);
// other code here actually does the work
[pool drain];
}
Also, as #Carl Norum suggested, you should use drain instead of release when you are done using the autorelelase pool.
Solution for a similar problem but using ARC.
If using ARC, you could get an error "'NSAutoreleasePool' is unavailable: not available in automatic reference counting mode".
Use:
- (void) startThread:(NSString*)strURL
{
// start new thread to load image
[NSThread detachNewThreadSelector:#selector(loadImageFromURL:) toTarget:self withObject:strURL];
}
- (void) loadImageFromURL:(NSString*)strURL
{
#autoreleasepool {
NSNumber* nn = [NSNumber numberWithInt:self.tag];
NSLog(#"loadURL: Tag number == %i", [nn intValue]);
// other code here actually does the work
}
}