Почему значения, соответствующие протоколу Swift, по умолчанию считаются типами значений? - PullRequest
1 голос
/ 19 января 2020

У меня есть протокол, который описывает только интерфейс.

protocol SampleProtocol {
var message: String? { get set }}

Почему компилятор всегда обрабатывает соответствующее значение / объект как тип значения?

Пример примера,

 // Protocol value usage
import Foundation


protocol SampleProtocol {
    var message: String? { get set }
}

final class SampleObject: SampleProtocol {
    var message: String?
}


final class Controller {

    var sampleValue: SampleProtocol! {
        didSet {
            print("New value has been set")
        }
    }
}


let controller = Controller()
controller.sampleValue = AlphaObject() // Correctly prints "New value has been set"
controller.sampleValue.message = "New message" // Prints again "New value has been set", like for value types

Ответы [ 2 ]

2 голосов
/ 19 января 2020

Ваш протокол SampleProtocol может быть принят class или struct. Swift использует поведение типа значения, который является более ограничительным типом, пока вы не скажете ему, что протокол будет использоваться только ссылочным типом class.

Добавьте соответствие AnyObject в ваш протокол для получить поведение ссылочного типа:

protocol SampleProtocol: AnyObject {
    var message: String? { get set }
}

Подробнее см. Руководство по программированию Swift 5.1 - Протоколы только для класса .

Примечания к руководству:

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


Историческая справка : Использование class ключевого слова :

До Swift 4.0 это было написано с использованием ключевого слова class:

protocol SampleProtocol: class {
    var message: String? { get set }
}

Пока что это работает, но в настоящее время это просто псевдоним типа для AnyObject и, вероятно, будет удален в более поздней версии Swift.

0 голосов
/ 19 января 2020

Это не создает копию объекта. Это один и тот же объект в обоих вызовах - если вы положите print(Unmanaged.passUnretained(sampleValue as AnyObject).toOpaque()) в didSet, вы увидите, что адрес тот же. Компилятор не знает, что имеет дело с типом значения или ссылочным типом. Если вы добавите ключевое слово класса в объявление протокола следующим образом:

protocol SampleProtocol: class { ... }

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

...