Лучший метод замены методов делегирования блоками

Я хочу создать категорию, чтобы заменить методы делегирования блоками обратного вызова для многих простых API-интерфейсов iOS. Аналогично блоку sendAsyc в NSURLConnection. Есть 2 метода, которые работают без утечек и работают нормально. Каковы плюсы / минусы каждого из них? Есть ли лучший способ?

Вариант 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, и вещи немного более разделены. Он прикрепляется к экземпляру через адрес функции.

9
задан puppybits 27 December 2011 в 16:40
поделиться