Swift中的循环引用测试验证

来源:互联网 发布:淘宝无线地址转换器 编辑:程序博客网 时间:2024/06/06 00:29

文中代码运行环境:
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 UIKitclass 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.deinitObjA.deinit

swift中还有一种常见的循环引用是在闭包中, 因为闭包等同于执行一段代码片段, 因为闭包的执行特性, 该闭包的调用时机不一定是在声明的时候就会执行到, 可能会在某个消息触发后, 执行闭包中的内容, 所以闭包在这种机制下, 可能就设计成, 闭包中所使用自身资源时(也就是self中的属性和方法)都是一种强引用关系, 以保证闭包在调用时, 对象是未被系统ARC自动释放的. 测试demo如下:

////  ViewController.swift//  ReferenceLoop////  Created by linx on 16/3/8.//  Copyright © 2016年 linx. All rights reserved.//import UIKitclass 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.

0 0
原创粉丝点击