2013-07-20 2 views
4

Я понимаю, что блоки являются объективными объектами c и могут быть помещены в NSDictionary напрямую без Block_copy при использовании ARC. Но я получил ошибку EXC_BAD_ACCESS с этим кодом:NSDictionary of Blocks как аргумент вызывает переполнение

- (void)viewDidLoad 
{ 
    [super viewDidLoad]; 
    [self method1:^(BOOL result){ 
     NSLog(@"method1WithBlock finished %d", result); 
    }]; 
} 

- (void) method1:(void (^)(BOOL))finish{ 

    NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:^(NSData *rcb){ 
     finish(YES); 
    }, @"success", 
          ^(NSError *error){ 
           finish(NO); 
         }, @"failure", nil]; 

    [self method2:dict]; 
} 


- (void) method2:(NSDictionary *)dict{ 
    void (^success)(NSData *rcb) = [dict objectForKey:@"success"]; 
    success(nil); 
} 

Если изменить method1: к этому, никакой ошибки не поднял.

- (void) method1:(void (^)(BOOL))finish{ 
    void (^success)(NSData *) = ^(NSData *rcb){ 
     finish(YES); 
    }; 

    void (^failure)(NSError *error) = ^(NSError *error){ 
     finish(NO); 
    }; 
    NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:success, @"success", 
          failure, @"failure", nil]; 
    [self method2:dict]; 
} 

Может кто-нибудь объяснить, почему я должен использовать автоматические переменные для хранения блоков, прежде чем положить их в словарь?

Я использую SDK iOS 6.1.

ответ

5

Согласно "Transitioning to ARC Release Notes", вы должны скопировать блок сохраненную в словаре (курсив мой):

блоки «просто работать», когда вы передаете блоки до стека в режиме ARC, такие как в возвращении. Вам больше не нужно вызывать Block Copy. Вам все еще нужно использовать [^{} copy] при переходе «вниз» стека в arrayWithObjects:и другие методы, которые делают сохранить.

Второй метод работает «случайно», потому что success и failure являются в __NSGlobalBlock__, а не «стек на основе блок», который должен быть скопирован в кучу.

+0

так что разница между двумя _method1: _ является блочным литералом на стеке и блочным объектом в куче? –

+0

является '__NSGlobalBlock__' документированным? –

+0

@ user1122742: На самом деле, когда вы вводите «po dict» в отладчике в вашем первом 'method1:', вы увидите, что 'success' является' __NSMallocBlock__' и 'error' является' __NSStackBlock__'. Разница, вероятно, вызвана тем, что 'dictionaryWithObjectsAndKeys:' принимает список переменных аргументов. Я просто оставлю это как подробно в реализации и придерживаюсь документации, в которой четко сказано, что вам нужно скопировать блоки, которые помещаются в массив или словарь. –

0

Я понимаю, что блоки являются объективными объектами c и могут быть помещены в NSDictionary непосредственно без Block_copy при использовании ARC.

Нет, они не являются общими объектами. Когда вы создаете блок, он находится в стеке, и не имеет значения, что это за счет сохранения, когда вы выходите из функции, которую он выталкивает из стека. Скопируйте его, чтобы он остался в живых.

0

Вы должны скопировать блоки перед передачей их методу, когда 1) блок будет храниться дольше, чем продолжительность вызова, и 2) параметр, с которым вы передаете его, является обычным типом указателя объекта (то есть id или NSObject *) вместо типа блока.

Это касается вашего звонка. dictionaryWithObjectsAndKeys: хранит аргумент в результирующем словаре и просто ожидает обычных аргументов указателя объекта (типа id) и не знает, передаете ли вы блоки или нет.

Причина второго условия, о котором я сказал, состоит в том, что если параметр метода уже принимает тип блока (например, для любых параметров обработчика завершения), то этот метод уже знает о специальных требованиях к управлению памятью блоков и, следовательно, будет возьмите на себя ответственность за копирование блока, если ему нужно его сохранить. В этом случае вызывающему абоненту не нужно беспокоиться об этом. Однако в вашем случае вы передаете его в общий метод, который не знает, что он получает блок, и, следовательно, не знает, чтобы его скопировать. Таким образом, вызывающий должен это сделать.

Может ли кто-нибудь объяснить, почему я должен использовать автоматические переменные для хранения блоков , прежде чем помещать их в словарь?

Об этом, ваш второй пример происходит с работы, потому что последние версии ARC компилятор супер консервативны блоков и вставляет копии всякий раз, когда вы назначаете его к переменной типа блока. Однако это не гарантируется спецификацией ARC и не гарантируется работа в будущем или в другом компиляторе. Вы не должны полагаться на это поведение.

Смежные вопросы