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

Протокол Swift доступен только для настройки?

почему я могу сделать это без ошибок:

var testDto = ModelDto(modelId: 1)
testDto.objectId = 2

пока я определяю это:

protocol DataTransferObject {
    var objectType: DtoType { get }
    var parentObjectId: Int { get set }
    var objectId: Int { get }
    var objectName: String { get set }
}

struct ModelDto: DataTransferObject {
    var objectType: DtoType
    var parentObjectId: Int
    var objectId: Int
    var objectName: String

    init(modelId: Int) {
        self.objectType = DtoType.Model
        self.objectId = modelId
        self.parentObjectId = -1
        self.objectName = String()
    }
}

Если определение в моем протоколе в основном игнорируется (определение getter, setter), почему я должен использовать их в любом случае?

4b9b3361

Ответ 1

В соответствии с официальной документацией :

Требования геттера и сеттера могут удовлетворяться соответствующим типом различными способами. Если объявление свойства включает как ключевые слова get, так и set, соответствующий тип может реализовать его с сохраненным свойством переменной или вычисленным свойством, которое является читаемым и записываемым (то есть тем, которое реализует как getter, так и setter). Однако это объявление свойства не может быть реализовано как свойство константы или вычисляемое свойство только для чтения. Если объявление свойства включает только ключевое слово get, оно может быть реализовано как любое свойство.

Ответ 2

Apple указывает на "Язык Swift программирования (Swift 3)" :

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

По этой причине все пять фрагментов кода на Playground являются действительными:

Пример # 1: свойство константы

protocol FullyNamed {
    var fullName: String { get }
}

struct Duck: FullyNamed {
    let fullName: String
}

let scrooge = Duck(fullName: "Scrooge McDuck")
print(scrooge.fullName) // returns "Scrooge McDuck"

Пример # 2: свойство variable

protocol FullyNamed {
    var fullName: String { get }
}

struct Duck: FullyNamed {
    var fullName: String        
}    

var scrooge = Duck(fullName: "Scrooge McDuck")
print(scrooge.fullName) // returns "Scrooge McDuck"

scrooge.fullName = "Scrooge H. McDuck"
print(scrooge.fullName) // returns "Scrooge H. McDuck"

Пример # 3: вычисленное свойство (только для получения)

protocol FullyNamed {
    var fullName: String { get }
}

struct Duck: FullyNamed {
    private var name: String
    var fullName: String {
        return name
    }
}

let scrooge = Duck(name: "Scrooge McDuck")
print(scrooge.fullName) // returns "Scrooge McDuck"

Пример # 4: вычисленное свойство (get и set)

protocol FullyNamed {
    var fullName: String { get }
}

struct Duck: FullyNamed {
    private var name: String
    var fullName: String {
        get {
            return name
        }
        set {
            name = newValue
        }
    }
}

var scrooge = Duck(name: "Scrooge McDuck")
print(scrooge.fullName) // returns "Scrooge McDuck"

scrooge.fullName = "Scrooge H. McDuck"
print(scrooge.fullName) // returns "Scrooge H. McDuck"

Пример # 5: свойство private(set) variable

/* Duck.swift located in Sources folder */

protocol FullyNamed {
    var fullName: String { get }
}

public struct Duck: FullyNamed {
    public private(set) var fullName: String

    public init(fullName: String) {
        self.fullName = fullName
    }

    public mutating func renameWith(fullName: String) {
        self.fullName = fullName
    }
}

/* Playground file */

var scrooge = Duck(fullName: "Scrooge McDuck")
print(scrooge.fullName) // returns "Scrooge McDuck"

scrooge.renameWith("Scrooge H. McDuck")
print(scrooge.fullName) // returns "Scrooge H. McDuck"

Apple также заявляет:

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

По этой причине два следующих фрагмента кода площадки: ARE NOT:

Пример # 1: свойство константы

protocol FullyNamed {
    var fullName: String { get set }
}

struct Duck: FullyNamed {
    let fullName: String
}

let scrooge = Duck(fullName: "Scrooge McDuck")
// Error message: Type 'Duck' does not conform to protocol 'FullyNamed'

Пример # 2: вычисленное свойство (только для получения)

protocol FullyNamed {
    var fullName: String { get set }
}

struct Duck: FullyNamed {
    private var name: String
    var fullName: String {
        return name
    }
}

var scrooge = Duck(name: "Scrooge McDuck")
// Error message: Type 'Duck' does not conform to protocol 'FullyNamed'

