2011-12-27 30 views
9

Tôi đang tìm cách tạo danh mục để thay thế các phương thức ủy nhiệm với các lần gọi lại chặn cho rất nhiều API iOS đơn giản. Tương tự như khối sendAsyc trên NSURLConnection. Có 2 kỹ thuật bị rò rỉ miễn phí và dường như hoạt động tốt. Những ưu/khuyết điểm về mỗi cái là gì? Có cách nào tốt hơn?Kỹ thuật tốt nhất để thay thế các phương thức đại biểu bằng các khối

Tùy chọn 1. Sử dụng danh mục để triển khai phương thức gọi lại của đại biểu trên NSObject với khối gọi lại bên ngoài được sắp xếp.

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

Điều này bổ sung thêm nhiều phương pháp trên NSObject và có thể gây ra sự cố với bất kỳ lớp nào khác đang cố gắng sử dụng phương pháp đại biểu tiêu chuẩn. Nhưng nó giữ khối sống với đối tượng và trả về cuộc gọi lại mà không có bất kỳ rò rỉ nào mà tôi đã tìm thấy.


Tùy chọn 2. Tạo một lớp trọng lượng nhẹ để chứa khối, liên kết động với lớp để nó ở lại trong heap và xóa nó khi hoàn thành gọi lại.

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

Tôi thích điều này không thêm bất kỳ thứ gì lên trên NSObject và mọi thứ được tách biệt hơn một chút. Nó gắn với cá thể thông qua địa chỉ của hàm.

+0

Tùy chọn 3: Phân lớp 'UIAlertView'. –

+0

Phải. Phân lớp hoạt động. Nhưng nó sẽ lộn xộn và có ít mã có thể tái sử dụng hơn khi tôi phân lớp mọi API táo để thêm một cuộc gọi phương thức. Ngoài ra tôi đang đặt tất cả các API này trong một lớp để dễ dàng nhập và sử dụng các danh mục cho phép phương thức gọi sạch hơn và gần hơn với các API của Apple. Nếu có kết thúc là một cách chung chung tốt đẹp để giữ và trả về khối, sau đó mã đó có thể được tái sử dụng với những thay đổi nhỏ bất cứ khi nào tôi cần phải thêm vào một phương pháp chặn không đồng bộ hơn cho một API táo. – puppybits

+0

Ok tôi hiểu rồi. Đó là một cam kết khá. Tôi chỉ cố gắng cứu bạn khỏi việc nhạo báng với thời gian chạy. –

Trả lời

2

Tôi đã có một vấn đề tương tự và chọn tùy chọn của bạn 2, nhưng với 2 bổ sung nhỏ:

  1. Rõ ràng đánh dấu các đại biểu nó thực hiện như thế này:

    @interface __DelegateBlock:NSObject <BlocksDelegate> 
    
  2. Kiểm tra để đảm bảo gọi lại không phải là số không trước khi gọi:

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

Vâng sau một vài ngày để ăn nó. Lựa chọn 2 thực sự là tốt hơn nhiều. Lời khuyên của bạn là tuyệt vời để làm sạch nó tốt hơn một chút. Cảm ơn. – puppybits

0

Đây là những gì tôi đã làm:

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 
Các vấn đề liên quan