用块替换委托方法的最佳技术

时间:2011-12-27 15:46:54

标签: objective-c ios objective-c-blocks

我正在寻找创建一个类别,用许多简单的iOS API替换委托方法和回调块。与NSURLConnection上的sendAsyc块类似。有两种技术是无泄漏的,似乎工作正常。每个人的利弊是什么?还有更好的方法吗?

选项1.使用类别在NSObject上实现委托的回调方法,并使用外部回调块作用域。

// 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上添加了很多方法,看起来它可能会导致尝试使用标准委托方法的任何其他类的问题。但是它使对象保持活动状态并返回回调而没有发现任何泄漏。


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

// 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 个答案:

答案 0 :(得分:2)

我遇到了类似的问题并选择了你的选项2,但是增加了两个:

  1. 明确标记它实现的委托如下:

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

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

答案 1 :(得分:0)

这就是我的所作所为:

typedef void(^EmptyBlockType)();

@interface YUYesNoListener : NSObject <UIAlertViewDelegate>

@property (nonatomic, retain) EmptyBlockType yesBlock;
@property (nonatomic, retain) EmptyBlockType noBlock;

+ (void) yesNoWithTitle:(NSString*)title message:(NSString*)message yesBlock:(EmptyBlockType)yesBlock noBlock:(EmptyBlockType)noBlock;

@end

@implementation YUYesNoListener

@synthesize yesBlock = _yesBlock;
@synthesize noBlock = _noBlock;

- (id) initWithYesBlock:(EmptyBlockType)yesBlock noBlock:(EmptyBlockType)noBlock
{
    self = [super init];
    if (self)
    {
        self.yesBlock = [[yesBlock copy] autorelease];
        self.noBlock = [[noBlock copy] autorelease];
    }
    return self;
}

- (void) alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
    if (buttonIndex == 0 && self.noBlock)
        self.noBlock();
    else if (buttonIndex == 1 && self.yesBlock)
        self.yesBlock();

    [_yesBlock release];
    [_noBlock release];
    [alertView release];
    [self release];
}

- (void) alertViewCancel:(UIAlertView *)alertView
{
    if (self.noBlock)
        self.noBlock();
    [_yesBlock release];
    [_noBlock release];
    [alertView release];
    [self release];
}

+ (void) yesNoWithTitle:(NSString*)title message:(NSString*)message yesBlock:(EmptyBlockType)yesBlock noBlock:(EmptyBlockType)noBlock
{
    YUYesNoListener* yesNoListener = [[YUYesNoListener alloc] initWithYesBlock:yesBlock noBlock:noBlock];
    [[[UIAlertView alloc] initWithTitle:title message:message delegate:yesNoListener cancelButtonTitle:@"No" otherButtonTitles:@"Yes", nil] show];
}

@end