Подтвердить что ты не робот

Swift не конвертирует Objective-C NSError ** в броски

У меня есть код устаревшего Objective-C, который объявляет метод вроде

- (void)doSomethingWithArgument:(ArgType)argument error:(NSError **)error

Как написано здесь https://developer.apple.com/library/ios/documentation/Swift/Conceptual/BuildingCocoaApps/AdoptingCocoaDesignPatterns.html

Swift автоматически переводит методы Objective-C, которые создают ошибки в методы, которые вызывают ошибку в соответствии с исходной ошибкой Swifts функции обработки.

Но в моем проекте описанные методы называются так:

object.doSomething(argument: ArgType, error: NSErrorPointer)

Кроме того, он выдает исключение во время выполнения, когда я пытаюсь использовать их, например:

let errorPtr = NSErrorPointer()
object.doSomething(argumentValue, error: errorPtr)

Нужно ли что-то еще для преобразования методов Objective-C "NSError **" в методы Swift "scrows"?

4b9b3361

Ответ 1

Только методы Objective-C, которые возвращают объект BOOL или (nullable) переведены в методы бросания в Swift.

Причина в том, что методы Cocoa всегда используют возвращаемое значение NO или nil чтобы указать отказ метода, а не просто установить объект ошибки. Это описано в Использование и создание объектов ошибок:

Важно: Успех или неудача указывается возвращаемым значением метода. Хотя методы Cocoa, которые косвенно возвращают объекты ошибки в ошибке Cocoaдомену гарантируется возврат таких объектов, если метод указывает на отказ путем прямого возврата nil или NO, вы всегда должны проверить, что возврат Значение nil или NO перед попыткой сделать что-либо с объектом NSError.

Например, интерфейс Objective-C

@interface OClass : NSObject

NS_ASSUME_NONNULL_BEGIN

-(void)doSomethingWithArgument1:(int) x error:(NSError **)error;
-(BOOL)doSomethingWithArgument2:(int) x error:(NSError **)error;
-(NSString *)doSomethingWithArgument3:(int) x error:(NSError **)error;
-(NSString * _Nullable)doSomethingWithArgument4:(int) x error:(NSError **)error;

NS_ASSUME_NONNULL_END

@end

отображается в Swift как

public class OClass : NSObject {

    public func doSomethingWithArgument1(x: Int32, error: NSErrorPointer)
    public func doSomethingWithArgument2(x: Int32) throws
    public func doSomethingWithArgument3(x: Int32, error: NSErrorPointer) -> String
    public func doSomethingWithArgument4(x: Int32) throws -> String
}

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

В противном случае вы бы назвали его из Swift как

var error : NSError?
object.doSomethingWithArgument(argumentValue, error: &error)
if let theError = error {
    print(theError)
}

Примечание: At

Я обнаружил, что Clang имеет атрибут, который заставляет функцию вызывать ошибку в Swift:

-(void)doSomethingWithArgument5:(int) x error:(NSError **)error
  __attribute__((swift_error(nonnull_error)));

отображается в Swift как

public func doSomethingWithArgument5(x: Int32) throws

и, похоже, работает "как ожидалось". Однако я не смог найти официальную документацию об этом атрибуте, поэтому было бы неплохо полагаться на него.

Ответ 2

Вам нужно, чтобы ваш метод возвращал BOOL, чтобы сообщить runtime, что ошибка должна быть или не должна быть выброшена. Также вы должны добавить параметр __autoreleasing в параметр ошибки, чтобы убедиться, что ARC случайно не выпустила ошибку, прежде чем вы сможете ее использовать:

- (BOOL)doSomethingWithArgument:(ArgType)argument error:(NSError * __autoreleasing *)error

Затем вы можете вызвать его из Swift следующим образом:

do {
    object.doSomethingWithArgument(someArgument)
} catch let err as NSError {
    print("Error: \(err)")
}