Попытка соответствовать StringLiteralConvertible

Я сталкиваюсь с несколькими странностями, пытаясь соответствовать StringLiteralConvertible:

class Person: StringLiteralConvertible {
    var name = ""

    init(name n:String){
        name = n
    }

    init(stringLiteral value: StringLiteralType){
        name = n
    }

    init(extendedGraphemeClusterLiteral value: ExtendedGraphemeClusterLiteralType){
        name = n
    }

    init(unicodeScalarLiteral value: UnicodeScalarLiteralType){
        name = n
    }
}




var ironMan = Person(name: "Tony Stark")
var spiderMan: Person = "Peter Parker"

Я реализовал как протоколы ExtendedGraphemeClusterLiteralConvertible, так и UnicodeScalarLiteralConvertible (что на Земле означает это).

Однако я все еще получал ошибки и должен был давать определения как для ExtendedGraphemeClusterLiteralType, так и UnicodeScalarLiteralType:

typealias ExtendedGraphemeClusterLiteralType = String
typealias UnicodeScalarLiteralType = String

Почему я должен был предоставить это, если он уже находится в стандартном заголовке???

Компилятор считал, что он по-прежнему имеет право жаловаться и вынудил меня добавить ключевое слово required в inits, хотя определение протоколов не включает ключевое слово required! Почему????

Следующий код компилируется, но я не понимаю, почему первая версия не скомпилировалась!

class Person: StringLiteralConvertible {
    var name = ""

    init(name n:String){
        name = n
    }

    typealias ExtendedGraphemeClusterLiteralType = String
    typealias UnicodeScalarLiteralType = String

    required convenience init(stringLiteral value: StringLiteralType){
        self.init(name: value)
    }

    required convenience init(extendedGraphemeClusterLiteral value: ExtendedGraphemeClusterLiteralType){
        self.init(name: value)
    }

    required convenience init(unicodeScalarLiteral value: UnicodeScalarLiteralType){
        self.init(name: value)
    }
}

Быстро, если вам нравится, когда компилятор кричит на вас, вам это понравится!:-P

Ответ 1

Почему typealiases:

Ответ на ваш первый вопрос заключается в том, что протоколы Swift, которые имеют связанные типы, могут считаться абстрактными и должны быть привязаны к определенному типу. Связанные типы являются альтернативой использованию более специализированного подхода, такого как класс MyClass: IsCompatibleWith <Int> {}. Я думаю, что причина этого состоит в том, чтобы иметь общие иерархии, но это ограничение также вызывает многие другие головные боли. Несмотря ни на что, разумно и ожидаемо, что вам нужно указать тип, к которому привязан протокол. В Swift вы делаете это с помощью типа слияния, связанного с тем, который вы хотите (строка в вашем случае).

Также обратите внимание, что связанный тип не указан в протоколе, протокол указывает только, что вы должны определить его. Тем не менее, если у вас нет противоречивых методов для вашего класса, компилятор обычно может вывести связанный тип (прочитайте это в документах где-нибудь).

Требуемые инициализаторы:

Похоже, что инициализаторы, указанные в протоколах, по умолчанию по умолчанию. В документации говорится, что вы можете реализовать их как разработанные или удобные, но в любом случае они необходимы:

Реализация класса требований инициализатора протокола

Вы можете реализовать требование инициализатора протокола на соответствие класс как назначенный инициализатор или инициализатор удобства. В обоих случаях вы должны отметить реализацию инициализации с помощью требуемый модификатор:

class SomeClass: SomeProtocol {
required init(someParameter: Int) {
// initializer implementation goes here
}
}

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

Из моего чтения, однако, он отвечает what, но не why.