views:

30

answers:

2

Hi

I've seen a few examples of using delegate with multiple threads for async image loading with cocoa's URL loading system.

I think the process goes like below.

Thread 1 : give thread 2 an operation to perform, and a delegate which thread 2 will access upon completion.

Thread 2 : upon completing the operation, calls a predefined selector on the delegate passed by thread 1.

I'd like to implement similar feature(async processing with delegate) with my own classes. The below pseudo code is similar to the process outlined above, but i'd like to make it more detailed for the sake of discussion of thread safety.

Thread 1 :
[begin critical section]
set delegate so that thread 2 can access
[end critical section]
fire an operation

-(void) dealloc (of the delegate object's class) {
[begin critical section]
set delegate to null
[end critical section]
}

Thread 2 :
actually works on operation
- upon completion.. [begin critical section]
get delegate
[delegate performSelectorOnMainThread @someSelector];
[end critical seciton]

Now, I'm worried about the thread safety of accessing the delegate. In other words, how do the 2nd thread makes sure that the delegate is still valid when the delegate object can go away anytime without waiting for the operation completion.

The pseudo code above is the best attempt I've made and wonder if it's gonna actually work. Furthermore, I've never seen any attempt on making the delegate thread safe in similar work such as http://www.markj.net/iphone-asynchronous-table-image/ Isn't the critical section safe guarding necessary?

Thanks

A: 

NSURLConnection loads data on a secondary thread but only invokes delegate methods on the main thread, so from the perspective of consuming data from NSURLConnection you shouldn't have to worry about thread safety. It's taken care of within NSURLConnection.

As far as the delegate remaining valid goes, you would probably just want to keep a reference to your URL connections and cancel any pending or in-progress connections when your controlling object is deallocated.

-(void)cancel of NSURLConnection states:

"Once this method is called, the receiver’s delegate will no longer receive any messages for this NSURLConnection."

ah thanks, I guess 'cancel' do similar to what I do with [critical sections]. Well I asked how I could implement what NSURLConnection do with my own classes, but good to know how other classes handle it. How does NSURLConnection handle when the delegate went away upon completing download assuming user didn't cancel the operation?
Eugene
After a connection either fails or successfully completes, it will never send another message to its delegate. The docs: "Unless a NSURLConnection receives a cancel message, the delegate will receive one and only one of connectionDidFinishLoading:, or connection:didFailWithError: message, but never both. In addition, once either of messages are sent, the delegate will receive no further messages for the given NSURLConnection."
A: 

You could simply have the worker retain its callback target. Then, it is guaranteed to be around when it goes to make the callback. This is what NSTimer does.

Jeremy W. Sherman
ah, i'd like to vote up, but i lack the reputation.(new to stack overflow)
Eugene
Btw, I thought you normally would want to (assign) for delegate to prevent cycling retain.
Eugene
This isn't really a delegate in the sense of something you're consulting for policy or giving a crack at an event that's going to be handled anyway. The "delegate" is just part of a callback function - call it a "target" instead: since Obj-C is object-oriented, you need both the function (method) and something to invoke the method on. This is the whole point of the worker object's existence: compute something, then callback. The object that kicked it off should expect to be around to receive the callback.
Jeremy W. Sherman
This is, I think, the thinking behind `NSTimer` retaining its target, as well. But `NSTimer` does end up causing retain cycle problems because it's frequently not a one-off, "go, do this, and get back to me," but a recurring event that keeps an owning reference to its target forever and ever. (Or at least until it's invalidated.)
Jeremy W. Sherman
Well, sometimes the object that kicked it off might not care much about receiving the call back, as in image loading. For instance, a user is looking through iphone app store's game category (and the images for the table view are loading) and decides to switch to a different category. In this case, 'game category delegate' might not be interested in receiving unnecessary images.
Eugene
In that case, the target should invalidate/cancel the tasks it no longer cares about.
Jeremy W. Sherman
How would i implement invalidate/cancel operation with custom classes? My pseudo code tries to implement the invalidating operation with setting delegate to nil. But what happens when 2nd thread post message with performSelectorOnMainThread to the target, and the message is waiting in the message queue, then the target gets destroyed. would it be safe? (Ah! i think performSelectorOnMainThread retains the target)
Eugene
Umm.. on second thought, what if the target is performing 'dealloc'. is performSelectorOnMainThread's retain legit?
Eugene
I'll have to make it clear that with my classes, it's hard to 'cancel' the operation: (the 2nd thread actually dispatches the job to another thread) So what i'm trying is to ignore the operation result when the target is gone.
Eugene
Yes, I believe `-performSelector:...` retains its target. You can cancel threads – check out `pthread_cancel()`, `pthread_test_cancel()`, and `pthread_setcancelstate()`. See also `-[NSThread cancel]` and `-[NSThread isCancelled]`.
Jeremy W. Sherman