# swift 中delegate的使用注意

原文连接:<https://link.juejin.im/?target=http%3A%2F%2Fmarkmiao.com%2F2017%2F03%2F10%2Fswift中delegate的使用注意%2F>

delegate是iOS开发中一种非常常用的设计模式，经常用作界面的反向传值。block可以做到同样的事情，曾有人言delegate是个臃肿的胖子，远不如block轻便，早该淘汰了。在ARC中，block中使用self要用weak修饰，注意循环引用。delegate也需要使用weak指针，避免内存泄露。

```
@property (nonatomic, weak)id<XDViewControllerDelegate> delegate;
```

ARC环境下在oc中定义delegate使用weak修饰，在delegate对象释放掉后，delegate没有强指针引用也会被释放掉，避免了访问不存在delegate对象的delegate方法而崩溃。可以在`dealloc`方法中做防御性操作：置空delegate。

```
- (void)dealloc {
    self.delegate = nil;
}
```

在最近的swift项目中用到delegate，由于没有使用weak修饰，控件循环引用无法释放，导致内存泄露。

场景是这样的: 在控制器中添加了A控件，A控件中有个子控件B，B有个代理方法，A作为他的代理，并实现了代理方法。在完成功能后，使用`removeFromSuperview()`移除A控件时，发现A和B的反初始化方法`deinit`都没调用，说明这两个控件没被释放掉，内存泄露了。

(这里有个小坑，`deinit { }`方法里面如果没有执行代码是不会调用的，使用deinit检测该控件是否被移除，记得在`deinit { }`里面写一行执行代码，比如`print("deinit"))`

因为我在定义delegate的时候，是这样定义的：

```swift
protocol workSelectViewDelegate {
    func addWorkBtnViewClick(workTitle: String, workColor: String)
}
class WorkSelectView: UIView{
    var delegate:workSelectViewDelegate?
}
```

由于delegate没有weak修饰，在移除A控件的时候，A中有B，B的代理又是A，A和B相互引用，导致谁也没能释放掉

将delegate使用weak修饰即可，直接在`var delegate`前面加`weak`，编译会报错。这是因为在swift中遵守protocol的类型有很多，其中有些类型不支持weak修饰，比如struct。这里需要限制protocol的类型遵守。比如下面指定这个protocol只能由class遵守：

```
protocol workSelectViewDelegate: class {
    func addWorkBtnViewClick(workTitle: String, workColor: String)
}
class WorkSelectView: UIView{
    weak var delegate:workSelectViewDelegate?
}
```

还有一种方式是定义oc类型的protocol，因为oc类型的protocol只有class实现。使用关键词`@objc`修饰protocol：

```swift
@objc protocol workSelectViewDelegate {
    func addWorkBtnViewClick(workTitle: String, workColor: String)
}
class WorkSelectView: UIView{
    weak var delegate:workSelectViewDelegate?
}
```

相比第二种方法，第一种方法明显才是swift该有的样子，第二种太依赖oc了。

说起依赖oc，想起来在delegate定义中的`optional`，在swift中定义`delegate`方法，如果没有可选修饰，遵守了代理而不实现代理方法，不是警告，而是直接报错。

要想使用可选实现的修饰`optional`，必须使用`@objc`修饰

```swift
@objc protocol workSelectViewDelegate{
    func addWorkBtnViewClick(workTitle: String, workColor: String)
    @objc optional func addTimeTextString()
}
```

这样还有一个问题，如果代理有结构体作为返回值的话，会不支持而报错。

需要另一种方法定义可选代理方法，协议拓展：

```swift
protocol workSelectViewDelegate: class{
    func addWorkBtnViewClick(workTitle: String, workColor: String)
    func addTimeTextString()
}
extension workSelectViewDelegate {
    func addTimeTextString() {

    }
}
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://philm.gitbook.io/philm-ios-wiki/mei-zhou-yue-du/swift-zhong-delegate-de-shi-yong-zhu-yi.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
