objective-c - 用 block 替换委托(delegate)方法的最佳技术

标签 objective-c ios objective-c-blocks

我正在寻求创建一个类别来用许多简单的 iOS API 的回调 block 替换委托(delegate)方法。类似于 NSURLConnection 上的 sendAsyc block 。有 2 种技术不会泄漏并且似乎工作正常。各自的优点/缺点是什么?有没有更好的办法?

选项 1. 使用类别在具有外部回调 block 作用域的 NSObject 上实现委托(delegate)的回调方法。

// Add category on NSObject to respond to the delegate
@interface NSObject(BlocksDelegate)
- (void) alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex;
@end

@implementation NSObject(BlocksDelegate)
- (void) alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
    // Self is scoped to the block that was copied
    void(^callback)(NSInteger) = (id)self;
    // Call the callback passed if
    callback(buttonIndex);
    [self release];
}
@end

// Alert View Category
@implementation UIAlertView (BlocksDelegate)
+ (id) alertWithTitle:(NSString*)title
              message:(NSString*)message
         clickedBlock:(void(^)(NSInteger))buttonIndexClickedBlock
    cancelButtonTitle:(NSString*)cancelButtonTitle
    otherButtonTitles:(NSString*)otherButtonTitles
{
    // Copy block passed in to the Heap and will stay alive with the UIAlertView
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:title 
                                                    message:message 
                                                   delegate:[buttonIndexClickedBlock copy]
                                          cancelButtonTitle:cancelButtonTitle 
                                          otherButtonTitles:otherButtonTitles, nil];

    // Display the alert
    [alert show];

    // Autorelease the alert
    return [alert autorelease];
}

@end

这在 NSObject 上添加了很多方法,似乎它可能会导致任何其他尝试使用标准委托(delegate)方法的类出现问题。但它使 block 与对象保持事件状态并返回回调,而没有我发现的任何泄漏。


选项 2。创建一个轻量级类来包含该 block ,动态地将它与该类相关联,以便它保留在堆中并在回调完成时将其删除。

// Generic Block Delegate
@interface __DelegateBlock:NSObject
typedef void (^HeapBlock)(NSInteger);
@property (nonatomic, copy) HeapBlock callbackBlock; 
@end

@implementation __DelegateBlock
@synthesize callbackBlock;
- (id) initWithBlock:(void(^)(NSInteger))callback
{
    // Init and copy Callback Block to the heap (@see accessor)
    if (self = [super init]) 
        [self setCallbackBlock:callback];
    return [self autorelease];
}
- (void) dealloc
{
    // Release the block
    [callbackBlock release], callbackBlock = nil;    
    [super dealloc];
}
- (void) alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
    // Return the result to the callback
    callbackBlock(buttonIndex);

    // Detach the block delegate, will decrement retain count
    SEL key = @selector(alertWithTitle:message:clickedBlock:cancelButtonTitle:otherButtonTitles:);
    objc_setAssociatedObject(alertView, key, nil, OBJC_ASSOCIATION_RETAIN);
    key = nil;

    // Release the Alert
    [alertView release];
}
@end

@implementation UIAlertView (BlocksDelegate)
+ (id) alertWithTitle:(NSString*)title
              message:(NSString*)message
         clickedBlock:(void(^)(NSInteger))buttonIndexClickedBlock
    cancelButtonTitle:(NSString*)cancelButtonTitle
    otherButtonTitles:(NSString*)otherButtonTitles
{
    // Create class to hold delegatee and copy block to heap
    DelegateBlock *delegatee = [[__DelegateBlock alloc] initWithBlock:buttonIndexClickedBlock];
    [[delegatee retain] autorelease];
    // Create delegater
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:title 
                                                    message:message 
                                                   delegate:delegatee
                                          cancelButtonTitle:cancelButtonTitle 
                                          otherButtonTitles:otherButtonTitles, nil];

    // Attach the Delegate Block class to the Alert View, increase the retain count
    objc_setAssociatedObject(alert, _cmd, delegatee, OBJC_ASSOCIATION_RETAIN);

    // Display the alert
    [alert show];
    return alert;
}

@end

我喜欢这样,它不会在 NSObject 之上添加任何东西,而且东西会更加分离。它通过函数的地址附加到实例。

最佳答案

我遇到了类似的问题并选择了您的选项 2,但增加了 2 个小部分:

  1. 像这样显式标记它实现的委托(delegate):

    @interface __DelegateBlock:NSObject <BlocksDelegate>
    
  2. 在调用之前检查以确保回调不为零:

    if (callbackBlock != nil) {
        callbackBlock(buttonIndex);
    }
    

关于objective-c - 用 block 替换委托(delegate)方法的最佳技术,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8646476/

相关文章:

iphone - 如何理解 Web 服务答案中的字符串或数组

ios - 在显示层之前应用 CGAffineTransform

c++ - 在 C++ 代码中返回 Obj-C block

objective-c - 属性字符串超链接未显示正确的光标

iPhone 图片幻灯片

ios - 隐藏式字幕/字幕不适用于AVPlayer,但在Safari中适用:HLS

ios - 创建一个swift扩展函数供所有controller访问

ios - 如何将 CGRect 值从钛发送到 ios 模块

ios - dispatch_async 是否复制内部 block

objective-c - AFNetworking 异步数据获取