Ответ 3

В вашем классе вы создадите сохраненное свойство с именем objectId. В вашем протоколе вы указываете, что для свойства требуется getter - это его единственное требование.

Если вы хотите, чтобы это свойство компьютера, как и ожидалось, вам нужно объявить objectId со следующим:

var objectId: Int{ return (someNumber) }

Без закрытия для вычисления значения это по умолчанию хранимое свойство.

Ответ 4

Рассмотрим следующее:

var testDto = ModelDto(modelId: 1)

Тип переменной testDto здесь известен как ModelDto. Известно, что ModelDto имеет изменяемую переменную var objectId: Int. Вы можете изменять objectId, потому что вы обращаетесь к объекту через интерфейс ModelDto, а не через интерфейс протокола, где он доступен только для получения.

Попробуйте следующее:

var testDto: DataTransferObject = ModelDto(modelId: 1)
testDto.objectId = 2 // compiler error

Приведенный выше пример не должен компилироваться. Поскольку тип testDto известен только как DataTransferObject, мы не знаем, что базовая реализация имеет настраиваемое свойство. Мы знаем только о свойстве gettable, объявленном в протоколе.

Короче говоря, вы объявили ModelDto иметь переменную get/set, поэтому было бы довольно странно, если бы Swift не позволил вам установить ее. Имея переменную get only, вы полагаетесь на то, что вы ссылаетесь на объект через протокол или изменяете objectId на ModelDto как допустимую переменную.

РЕДАКТИРОВАТЬ: Чтобы устранить вашу путаницу в отношении того, почему ModelDto разрешено иметь настраиваемую переменную. Это то же самое, что и ModelDto, чтобы иметь другие функции, чем те, которые определены в протоколе. Getters и seters на самом деле являются просто функциями, поэтому протокол, требующий getter, не исключает возможности реализации из сеттера. То же самое можно сделать в Objective C. Протоколы являются описательными, а не ограничительными.

Ответ 5

Поведение, которое вы видите в своем примере кода, называется скрытием элемента. Скрытие членов происходит в объектно-ориентированных языках, когда новый член объявляется с тем же именем или сигнатурой унаследованного, поэтому, имея:  var objectId: Int в вашей реализации структуры вы фактически создаете новый член с именем objectId и скрываете свойство, унаследованное от протокола.

Чтобы выполнить контракт между вашей структурой и вашим протоколом, objectId может быть объявлен как:

  let objectId: Int = 1

или

var objectId: Int {
        get {
            return 1
        }
    }

Ответ 6

Я отвечаю на вопрос в этом общем смысле.

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

(Если вы пришли из мира Objective-C:) get означает readOnly, то есть мне позволено узнать количество ног, которые есть у животного. Мне не разрешено устанавливать его. get и set вместе означают readWrite, то есть мне разрешено знать вес животного, в то время как я также могу установить/изменить вес животного

В следующем примере.

protocol Animal {
    var weight : Int { get set }
    var limbs : Int { get }
}

Если у вас есть только getter и попытка скрыть сеттер (используя private (set)... тогда вы НЕ получите ошибку... скорее всего, что вы хотели и как это должно быть сделано!

Вероятно, что вы намеревались:

class Cat : Animal {
    private (set) var limbs: Int = 4 // This is what you intended, because you only have get requirements...and don't want any conforming type to be able to set it ie don't want others do catInstance.limbs = 22
    var weight: Int = 15

}

var smallCat = Cat()
smallCat.weight = 20 // Good!

// attempting to set it will create an error!!!
smallCat.limbs = 5 // Error: Cannot assign to property: 'limbs' setter is inaccessible

Вероятно, что вы не намеревались:

class Panda : Animal {
    var limbs: Int = 4 // This is OK, but it kinda defeats the purpose of it being a get only
    var weight: Int = 200   
}

var littlPanda = Panda()

littlPanda.weight = 40 // Good
littlPanda.limbs = 30 // NO Error!!! Likely unintended

В основном с {get} еще предстоит выполнить некоторую дополнительную работу, которую компилятор вам не скажет... ВЫ должны добавить private (set) для достижения предполагаемого поведения


Если ваше свойство имеет сеттер и вы пытаетесь скрыть сеттер, вы увидите ошибку.

class Dog : Animal {
    private (set) var limbs: Int = 4
    private (set) var weight: Int = 50  // Error: Setter for property 'weight' must be declared internal because it matches a requirement in internal protocol 'Animal'
}

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