Re: How To Safely Invoke a Block
Re: How To Safely Invoke a Block
- Subject: Re: How To Safely Invoke a Block
- From: Ken Thomases <email@hidden>
- Date: Wed, 21 Nov 2012 05:25:42 -0600
On Nov 21, 2012, at 5:09 AM, Tom Davie wrote:
> On 21 Nov 2012, at 10:56, Andreas Grosam <email@hidden> wrote:
>
>> I've defined a class Foo that defines a block via a property:
>>
>> @property (copy) void (^progressHandler)(RXProgressState progressState, NSInteger totalBytesWritten, NSInteger totalBytesExpectedToWrite);
>>
>> The property is synthesized by the compiler, ARC enabled.
>>
>> The block is usually invoked on a private thread for several times until an action finishes (an asynchronous NSURLConnection).
>> A client of class Foo should be able to set the block property at any time from any thread - that is it should also be able to set the block to NULL. Now, I'm worried about thread safety.
>>
>> First, is it a good measurement to use a property with the "atomic" attribute?
>
> Not really, as your block may be released (by the setter) between getting the block and invoking it.
An atomic property's synthesized getter does the equivalent of retain plus autorelease. So, the caller can rely on the received value staying valid for the current scope.
<https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjectiveC/Chapters/ocProperties.html#//apple_ref/doc/uid/TP30001163-CH17-SW28>
> Grand Central Dispatch is your friend.
> @implementation MyClass
>
> static dispatch_once_t onceToken;
> static dispatch_queue_t dispatchQueue;
>
> typedef (void(^ProgressHandler)(RXProgressState progressState, NSInteger totalBytesWritten, NSInteger totalBytesExpectedToWrite);
>
> - (void)setProgressHandler:(ProgressHandler)progressHandler
> {
> dispatch_once(&onceToken, ^
> {
> dispatchQueue = dispatch_queue_create("RXProgressQueue", DISPATCH_QUEUE_SERIAL);
> });
>
> dispatch_sync(dispatchQueue, ^()
> {
> if (_progressHandler != progressHandler)
> {
> [_progressHandler release];
> _progressHandler = [progressHandler copy];
> }
> });
> }
>
> - (void)progressHandler
> {
> return _progressHandler;
> }
>
> - (void)callSite
> {
> …
> dispatch_once(&onceToken, ^
> {
> dispatchQueue = dispatch_queue_create("RXProgressQueue", DISPATCH_QUEUE_SERIAL);
> });
> dispatch_sync(dispatchQueue, ^()
> {
> ProgressHandler handler = [self progressHandler];
> handler(…);
> });
> …
> }
You are running the block within the dispatch_sync() block, meaning that the queue is monopolized for however long that runs. That's likely undesirable and prone to deadlocks.
If you wanted to do something like this, you would declare 'handler' as __block outside of the dispatch_sync(), retrieve it with the dispatch_sync() block, and then call it after that returns. ARC will ensure that it's retained by the implicitly-strong 'handler' variable. However, that's overkill.
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