I am writing the middleware for a cocoa application, and am debating over how exactly to design callbacks for many long running processes.
When the UI will call a function which executes for a long time, it needs to provide a delegate to allow at least:
- Report of Success (with return value)
- Report of Failure (with error value)
- Report of Progress (completed, expected total)
I have tried a few techniques in the past, shown below
@interface MyClass {
}
//Callback Option1, delgate conforming to protocol
-(void) longRunningProcess2:(id<CallbackProtocol>) delegate;
//Callback Option2, provide a delegate and allow it to provide selectors to callback
-(void) longRunningProcess3:(id) delegate success:(SEL) s1 failure:(SEL) s2 progress:(SEL) s3
@end
For Options 1, the question is then how to phrase the delegate response. The first way I considered is (the function names are minimal for simplicity)
//have a generic callback protocol for every function
@protocol CallbackProtocolGeneric
-(void) success:(id) returnValue;
-(void) failure:(NSError*) error;
@optional
-(void) progress:(NSInteger) completed of:(NSInteger) total;
@end
//have a separate protocol for every function
@protocol CallbackProtocolWithObjectAForOperation1
-(void) objectA:(ObjectA*) objectA operation1SucceedWithValue:(ReturnObject*) value;
-(void) objectA:(ObjectA*) objectA operation1FailedWithError:(NSError*) error;
@optional
-(void) objectA:(ObjectA*) objectA operation1didProgress:(NSInteger) completed of:(NSInteger) total;
@end
In my experience, Callback Option 1 using a generic protocol was difficult to use because if a class wanted to be a callback for multiple operations it could not distinguish which callback it was receiving.
Callback Option2 was cumbersome to use and felt un-natural to use. Plus if the protocol was extended it would require modifying every call.
Callback Option1 using a specific protocol for each process seems to be the most readable and scalable method, however I wonder if making a new protocol for every single function is too verbose (Say a given object has 10+ such 'long operations', then 10 different protocols).
What conclusions have other people come to when implementing such designs?
--edit: In reply to Dave DeLong's answer
I have three classes which have 'long operations', non of the operations in each class or between classes are really related. Some are network resource requests, others are long processing requests.
--edit: A side note, I seem to have a problem where I cannot invoke run loop selectors for messages which have more than one argument. Is this a design limitation or is there a way around this?
For example I have a message such as -(id) someMessage:(id) value1 otherData:(id) value2 moreData:(id) value3
The performSelector functions which queue runLoop's do not support such selectors.