Swift中的循环引用测试验证
2016-03-08 16:17
323 查看
文中代码运行环境:
swift版本: 2.1.1
Xcode版本: 7.2.1 (7C1002)
系统版本: OS X 10.11.3 (15D21)
ARC: Automatic Reference Counting
MRC: Mannul Reference Counting
swift语言在设计时, 使用LLVM编译器提供了ARC循环计数引用, 减轻了程序员在早期OC时代MRC需要手动控制引用计数的负担.
最简单的描述循环引用的模型是创建两个简单的类, 其中持有一个指向B类对象的成员, 同时B类持有一个指向A类对象的成员. demo如下:
运行输出结果:
对比参照组使用同上例子, 并取消ObjA与ObjB对象的互相指向关系
修改ViewController.viewDidLoad实现代码:
打印输出结果:
swift中还有一种常见的循环引用是在闭包中, 因为闭包等同于执行一段代码片段, 因为闭包的执行特性, 该闭包的调用时机不一定是在声明的时候就会执行到, 可能会在某个消息触发后, 执行闭包中的内容, 所以闭包在这种机制下, 可能就设计成, 闭包中所使用自身资源时(也就是self中的属性和方法)都是一种强引用关系, 以保证闭包在调用时, 对象是未被系统ARC自动释放的. 测试demo如下:
运行输出如下:
此时, 修改ViewController.viewDidLoad函数实现, 增加ObjA对象在闭包中引用到自身对象的实例方法.
运行输出结果如下:
在第一种循环引用的例子中, ObjA与ObjB相互持有对象引用的对象, 解决方法是在其中任何一方声明变量时, 增加weak关键字, 即可解决对象互相引用的问题.
在第二种循环引用的例子中, 解决闭包循环引用自身的问题方法是, 在声明闭包时, 增加[weak self]声明, 以表示不增加对自身self的引用计数, 这也是网上提高的比较多的一种方法, 自己在实际写这篇博客的时候, 发现也可以将self作为对象, 以参数的方式传入到闭包中, 这时, 不加上[weak self]也是可行的, 只是这种方法在实际使用中可能会徒增繁琐.两种闭包避免循环引用的代码如下:
由上述闭包循环引用的第二种解决方式中引申出一个新的问题, 一个新的小白问题, 对象作为函数参数时, 对引用计数是否有影响. 如何影响?
使用代码进行验证, 设计测试代码:
打印输出结果:
结果证明, abc方法中的obj.output()方法能够顺利执行,经过单步调试验证, 进入到abc函数, s1执行前, self.value和obj指向同一个内存地址, 满足我们实验的前提条件, s1执行后, s2执行前, self.value的值是nil, obj的值不变, 继续单步执行, obj.output()顺利执行, debug输出”ObjA.output()”, 最后ObjA.deinit执行.
经过如上实验, 得出大致结论, 因为类对象是传引用方式, 所以会增加其引用计数, 以确保在函数执行过程中, 持有该对象且对象不会被ARC机制释放其资源, 函数执行完毕后, 参数的引用计数减1.
swift版本: 2.1.1
Xcode版本: 7.2.1 (7C1002)
系统版本: OS X 10.11.3 (15D21)
ARC: Automatic Reference Counting
MRC: Mannul Reference Counting
swift语言在设计时, 使用LLVM编译器提供了ARC循环计数引用, 减轻了程序员在早期OC时代MRC需要手动控制引用计数的负担.
最简单的描述循环引用的模型是创建两个简单的类, 其中持有一个指向B类对象的成员, 同时B类持有一个指向A类对象的成员. demo如下:
// // ViewController.swift // ReferenceLoop // // Created by linx on 16/3/8. // Copyright © 2016年 linx. All rights reserved. // import UIKit class ObjA { var link: ObjB? init() { print("ObjA.init()") } deinit { print("ObjA.deinit") } } class ObjB { var link: ObjA? init() { print("ObjB.init()") } deinit { print("ObjB.deinit") } } class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. let oa = ObjA() let ob = ObjB() oa.link = ob ob.link = oa } }
运行输出结果:
ObjA.init() ObjB.init()
对比参照组使用同上例子, 并取消ObjA与ObjB对象的互相指向关系
修改ViewController.viewDidLoad实现代码:
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. let oa = ObjA() let ob = ObjB() } }
打印输出结果:
ObjA.init() ObjB.init()
ObjB.deinit
ObjA.deinit
swift中还有一种常见的循环引用是在闭包中, 因为闭包等同于执行一段代码片段, 因为闭包的执行特性, 该闭包的调用时机不一定是在声明的时候就会执行到, 可能会在某个消息触发后, 执行闭包中的内容, 所以闭包在这种机制下, 可能就设计成, 闭包中所使用自身资源时(也就是self中的属性和方法)都是一种强引用关系, 以保证闭包在调用时, 对象是未被系统ARC自动释放的. 测试demo如下:
// // ViewController.swift // ReferenceLoop // // Created by linx on 16/3/8. // Copyright © 2016年 linx. All rights reserved. // import UIKit class ObjA { var block: () -> () = { self.abc() } init() { print("ObjA.init()") } deinit { print("ObjA.deinit") } func abc() { print("ObjA.abc()") } } class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. let oa = ObjA() } }
运行输出如下:
ObjA.init() ObjA.deinit
此时, 修改ViewController.viewDidLoad函数实现, 增加ObjA对象在闭包中引用到自身对象的实例方法.
class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. let oa = ObjA() ob.block() } }
运行输出结果如下:
ObjA.init()
在第一种循环引用的例子中, ObjA与ObjB相互持有对象引用的对象, 解决方法是在其中任何一方声明变量时, 增加weak关键字, 即可解决对象互相引用的问题.
在第二种循环引用的例子中, 解决闭包循环引用自身的问题方法是, 在声明闭包时, 增加[weak self]声明, 以表示不增加对自身self的引用计数, 这也是网上提高的比较多的一种方法, 自己在实际写这篇博客的时候, 发现也可以将self作为对象, 以参数的方式传入到闭包中, 这时, 不加上[weak self]也是可行的, 只是这种方法在实际使用中可能会徒增繁琐.两种闭包避免循环引用的代码如下:
//[weak self] var block: (Void) -> Void { [weak self] in self?.abc() // 因为采用弱引用self, 闭包严格上来说, 不拥有self对象, 所以使用self?.abc(),而不是self!.abc() } //将self作为参数传入闭包中 var block: (obj: ObjA) -> Void { (oa) in oa.abc() } // 调用时 let oa = ObjA() oa.block(obj: oa)
由上述闭包循环引用的第二种解决方式中引申出一个新的问题, 一个新的小白问题, 对象作为函数参数时, 对引用计数是否有影响. 如何影响?
使用代码进行验证, 设计测试代码:
var value: ObjA? = ObjA() func abc(obj: ObjA) { obj.output() // output()是ObjA类的一个实例方法 // 如果在调用obj.output之前, 将obj的引用计数减1, 那么obj.output()方法能否顺利执行, 以此检验函数参数是否会影响对象的引用计数问题 } func abc(obj: ObjA) { self.value = nil // s1. obj.output() // s2. }
打印输出结果:
ObjA.init() ObjA.output() ObjA.deinit
结果证明, abc方法中的obj.output()方法能够顺利执行,经过单步调试验证, 进入到abc函数, s1执行前, self.value和obj指向同一个内存地址, 满足我们实验的前提条件, s1执行后, s2执行前, self.value的值是nil, obj的值不变, 继续单步执行, obj.output()顺利执行, debug输出”ObjA.output()”, 最后ObjA.deinit执行.
经过如上实验, 得出大致结论, 因为类对象是传引用方式, 所以会增加其引用计数, 以确保在函数执行过程中, 持有该对象且对象不会被ARC机制释放其资源, 函数执行完毕后, 参数的引用计数减1.
相关文章推荐
- 深入理解PHP之匿名函数
- Apple Swift学习教程
- 最后一次说说闭包
- 介绍 Fedora 上的 Swift
- Ruby中使用Block、Proc、lambda实现闭包
- LUA中的闭包(closure)浅析
- Lua中的闭包学习笔记
- C#中函数的创建和闭包的理解
- 深入理解javascript作用域和闭包
- javascript作用域和闭包使用详解
- 谈谈JavaScript中的函数与闭包
- 细品javascript 寻址,闭包,对象模型和相关问题
- JavaScript中的闭包原理分析
- 浅谈javascript中的闭包
- 学习javascript的闭包,原型,和匿名函数之旅
- javascript 闭包详解
- JavaScript 匿名函数和闭包介绍
- JavaScript 闭包深入理解(closure)
- 深入理解JavaScript 闭包究竟是什么