hi i am using Xcode 5+ and iOS 7+ , and implementing NSOperationQueue. i create a subclass of NSOpeartion and say i have 50 + operation added to NSOperationQueue. isExecuting and isFinished is overridden in NSOperation`s start method -
-(void)start{
// soeme code is here
[self willChangeValueForKey:#"isExecuting"];
_isExecuting = YES;
[self didChangeValueForKey:#"isExecuting"];
[self willChangeValueForKey:#"isFinished"];
_ isFinished = NO;
[self didChangeValueForKey:#"isFinished"];
}
and after completion task i write this code
[self willChangeValueForKey:#"isExecuting"];
_isExecuting = NO;
[self didChangeValueForKey:#"isExecuting"];
[self willChangeValueForKey:#"isFinished"];
_ isFinished = YES;
[self didChangeValueForKey:#"isFinished"];
MaxConcurrentOperationCount is 2. But after completion of 2 operation next (3rd operation) doesn't get execute main method. please give me some clarification about when this issue comes.
Thanks
Upon startup, you should set isExecuting to YES but you should not set isFinished at all, especially not to YES.
When your async operation is done, you need to set isExecuting to NO, not YES, and you need to set isFinished to YES but you are setting isExecuting a 2nd time.
Related
This code works, and postSpamListUpdatedNotification is called
- (void) postSpamListUpdatedNotification
{
[NIDPrivateUtils postNotification:kNIDSpamListsUpdated andError:nil];
}
[self performSelector:#selector(postSpamListUpdatedNotification) withObject:nil];
But if I change it to this, then postSpamListUpdateNotification is never called. Why?
[self performSelector:#selector(postSpamListUpdatedNotification) withObject:nil afterDelay:2.0];
You likely don't have a runloop on this thread. performSelector:withObject:afterDelay: requires a runloop, but performSelector: doesn't.
I've been trying to fix an issue in our NSOperation subclass and I feel it may be related to our manual change notifications for KVO. All the sources I've checked seem to do the following when updating the NSOperation state:
[self willChangeValueForKey:#"isExecuting"];
[self willChangeValueForKey:#"isFinished"];
_isExecuting = NO;
_isFinished = YES;
[self didChangeValueForKey:#"isFinished"];
[self didChangeValueForKey:#"isExecuting"];
In contrast, we have been doing it like this:
[self willChangeValueForKey:#"isExecuting"];
_isExecuting = NO;
[self didChangeValueForKey:#"isExecuting"];
[self willChangeValueForKey:#"isFinished"];
_isFinished = YES;
[self didChangeValueForKey:#"isFinished"];
Can anybody tell me why the former seems to be the recommended way of doing this?
It also seems that Apple's KVO docs recommend the first approach as well. Unfortunately they don't explain why.(https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/KeyValueObserving/Articles/KVOCompliance.html#//apple_ref/doc/uid/20002178-SW3)/
The reason is that, conceptually, the operation is changing both states together. You want observers to be notified only after the internal state has been updated for both properties, so that, when handling the notification, the observers see consistent state.
If you do:
[self willChangeValueForKey:#"isExecuting"];
_isExecuting = NO;
[self didChangeValueForKey:#"isExecuting"];
then observers will get the change notification for the isExecuting property during that didChange... call. If they check the operation properties in their handler, they could see that the operation is not executing (isExecuting returns NO) but also not finished (isFinished still returns NO). That doesn't make sense. The observers could do something odd as a result.
I've implemented NSOperation without following the pattern
[self willChangeValueForKey:#"isExecuting"];
[self willChangeValueForKey:#"isFinished"];
_isExecuting = NO;
_isFinished = YES;
[self didChangeValueForKey:#"isFinished"];
[self didChangeValueForKey:#"isExecuting"];
instead by simply doing
self.executing = NO;
self.finished = YES;
.. without problems (never ending operations or the like) when using NSOperationQueues. It seems NSOperationQueue only listens for 'IsFinished' to determine if an NSOperation is truly finished. This other answer explains better.
NSOperation KVO isFinished
How to identify Nsoperation dynamically.
I am creating a NSoperation subclass
- (id)initWithConnectDevice:(ConnectDevice *)cDevice toPeripheral:(CBPeripheral*)peripheral oPerationIndex:(int) index{
if (self = [super init]) {
operationIndex = index;
executing = NO;
finished = NO;
self.connectDevice = cDevice;
[self.connectDevice setDelegate:self];
self.connectedPeripheral = peripheral;
}
return self;
}
-(BOOL)isConcurrent{
return YES;
}
- (BOOL)isExecuting {
return executing;
}
- (BOOL)isFinished {
return finished;
}
-(void) terminateOperation {
[self willChangeValueForKey:#"isFinished"];
[self willChangeValueForKey:#"isExecuting"];
finished = YES;
executing = NO;
[self didChangeValueForKey:#"isExecuting"];
[self didChangeValueForKey:#"isFinished"];
}
- (void)start {
#autoreleasepool {
if (self.isCancelled){
[timer invalidate];
[self willChangeValueForKey:#"isFinished"];
finished = YES;
[self didChangeValueForKey:#"isFinished"];
return;
}
timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(timerFired:) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] run];
}
}
-(void)timerFired:(id)sender{
NSLog(#"timerFired");
}
I am scanning for BLE devices. For example I will found 3 devices, 3 buttons will create.
I am calling this class every time when I am clicking on that button.That means, When I click on button, I am connecting to bluetooth device and get the data from that device for every second thats why I am using timer in start method.
Like that I have multiple bluetooth devices, whenever I clicked on button, I want to create multiple instances of Operation Queue class.
Now, I want to identify which data is coming from which thread.
Could you please help me....
This the way I am calling above class from viewcontroller
OperationQueue *queue = [[OperationQueue alloc] initWithConnectDevice:connectDevices toPeripheral:peripheral oPerationIndex:operationIndex];
queue.delegate = self;
[[[AppDelegate app] mainQueue] addOperation:queue];
operationIndex = operationIndex+1;
Each of your operations has two identifying properties already - the connected device and the index. When the timer fires, depending what you want to do with the data, you can use these properties to tell where the data is coming from.
You can either have a delegate property on the operation, where a delegate method is called when the timer fires that takes the device and the received data as parameters, or you the operation could have a block property, which takes a block to be executed whenever data is received - the block would have the device and the received data as parameters.
Assuming you want to update the UI when the data is received, be sure to call the delegate method or execute the block on the main thread.
Is there a way in the normal obj-c or cocos2d to make a delay inside a if-else block?
Like
if ([self isValidTileCoord:cTileCoord] && ![self isWallAtTileCoord:cTileCoord])
{
[self addChild:circle0];
//wait two seconds
//perform another task
}
Just a simple lag to wait between two tasks, or stalling an action. Is there any simple way to do this?
There are many ways to do this. I would use GCD
if ([self isValidTileCoord:cTileCoord] && ![self isWallAtTileCoord:cTileCoord])
{
[self addChild:circle0];
//wait two seconds
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC), dispatch_get_current_queue(), ^{
//perform another task;
});
}
You can use the performSelector: withObject: afterDelay: method to delay tasks:
if ([self isValidTileCoord:cTileCoord] && ![self isWallAtTileCoord:cTileCoord])
{
[self addChild:circle0];
//wait two seconds
[self performSelector:#selector(continueTask) withObject:nil afterDelay:2.0];
}
And in the selector method:
-(void)continueTask
{
//perform another task
}
Inside the cocos2d you can also run an Action on the node like,
[self runAction:[CCSequence actions:[CCDelayTime actionWithDuration:2.0],[CCCallFunc actionWithTarget:self selector:#selector(anothertaks)],nil]];
Perform selector will also work but the problem is that all the schedulers of cocos2D get paused when the app go to background but on the other side perform selector will still count the time, so some time it create task,animation,etc syncing problems.
Perform selector:
[self performSelector:#selector(continueTask) withObject:nil afterDelay:2.0];
I have a function drawView which is thread safe and does drawing for short periods of game animation. I have functions startAnimating and stopAnimating. I want a background thread to call drawView at a regular rate but only during the period that the animation is enabled.
In startAnimating I was going to call the view's performSelectorInBackground:withObject: to get the thread running.
I'm a little confused about how to do the thread communication and initialize the drawing thread: specifically, setting up a runloop to receive display link messages and then at the end notifying the thread that it should exit and exiting the run loop cleanly when stopAnimating is called from the main thread. I want to ensure that drawView is never called after stopAnimating, and also that the drawing thread is not cancelled abruptly in the middle of the drawing operation. I have seen a lot of very poor answers to this kind of question on line.
OK after reading the Apple pages all evening, I finally solved it with this code:
// object members
NSThread *m_animationthread;
BOOL m_animationthreadrunning;
- (void)startAnimating
{
//called from UI thread
DEBUG_LOG(#"creating animation thread");
m_animationthread = [[NSThread alloc] initWithTarget:self selector:#selector(animationThread:) object:nil];
[m_animationthread start];
}
- (void)stopAnimating
{
// called from UI thread
DEBUG_LOG(#"quitting animationthread");
[self performSelector:#selector(quitAnimationThread) onThread:m_animationthread withObject:nil waitUntilDone:NO];
// wait until thread actually exits
while(![m_animationthread isFinished])
[NSThread sleepForTimeInterval:0.01];
DEBUG_LOG(#"thread exited");
[m_animationthread release];
m_animationthread = nil;
}
- (void)animationThread:(id)object
{
#autoreleasepool
{
DEBUG_LOG(#"animation thread started");
m_animationthreadrunning = YES;
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
CADisplayLink *displaylink = [CADisplayLink displayLinkWithTarget:self selector:#selector(displayLinkAction:)];
[displaylink setFrameInterval:3];
[displaylink addToRunLoop:runLoop forMode:NSDefaultRunLoopMode];
while(m_animationthreadrunning)
{
[runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
DEBUG_LOG(#"runloop gap");
}
[displaylink removeFromRunLoop:runLoop forMode:NSDefaultRunLoopMode];
DEBUG_LOG(#"animation thread quit");
}
}
- (void)quitAnimationThread
{
DEBUG_LOG(#"quitanimationthread called");
m_animationthreadrunning = NO;
}
- (void)displayLinkAction:(CADisplayLink *)sender
{
DEBUG_LOG(#"display link called");
//[self drawView];
}
The reason I use the line [self performSelector:#selector(quitAnimationThread) onThread:m_animationthread withObject:nil waitUntilDone:NO] and not simply set m_animationthreadrunning = NO in stopAnimating is because the run loop may not return in a timely fashion, but calling a selector forces it to return.