类在遵循协议后的可变性存在疑问。

huangapple go评论86阅读模式
英文:

Mutability of a class after conforming to protocol is in question

问题

I was trying to implement a Networking Module for my personal project and I was following TDD. When I am writing the test cases I came across a compile error and I was not able to map the error with Swift concepts. I could have easily ignored it and done the implementation in a different way. But I was not feeling right to do it as I want to know the exact reason for the error.

这段代码中,我试图为我的个人项目实现一个网络模块,并且我在遵循测试驱动开发(TDD)的过程中遇到了一个编译错误,但我无法将这个错误与Swift的概念联系起来。我本可以轻松地忽略它,并以不同的方式进行实现。但是,我并不希望这样做,因为我想要知道错误的确切原因。

This is the code snippet.

这是代码片段。

import UIKit

protocol HTTPClient {
var url: String? {get set}
func load()
}

class HTTPClientSpy: HTTPClient {
var url: String?

  1. func load() {
  2. }

}

class Feed {
let client: HTTPClient

  1. init(client: HTTPClient) {
  2. self.client = client
  3. }
  4. func loadFeed() {
  5. client.url = "" //Compile Error -> Cannot assign to property: 'client' is a 'let' constant
  6. client.load()
  7. }

}

When I do the following change the error goes away

当我做以下更改时,错误消失了

class Feed {
let client: HTTPClientSpy

  1. init(client: HTTPClientSpy) {
  2. self.client = client
  3. }
  4. func loadFeed() {
  5. client.url = "" //No compile errors
  6. client.load()
  7. }

}

Swift classes are mutable, which means we should be able to change the properties of the instance even if we create a constant reference to the instance. It seems there is something to do with the protocol conformance in this case which I cannot understand.

Swift的类是可变的,这意味着即使我们创建了对实例的常量引用,我们仍应该能够更改实例的属性。在这种情况下,似乎与协议的遵循有关,这一点我无法理解。

Can someone please explain the theory behind this?

请有人解释一下背后的理论吗?

英文:

I was trying to implement a Networking Module for my personal project and I was following TDD. When I am writing the test cases I came across a compile error and I was not able to map the error with Swift concepts. I could have easily ignored it and done the implementation in a different way. But I was not feeling right to do it as I want to know the exact reason to the error.

This is the code snippet.

  1. import UIKit
  2. protocol HTTPClient {
  3. var url: String? {get set}
  4. func load()
  5. }
  6. class HTTPClientSpy: HTTPClient {
  7. var url: String?
  8. func load() {
  9. }
  10. }
  11. class Feed {
  12. let client: HTTPClient
  13. init(client: HTTPClient) {
  14. self.client = client
  15. }
  16. func loadFeed() {
  17. client.url = "" //Compile Error -> Cannot assign to property: 'client' is a 'let' constant
  18. client.load()
  19. }
  20. }

When I do the following change the error goes away

  1. class Feed {
  2. let client: HTTPClientSpy
  3. init(client: HTTPClientSpy) {
  4. self.client = client
  5. }
  6. func loadFeed() {
  7. client.url = "" //No compile errors
  8. client.load()
  9. }
  10. }

Swift classes are mutable. which means we should be able to change the properties of the instance even if we create a constant reference to the instance. It seems there is something to do with the protocol conformance in this case which I cannot understand.

Can someone please explain the theory behind this?

答案1

得分: 3

Swift类是可变的,这意味着即使我们创建了对实例的常量引用,我们也应该能够更改实例的属性。

的确,你是对的。但是structs也可以符合你的HTTPClient协议:

  1. struct SomeStruct: HTTPClient {
  2. var url: String?
  3. func load() {}
  4. }

如果Feed接收了SomeStruct的实例,那么client.url = ""将不起作用。通常情况下,如果你使用let声明了一个结构体类型的变量,你不能更改结构体变量的var

  1. let client = SomeStruct()
  2. client.url = "" // 错误

编译器不知道Feed中的client是存储一个结构体还是一个类,所以它试图确保安全,不允许你更改url

你可以通过添加 : AnyObject 来确保只有类才能符合该协议:

  1. protocol HTTPClient: AnyObject {
  2. // ...
  3. }
英文:

> Swift classes are mutable. which means we should be able to change the properties of the instance even if we create a constant reference to the instance.

Indeed you are right. But structs can conform to your HTTPClient protocol too:

  1. struct SomeStruct: HTTPClient {
  2. var url: String?
  3. func load() {}
  4. }

If Feed were passed an instance of SomeStruct, then client.url = "" would not work. Normally you cannot change the vars of a struct-typed variable, if you declared the variable with let.

  1. let client = SomeStruct()
  2. client.url = "" // error

The compiler doesn't know whether client in Feed is storing a struct or a class, so it tries to be safe and doesn't let you change url.

You can make sure that only classes can conform to the protocol by adding : AnyObject:

  1. protocol HTTPClient: AnyObject {
  2. // ...
  3. }

huangapple
  • 本文由 发表于 2023年5月13日 10:01:47
  • 转载请务必保留本文链接:https://go.coder-hub.com/76240787.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定