2011-12-27 94 views
9

我在创建一个类别,用很多简单的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中的顶部添加任何东西,事情多一点分开。它通过函数的地址附加到实例。

+0

方案3:子类'UIAlertView'。 – 2011-12-27 20:54:31

+0

对。子类化作品。但是,为了添加一个方法调用,我将每个苹果API进行子类化时,它会变得杂乱无章并且可重用代码更少。此外,我将所有这些API放在一个类中,因此很容易导入和使用类别,这样方法调用就可以更清晰,更接近Apple API。如果最终成为一个很好的通用方法来保存并返回块,那么无论何时我需要将另一个异步块方法添加到Apple API时,该代码都可以重复使用,并进行较小的更改。 – puppybits 2011-12-28 15:11:47

+0

好的,我明白了。这是一项非常重要的任务。我只是试图让你免受运行时间的影响。 – 2011-12-28 19:51:27

回答

2

我也有类似的问题,并选择您的选项2,但与2个小的补充:

  1. 明确标示它实现这样的委托:

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

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

是的,经过几天的炖过。选项2真的好多了。你的提示很好,清理起来好一点。谢谢。 – puppybits 2012-01-08 15:19:26

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