Re: Recurrent NSInvocationOperations on an NSOperationQueue?
Re: Recurrent NSInvocationOperations on an NSOperationQueue?
- Subject: Re: Recurrent NSInvocationOperations on an NSOperationQueue?
- From: Ken Thomases <email@hidden>
- Date: Fri, 14 Aug 2015 21:01:31 -0500
On Aug 14, 2015, at 8:43 PM, Carl Hoefs <email@hidden> wrote:
> On Aug 14, 2015, at 6:24 PM, Ken Thomases <email@hidden> wrote:
>>
>> -performSelector:withObject:afterDelay: depends on the run loop. The threads that service any non-main NSOperationQueue don't run their run loop. In fact, you can't rely on them surviving past the end of your operation's code returning.
>>
>> You will need to shunt this to a thread which reliably runs its run loop. The obvious candidate is the main thread.
>>
>> You could do some combination of -performSelectorOnMainThread:withObject:waitUntilDone: with -performSelector:withObject:afterDelay:, but it's really much simpler to use GCD.
>>
>> One approach is to use dispatch_after(). You could schedule a task to invoke -doStatusChecks on the main thread:
>>
>> dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 60ULL * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
>> [self doStatusChecks];
>> });
>>
>> Depending on why you're using a custom operation queue, you might skip the middle man and do:
>>
>> dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 60ULL * NSEC_PER_SEC), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
>> [self checkStatus];
>> });
>>
>> Finally, since this is a recurring task, you can use a dispatch timer source to do it all:
>>
>> dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));
>> dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 60ULL * NSEC_PER_SEC, 1ULL * NSEC_PER_SEC);
>> dispatch_source_set_event_handler(timer, ^{
>> [self checkStatus];
>> });
>> dispatch_resume(timer);
>>
>
> Thanks, Ken. The reason I'm using a custom operation queue instead of GCD is that I need to permit only exclusive access to the device (e.g., to obtain its status requires writing to it), but allow other operations enqueued to use the device, not just the status checks. So I have an NSOperationQueue that has maxConcurrentOperationCount set to 1, but I need a status-check operation to be automatically queued up 1 minute after the last one has run.
>
> So, if the device is accessed otherwise the status checks won't interfere with that because only 1 operation can be active in the queue, thus everything stays well ordered and exclusive access is permitted to all, one at a time. I just can't figure a way to have the status check operation trigger another one to be magically enqueued 60 seconds later.
You can use a serial GCD queue for the same purpose with my above suggestions. But even if you want to keep using an NSOperationQueue, you can still use my first dispatch_after() suggestion. It works just like your code except for replacing -performSelector:withObject:afterDelay: with dispatch_after(). This changes doesn't affect anything about how -doStatusChecks works. It will still use your NSOperationQueue.
You don't need to use the main GCD queue for my dispatch_after() suggestion, you can use a global concurrent queue. Either works.
Regards,
Ken
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden