在闭包中引用属性需要明确的“自我”。使捕获语义明确
Posted
技术标签:
【中文标题】在闭包中引用属性需要明确的“自我”。使捕获语义明确【英文标题】:Reference to property in closure requires explicit 'self.' to make capture semantics explicit 【发布时间】:2015-09-19 07:03:42 【问题描述】:尝试将 Web 服务中的 html 加载到 webview 中,我收到此错误:
在闭包中引用属性“webviewHTML”需要明确的“self”。使捕获语义明确
这是什么意思,如何将 HTML 字符串加载到我的 Web 视图中?
func post(url: String, params: String)
let url = NSURL(string: url)
let params = String(params);
let request = NSMutableURLRequest(URL: url!);
request.HTTPMethod = "POST"
request.HTTPBody = params.dataUsingEncoding(NSUTF8StringEncoding)
let task = NSURLSession.sharedSession().dataTaskWithRequest(request)
data, response, error in
if error != nil
print("error=\(error)")
return
var responseString : NSString!;
responseString = NSString(data: data!, encoding: NSUTF8StringEncoding)
webviewHTML.loadHTMLString(String(responseString), baseURL: nil)
task.resume();
【问题讨论】:
【参考方案1】:self
的使用是对在闭包中引用(也称为捕获)构造(类/结构/枚举)的明确确认,这意味着 self
不会被解除分配,直到所述闭包被解除分配。
仔细想想,self
很可能被推断出来(事实上,当您在闭包外使用 webviewHTML
时),但这是一个有意的设计决策,而不是推断它,因为 Swift 是一种安全第一语言。
【讨论】:
也许,但这会降低抽象级别并降低语言的规律性。此外,它更安全,因为使用了引用计数。【参考方案2】:在回答这个问题之前,您必须知道什么是记忆周期。见Resolving Strong Reference Cycles Between Class Instances From Apple's documenation
现在您知道什么是内存循环了:
这个错误是 Swift 编译器告诉你的
“嘿,NSURLSession closure 正在尝试 保留 webviewHTML 堆,因此 self ==> 创建一个内存循环,我不 认为克拉克先生想要这里。想象一下,如果我们提出一个需要 永远,然后用户导航离开此页面。它不会 离开堆。我只是告诉你这个,但你克拉克先生必须创建一个对自我的弱引用并在闭包中使用它。”
我们使用[weak self]
创建(即capture)weak 引用。我强烈建议您查看附加链接,了解捕获的含义。
有关更多信息,请参阅斯坦福课程的this moment。
正确代码
func post(url: String, params: String)
let url = NSURL(string: url)
let params = String(params);
let request = NSMutableURLRequest(URL: url!);
request.HTTPMethod = "POST"
request.HTTPBody = params.dataUsingEncoding(NSUTF8StringEncoding)
let task = NSURLSession.sharedSession().dataTaskWithRequest(request)
[weak weakSelf self] data, response, error in
if error != nil
print("error=\(error)")
return
var responseString : NSString!;
responseString = NSString(data: data!, encoding: NSUTF8StringEncoding)
weakSelf?.webviewHTML.loadHTMLString(String(responseString), baseURL: nil)
// USED `weakSelf?` INSTEAD OF `self`
task.resume();
有关详细信息,请参阅此Shall we always use [unowned self] inside closure in Swift
【讨论】:
很好的答案,谢谢 - 现在这更有意义了。 也就是说你通常做的是:将data, response, error in
更改为:[weak self] data, response, error in
,然后在闭包中取消引用self
而不是取消引用self?
,因为self
现在已生成成为nil
-able(又名nullable
)因此你必须打开它
这里有一些微妙之处。斯坦福课程的视频使用 [weak self] 来防止视图保留在堆上,这是为了避免删除引用循环。只有当闭包引用 self 并且 self 保持对闭包的引用时才会发生循环(这在 Apple 文档中明确说明:“如果您将闭包分配给类实例的属性,也会发生强引用循环,并且该闭包的主体捕获实例。”。这里的示例代码不保留对闭包的引用,因此没有强引用循环
@Dale 感谢您的评论。 这里的示例代码没有保留对闭包的引用,所以没有强引用循环你是什么意思?实例本身不是对post
函数有强引用吗?因此到task
?
@Honey 变量 task 和 request 都是函数 post() 中的局部变量。一旦 post 退出,这两个变量都会被释放,即使 NSURLSession 将继续异步执行。您可以通过在调用 post() 之后的代码和闭包中设置断点来确认此行为。在执行闭包之前,您应该看到 post exit 返回给调用者。以上是关于在闭包中引用属性需要明确的“自我”。使捕获语义明确的主要内容,如果未能解决你的问题,请参考以下文章