Приведение типов как в Swift

Я пытаюсь понять as приведение типов.

Читая главу о приведении типов в документации Apple, у меня есть два синтаксиса для понижения приведения (операторы as? и as!), но я ничего не нашел о as. Поэтому я подумал, что никогда не должен был использовать этот кинк оператора, но вчера, когда я печатал код с оператором do-try-catch, я встретил это:

catch let error as NSError {
      print(error)
}

Изначально ошибка имела тип, соответствующий Error protocol. Теперь, используя приведение as NSError, он стал экземпляром класса NSError.

Но мой вопрос: что делает оператор? Конечно, это не понижение. Можно ли его использовать для «конвертирования» объекта?

ИЗМЕНИТЬ Я не думаю, что это дубликат. В моем случае переменная ошибки не является классом и не наследуется от суперкласса, поэтому у меня нет повышения. Это даже не pattern matching. Я уже прочитал как страницу блога Swift, так и эту ветку на StackOverflow.

EDIT 2 из блога Swift

Swift 1.2 разделяет понятия гарантированного преобразования и принудительного преобразования на два отдельных оператора. Гарантированное преобразование по-прежнему выполняется с оператором as, но принудительное преобразование теперь использует as! оператор. ! предназначен для указания того, что преобразование может завершиться ошибкой. Таким образом, вы сразу узнаете, какие преобразования могут привести к сбою программы.

Приведенный выше текст не работает для меня, потому что, если я попытаюсь использовать оператор as! вместо as, компилятор пожалуется мне.

ИЗМЕНИТЬ 3 Даже в Использование Swift с документацией Cocoa и Obj-C используют синтаксис let-as? для проверки и приведения к протоколу. Итак, почему в моем случае я не могу его использовать?


person ndPPPhz    schedule 31.12.2016    source источник
comment
developer.apple.com/swift/blog/?id=23   -  person EI Captain v2.0    schedule 31.12.2016
comment
Обратите внимание, что последний as — это не оператор, а часть синтаксиса let-as.   -  person Sergey Kalinichenko    schedule 31.12.2016
comment
Здесь оператор as используется для соединения типов Objective-C и типов Swift. Например. Error и NSError, Double и NSNumber и т. д.   -  person Alistra    schedule 31.12.2016
comment
@Alistra, но Error — это протокол, а NSError — это класс.   -  person ndPPPhz    schedule 31.12.2016
comment
@dasblinkenlight Но я не нашел синтаксис let-as. Я нашел это с оператором вниз (пусть как?)   -  person ndPPPhz    schedule 31.12.2016
comment
я добавил два редактирования   -  person ndPPPhz    schedule 31.12.2016
comment
Ваш пример кода определенно представляет собой pattern, это value-binding-pattern, содержащий as-pattern.   -  person OOPer    schedule 01.01.2017
comment
@ndPPPhz да, и если я правильно понимаю, все, что возвращается как протокол Error в swift, может использоваться в Objective-C, и оно преобразуется в NSError   -  person Alistra    schedule 01.01.2017


Ответы (2)


Прежде всего, как указано в комментарии dasblinkenlight, ваш фрагмент кода не использует оператор-приведения типов. Проверьте синтаксис do-statement, и вы можете найти их:

catch-clausecatch­ шаблонopt where-clauseopt code-block

шаблонvalue-binding-pattern

шаблон привязки значенияvar шаблон | let шаблон

шаблонtype-casting-pattern

шаблон приведения типовis-pattern | как шаблон

как шаблоншаблон ­as тип

Таким образом, ваш EDIT 2 не имеет смысла, нет синтаксиса, принимающего as! в catch-clause.


Но этот код (с использованием оператора приведения типов) работает, поэтому я пытаюсь объяснить, как использовать as-приведение.

    enum MyError: Error {
        case bad
        //...
    }
    let error: Error = MyError.bad
    let nsError = error as NSError

Как показано в связанной статье в комментарии EI Captain v2.0, as-приведение используется для гарантированного преобразования. Я собрал несколько вариантов использования таких преобразований.

  • Преобразование

    class Animal {}
    class Dog: Animal {}
    let d = Dog()
    d as Animal     // upcast succeeds
    

    Как показано в статье, приведение всегда завершается успешно, поэтому вы можете использовать as.

  • Указание литерального типа

    let byte = 123 as UInt8
    let ch = "a" as UnicodeScalar
    

    В Swift литералы бестиповые, поэтому вы можете использовать as для указания типов литералов.

    В случае, если Swift может вывести тип литерала, вы можете опустить такое as-приведение:

    let byte: UInt8 = 123
    let ch: UnicodeScalar = "a"
    
  • Устранение неоднозначности перегруженных методов

    class MyClass {
        func aMethod(_ arg: String) {
            print(arg)
        }
        func aMethod(_ arg: Int) {
            print("\"\(arg)\"")
        }
    }
    let obj = MyClass()
    let theFunc = obj.aMethod as (String)->Void
    theFunc("abc") //->abc
    
  • Всегда успешное соединение

    let str: String = "a String"
    let nsStr = str as NSString
    
    let intArr: [Int] = [1,2,3]
    let nsArr = intArr as NSArray
    

    Пример let nsError = error as NSError включен в эту категорию, и вам необходимо прочитать эта статья внимательно, чтобы понять, почему это соединение всегда успешно.


Для вашего EDIT 3.

Возможно, вам придется различать эти два синтаксиса:

let a: Any = 1

//Using if-let -- Optional binding, `(a as? Int)` is an expression using type-casting-operator which generates an Optional result
if let intA = (a as? Int) {
    print("\(intA) is Int")
}

//Using if-case -- pattern matching, `(let intA as Int)` is a pattern using as-pattern
if case (let intA as Int) = a {
    print("\(intA) is Int")
}

Как уже отмечалось, catch ведет шаблон, и вы не можете использовать as? в шаблоне.

person OOPer    schedule 01.01.2017

as

Используйте as для типов, которые Apple проделала некоторую работу по обработке преобразования в фоновом режиме. Обычно это типы Foundation, которые Apple подключила к Swift и хочет, чтобы у вас был быстрый способ преобразования туда и обратно из их эквивалентов ObjC, например:

String <-> NSString
URL    <-> NSURL
Array  <-> NSArray
Data   <-> NSData

Эти приведения всегда успешны, и Xcode предупредит вас, если вы используете as? или as!. В вашем конкретном случае Apple вмешалась в фоновом режиме, чтобы протокол Error и NSError можно было приводить друг к другу и друг от друга.

as!

Используйте as!, если вы знаете, что объект может быть приведен к другому типу. as! приведет к сбою вашего приложения, если объект не может быть преобразован (например, когда sender на самом деле является UITextField)

let button = sender as! UIButton    // you are sure that the sender is always
                                    // a UIButton or your app will crash

as?

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

// Exit the function if the sender is not a UIButton
guard let sender = sender as? UIButton else {
    return
}

// Only execute the block if the sender is UIButton
if let button = sender as? UIButton {
    // ...
}
person Code Different    schedule 01.01.2017