Установка NSError внутри блока с использованием ARC - PullRequest
10 голосов
/ 02 августа 2011

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

- (BOOL)frobnicateReturningError:(NSError **)error
{
    NSArray *items = [NSArray arrayWithObjects:@"One", @"Two", @"Three", nil];

    __block Frobnicator *blockSelf = self;
    [items enumerateObjectsUsingBlock:^(id item, NSUInteger idx, BOOL *stop) {
        [blockSelf doSomethingWithItem:item error:error];
    }];
}

Компилируется, но с учетом error может быть изменено doSomethingWithItem Я попытался создать локальный NSError для изменяемого блока, который затем будет использоватьсяустановить исходное значение error после перечисления (которое я не показывал):

- (BOOL)frobnicateReturningError:(NSError **)error
{
    NSArray *items = [NSArray arrayWithObjects:@"One", @"Two", @"Three", nil];

    __block Frobnicator *blockSelf = self;
    __block NSError *blockError = nil;
    [items enumerateObjectsUsingBlock:^(id item, NSUInteger idx, BOOL *stop) {
        [blockSelf doSomethingWithItem:item error:&blockError];
    }];
}

Это не скомпилируется со следующей ошибкой:

передаваемый адрес не-local объект в параметре __autoreleasing для обратной записи

Поиск в Google для этой ошибки возвращает результаты только из самого исходного кода Clang.

Одно решение, которое работает, но немного уродливодолжен иметь внутренний и внешний указатель ошибки:

- (BOOL)frobnicateReturningError:(NSError **)error
{
    NSArray *items = [NSArray arrayWithObjects:@"One", @"Two", @"Three", nil];

    __block Frobnicator *blockSelf = self;
    __block NSError *outerError = nil;
    [items enumerateObjectsUsingBlock:^(id item, NSUInteger idx, BOOL *stop) {
        NSError *innerError = nil;
        [blockSelf doSomethingWithItem:item error:&innerError];
        outerError = innerError;
    }];
}

Как правильно установить NSError изнутри блока?

Ответы [ 2 ]

8 голосов
/ 02 августа 2011

Попробуйте это:

// ...
__block NSError *blockError = nil;
[items enumerateObjectsUsingBlock:^(id item, NSUInteger idx, BOOL *stop) {
    NSError *localError = nil;
    if (![blockSelf doSomethingWithItem:item error:&localError]) {
        blockError = localError;
    }
}];
// ...

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

1 голос
/ 06 октября 2017

Как правильно установить NSError внутри блока?

Как видно из " Что нового в LLVM? " @ 14:55,Есть два метода для решения проблемы с NSError, которая неявно автоматически выпускается.

Самое простое решение - использовать __strong

- (BOOL)frobnicateReturningError:(NSError *__strong *)error
{
    NSArray *items = [NSArray arrayWithObjects:@"One", @"Two", @"Three", nil];

    __block Frobnicator *blockSelf = self;

    [items enumerateObjectsUsingBlock:^(id item, NSUInteger idx, BOOL *stop) {
        NSError *innerError = nil;
        [blockSelf doSomethingWithItem:item error:&innerError];
        if(innerError && error) {
          *error = [NSError errorWithDomain:...];
        }
    }];
}

Второе исправление - это использование __block

- (BOOL)frobnicateReturningError:(NSError **)error
{
        NSArray *items = [NSArray arrayWithObjects:@"One", @"Two", @"Three", nil];

        __block Frobnicator *blockSelf = self;
        __block NSError *strongError = nil;

        [items enumerateObjectsUsingBlock:^(id item, NSUInteger idx, BOOL *stop) {
            NSError *innerError = nil;
            [blockSelf doSomethingWithItem:item error:&innerError];
            if(innerError) {
              strongError = [NSError errorWithDomain:...];
            }
        }];
        if (error) *error = strongError;
    }
...