代码之家  ›  专栏  ›  技术社区  ›  Akusete

可可回调设计:最佳实践[结束]

  •  5
  • Akusete  · 技术社区  · 16 年前

    当UI将调用执行时间较长的函数时,它需要提供一个委托,以允许至少:

    • 成功报告(带返回值)
    • 故障报告(带错误值)

    我在过去尝试过一些技巧,如下所示

    @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
    

    对于选项1,问题是如何表达代表的回答。我考虑的第一种方法是(为了简单起见,函数名是最小的)

    //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
    

    以我的经验, 回调选项1 使用 通用协议 很难使用,因为如果一个类想成为多个操作的回调,它无法区分它接收的是哪个回调。

    回拨选项2 使用起来很麻烦,感觉不自然。另外,如果协议被扩展,则需要修改每个调用。

    回拨选项1 使用 特定协议 因为每个进程似乎都是可读性和可伸缩性最高的方法,但是我想知道为每个函数创建一个新协议是否太冗长(比如说一个给定的对象有10个以上这样的“长操作”,然后是10个不同的协议)。

    --编辑:

    我有三个有“长操作”的类,每个类中或类之间的操作都不是真正相关的。有些是网络资源请求,有些是长处理请求。

    --编辑: 顺便说一句,我似乎有一个问题,我不能调用运行循环选择器的消息有一个以上的参数。这是设计上的限制还是有办法解决的?

    例如,我有一个消息,如

    队列runLoop的performSelector函数不支持此类选择器。

    2 回复  |  直到 16 年前
        1
  •  5
  •   Tom Dalling    16 年前

    我选择了 longRunningProcess2 longRunningProcess3 只是因为如果可以看到协议上的方法声明,就更容易理解,而不是依赖文档来确定回调方法参数是什么。

    块方法如下所示:

    -(void) longRunningProcessWithSuccessHandler:(void(^)(ReturnObject* value))successHandler
                                    errorHandler:(void(^)(NSError* error))errorHandler
                                 progressHandler:(void(^)(NSInteger completed, NSInteger total))progressHandler;
    {
        NSInteger totalItems = 10;
        NSInteger item = 0;
        for(item = 0; item < totalItems; ++item){
            [self processItem:item];
            progressHandler(item, totalItems);
        }
    
        BOOL wasSuccessful = ?;
        if(wasSuccessful){
            ReturnObject* value = ?;
            successHandler(value);
        } else {
            NSError* error = ?;
            errorHandler(error);
        }
    }
    

    你可以这样调用这个方法:

    [SomeObj longRunningProcessWithSuccessHandler:^(ReturnObject* value) { [self showReturnObject:value]; }
                                     errorHandler:^(NSError* error){ [self presentError:error]; }
                                  progressHandler:^(NSInteger completed, NSInteger total) { [self updateProgressToPercent:(double)completed/total]; }];
    
        2
  •  3
  •   Dave DeLong    16 年前

    我会用一个单一的协议路由,类似于你的 CallbackProtocolGeneric 选项,但我将其扩展为:

    - (void) operation:(id)operation didFinishWithReturnValue:(id)returnValue;
    - (void) operation:(id)operation didFailWithError:(NSError *)error;
    - (void) operation:(id)operation hasCompleted:(NSInteger)progress ofTotal:(NSInteger)total;
    

    - (void) operation:(id)operation didFinishStep:(NSInteger)stepNumber withReturnValue:(id)returnValue;
    - (void) operation:(id)operation didFailStep:(NSInteger)stepNumber withError:(NSError *)error;
    - (void) operation:(id)operation step:(NSInteger)step hasCompleted:(NSInteger)progress ofTotal:(NSInteger)total;
    

    “step”参数可以是某个值,该值指示此特定对象正在执行的“10+长操作”中的哪一个。