当你将self分配给delegate时,发生了什么?

3

我刚接触Swift,很难理解将self分配给委托的目的。其中一部分困难在于,委托似乎以两种不同的方式使用。

第一种用法是在特定事件发生时从一个类发送消息到另一个类,几乎就像状态管理。 第二种是启用“将某些责任委托给另一种类型的实例的类或结构”,如文档中所述。 我感觉这两个基本上是相同的,但我还没有领会到。

protocol PersonProtocol {
    func getName() -> String
    func getAge() -> Int
}

class Person {
    var delegate: PersonProtocol?
    
    func printName() {
        if let del = delegate {
            print(del.getName())
        } else {
            print("The delegate property is not set")
        }
    }
    
    func printAge() {
        if let del = delegate {
            print(del.getAge())
        } else {
            print("The delegate property is not set")
        }
    }
}

class ViewController: UIViewController, PersonProtocol {
    var person: Person!
    
    override func viewDidLoad() {
        person.delegate = self
        
        person.printAge()
        person.printName()
    }
    
    func getAge() -> Int {
        print("view controller")
       return 99
    }
    
    func getName() -> String {
        return "Some name"
    }
}

在这种情况下,person.delegate = self 的目的是什么?难道不是已经要求 ViewController 符合 PersonProtocol 了吗?
3个回答

4

如果你观察Person类,你会发现delegate为nil。如果你没有执行person.delegate = self,delegate将保持为nil。

换句话说,将ViewController分配给person.delegate允许Person识别代表是谁(即,对ViewController有一个引用),这样你就可以从Person类成功执行像delegate?.getName()或delegate?.getAge()这样的语句。


4

我有一种感觉,这两个本质上是相同的。

第一个是第二个的一个特例。"从一个类发送消息到另一个类"只是"转移它的一些职责"的一种特定方式。"消息"就是"职责"。

在这种情况下,person.delegate = self 的目的是什么?

在这里,person 委托(即转移)一些职责给另一个对象。它通过向另一个对象发送消息来实现这一点。首先,它需要确定它可以将这些职责委派给哪些对象。这是通过要求其 delegate 符合 PersonProtocol 来实现的,因为 PersonProtocol 定义了 Person 将要发送的消息。

接下来,person 需要知道确切地应该将这些消息发送给哪个对象。这就是 person.delegate = self 的作用。记住,在此之前,person 对你的 ViewController 一无所知。除了使用 = self,你还可以这样说:

person.delegate = SomeOtherClassThatConformsToPersonProtocol()

Person对象将向代理对象发送消息,而不是直接调用ViewController中的方法。

如果没有使用代理,ViewController已经需要遵循PersonProtocol协议了吗?

是的,但是如果没有使用代理,person不知道应该将其消息发送给哪个对象,因此ViewController中的方法不会被调用。


请注意,delegate属性应声明为weak,以避免保留环。当你执行person.delegate = self时,你会得到一个保留环: selfperson有强引用,person也通过delegate属性对self有强引用。


谢谢你的回答。所以ViewControllerPerson类的代理,能够调用Person的方法?这对Person有什么价值?或者对ViewController有什么价值? - Kevvv
@Kevvv ViewControllerperson 的代理,对吗?现在,Person 可以在需要名字或年龄时通知 ViewController。正如你可能已经意识到的那样,这不是 Person 应该工作的方式... Person 应该知道自己的年龄和姓名,而不是询问 ViewController... 因此,你给你的类和协议取的名称是如何使用委托模式的一个很糟糕的例子。更好的例子可以在你问题中链接的文档中找到。在那里,SnakesAndLadders 在游戏开始和结束等情况下通知它的代理。 - Sweeper
UIKit 中的一个例子是 UITableViewDelegate 通过 tableView(_:heightForRowAt:) 向其代理询问每行应该有多高。 - Sweeper

2
这意味着 Person 无法执行 getName()getAge(),所以 Person 类将其委托给其他数据源。 假设您的视图控制器有一个名为 PersonDataSource 的数据源类来处理 API 获取这些信息。
class PersonDataSource: PersonProtocol { 
    func getAge() -> Int {
        print("view controller")
       return 99
    }
    
    func getName() -> String {
        return "Some name"
    }
 } 

所以视图控制器将会是这样的:
class ViewController: UIViewController {
    var person: Person!
    var personDataSource = PersonDataSource()
    override func viewDidLoad() {
        person.delegate = personDataSource
        
        person.printAge()
        person.printName()
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接