Swift协议只能设置?

时间:2015-06-01 12:46:10

标签: ios swift protocols getter-setter

为什么我可以毫无错误地执行此操作:

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 definition),为什么我还要使用它们呢?

6 个答案:

答案 0 :(得分:43)

Apple在"Swift Programming Language (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:变量属性

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:计算属性(获取并设置)

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)变量属性

/* 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还声明:

  

如果协议要求属性可获取和可设置,则不能通过常量存储属性或只读计算属性来满足该属性要求。

因此,以下两个Playground代码段有效:

示例#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:计算属性(仅限获取)

protocol FullyNamed {
    var fullName: String { get }
}

struct Duck: FullyNamed {
    var fullName: String {return "Scrooge McDuck"}

    init(fullName: String) {
        self.fullName = fullName 
  // Error Message Cannot assign to Property: "FullName" is get only
    }
}

答案 1 :(得分:22)

根据official documentation

getter和setter要求可以通过各种方式的符合类型来满足。如果属性声明包含get和set关键字,则符合类型可以使用存储的变量属性或可读写的计算属性(即实现getter和setter的计算属性)来实现它。但是,该属性声明不能实现为常量属性或只读计算属性。 如果属性声明仅包含get关键字,则可以将其实现为任何类型的属性。

答案 2 :(得分:2)

请考虑以下事项:

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更改为let变量。

编辑:解决您为什么允许ModelDto拥有可设置变量的困惑。它与ModelDto允许具有除协议中定义的其他功能的方式相同。 getter和setter实际上只是函数,因此需要getter的协议并不排除实现也有setter。目标C也是如此。协议是描述性的,而非限制性的。

答案 3 :(得分:1)

在您的课程中,您将创建一个名为objectId的存储属性。在您的协议中,您指定该属性需要一个getter - 这是它唯一的要求。

如果您希望它是计算机属性,就像您期望的那样,则需要使用以下内容声明objectId

var objectId: Int{ return (someNumber) }

如果没有闭包来计算值,默认情况下它是一个存储属性。

答案 4 :(得分:1)

我回答了这个问题的一般意义。

在解决问题之前,你必须知道get& set意思是。

(如果你来自Objective-C世界:) get意味着 readOnly ,那就是我可以知道动物的腿数具有。我不允许设置它。 get& set一起表示 readWrite ,即我可以知道动物的体重,同时我也可以设置/更改动物的体重

使用以下示例。

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

如果你只有getter,并试图隐藏setter(使用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}还有一些额外的工作要完成,编译器不会告诉你...你必须添加{ {1}}实现预期的行为

如果你的属性有setter而你试图隐藏setter,那么你实际上会看到一个错误。

private (set)

你不允许隐藏,因为你答应提供一个二传手......

答案 5 :(得分:0)

您在代码示例中看到的行为称为成员隐藏。当使用相同的名称或继承的成员声明新成员时,成员隐藏发生在面向对象的语言中,因此具有:  var objectId: Int 在结构实现中,您实际上是在创建一个名为objectId的新成员,并隐藏从协议继承的属性。

为了遵守您的结构和协议之间的合同, objectId 可以声明为:

  let objectId: Int = 1

var objectId: Int {
        get {
            return 1
        }
    }