I am facing the strange problem with dispatch_async
I have two separate projects: one is for building the framework and another is for using that framework for some purposes.
I am trying to execute very simple piece of code (without any additional code even):
dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_async(dispatch_get_main_queue(), ^{
});
});
so if i put this dispatch_async to my project it works just perfect,
but if put it to some method of my framework project and build it and then call that method from the project which is using that framework then i receive EXC_BAD_ACCESS error on first dispatch_async line on objc_retainAutoreleasedReturnValue stack
Although this dispatch_async will work only with dispatch_get_main_queue() queue
I tried to create different queues with different priorities, i tried static queues,
and they are actually created, but dispatch_async crashes every time
I believe this is a problem related to some flags in build settings in my framework project, but unfortunately i cannot find anything what can help
UPDATE:
I forgot to say, that this perfectly works on simulator, but does not work on the device
And here is an example of my code:
[[SingleTonOfFramework sharedInstance] executeRequest:#"SomeRequest"
success:^(id response) {
NSLog(#"works");
}];
SingleTonOfFramework - is a class from my framework
and this is what it does:
- (void) executeRequest:(id) request success:(void (^)(id response))success {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_async(dispatch_get_main_queue(), ^{
success(#"COOL!");
});
});
}
Related
I'm working on an app that can remove large amounts of files. When I invoke the NSFileManager's removeItemAtPath method, the app's UI locks until the operation finishes (this can take a while).
I tried fixing this by invoking the method using performSelectorInBackground but it didn't work.
Any ideas?
Thanks in advance.
You could try using GCD to do it in a background thread.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
[[NSFileManager defaultManager] removeItemAtPath:path];
});
I recently started using Ben Gottlieb's Twitter-OAuth-iPhone class to post status updates in app. I have successfully done so, when executed on the application's main thread. But, when I throw it in a "NSThread detachNewThreadSelector" the post never makes it to twitter. It processes fine and I get a proper [connection identifier], but the results never make it on twitter. Any idea how I can get this to run in a thread without bringing it back to the main thread?
Code added
Tweet method
-(void)tweet{
if([_engine isAuthorized]){
[_engine sendUpdate:#"Tweeting"];
}
}
Doesn't work:
[NSThread detachNewThreadSelector:#selector(tweet) toTarget:self withObject:nil];
Does work (from main app):
[self tweet];
Does work (from within a thread):
[self performSelectorOnMainThread:#selector(tweet) withObject:nil waitUntilDone:NO];
Basically, i have a method that takes a few seconds to complete as it copies some files using NSFileManager. This is invoked on the touchesMoved event when the user picks up a draggable UIView icon. However, there's a slight delay before the icon's position is updated. I'm guessing it's waiting for that method to copy it's files before continuing. The method HAS to be triggered on touchesMoved, so please don't suggest moving it.
How can i execute a method that takes about a second to complete, without holding up the code?
(..and don't worry the copy method doesn't get repeatedly called from the touchesMoved event)
You could perform the task in the background using performSelectorInBackground:...:
http://developer.apple.com/library/ios/#documentation/Cocoa/Reference/Foundation/Classes/NSObject_Class/Reference/Reference.html
This prevent that selector from blocking the main thread.
Example:
[self performSelectorInBackground:#selector(myMethod) withObject:nil];
Do it in a background thread. Leave the main thread to deal with UI stuff only.
Technically you could divide the copying of files into very small chunks, and tell the current NSRunLoop to dispatch between each file copy.
But practically just say no to any IO access on the main thread, all IO access should be done in the background. Even the slightest block on the main thread will make the UI stutter and be unresponsive, Android user might accept that, iOS user do not.
Your options are numerous, and easy to implement. You could do a simple performSelector–:
-(void)backgroundWorker {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// Do your stuff
[pool release];
}
-(void)startDoingIOStuff {
[self performSelectorInBackground:#selector(backgroundWorker)
withObject:nil];
}
You could do it practically inline using a block and GCD:
-(void)startDoingIOStuff {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, NULL),
^{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// Do your stuff
[pool release];
});
}
Or you could use an NSOperation on a NSOperationQueue. I have written a longer blog post on this topic, including source code that is available here: http://blog.jayway.com/2010/08/19/future-cocoa-operation/
Before immediately resorting to a secondary thread, it would certainly be worth a try to use a plain old performSelector on self. For example:
[self peformSelector:#selector(copyFiles) withObject:nil afterDelay:0.0];
Note that this is different from doing:
[self copyFiles];
The peformSelector version basically says "do copyFiles ASAP, OK?", but doesn't block everything while waiting for it to be done. In other words, it's possible that the perform selector version would allow the main event loop to update the UI (thereby preventing the apparent visual lag) before the file copying is actually done.
I've got a lovely OpenGLES code slice that renders up images for me. When I want to, I can call a function on it:
-(UIImage *)renderToImage;
That does a lot of rendering work and returns me an image. This includes the generation of FBOs, textures, etc.
Lately, I've found myself needing to enhance this. The image generation takes four seconds, so I want to pass off the work to another thread and let the app continue. This seemed simple enough. I made a method with this code:
-(void) generateRandomNewImage:(MyViewController *)evc{
UIImage * renderedImage = [self renderToImage];
NSString * fileLoc = [self writeToTempFile:renderedImage];
NSLog(#"File location:%#",fileLoc);
[evc performSelectorOnMainThread:#selector(imageGenerationComplete:) withObject:fileLoc waitUntilDone:NO];
}
Hopefully you can see the logic going on here. This method renders the image, saves it to the filesystem, and calls a method on the main thread's viewcontroller to let it know the file is ready. This code is inside my opengl renderer. It's called here, in the main thread's viewcontroller:
thread = [[NSThread alloc] initWithTarget:renderer
selector:#selector(generateRandomNewImage:)
object:self];
[thread start];
To me, that seems fine too. When I run this code, I get told in my console that my framebuffer object status were error'ed, with a status of zero. I have no idea why. As a result, I get a blank image (saving to the temp files work, by the way, I've tested them).
To test, I put all of this code into the main thread, didn't create any new threads or anything. It all worked fine. As soon as I try and pass off the image generation to another thread, I hit problems.
Using OpenGL in another thread is not that simple as that, only one thread can use a OpenGL context at a time, and your second thread doesn't have a OpenGL context, thus all OpenGL calls fail.
Solution: Create another OpenGL context for the second thread, and read this.
I have got a memory bug that seems to boil down to something happening in a thread. I am having difficulties troubleshooting this.
I have a UIViewController, that when active, i.e. the user is using its view, retrieves updates from a web service in an NSThread.
This is done every 3 minutes and this delay is controlled by a:
[self performSelector:#selector(timerDone) withObject:nil afterDelay:180.0];
The timerDone method now starts the NSThread that retrieves the web service data and also it sends the performSelector message again. This is a little "check for updates, populate views, shut everything down, repeat" routine that works just fine.
Now, the user can of course suddenly tap a button an load up a second UIViewController. When this happens I call:
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(timerDone) object:nil];
And do my cleaning up in the dealloc method.
My question is now: What happens if the NSThread was running while the user changed the view and set in motion the deconstruction of this object that is the starting point of the NSThread?
Should I keep a BOOL around that tells me if the NSThread is still active, and if so, what to do with the NSThread if this is the case.
The threading is done like this:
- (void) runTimer {
[self performSelector:#selector(timerDone) withObject:nil afterDelay:180];
}
- (void) timerDone {
[self performSelector:#selector(runTimer) withObject:nil afterDelay:2];
[NSThread detachNewThreadSelector:#selector(updateAllVisibleElements) toTarget:self withObject:nil];
}
- (void) updateAllVisibleElements {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
//call approiate web service
[pool release];
}
You have two problems here: first, you're using performSelector:withObject:afterDelay: to do what an NSTimer does best (periodic callback). cancelPreviousPerformRequestsWithTarget:selector:object: can be quite expensive, and because of your threading is likely creating race conditions.
Second problem: each thread has its own run loop, and both mechanisms (performSelector:... and NSTimer) and are tied to the current thread's run loop.
Here's what I recommend: Create a single, long-lived NSThread with its own explicit run loop for all your update needs. Look at the Threading Programming Guide for some good example code of this. On that thread, set up a 3-minute repeating NSTimer. Every 3 minutes, update.
If you need to schedule an update outside the three-minute cycle, then you use performSelector:onThread:withObject:waitUntilDone: to call your updateAllVisibileElements. The way I generally do this is to encapsulate all of the thread logic into a single object (WebServiceController or whatever). It creates it own NSThread and saves it in an ivar. Then I use code like this:
- (void)requestUpdate
{
if ([NSThread currentThread] != self.thread)
{
[self performSelector:#selector(update) onThread:self.thread withObject:nil waitUntilDone:NO];
return;
}
else
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
//call approiate web service
[pool drain];
}
}
One more note: you mention that the background thread "populates views." A background thread should never call into UIKit. UIKit is not thread safe and should only be called on the main thread. I typically achieve this by posting notifications onto the main thread which the view controllers observe. The "updating" object should not know anything about the UI. That breaks the Model-View-Controller paradigm of Cocoa.