Блоки Objective C в массиве, переданном в подкласс UIAlertView - PullRequest
4 голосов
/ 28 ноября 2011

(Рабочее решение, основанное на ответах, приведено в конце этого поста.)

Я подумал, что это будет аккуратный способ обрабатывать обратные вызовы, которые должны обрабатываться определенным представлением предупреждений,поэтому у меня нет единого метода делегата, отфильтровывающего все нажатия кнопок оповещения.Вот код:

#import "LSAlertView.h"

@implementation LSAlertView

- (id) initWithTitle:(NSString *)title 
             message:(NSString *)message 
        actionBlocks:(NSArray*)_actionBlocks 
   cancelButtonTitle:(NSString *)cancelButtonTitle 
   otherButtonTitles:(NSString *)otherButtonTitles, ... 
{

    self = [super initWithTitle:title 
                  message:message 
                  delegate:self 
                  cancelButtonTitle:cancelButtonTitle 
                  otherButtonTitles:otherButtonTitles,nil];
    if (self) {
        self.cancelButtonIndex = 0;
        actionBlocks = [_actionBlocks retain];
        [self show];
    }
    return self;
}

- (void) dealloc {
    [actionBlocks release];
    [super dealloc];
}

- (void) alertView:(UIAlertView *)alertView 
    clickedButtonAtIndex:(NSInteger)buttonIndex 
{
    void (^action)(void) = [actionBlocks objectAtIndex:buttonIndex];
    action();
}

@end

Это прекрасно работает для двух кнопок, настроенных так:

- (void) restartSearches {
    NSArray *actionBlocks = [NSArray arrayWithObjects:
                             ^{NSLog(@"Cancel Button Selected");},
                             ^{NSLog(@"Delete Button Selected");},
                             nil];

    alertDeletingSearches = [[LSAlertView alloc] 
                                initWithTitle:@"You Are About To Delete Your Current Searches" 
                                message:@"Select Delete to Continue" 
                                actionBlocks:actionBlocks 
                                cancelButtonTitle:@"Cancel" 
                                otherButtonTitles:@"Delete", nil];
    [alertDeletingSearches release];
}

Но как только я добавлю несколько полезных вызовов в один из блоков, напримерэто

- (void) restartSearches {
    NSArray *actionBlocks = [NSArray arrayWithObjects:
                             ^{NSLog(@"Cancel Button Selected");},
                             ^{
                                 [mapController.theMap removeAnnotations:mapController.theMap.annotations];
                                 [dataInterface deleteDB];
                                 [[NSNotificationCenter defaultCenter] 
                                     postNotificationName:@"changeToFavorites" 
                                     object:nil];
                                 NSLog(@"Delete Button Selected");
                             },
                             nil];

    alertDeletingSearches = [[LSAlertView alloc] 
                                initWithTitle:@"You Are About To Delete Your Current Searches" 
                                message:@"Select Delete to Continue" actionBlocks:actionBlocks 
                                cancelButtonTitle:@"Cancel" 
                                otherButtonTitles:@"Delete", nil];
    [alertDeletingSearches release];    
}

зависает, и я получаю ошибку EXC_BAD_ACCESS.

Я делаю что-то в корне неправильно или в моей логике есть небольшая ошибка?

ОБНОВЛЕНИЕ

Решил проблему с вариадностью, используя предложенное Фирозом предложение ниже.(Следует примерам, приведенным на Numbergrinder )

- (id) initWithTitle:(NSString *)title message:(NSString *)message actionBlocks:(NSArray*)_actionBlocks cancelButtonTitle:(NSString *)cancelButtonTitle otherButtonTitles:(NSString *)otherButtonTitles, ... {


self = [super initWithTitle:title message:message delegate:self cancelButtonTitle:cancelButtonTitle otherButtonTitles:otherButtonTitles, nil];

if (self) {
    va_list args;
    va_start(args, otherButtonTitles);

    NSString* buttonTitle;
    while ((buttonTitle = va_arg(args, NSString *))) {
        [super addButtonWithTitle:buttonTitle];
    }

    self.cancelButtonIndex = 0;
    actionBlocks = [_actionBlocks retain];
    [self show];
}

return self;

}

Вот файл заголовка:

@interface LSAlertView : UIAlertView <UIAlertViewDelegate> {

NSArray *actionBlocks;

}

- (id) initWithTitle:(NSString *)title message:(NSString *)message actionBlocks:(NSArray*)_actionBlocks cancelButtonTitle:(NSString *)cancelButtonTitle otherButtonTitles:(NSString *)otherButtonTitles, ...;

@end

Ответы [ 2 ]

2 голосов
/ 28 ноября 2011

Итак, я вижу пару проблем с этим.

Во-первых, вам нужно скопировать эти блоки, когда вы помещаете их в массив.Эти блоки создаются в стеке.Если вы хотите передать их в представление предупреждений и ожидаете, что представление предупреждений сохранит их для последующего использования, сначала необходимо скопировать их в кучу.

Так что-то вроде этого должно работать:

NSArray *actionBlocks = [NSArray arrayWithObjects:
                         [[^{NSLog(@"Cancel Button Selected");} copy] autorelease],
                         [[^{
                             [mapController.theMap removeAnnotations:mapController.theMap.annotations];
                             [dataInterface deleteDB];
                             [[NSNotificationCenter defaultCenter] postNotificationName:@"changeToFavorites" object:nil];
                             NSLog(@"Delete Button Selected");
                         } copy] autorelease]
                         , nil];

Обратите внимание на [^ someBlock copy] вокруг каждого литерала блока там.Это должно решить одну проблему.

Другая проблема, на которую я не знаю ответа, заключается в том, что это метод с переменным числом аргументов (принимает переменное число аргументов).Я не знаю, как в методе с переменными значениями развернуться и вызвать другой метод с переменными значениями (инициализатор UIAlertView), если только у вас нет варианта второго метода, который принимает va_list.Это та же проблема, что и в C, унаследованная в Objective C, насколько я понимаю.

Я думаю, вы еще не сталкивались с этим, потому что вы не попробовали достаточно кнопок для этого.

РЕДАКТИРОВАТЬ

Думая об этом дальшеЯ думаю, вы могли бы обойти вторую проблему, перебрав varargs и затем вызвав [self addButtonWithTitle: arg] для каждого из них.

1 голос
/ 28 ноября 2011

Вы можете найти Lambda Alert полезным:

LambdaAlert *alert = [[LambdaAlert alloc]
    initWithTitle:@"Test Alert"
    message:@"See if the thing works."];
[alert addButtonWithTitle:@"Foo" block:^{ NSLog(@"Foo"); }];
[alert addButtonWithTitle:@"Bar" block:^{ NSLog(@"Bar"); }];
[alert addButtonWithTitle:@"Cancel" block:NULL];
[alert show];

И

LambdaSheet *sheet = [[LambdaSheet alloc] initWithTitle:@"Action Sheet"];
[sheet addButtonWithTitle:@"Miles" block:^{ NSLog(@"Trumpet"); }];
[sheet addButtonWithTitle:@"Trane" block:^{ NSLog(@"Saxophone"); }];
[sheet addDestructiveButtonWithTitle:@"Monk" block:^{ NSLog(@"Piano"); }];
[sheet addCancelButtonWithTitle:@"Back to the Head"];
[sheet showInView:window];

Статическая библиотека, которую легко включить в проект с помощью рабочей области Xcode.

...