Восстановление с ошибок

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

Идеально, восстановление attempter должно быть независимым объектом, знающим что-то об условиях ошибки и как лучше всего обойти те условия. Приложение могло даже иметь объект, роль которого должна восстановиться с ошибок различных видов. Восстановление attempter должно реализовать по крайней мере один из двух методов NSErrorRecoveryAttempting неофициальный протокол: attemptRecoveryFromError:optionIndex:delegate:didRecoverSelector:contextInfo: или attemptRecoveryFromError:optionIndex:. Это реализует прежний метод для представленного документа модально предупреждений ошибки и последний метод для модальных приложением предупреждений.

Необходимо также подготовить ошибочный объект так, чтобы восстановление после ошибки могло иметь место. Чтобы сделать это, добавьте три элемента к пользовательскому информационному словарю ошибочного объекта:

Перечисление 5-1 иллюстрирует случай, включающий NSXMLDocument класс. В этом примере, NSDocument возразите пытается создать внутреннее дерево, представляющее XML-документ с помощью initWithContentsOfURL:options:error: метод NSXMLDocument. Если попытка перестала работать, обычная причина состоит в том, что источник, XML уродлив — например, существует недостающий конечный тэг для элемента, или значение атрибута не заключается в кавычки. Если источник, XML «приведен в порядок» сначала для решения структурных проблем, может быть возможно создать дерево XML.

В примере в Перечислении 5-1, если вызов initWithContentsOfURL:options:error: возвращает ошибочный объект ссылкой, объект документа настраивает ошибочный объект, добавляя (среди прочего) объект восстановления-attempter, локализованные опции восстановления и локализованное предложение восстановления к его пользовательскому информационному словарю. Тогда это отправляет presentError:modalForWindow:delegate:didPresentSelector:contextInfo: к self.

Перечисление 5-1  , Подготавливающееся к восстановлению после ошибки

- (BOOL)readFromURL:(NSURL *)furl ofType:(NSString *)type error:(NSError **)anError {
    NSError *err;
 
    // xmlDoc is an NSXMLDocument instance variable.
    if (xmlDoc != nil) {
        xmlDoc = nil;
    }
 
    xmlDoc = [[NSXMLDocument alloc] initWithContentsOfURL:furl
            options:NSXMLNodeOptionsNone error:&err];
 
    if (xmlDoc == nil && err) {
        NSString *newDesc = [[err localizedDescription] stringByAppendingString:
            ([err localizedFailureReason] ? [err localizedFailureReason] : @"")];
 
        NSDictionary *newDict = @{ NSLocalizedDescriptionKey : newDesc,
            NSURLErrorKey : furl,
            NSRecoveryAttempterErrorKey : self,
            NSLocalizedRecoverySuggestionErrorKey :
                NSLocalizedString(@"Would you like to tidy the XML and try again?", @""),
            NSLocalizedRecoveryOptionsErrorKey :
                @[NSLocalizedString(@"Try Again", @""), NSLocalizedString(@"Cancel", @"")] };
 
        NSError *newError = [[NSError alloc] initWithDomain:[err domain]
            code:[err code] userInfo:newDict];
        [self presentError:newError modalForWindow:[self windowForSheet]
            delegate:self
            didPresentSelector:@selector(didPresentErrorWithRecovery:contextInfo:)
            contextInfo:nil];
    }
// ...

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

От ошибочного объекта отказываются цепочка ошибочного респондента и NSApp дисплеи это. Когда пользователь нажимает любую кнопку ошибочного предупреждения, NSApp проверки, чтобы видеть, имеет ли ошибочный объект и восстановление attempter и опции восстановления. Если оба из этих условий являются истиной, она вызывает метод, реализованный восстановлением attempter, который соответствует режиму предупреждения (т.е. модальный документом или модальный приложением).

Перечисление 5-2 показывает, как восстановление attempter для XML-документа реализует attemptRecoveryFromError:optionIndex:delegate:didRecoverSelector:contextInfo: метод.

Перечисление 5-2  , Восстанавливающееся с ошибки и сообщающее модальному делегату

- (void)attemptRecoveryFromError:(NSError *)error
                     optionIndex:(unsigned int)recoveryOptionIndex
                        delegate:(id)delegate
              didRecoverSelector:(SEL)didRecoverSelector
                     contextInfo:(void *)contextInfo {
 
    BOOL success = NO;
    NSError *err;
    NSInvocation *invoke = [NSInvocation invocationWithMethodSignature:
                               [delegate methodSignatureForSelector:didRecoverSelector]];
    [invoke setSelector:didRecoverSelector];
 
    if (recoveryOptionIndex == 0) { // Recovery requested.
        xmlDoc = [[NSXMLDocument alloc] initWithContentsOfURL:[[error userInfo]
                objectForKey:NSURLErrorKey] options:NSXMLDocumentTidyXML error:&err];
        if (xmlDoc != nil) {
            success = YES;
        }
    }
    [invoke setArgument:(void *)&success atIndex:2];
    if (err)
        [invoke setArgument:&err atIndex:3];
    [invoke invokeWithTarget:delegate];
}

Ключевая роль вышеупомянутого примера является тестом, который восстановление attempter делает, чтобы определить, нажал ли пользователь кнопку «Try Again»: это проверяет значение recoveryOptionIndex. Если пользователь действительно нажимал эту кнопку, восстановление attempter вызывает initWithContentsOfURL:options:error: метод снова, на сей раз с NSXMLDocumentTidyXML опция. Тогда это создает и вызывает NSInvocation объект, таким образом отправляя требуемое сообщение модальному делегату ошибочного предупреждения. Объект вызова включает эти два параметра, требуемые селектором делегата: булевская переменная, указывающая, успешно выполнилась ли попытка восстановления и “информационный параметр” контекста, в этом случае, содержащий любой ошибочный объект, возвратилась из попытки восстановления.

Когда модальный делегат получает сообщение от восстановления attempter, как в Перечислении 5-3, это может ответить соответственно.

Перечисление 5-3  Модальный делегат, отвечающий на восстановление attempter

- (void)didPresentErrorWithRecovery:(BOOL)didRecover
            contextInfo:(void *)contextInfo {
 
    NSError *theError = (NSError *)contextInfo;
    if (didRecover) {
        [tableView reloadData];
    } else if (theError && [theError isKindOfClass:[NSError class]]) {
        [NSAlert alertWithError:theError];
    }
}