Swift中的Weak Strong Dance
亲爱的博客园的关注着博主文章的朋友们告诉你们一个很不幸的消息哦,
这篇文章将会是博主在博客园发表的最后一篇文章咯,
因为之后的文章博主只会发布到这里哦 http://daiweilai.github.io/
新博客排版布局更好,阅读体验更佳,欢迎吐槽、留言、订阅哟
马上又要过年了,诶,再也不能像当初那样无耻地逗利是了(我们广东的方言讨红包的意思)
图1
图2
看来今年没利了
谁让哥已经工作了呢。
公司今年的开发任务算是完结了,苹果又极不负(hǎo)责(yàng)任(de)地放圣诞不审核了,所以这半个月就该清闲下来了。
博主掐指一算,Swift已经养到2.1了,并且也开源了,这样看来Swift也够肥了,语法也绝逼不会再有大改动了,是该再次抓起来了。
为什么说再呢,其实在当初Swift Beta版本的时候,我们项目经理尝试了一下palyground后,一拍手,棒棒哒,”我们用Swift开发接下来的项目吧”,然后就是Swift1.0 … 2.0,每一次升级,每一次语法更迭,每一次XCode打开的那一刻都是满江红,那触目惊心的场面无数次让博主受尽折磨。最后庆幸的是,这个项目死了,哦耶!
好吧,重新捡起Swift吧,今天的这篇文章是一篇激情翻译大作,而且本人的英文水平获得了原作者充分的肯定:
图3
看到了吧,毫无障碍地交流!!我们愉快地聊了很久~~ ,激情四射,biu biu ~
好吧回到博文的主题中来,这次我们说说“Weak/Strong Dance”
在block中解决循环引用要追寻的2011 WWDC Session #322 当时惊艳的代码是这样的:
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:_observer];
}
- (void)loadView
{
[super loadView];
__weak TestViewController *wself = self;
_observer = [[NSNotificationCenter defaultCenter] addObserverForName:@"testKey"
object:nil
queue:nil
usingBlock:^(NSNotification *note) {
TestViewController *sself = wself;
[sself dismissModalViewControllerAnimated:YES];
}];
}
或者可以看看AFNetWorking是这样用block的:
__weak __typeof(self)weakSelf = self;
AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) {
__strong __typeof(weakSelf)strongSelf = weakSelf;
strongSelf.networkReachabilityStatus = status;
if (strongSelf.networkReachabilityStatusBlock) {
strongSelf.networkReachabilityStatusBlock(status);
}
};
我们都很熟悉Objective-C中的“weak/strong dance”,但是寂寞无聊的我突然就很想知道Swift语言中该怎么做呢?是否存在传说中的最佳实践呢?
好啦,翻译开始!
首先,我们祭出一个在闭包中没有使用weak的引用导致的循环引用的例子
class C {
let name: String
var block: (() -> Void)?
init(name: String) {
self.name = name
block = {
self.doSomething()
}
}
deinit { print("Destroying \(name)") }
func doSomething() { print("Doing something for \(name)") }
}
var c = C(name: "one")
print(c.name)
c = C(name: "two")
print(c.name)
输出
one
two
这是一个巨基础又明显的循环引用的例子,self -> block ->self
所以,deinit
方法是绝逼不会被执行的,即使你把 c
重新指向nil
或者其他的实例,c
也不会被销毁,这就是顽固又调皮的循环引用了,尤其是当你把c
指向nil
之后,这个对象你就再也引用不了了,它就静静的躺在堆内存里面,遗世而独立,然后你就堆内存泄露了,然后你就淡淡的忧伤从下体传来 ~ ~没有然后了
其实Swift中闭包的参数列表(Capture List) 已经能够很好的让你获取一个weak self
来避免循环引用了,但这还达不到我们的要求,只有weak
是构不成“weak/strong dance”滴。
使用闭包参数列表
class C {
let name: String
var block: (() -> Void)?
init(name: String) {
self.name = name
block = { [weak self] in // <-- 这里做出一些改变
self?.doSomething()
}
}
deinit { print("Destroying \(name)") }
func doSomething() { print("Doing something for \(name)") }
}
var c = C(name: "one")
print(c.name)
c = C(name: "two")
print(c.name)
输出
one
Destroying one
two
这样就没有循环引用啦~
在闭包中使用self
使用 [weak self]
有一个细节,就是self
在闭包中会变成Optional
从上面的代码中self?.doSomething()
就可以看出来了。
但是如果你在这个闭包中狂轰乱炸的使用self?
(多次使用self?
),问题就来了,因为这个self?
是一个弱引用的,那么你没法确定在这个闭包中所有的self?
操作都能执行完毕,毕竟若引用的self
可能随时都挂掉,然后怒举一个栗子:
图4
class C {
deinit { println("Destroying C") }
func log(msg: String) { println(msg) }
func doClosure() {
dispatch_async(dispatch_get_global_queue(0, 0)) { [weak self] in
self?.log("before sleep")
usleep(500)
self?.log("after sleep")
}
}
}
var c: C? = C() // Optional, so we can set it to nil
c?.doClosure()
dispatch_async(dispatch_get_global_queue(0, 0)) {
usleep(100)
c = nil // This will dealloc c
}
dispatch_main()
输出
before sleep
Destroying C
小提示:当然一般来说在
dispatch_async()
中你不必担心会有循环引用,因为self并不会持有dispatch_async()
的block,所以上述的代码中并不会真的导致循环引用,如果你的闭包并不是很注重结果的,那么self
为nil
闭包就不会再执行,这个还是挺有用的。
上述的代码中不会打印after sleep
,因为self?
在打印这句话之前已经挂掉了。
通常这种无根之源的bug可以把你整的半死。所以通常遇到这种闭包中多次试用self?
的操作的时候,一般会把self?
变为又粗又壮的strong self
,(博主也是又粗又壮的,捂脸~~)这就是传说中的“weak/strong dance”,这个舞蹈,额,什么鬼,为什么把这个技术叫做dance啊,我觉得叫做美队解禁奥义技
还不错,妇联里面的美国队长也是由weak
变成strong
的嘛~,好吧,扯太远,菊花都扯疼了,我们这是在技术翻译呢!要严肃!要尊重原作者!我们还是叫dance吧,有了这个dance之后呢,我们就能确保一旦闭包被执行,self
就不会为nil
。
但是,就像文章开头说的,对于在Swift
的“weak/strong dance”中变回strong的这部分的最佳实践是什么我也不是很确定的。。。
获取强引用的一些想法
使用可选绑定if let
func doClosure() {
dispatch_async(dispatch_get_global_queue(0, 0)) { [weak self] in
if let strongSelf = self { // <-- 这里就是精髓了
strongSelf.log("before sleep")
usleep(500)
strongSelf.log("after sleep")
}
}
}
// or in Swift 2, using `guard let`:
dispatch_async(dispatch_get_global_queue(0, 0)) { [weak self] in
guard let strongSelf = self else { return } // <-- 这里就是精髓了
strongSelf.log("before sleep")
usleep(500)
strongSelf.log("after sleep")
}
输出
before sleep
after sleep
Destroying C
优点:
- 很明显的看出整个操作的流程
- 在闭包中拿到了非可选的本地变量
缺点:
- 很不幸的是我们不能
if let self = self
,因为self
是常量,不可变,这样的话我们就只能if let strongSelf = self
在闭包的作用域中都要使用丑陋的strongSelf
了。 - 在swift的闭包中,如果你没有试用
strongSelf
而是使用了self
,这样编译器会警告!因为这个时候self
是可选的嘛,相比较OC中,就不会警告了。(这句话哥读了21遍,为什么觉得这个不是缺点呢)
使用withExtendedLifetime
在Swift的标准库中有一个函数:withExtendedLifetime()
,感觉就像Apple这个金鱼佬故意诱导我们使用这个函数来实现“weak/strong dance”。
/// Evaluate `f()` and return its result, ensuring that `x` is not
/// destroyed before f returns.
func withExtendedLifetime<T, Result>(x: T, @noescape _ f: () -> Result) -> Result
那就试试
func doClosure() {
dispatch_async(dispatch_get_global_queue(0, 0)) { [weak self] in
withExtendedLifetime(self) {
self!.log("before sleep")
usleep(500)
self!.log("after sleep")
}
}
}
优点:
- 闭包中不再需要使用丑陋的
strongSelf
了
缺点:
self
还是他妈可选的,调用方法什么的还是要!?,还是要解包,博主突然想起自己的一个技能:单手解,呵呵
自定义一个withExtendedLifetime()
这个方法是 @jtbandes 这哥们想的,大概会是这样:
extension Optional {
func withExtendedLifetime(body: Wrapped -> Void) {
if let strongSelf = self {
body(strongSelf)
}
}
}
// Then:
func doClosure() {
dispatch_async(dispatch_get_global_queue(0, 0)) { [weak self] () -> Void in
self.withExtendedLifetime {
$0.log("before sleep")
usleep(500)
$0.log("after sleep")
}
return
}
}
优点:
- Follows naming conventions set by the standard library.(原文) 感觉没优点~
缺点:
strongSelf
变成了使用$0
,博主认为哦,还是很丑陋,并且可读性更差了- In this case, I had to add some extra type info to the
dispatch_async()
closure. I’m not totally sure why. 不知道他说什么鬼~
翻译至此结束了
后记
关于Swift中 “Weak/Strong Dance”,中的Weak部分,大家可以参阅喵大的这篇文章 内存管理,weak 和 unowned 。
用回了一个多礼拜的Swift真是感受颇多,虽然Xcode在写Swift就像纯文本编辑器一样,但是我还是想说一句:Swift真™安全!想crash都难咯。
还有 Happy Christmas!
收笔,走人。
Swift中的Weak Strong Dance的更多相关文章
- iOS开发笔记15:地图坐标转换那些事、block引用循环/weak–strong dance、UICollectionviewLayout及瀑布流、图层混合
1.地图坐标转换那些事 (1)投影坐标系与地理坐标系 地理坐标系使用三维球面来定义地球上的位置,单位即经纬度.但经纬度无法精确测量距离戒面积,也难以在平面地图戒计算机屏幕上显示数据.通过投影的方式可以 ...
- Swift内存管理、weak和unowned以及两者区别(如何使用Swift 中的weak与unowned?)
Swift 是自动管理内存的,这也就是说,我们不再需要操心内存的申请和分配. 当我们通过初始化创建一个对象时,Swift 会替我们管理和分配内存.而释放的原则遵循了自动引用计数 (ARC) 的规则:当 ...
- iOS开发 -------- Block技术中的weak - strong
一 Block是什么? 我们使用^运算符来声明一个Block变量,而且在声明完一个Block变量后要像声明普通变量一样,后面要加; 声明Block变量 int (^block)(int) = NULL ...
- swift 中使用weak self
request.responseString(encoding: NSUTF8StringEncoding) {[weak self] (res) -> Void in if let stron ...
- OC中@property属性关键字的使用(assign/weak/strong/copy)
OC中@property属性关键字的使用(assign/weak/strong/copy) 一.assign 用于 ‘基本数据类型’.‘枚举’.‘结构体’ 等非OC对象类型 eg:int.bool等 ...
- iOS开发之OC与swift开发混编教程,代理的相互调用,block的实现。OC调用Swift中的代理, OC调用Swift中的Block 闭包
本文章将从两个方向分别介绍 OC 与 swift 混编 1. 第一个方向从 swift工程 中引入 oc类 1. 1 如何在swift的类中使用oc类 1.2 如何在swift中实现oc的代理 ...
- 初识Swift中的值和引用,循坏引用、代理的注意点
1.0 在Swift中分有值类型和引用类型 Int .String . 结构体和枚举都属于值类型, 将值类型传递给方法是,将在内存中创建其副本,并传递这个副本:这样我们就可以随心所欲修改它,而不用担心 ...
- Swift中的可选链与内存管理(干货系列)
干货之前:补充一下可选链(optional chain) class A { var p: B? } class B { var p: C? } class C { func cm() -> S ...
- 【iOS】在Swift中使用JSONModel
前言 首先所有的Model还是使用oc来写——看到这一句是不是想关网页了- - #,在swift里面直接写一直报错所以就将就用oc来写了,这里主要是分享一下搭配Alamofire使用的经验. 声明 欢 ...
随机推荐
- 基于Bind实现的DNS正反向解析及主从DNS的配置
一.什么是DNS? 1.1 简单的理解,Domain Name System,是互联网一项核心的服务,他作为一个桥梁可以将域名和IP地址相互因素的一个分布式数据库,能够使人更加方便的访问互联网,而不用 ...
- Qt Pro file
Qt keys qmake Manual Building Common Project Types #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ...
- Orientation of phone Image
相机拍摄的图像方向问题 Description 很多时候,我们习惯把手机相机拍摄的图像在电脑上面查看.有的时候在手机上面看图像是正的,可是电脑端查看是反的:有的时候手机和电脑都是反的:有的时候都是正的 ...
- 特性(property)
6.4 特性(property) 1 什么是特性property property是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值 import math class Circle: def ...
- sql时间戳转日期格式
FROM_UNIXTIME(ctime, '%Y-%m-%d %H:%i:%s')
- vue生命周期小笔记
一张图谨记vue每个生命周期的获取状态 beforecreate :可以在这加个loading事件 created :在这结束loading,还做一些初始化,实现函数自执行 mounted : 在这发 ...
- windows下Apache的虚拟主机配置
1.Apache虚拟主机: 在Apache上有关于虚拟主机的具体说明,具体可以参考Apache手册,这里简单的说一下虚拟主机主要分为两种: 1.基于主机名的虚拟主机(一个IP地址,多个网站) 2.基于 ...
- loadrunner - 问题汇总及解决方案(持续更新)
1.在此界面点击Run Load Tests提示: ”Can not save the license information because access to the registry is de ...
- struct pollfd
struct pollfd 2010年04月15日 星期四 下午 03:59 int poll (struct pollfd *fds, size_t nfds , int timeout); str ...
- 用U盘安装Ubuntu主系统
用U盘安装Ubuntu主系统 0. 学习Ubuntu系统的的动机 ubuntu系统的内核是linux系统,通过Ubuntu的学习,掌握lInux服务器搭建!!!! 硬件要求:闲置的笔记本 + U盘 ...