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

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

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?

func load() {

}

}

class Feed {
let client: HTTPClient

init(client: HTTPClient) {
    self.client = client
}

func loadFeed() {
    client.url = "" //Compile Error -> Cannot assign to property: 'client' is a 'let' constant
    client.load()
}

}

When I do the following change the error goes away

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

class Feed {
let client: HTTPClientSpy

init(client: HTTPClientSpy) {
    self.client = client
}

func loadFeed() {
    client.url = "" //No compile errors
    client.load()
}

}

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.

import UIKit

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

class HTTPClientSpy: HTTPClient {
    var url: String?
    
    func load() {
        
    }
}

class Feed {
    let client: HTTPClient

    init(client: HTTPClient) {
        self.client = client
    }

    func loadFeed() {
        client.url = "" //Compile Error -> Cannot assign to property: 'client' is a 'let' constant
        client.load()
    }
}

When I do the following change the error goes away

class Feed {
    let client: HTTPClientSpy

    init(client: HTTPClientSpy) {
        self.client = client
    }

    func loadFeed() {
        client.url = "" //No compile errors
        client.load()
    }
}

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协议:

struct SomeStruct: HTTPClient {
    var url: String?
    func load() {}
}

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

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

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

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

protocol HTTPClient: AnyObject {
    // ...
}
英文:

> 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:

struct SomeStruct: HTTPClient {
    var url: String?
    func load() {}
}

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.

let client = SomeStruct()
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:

protocol HTTPClient: AnyObject {
    // ...
}

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:

确定