Как соответствовать установленным и полученным переменным протокола?

Я играю с протоколами и как их соблюдать.

protocol Human {    
    var height: Int {get set}    
}

struct boy : Human { 
    var height: Int  {return 5} // error!
}

Я пытаюсь изучить различные способы, которыми я могу реализовать набор и получить. Однако приведенный выше код выдает следующую ошибку:

тип "мальчик" не соответствует протоколу "Человек"

Однако при написании ниже не будет ошибок:

struct boy : Human { 
    var height = 5 // no error
}

Я не понимаю ни разницы, ни того, что именно нужно реализовать, когда вы также можете установить переменную. Я изучал различные вопросы и учебные пособия, но все они просто пишут и идут без каких-либо более глубоких объяснений.

EDIT: убедитесь, что вы видите Imanou ответ здесь. Это сильно объясняет разные сценарии.

Ответ 1

Из Swift Reference:

Требования к свойствам

...
 Протокол не указывает, должно ли свойство быть сохраненным свойством или вычисленным свойством - оно указывает только требуемое имя и тип свойства.
...
Требования к свойствам всегда объявляются как переменные свойства с префиксом ключевого слова var. Получаемые и настраиваемые свойства указываются путем записи { get set } после их объявления типа, а свойства gettable указываются путем записи { get }.

В вашем случае

var height: Int  {return 5} // error!

- вычисленное свойство, которое можно получить только, это ярлык для

var height: Int {
    get {
        return 5
    }
}

Но для протокола Human требуется свойство, которое можно получить и настроить. Вы можете либо соответствовать сохраненной переменной (как вы заметили):

struct Boy: Human { 
    var height = 5
}

или с вычисленным свойством, которое имеет как getter, так и setter:

struct Boy: Human { 
    var height: Int {
        get {
            return 5
        }
        set(newValue) {
            // ... do whatever is appropriate ...
        }
    }
}

Ответ 2

Предпосылка:

Зайдите на игровую площадку и просто напишите фрагмент ниже:

var height: Int {
    get {
        return 5
    }
}    

или аналогично:

var height: Int {
    return 5
}    

Попробуйте напечатать значение height, очевидно, работает. Пока все хорошо

print(height) // prints 5

Однако, если вы попытаетесь установить его новое значение, вы получите сообщение об ошибке:

height = 8 // ERROR  

ошибка: нельзя присвоить значению: свойство 'height' доступно только для получения


Ответ:

Основываясь на ответе Мартина, я сначала написал:

set(newValue) {
    height = newValue 
}

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

Поэтому я написал похожий код

protocol Human {

    var height: Float {get set}

}

struct Boy: Human {

    // inch
    var USheight : Float

    // cm
    var height: Float {
        get {
            return 2.54 * USheight
        }
        set(newValue) {
         USheight = newValue/2.54

        }
    }
}

// 5 ft person
var person = Boy(USheight: 60)
 // interestingly the initializer is 'only' based on stored properties because they
 // initialize computed properties. 


// equals to 152cm person
print(person.height) // 152.4

Совет для профессионалов: когда вы должны сделать свои свойства доступными только для чтения?

Обычно, если вы делаете свойство доступным только для чтения, то есть { get }, потому что эти свойства вычисляются, и вы не хотите, чтобы объект управлял им.

Пример у вас есть объект JSON. Он имеет несколько больших объектов, таких как:

JSONData
 - userInfo (name, address, age)
 - devices (iPads, iPhones, Mac books)
 - credentials (basic iCloud, pro iCloud, celebrity)

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

protocol Credentials {
    var role: String { get }

    init(person: Person)
}

class Person {
    var userInfo: String
    var devices: [String]
    var creds: Credentials {
        Credentials(person: self)
    }

    init(userInfo: userInfo, devices: [String]) {
        self.userInfo = userInfo
        self.devices = devices
    }
}