So there's a number of ways to run all the elements of an array through a selector or block of code. makeObjectsPerformSelector: and more
If you have 4 or 16 cores on hand, you may well want to send all the processing off to different processes.
Even on iOS, it could well be sensible to send them all away, or at least let them be finished whichever way the wind blows.
What is the best and neatest way to do this in the cocoa milieu?
Again, what if you want to send them away to be finished at any time, i.e., do not wait for the enumeration to finish before executing the following instruction...?
If you want concurrency and synchronous behaviour (i.e., wait for the enumeration to finish before executing the following instruction), -[NSArray enumerateObjectsWithOptions:usingBlock:] will do that if you pass the NSEnumerationConcurrent option. This method is available on Mac OS 10.6+ and iOS 4.0+.
If you want asynchronous behaviour, you can use one of the standard multithreading solutions such as NSOperation or GCD combined with -enumerateObjectsWithOptions:usingBlock:. For instance,
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[array enumerateObjectsWithOptions:NSEnumerationConcurrent
usingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
// do something with obj
}];
});
If you can target iOS 4+ or Mac OS X 10.6+, use Grand Central Dispatch (and I'm using a category because I think they're cool):
#import <dispatch/dispatch.h>
#interface NSArray (DDAsynchronousAdditions)
- (void) makeObjectsPerformSelectorAsynchronously:(SEL)selector;
- (void) makeObjectsPerformSelectorAsynchronously:(SEL)selector withObject:(id)object;
#end
#implementation NSArray (DDAsynchronousAdditions)
- (void) makeObjectsPerformSelectorAsynchronously:(SEL)selector {
[self makeObjectsPerformSelectorAsynchronously:selector withObject:nil];
}
- (void) makeObjectsPerformSelectorAsynchronously:(SEL)selector withObject:(id)object {
for (id element in self) {
dispatch_async(dispatch_get_global_queue(0,0), ^{
[element performSelector:selector withObject:object];
});
}
}
#end
//elsewhere:
[myArray makeObjectsPerformSelectorAsynchronously:#selector(doFoo)];
Or, if you don't want to use a category...
[myArray enumerateObjectsUsingBlock:^(id obj, NSUInteger index, BOOL * stop) {
dispatch_async(dispatch_get_global_queue(0,0), ^{
[obj performSelector:#selector(doFoo)];
};
}];
Related
I want to call a function in applicationDidEnterBackground this function is defined in other controller.
I have made an object to access it but it seems that function is getting killed when called.
Here is the function it basically calculates the distance and postes a notification
-(void)calculateDistance
{
for (NSMutableDictionary *obj in placeName) {
CLLocation *userLocation = [[AppHelper appDelegate] mLatestLocation];
CLLocation *annotation1 = [[CLLocation alloc] initWithLatitude:[[obj objectForKey:#"Lat"]doubleValue] longitude:[[obj objectForKey:#"long"]doubleValue]];
CGFloat distanceTemp = [annotation1 getDistanceFrom:userLocation];
[obj setObject:[NSNumber numberWithFloat:distanceTemp] forKey:#"distance"];
[annotation1 release];
}
if ([placeName count])
{
NSArray *sortedArray=[placeName sortedArrayUsingFunction:intSort context:NULL];
self.placeName = [NSMutableArray arrayWithArray:sortedArray];
NSMutableArray *arrayTemp = [[NSMutableArray alloc] initWithArray:placeName];
for (int i =0; i < [placeName count]; i++)
{
// NSArray *sortedArray=[placeName sortedArrayUsingFunction:intSort context:NULL];
NSMutableArray *tempArray = [sortedArray objectAtIndex:i];
//DLog(#"sortedArray%#", sortedArray);8=
NSNumber *DistanceNum = [tempArray objectForKey:#"distance"];
NSLog(#"distance%#:::",DistanceNum);
NSInteger intDistance = (int)[DistanceNum floatValue];
if(intDistance<500)
{
NSLog(#"ho gaya bhai");
NSString *notifications =#"Yes";
[[AppHelper mDataManager] setObject:notifications forKey:#"notifications"];
NSLog(#"notifications:%#",notifications);
RemindMeViewController *object = [[RemindMeViewController alloc] initWithNibName:#"RemindMeViewController" bundle:nil];
// RemindMeViewController *object=[[RemindMeViewController alloc]initWithNibName];
NSLog(#"notifications set");
[object scheduleNotification];
}
else
{
// [arrayTemp removeObjectAtIndex:i];
}
}
//after for loop is ended
self.placeName= arrayTemp;
DLog(#"remaining",arrayTemp);
[arrayTemp release];
[mTableView reloadData];
}
}
How long is your function taking to complete? You only have 5 seconds to perform tasks in applicationDidEnterBackground: and return.
From Apple's UIApplicationDelegate Protocol Reference:
Your implementation of this method has approximately five seconds to
perform any tasks and return. If you need additional time to perform
any final tasks, you can request additional execution time from the
system by calling beginBackgroundTaskWithExpirationHandler:. In
practice, you should return from applicationDidEnterBackground: as
quickly as possible. If the method does not return before time runs
out your application is terminated and purged from memory.
You should perform any tasks relating to adjusting your user interface
before this method exits but other tasks (such as saving state) should
be moved to a concurrent dispatch queue or secondary thread as needed.
Because it's likely any background tasks you start in
applicationDidEnterBackground: will not run until after that method
exits, you should request additional background execution time before
starting those tasks. In other words, first call
beginBackgroundTaskWithExpirationHandler: and then run the task on a
dispatch queue or secondary thread.
As far as I know you should not call any time consuming functions in applicationDidEnterBackground since the app will get suspended after a short amount of time.
From Apple's IOS Programming Guide
Most applications that enter the background state are moved to the suspended state shortly thereafter. While in this state, the application does not execute any code and may be removed from memory at any time. Applications that provide specific services to the user can request background execution time in order to provide those services.
Gool luck :)
have you tried this, for example using a NSThread or make some logic to call this method
- (void)applicationDidEnterBackground:(UIApplication *)application {
/*
Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
If your application supports background execution, called instead of applicationWillTerminate: when the user quits. */}
// inside this method try to call the calculate position method may be it will works(try nsthread here)
I'm building my own activity indicator like class that's supposed to fade in before a heavy operation, and fade out when the operation is complete. This is working fine, until I run into the following scenario:
[[MyLoaderClass sharedInstance] displayLoaderInView:self.view];
for( int i = 0; i < 1000; i++ ) {
NSLog(#"Performing heavy operation...");
}
[[MyLoaderClass sharedInstance] removeLoaderInView:self.view];
What's happening on the first line is that my loader view is alloced, subviewed and told to fade in with a standard UIView animation. However, the animation doesn't start (as shown by the setAnimationWillStartSelector:) until after the heavy operation is complete.
Now, heavy operations on the main thread are of course to be avoided, but I still want my loader class to work no matter what programmers might throw at it.
I tried moving the loader into a separate thread and animating it from there which worked great, but led to crashes because it's not cool to manipulate views from threads other than the main thread.
My question: Is it possible to do what I want, and/or should I bother with it at all?
As an alternative to Joshua Smith's suggestion, in case being on a different thread messes with your operation, just make sure you drop out to the runloop between starting the UIView animations and starting your heavy code. E.g.
...
[[MyLoaderClass sharedInstance] displayLoaderInView:self.view];
[self performSelector:#selector(performHeavyOperation) withObject:nil afterDelay:0];
}
- (void)performHeavyOperation
{
for( int i = 0; i < 1000; i++ ) {
NSLog(#"Performing heavy operation...");
}
[[MyLoaderClass sharedInstance] removeLoaderInView:self.view];
}
The performSelector:withObject:afterDelay: causes the nomated selector to be scheduled on the runloop in the future. Setting a delay of 0 means it is added to the runloop to occur as soon as possible.
For various reasons, quite a lot of UIView stuff takes effect only if you allow the call stack to unwind all the way to the call stack. That's so that, e.g. if you did:
view.frame = aNewFrame;
view.someOtherPropertyThatWouldCauseAVisibleChange = anotherValue;
Then the UIView will end up redrawing itself only once, not twice.
Put your heavy operation in an NSOperationQueue, then it will not block the main thread.
#interface MyClass : NSOperation {
}
#end
#implementation MyClass
-(void) main {
for( int i = 0; i < 1000; i++ ) {
NSLog(#"Performing heavy operation...");
}
}
#end
Then, in your above code:
[[MyLoaderClass sharedInstance] displayLoaderInView:self.view];
NSOperationQueue *q = [[NSOperationQueue alloc] init];
MyClass *c = [[[MyClass alloc] init] autorelease];
[q addOperation:c];
[q waitUntilAllOperationsAreFinished];
[[MyLoaderClass sharedInstance] removeLoaderInView:self.view];
Read the docs, too, you'll need them: http://developer.apple.com/library/mac/#documentation/cocoa/reference/NSOperationQueue_class/Reference/Reference.html
NSOperationQueue's are awesome, but not exactly intuitive.
I would like to see if I can make a "search as you type" implementation, against a web service, that is optimized enough for it to run on an iPhone.
The idea is that the user starts typing a word; "Foo", after each new letter I wait XXX ms. to see if they type another letter, if they don't, I call the web service using the word as a parameter.
The web service call and the subsequent parsing of the result I would like to move to a different thread.
I have written a simple SearchWebService class, it has only one public method:
- (void) searchFor:(NSString*) str;
This method tests if a search is already in progress (the user has had a XXX ms. delay in their typing) and subsequently stops that search and starts a new one. When a result is ready a delegate method is called:
- (NSArray*) resultsReady;
I can't figure out how to get this functionality 'threaded'.
If I keep spawning new threads each time a user has a XXX ms. delay in the typing I end up in a bad spot with many threads, especially because I don't need any other search, but the last one.
Instead of spawning threads continuously, I have tried keeping one thread running in the background all the time by:
- (void) keepRunning {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
SearchWebService *searchObj = [[SearchWebService alloc] init];
[[NSRunLoop currentRunLoop] run]; //keeps it alive
[searchObj release];
[pool release];
}
But I can't figure out how to access the "searchFor" method in the "searchObj" object, so the above code works and keeps running. I just can't message the searchObj or retrieve the resultReady objects?
Hope someone could point me in the right direction, threading is giving me grief:)
Thank you.
Ok, I spend the last 8 hours reading up on every example out there.
I came to realize that I would have to do some "Proof of Concept" code to see if there even would be a speed problem with building a new thread for "each" keystroke.
It turns out that using NSOperation and NSOperationQueue is more than adequate, both in terms of speed and especially in terms of simplicity and abstraction.
Is called after each keystroke:
- (void) searchFieldChanged:(UITextField*) textField {
[NSObject cancelPreviousPerformRequestsWithTarget:self];
NSString *searchString = textField.text;
if ([searchString length] > 0) {
[self performSelector:#selector(doSearch:) withObject:textField.text afterDelay:0.8f];
}
}
This is mainly to stop the code form initiating a search for keystrokes that are less than 800 ms. apart.
(I would have that a lot lower if it where not for the small touch keyboard).
If it is allowed to time out, it is time to search.
- (void) doSearch:(NSString*) searchString {
[queue cancelAllOperations];
ISSearchOperation *searchOperation = [[ISSearchOperation alloc] initWithSearchTerm:searchString];
[queue addOperation:searchOperation];
[searchOperation release];
}
Cancel all operations that is currently in the queue. This is called every time a new search is
started, it makes sure that the search operation already in progress gets closed down in an orderly fashion, it also makes sure that only 1 thread is ever in a "not-cancelled" state.
The implementation for the ISSearchOperation is really simple:
#implementation ISSearchOperation
- (void) dealloc {
[searchTerm release];
[JSONresult release];
[parsedResult release];
[super dealloc];
}
- (id) initWithSearchTerm:(NSString*) searchString {
if (self = [super init]) {
[self setSearchTerm:searchString];
}
return self;
}
- (void) main {
if ([self isCancelled]) return;
[self setJSONresult:/*do webservice call synchronously*/];
if ([self isCancelled]) return;
[self setParsedResult:/*parse JSON result*/];
if ([self isCancelled]) return;
[self performSelectorOnMainThread:#selector(searchDataReady:) withObject:self.parsedResult waitUntilDone:YES];
}
#end
There are two major steps, the downloading of the data from the web service and the parsing.
After each I check to see if the search has been canceled by [NSOperationQueue cancelAllOperations] if it has, then we return and the object is nicely cleaned up in the dealloc method.
I will probably have to build in some sort of time out for both the web service and the parsing, to prevent the queue from choking on a KIA object.
But for now this is actually lightning fast, in my test I am searching an 16.000 entries dictionary and having Xcode NSLog it to the screen (slows things down nicely), each 800 ms. I issue a new search string via a timer and thereby canceling the old before it has finished its NSLog results to screen loop.
NSOperationQueue handles this with no glitches and never more that a few ms. of two threads being executed. The UI is completely unaffected by the above tasks running in the background.
-(void)setX:(int)x andY:(int)y andObject:(Sprite*)obj
{
[obj setPosition:CGPointMake(x,y)];
}
Now, I want to call above method, using following timer.
[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector() userInfo:nil repeats:NO];
What to set Here?
How to Pass arguments? (as per my knowledge - selector specifies only the method to invoke)
You'll need to used +[NSTimer scheduledTimerWithTimeInterval:invocation:repeats:] instead. By default, the selector used to fire a timer takes one parameter. If you need something other than that, you have to create an NSInvocation object, which the timer will use instead.
If you have a fairly complex set of arguments that you want to use to invoke the method, I would recommend capturing the arguments into something that holds a configuration and can do whatever it is that needs doing based on that configuration...
Something with an interface like this:
PositionSetter.h:
#interface PositionSetter : NSObject
{
NSInteger x;
NSInteger y;
Sprite *target;
}
+ positionSetterWithX: (NSInteger) xPos y: (NSInteger) yPos sprite: (Sprite *) aSprite;
- (void) applyPosition;
#end
PositionSetter.m:
#interface PositionSetter()
#property(readwrite, nonatomic) NSInteger x;
#property(readwrite, nonatomic) NSInteger y;
#property(readwrite, nonatomic, retain) Sprite *target;
#end
#implementation PositionSetter
#synthesize x, y, target;
+ positionSetterWithX: (NSInteger) xPos y: (NSInteger) yPos sprite: (Sprite *) aSprite;
{
PositionSetter *positionSetter = [PositionSetter new];
positionSetter.x = xPos;
positionSetter.y = yPos;
positionSetter.target = aSprite;
return [positionSetter autorelease];
}
- (void) applyPosition;
{
[self.target setPosition:CGPointMake(self.x,self.y)];
}
#end
Usage is quite straightforward:
positionSetter = [PositionSetter positionSetterWithX: 42 y: 21 sprite: mySprite];
[positionSetter performSelector: #selector(applyPosition) withObject: nil afterDelay: 1.0];
While a tad more code, the resulting implementation will be fast enough -- probably faster than NSInvocation, but fast enough to be irrelevant given that this is gonna cause drawing -- and a heck of a lot more flexible. I could easily see refactoring the above into driving, say, CoreAnimation.
Copied from an answer by Matt Ball:
- (void)startMyTimer {
/* ... Some stuff ... */
NSDictionary *userDict;
userDict = [NSDictionary dictionaryWithObjectsAndKeys:someValue,
#"value1",
someOtherValue,
#"value2", nil];
[NSTimer scheduledTimerWithTimeInterval:0.1
target:self
selector:#selector(callMyMethod:)
userInfo:userDict
repeats:YES];
}
- (void)callMyMethod:(NSTimer *)theTimer {
NSString *value1 = [[theTimer userInfo] objectForKey:#"value1"];
NSString *value2 = [[theTimer userInfo] objectForKey:#"value2"];
[self myMethod:value1 setValue2:value2];
}
If you use a target-action timer, you can't have the timer directly call an arbitrary method. A timer's action must have a very specific signature. You can pass additional data in the userinfo dictionary and have the timer's action call the method you ultimately want, or you can use the invocation form as Dave said. Personally, I usually do the former, because I find NSInvocations to be annoying and setting one up can actually take more code than just writing an intermediary method.
You can pass an NSDictionary*, or some other object, as the userInfo and put the arguments in that.
As an alternative to NSTimer, on iOS 4.0+ and 10.6+, you could use Grand Central Dispatch and dispatch sources to do this using a block. Apple has the following code for this in their Concurrency Programming Guide:
dispatch_source_t CreateDispatchTimer(uint64_t interval, uint64_t leeway, dispatch_queue_t queue, dispatch_block_t block)
{
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
if (timer)
{
dispatch_source_set_timer(timer, dispatch_walltime(NULL, 0), interval, leeway);
dispatch_source_set_event_handler(timer, block);
dispatch_resume(timer);
}
return timer;
}
You could then set up a one-second timer event using code like the following:
dispatch_source_t newTimer = CreateDispatchTimer(1ull * NSEC_PER_SEC, (1ull * NSEC_PER_SEC) / 10, dispatch_get_main_queue(), ^{
[self setX:someValue andY:otherValue andObject:obj];
});
as long as you store and release your timer when done. This can even let you trigger a timer to execute items on a background thread by using a concurrent queue instead of the main queue used above.
This can avoid the need for boxing and unboxing arguments.
Create dictionary with those arguments and pass that dictionary with timer userinfo. That will solve your problem
I have a background task that updates a view. That task calls -setNeedsDisplay to have the view drawn.
This works:
- (void) drawChangesTask;
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
if (pixels) {
drawChanges((UInt32 *) origPixels, (UInt32 *) pixels, CGBitmapContextGetBytesPerRow(ctx)/4, CGBitmapContextGetHeight(ctx), count--);
if (count < 0) {
count = 150;
}
else
[self performSelectorInBackground:#selector(drawChangesTask) withObject:nil ];
[self performSelectorOnMainThread:#selector(setNeedsDisplay) withObject:nil waitUntilDone:NO ];
}
[pool release];
}
This does not work:
- (void) drawChangesTask;
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
if (pixels) {
drawChanges((UInt32 *) origPixels, (UInt32 *) pixels, CGBitmapContextGetBytesPerRow(ctx)/4, CGBitmapContextGetHeight(ctx), count--);
if (count < 0) {
count = 150;
}
else
[self performSelectorInBackground:#selector(drawChangesTask) withObject:nil ];
[self setNeedsDisplay];
}
[pool release];
}
Anyone know why? When I say it doesn't work, I mean that it runs tens of iterations, sometimes I see portions of my image shifted up or down, or entirely blank, and then the deugger give me an “EXC_BAD_ACCESS” somewhere in CoreGraphics.
Also, if I don't handle the autorelease pool myself, then I get leaking error messages. Don't understand why that is either. My drawChanges() doesn't create any new objects. Here's the error:
2009-08-17 11:41:42.358 BlurApp[23974:1b30f] *** _NSAutoreleaseNoPool(): Object 0xd78270 of class NSThread autoreleased with no pool in place - just leaking
UIKit simply isn't thread-safe — you need to call methods that update UIKit controls on the main thread.
I think that this line:
[self performSelectorInBackground:#selector(drawChangesTask) withObject:nil];
Is causing trouble. Have you tried simply calling it again on the current thread? If you need the runloop to execute between the calls, use:
[self performSelector:#selector(drawChangesTask) withObject:nil afterDelay:0.0];
This will call the method on the current thread after the method you're in has finished and the runloop has gone round once.
Problem here is that UIKit is not thread safe, if you tell your UI to do something from a background thread nothign is guaranteed, what you want to do is use the performSelectorOnMainThread method to do updates t o your UI elements