Определение метода, который возвращает ошибку
Как определить метод, который возвращает ошибку, а также значение?
Например, когда я вызываю сохраненный метод managedObjectContext, метод возвращает логическое значение вместе с ошибкой:
if(![context save:&error]) {
NSLog(@"%@", error);
}
Не могли бы вы дать мне прямой пример определения метода, стоящего за этим?
Редактирование / обновление: с той же точки зрения, как было бы возможно передать несколько ошибок. Я делаю что-то не так (я, вероятно, еще не понимаю концепцию), которая не работает:
NSArray *errors = nil;
[self throwMultipleErrors:&errors];
for(id error in errors) {
NSLog(@"Muliple error: %@", error);
}...
-(BOOL)throwMultipleErrors:(NSMutableArray **) errors {
[*errors addObject:@"First Error"];
[*errors addObject:@"Second Error"];
[*errors addObject:@"Third Error"];
return YES;
}
3 ответа
В вашем примере подпись метода:
- (BOOL)save:(NSError **)error;
Это позволяет вызывающей стороне передать в NSError
как адрес, а не указатель (указатель на указатель, **
а также &
), что метод затем может быть назначен из другой области. И если метод возвращает false, вызывающая сторона может затем проверить содержимое переменной error, как показано в вашем примере.
Смотрите этот вопрос для получения дополнительной информации об использовании &
перед переменными и (NSError **)
подписи.
Почему `&` (амперсанд) ставится перед некоторыми параметрами метода?
Пример реализации:
- (BOOL)save:(NSError **)error
{
NSAssert(error != nil, @"Passed NSError must be nil!");
// Attempt to do a save
if (saveDidFail) {
NSDictionary *userInfo = [NSDictionary dictionaryWithObjectAndKeys:@"Save Failed", kCustomErrorKey, nil];
*error = [NSError errorWithDomain:@"domain" code:999 userInfo:userInfo];
return NO;
}
return YES;
}
Используя ваш пример, подпись метода будет:
-(BOOL)save:(NSError**)error;
Теперь, когда вы используете двойные указатели, вы должны быть осторожны в программировании. Во-первых, вы должны убедиться, что значение error
не является nil
, затем *error
является nil
, Вы должны сделать это, потому что вы не знаете, что делать с памятью для существующего объекта. Например:
NSError *error = nil;
[self save:&error];
было бы правильно. Но
NSError *error = [[NSError alloc] init];
[self save:&error];
это проблема, потому что ваш save:
метод не будет, если error
является retained
или же autoreleased
, если ты release
это и это autoreleased
тогда ваше приложение в конечном итоге рухнет. И наоборот, если вы этого не сделаете release
это и есть retained
вы теряете память. Я рекомендую проверить это с помощью assert, чтобы проблема была быстро устранена, прежде чем код будет зарегистрирован.
NSAssert(!error || !*error, @"*error must be nil!");
Наконец, при настройке *error
Вы должны убедиться, что error
не является nil
, Вам никогда не разрешается устанавливать значение адреса памяти 0x0
, Если вы не проверяете и если вы проходите nil
в методе вы потерпите крах, если попытаетесь установить его.
if (error)
{
*error = [NSError ...];
return NO;
}
Чтобы вернуть массив, я бы сделал что-то вроде этого:
-(BOOL)throwMultipleErrors:(NSError **) error {
NSAssert(!error | !*error, @"*error must be nil");
NSMutableArray *errorList = nil;
if (error)
errorList = [NSMutableArray array];
[errorList addObject:@"First Error"];
[errorList addObject:@"Second Error"];
[errorList addObject:@"Third Error"];
if (error && [errorList count] > 0)
{
*error = [NSError errorWithDomain:@"Some error"
code:0
userInfo:@{@"suberrors" : [NSArray arrayWithArray:errorList]}];
return NO;
}
return YES;
}
Обратите внимание, что возвращаемый массив является неизменным и создается внутри метода. На самом деле нет причин для того, чтобы этот массив был изменяемым за пределами этого метода, а для того, чтобы массив создавался вне его. Инкапсуляция массива в NSError
позволяет легко вписаться в существующую цепочку NSError
методы.
Заметки
Конечно, вы бы на самом деле бросили [self save:&error];
позвонить в if()
заявление для проверки возвращаемого значения. Заметьте, однако, что мы проходим в &error
а не просто error
, Это потому, что нам нужно передать указатель на error
не error
сам. &error
читается как "Адрес ошибки".
Мы должны быть осторожны с синтаксисом здесь. Когда мы передаем NSError в метод: + (instancetype)stringWithContentsOfFile:(NSString *) кодировка пути:(NSStringEncoding)enc error:(NSError * *)error
мы передаем дескриптор (указатель на указатель) на объект ошибки, который еще не был инициализирован. Если вызываемое сообщение сталкивается с проблемой, оно инициализирует и заполняет объект ошибки. Он не "вернет" ошибку, просто заполните значения переданным параметром.
поэтому, если я хочу определить сообщение, которое возвращает значение, а также принимает параметр ошибки, следуйте этому руководству: http://www.cimgf.com/2008/04/04/cocoa-tutorial-using-nserror-to-great-effect/