Adriana
Adriana

Reputation: 806

Method with blocks inside NSOperation - how does it work?

I'm working on an iOS project that has to work from iOS4. I have an NSOperationQueue and I add an operation. The main method of the operation looks something like this:

-(void)main 
{
    [self.client getStuffSuccess:^(Stuff *s) {
        //Do something on success
    } failure:^(NSError *error) {
        //Do something on failure
    }
}

The code inside the block will only be called when getStuff calls success or failure. I thought that in the meanwhile, my operation would be removed from the NSOperationQueue and the block wouldn't be called. However, I tested it and the block was in fact called. It's called no matter if the client calls the success block on the dispatch_get_main_queue or on the thread that called it - in this case the operation above.

Before the block is called, the method isFinished is actually returning true (I overrode the isFinished method and checked the value), so can someone explain me how is it possible that the block is being called?

I'm asking all this because although this works fine for one call, when I add it in a cycle of a few hundred iterations, I get an EXC_BAD_ACCESS and understanding the above might help me on the debugging.

Upvotes: 1

Views: 1613

Answers (3)

jsd
jsd

Reputation: 7703

Since getStuffSuccess:failure is asynchronous, you need to use a concurrent operation. Here is a helpful blog post: http://www.dribin.org/dave/blog/archives/2009/05/05/concurrent_operations/

Upvotes: 0

Rob Napier
Rob Napier

Reputation: 299275

The code inside the block will only be called when getStuff calls success or failure. I thought that in the meanwhile, my operation would be removed from the NSOperationQueue and the block wouldn't be called.

What leads you to believe this. A block is a closure, a self-contained block of code. It does not rely on the existence of some other object (the NSOperation in this case) in order to exist. You might want it to rely on that other object, but that's up to you to enforce. Ideally, I'd make getStufSuccess:failure: synchronous. If you can't you can use an NSCondition or call NSRunLoop methods to block the thread cheaply until it's done.

You also need to consider thread safety here. Your problem might not have to do with the operation going away, but your block doing something that isn't thread-safe.

Upvotes: 1

waggles
waggles

Reputation: 2854

If your -getStuffSuccess:failure doesn't block the thread (i.e. the method is asynchronous), then your -main will complete and your operationQueue may deallocate your operation before the success or failure blocks are called. You can block the thread by adding a:

while(notProcessed){
    sleep(0.1);
    //Make sure your success and failure functions update notProcessed BOOL
}

so that main never completes before you've had a chance to call the closures. Or just use a synchronous method. This is generally fine because you won't be on the UI thread anyways.

Upvotes: 0

Related Questions