xCode 4.4.1 OSX 10.8.2, looks like [operation cancelAllOperations]; isn't working
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
NSOperationQueue *operation = [[NSOperationQueue alloc] init];
[operation setMaxConcurrentOperationCount: 1];
[operation addOperationWithBlock: ^{
for (unsigned i=0; i < 10000000; i++) {
printf("%i\n",i);
}
}];
sleep(1);
if ([operation operationCount] > 0) {
[operation cancelAllOperations];
}
}
results 9999999
Inside your block, particularly inside the loop, call -isCancelled on the operation. If it's true, then return.
NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];
[operationQueue setMaxConcurrentOperationCount: 1];
NSBlockOperation *operation = [[NSBlockOperation alloc] init];
__weak NSBlockOperation *weakOperation = operation;
[operation addExecutionBlock: ^ {
for (unsigned i=0; i < 10000000; i++) {
if ([weakOperation isCancelled]) return;
printf("%i\n",i);
}
}];
[operationQueue addOperation:operation];
sleep(1);
if ([operationQueue operationCount] > 0) {
[operationQueue cancelAllOperations];
}
A queue can't just stop the operation's execution arbitrarily - what if some shared resources were being used by the operation that never got cleaned up? It's your responsibility to orderly end the operation when it becomes known to be cancelled. From Apple's docs:
An operation object is responsible for calling isCancelled
periodically and stopping itself if the method returns YES.
Related
I'm trying to do a simple chat in iOS with Obj-C, and when I press the "send" button, I save the object into Parse and I retrieve all the messages.
When it succeeds, I retrieve all the messages so the last one (mine) should be retrieved as well, but the last one never gets called. What should I do?
This is my code:
NSMutableArray *messagesArray;
- (IBAction)sendButtonTapped:(id)sender {
self.sendButton.enabled = false;
self.textField.enabled = false;
PFObject *objectChat = [PFObject objectWithClassName: #"ChatClass"];
objectChat[#"text"] = self.textField.text;
[objectChat saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
self.sendButton.enabled = true;
self.textField.enabled = true;
if (succeeded) {
self.textField.text = #"";
[self retrieveMessages];
NSLog(#"success!");
} else {
NSLog(error.description);
}
}];
}
-(void) retrieveMessages {
NSDate *now = [NSDate date];
PFQuery *query = [PFQuery queryWithClassName:#"ChatClass"];
[query whereKey:#"createdAt" lessThanOrEqualTo:now];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (error == nil) {
messagesArray = [[NSMutableArray alloc] init];
for (int i = 0; i < [objects count]; i++) {
[messagesArray addObject:[[objects objectAtIndex:i] objectForKey:#"text"]];
}
runOnMainQueueWithoutDeadlocking(^{
[self.messageTableView reloadData];
});
}
}];
}
void runOnMainQueueWithoutDeadlocking(void (^block)(void))
{
if ([NSThread isMainThread])
{
block();
}
else
{
dispatch_async(dispatch_get_main_queue(), block);
}
}
Thank you very much in advance.
Regards.
Perhaps there's enough of a clock difference with the parse server that the time qualification misses the object just created. Removing that line should do the trick.
I am creating a work queue to perform tasks in the background. The code is below. The problem is that selector called by the timer is called twice every period, by 2 different timers.
The queue (UpdateController) is created in didFinishLaunchingWithOptions of the AppDelegate:
...
[self setUpdateController:[[FFUpdateController alloc] initWithRootDetailViewController:rdvc]];
[[self updateController] start];
...
Here’s the UpdateController initializer
- (id) initWithRootDetailViewController:(FFRootDetailViewController*)rdvc
{
if (self = [super init])
{
_rootDetailViewController = rdvc;
_updateQueue = [[NSOperationQueue alloc] init];
}
return self;
}
Here’s UpdateController start
- (void) start
{
//sweep once a minute for updates
[self setTimer:[NSTimer scheduledTimerWithTimeInterval:60.0 target:self selector:#selector(sweepForUpdates:) userInfo:nil repeats:YES]];
}
Here is sweepForUpdates, the selector called by the timer:
- (void) sweepForUpdates:(NSTimer*)timer
{
FormHeader* fh;
NSInvocationOperation* op;
NSInteger sectionIdx = [[self dataController] sectionIndexForFormTypeWithTitle:SFTShares];
NSInteger headerCount = [[self dataController] numberOfRowsInSection:sectionIdx];
NSArray* changed;
NSMutableDictionary* params;
NSLog(#"Notice - sweepForUpdates(1) called:");
for (NSInteger i = 0; i < headerCount; i++)
{
fh = [[self dataController] formHeaderAtIndexPath:[NSIndexPath indexPathForRow:i inSection:sectionIdx]];
changed = [[self dataController] formDatasModifiedSince:[fh modifiedAt] ForFormHeader:fh];
if ([changed count])
{
NSLog(#"Error - sweepForUpdates(2) update: changes to update found");
params = [[NSMutableDictionary alloc] init];
[params setObject:fh forKey:#"formHeader"];
[params setObject:[self rootDetailViewController] forKey:#"rootDetailViewController"];
op = [[NSInvocationOperation alloc] initWithTarget:[FFParseController sharedInstance] selector:#selector(updateRemoteForm:) object:params];
if ([[[self updateQueue] operations] count])
{
[op addDependency:[[[self updateQueue] operations] lastObject]];
}
[[self updateQueue] addOperation:op];
}
else
{
NSLog(#"Error - sweepForUpdates(3) save: no changes found");
}
}
NSLog(#"Notice - sweepForUpdates(4) done:");
}
In this case there are 2 objects to examine for updates. Here is the console output for 1 sweep:
2015-02-16 09:22:28.569 formogen[683:806645] Notice - sweepForUpdates(1) called:
2015-02-16 09:22:28.580 formogen[683:806645] Error - sweepForUpdates(3) save: no changes found
2015-02-16 09:22:28.583 formogen[683:806645] Error - sweepForUpdates(3) save: no changes found
2015-02-16 09:22:28.584 formogen[683:806645] Notice - sweepForUpdates(4) done:
2015-02-16 09:22:29.249 formogen[683:806645] Notice - sweepForUpdates(1) called:
2015-02-16 09:22:29.254 formogen[683:806645] Error - sweepForUpdates(3) save: no changes found
2015-02-16 09:22:29.256 formogen[683:806645] Error - sweepForUpdates(3) save: no changes found
2015-02-16 09:22:29.256 formogen[683:806645] Notice - sweepForUpdates(4) done:
Neither object has updates, which is correct. But I do not understand why the selector is called twice.
Thanks
Add logging to start. You probably call it more than once.
Note that UpdateController can never deallocate, because the timer is retaining it. That may be ok, but keep that in mind if you believe you're deallocating it (and its timer).
Just trying to do a simple example with NSOperationQueue & NSInvocationOperation. Here's my code:
- (void) runMethodsViaOperationQueue {
NSOperationQueue *thisQueue = [[NSOperationQueue alloc] init];
NSInvocationOperation *logMethod1Invocation = [[NSInvocationOperation alloc]
initWithTarget:self
selector:#selector(logMethod1)
object:nil];
[thisQueue addOperation:logMethod1Invocation];
}
logMethod1 is just a looped NSLog statement, as in:
- (void) logMethod1 {
for (int a = 0; a < 10; a++) {
NSLog(#"%s --> logMethod1: %i", __FUNCTION__, a);
if (a == 9) {
NSLog(#"%s --> ==================", __FUNCTION__);
}
}
}
The class is instantiated in the main, where runMethodsViaOperationQueue is called.
MyOperationTestingClass *instantiateIt = [[MyOperationTestingClass alloc] init];
[instantiateIt runMethodsViaOperationQueue];
Thing is, when runMethodsViaOperationQueue executes nothing outputs as I'd expect via NSLog. Can anyone help me clarify why this isn't working?
I was really trying to extrapolate to more than one NSInvocationOperation, so create an array of the NSInvocationOperations:
NSArray *iOps = [NSArray arrayWithObjects:logMethod1Invocation, logMethod2Invocation, nil];
Then use this array with addOperations:WaitUntilFinished:
[thisQueue addOperations:iOps waitUntilFinished:YES];
When executed, the output will show more than one thread -
2013-02-15 20:32:29.276 NSOperationTest[1060:1b03] -[MyOperationTestingClass logMethod1] --> logMethod1: 0
2013-02-15 20:32:29.277 NSOperationTest[1060:1a03] -[MyOperationTestingClass logMethod2] --> logMethod2: 0
2013-02-15 20:32:29.280 NSOperationTest[1060:1b03] -[MyOperationTestingClass logMethod1] --> logMethod1: 1
2013-02-15 20:32:29.280 NSOperationTest[1060:1a03] -[MyOperationTestingClass logMethod2] --> logMethod2: 1
I have one basic question,
While working with NSOutputStream, should we wait for NSStreamEventHasSpaceAvailable to send the packet, so we can call , [NSOutputStream write] as and when its needed,
I believe NSStream should take care of write function...
if this is not correct, then please provide your views on following logic,
===== To Write on NSOutputStream =================
Have Queue to add packet, that to be sent
// StreamQueue.h
#interface StreamQueue : NSObject <NSCoding>
{
NSMutableArray * data;
NSRecursiveLock * theLock;
}
#pragma mark �Initialization & Deallocation�
- (id)init;
- (id)initWithQueue:(CommQueue *)queue;
- (id)initWithCoder:(NSCoder *)coder;
- (void)dealloc;
- (void)encodeWithCoder:(NSCoder *)coder;
#pragma mark
#pragma mark �Accessor Methods�
- (int)size;
- (BOOL)isEmpty;
- (id)top;
- (NSArray *)data;
#pragma mark
#pragma mark �Modifier Methods�
- (void)enqueue:(id)object;
- (id)dequeue;
- (void)removeAll;
#end
and its implementation
#import "StreamQueue.h"
#implementation StreamQueue
#pragma mark �Initialization & Deallocation�
- (id)init
{
if (self = [super init]) {
data = [[NSMutableArray alloc] init];
theLock = [[NSRecursiveLock alloc] init];
}
return self;
}
- (id)initWithQueue:(StreamQueue *)queue
{
if (self = [super init]) {
data = [[NSMutableArray alloc] initWithArray:[queue data]];
theLock = [[NSRecursiveLock alloc] init];
}
return self;
}
- (id)initWithCoder:(NSCoder *)coder
{
if (self = [super init]) {
data = [[NSMutableArray alloc] initWithArray:[coder decodeObject]];
theLock = [[NSRecursiveLock alloc] init];
}
return self;
}
- (void)dealloc
{
[data release];
[theLock release];
[super dealloc];
}
- (void)encodeWithCoder:(NSCoder *)coder;
{
[coder encodeObject:data];
}
#pragma mark
#pragma mark �Accessor Methods�
- (int)size
{
int size;
[theLock lock];
size = [data count];
[theLock unlock];
return size;
}
- (BOOL)isEmpty
{
BOOL empty;
[theLock lock];
empty = ([data count] == 0);
[theLock unlock];
return empty;
}
- (id)top
{
id object = nil;
[theLock lock];
if (![self isEmpty])
object = [data objectAtIndex:0];
[theLock unlock];
return object;
}
- (NSArray *)data
{
NSArray * array;
[theLock lock];
array = [NSArray arrayWithArray:data];
[theLock unlock];
return array;
}
#pragma mark
#pragma mark �Modifier Methods�
- (void)enqueue:(id)object
{
[theLock lock];
[data addObject:object];
[theLock unlock];
}
- (id)dequeue
{
id object = [self top];
if (object != nil) {
[theLock lock];
[object retain];
[data removeObjectAtIndex:0];
[theLock unlock];
}
return [object autorelease];
}
- (void)removeAll
{
[theLock lock];
while (![self isEmpty])
[data removeObjectAtIndex:0];
[theLock unlock];
}
#end
Now when Application have something to send over socket(NSStream), it should add it into the queue,
-(bool)sendRawData:(const uint8_t *)data length:(int)len{
// if still negotiating then don't send data
assert(!networkConnected);
NSData *pData = [NSData dataWithBytes:(const void *)data length:len];
// pToSendPacket is of type StreamQueue
[pToSendPacket enqueue:pData];
return;
}
and this piece of code when we get NSHasSpaceAvailableEvent
-(void)gotSpaceAvailable{
// is there any pending packets that to be send.
NSData *pData = (NSData *)[pToSendPacket dequeue];
if(pData == nil){
// no pending packets..
return;
}
const uint8_t *data = (const uint8_t *)[pData bytes];
int len = [pData length];
int sendlength = [pOutputStream write:data maxLength:len];
if(sendlength == -1 ){
NSError *theError = [pOutputStream streamError];
NSString *pString = [theError localizedDescription];
int errorCode = [theError code];
return ;
}
}
I was expecting Application will keep on receiving the event, whenever OutputStream sends data, but i recieved only once... :(
Please help ...
If you don't wait for the event, the write call will block until space is available. Generally you want to aim to design your code to work asychronously, so waiting for NSStreamEventHasSpaceAvailable is the best solution.
As for when you receive the space available notification, see the documentation here:
If the delegate receives an NSStreamEventHasSpaceAvailable event and
does not write anything to the stream, it does not receive further
space-available events from the run loop until the NSOutputStream
object receives more bytes. When this happens, the run loop is
restarted for space-available events. If this scenario is likely in
your implementation, you can have the delegate set a flag when it
doesn’t write to the stream upon receiving an
NSStreamEventHasSpaceAvailable event. Later, when your program has
more bytes to write, it can check this flag and, if set, write to the
output-stream instance directly.
There is no firm guideline on how many bytes to write at one time.
Although it may be possible to write all the data to the stream in one
event, this depends on external factors, such as the behavior of the
kernel and device and socket characteristics. The best approach is to
use some reasonable buffer size, such as 512 bytes, one kilobyte (as
in the example above), or a page size (four kilobytes).
So you should be getting regular NSStreamEventHasSpaceAvailable events as long as you do write data for each event.
I'm just trying to close an NSPanel after a couple second delay, but I can't get my NSTimer to start. It will fire if I explicitly call the fire method on it, but it will never go by itself. Here's my code:
- (void)startRemoveProgressTimer:(NSNotification *)notification {
NSLog(#"timer should start");
timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:#selector(removeProgress:) userInfo:nil repeats:NO];
}
- (void)removeProgress:(NSTimer *)timer {
[progressPanel close];
}
I do have some threading in my code as such. I assume this is what's messing my timer up.
-(void)incomingTextUpdateThread:(NSThread*)parentThread {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
//mark the thread as running
readThreadRunning = TRUE;
const int BUFFER_SIZE = 100;
char byte_buffer[BUFFER_SIZE]; //buffer for holding incoming data
int numBytes = 0; //number of bytes read
NSString *text; //incoming text from the serial port
[NSThread setThreadPriority:1.0];
//this will loop until the serial port closes
while (TRUE) {
//read() blocks until some data is available or the port is closed
numBytes = read(serialFileDescriptor, byte_buffer, BUFFER_SIZE);
if(numBytes > 0) {
//creat a string from the incoming bytes
text = [[[NSString alloc] initWithBytes:byte_buffer length:numBytes encoding:[NSString defaultCStringEncoding]] autorelease];
if(!([text rangeOfString:SEND_NEXT_COORDINATE].location == NSNotFound)) {
//look for <next> to see if the next data should be sent
if(coordinateNum <[coordinatesArray count]) {
[self sendNextCoordinate]; //send coordinates
}
else {
[self writeString:FINISH_COORDINATES_TRANSMIT]; //send <end> to mark transmission as complete
NSNumber *total = [NSNumber numberWithUnsignedInteger:[coordinatesArray count]];
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:total forKey:#"progress"];
[[NSNotificationCenter defaultCenter] postNotificationName:#"uploadProgressChange" object:self userInfo:userInfo]; //update progress bar to completed
}
}
[self performSelectorOnMainThread:#selector(appendToIncomingText:) withObject:text waitUntilDone:YES]; //write incoming text to NSTextView
} else {
break; //Stop the thread if there is an error
}
}
// make sure the serial port is closed
if (serialFileDescriptor != -1) {
close(serialFileDescriptor);
serialFileDescriptor = -1;
}
// mark that the thread has quit
readThreadRunning = FALSE;
// give back the pool
[pool release];
}
Which is called from another method by: [self performSelectorInBackground:#selector(incomingTextUpdateThread:) withObject:[NSThread currentThread]];
Thank you rgeorge!!
Adding the timer to the run loop manually made it work!
timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:#selector(removeProgress:) userInfo:nil repeats:NO];
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];