weak的使用

转自:http://www.jianshu.com/p/e30fd63c8e0f


什么是循环引用

Swift 是自动管理内存的,这也就是说,我们不再需要操心内存的申请和分配。当我们通过初始化创建一个对象时,Swift 会替我们管理和分配内存。而释放的原则遵循了自动引用计数 (ARC) 的规则:当一个对象没有引用的时候,其内存将会被自动回收。这套机制从很大程度上简化了我们的编码,我们只需要保证在合适的时候将引用置空 (比如超过作用域,或者手动设为 nil 等),就可以确保内存使用不出现问题。
但是,所有的自动引用计数机制都有一个从理论上无法绕过的限制,那就是循环引用 (retain cycle) 的情况。
假设我们有两个类 A 和 B, 它们之中分别有一个存储属性持有对方:

class A {
   let b: B
   init() {
       b = B()
       b.a = self
   }

   deinit {
       println("A deinit")
   }
}
class B {
   var a: A? = nil
   deinit {
       println("B deinit")
  }
}

在 A 的初始化方法中,我们生成了一个 B 的实例并将其存储在属性中。然后我们又将 A 的实例赋值给了 b.a。这样 a.b 和 b.a 将在初始化的时候形成一个引用循环。现在当有第三方的调用初始化了 A,然后即使立即将其释放,A 和 B 两个类实例的 deinit 方法也不会被调用,说明它们并没有被释放。

func application(application: UIApplication!, didFinishLaunchingWithOptions launchOptions: NSDictionary!) 
             -> Bool {

    // Override point for customization after application launch.

    var obj: A? = A()
    obj = nil
    // 内存没有释放

    return true
}
在 Swift 里防止循环引用

为了防止这种人神共愤的悲剧的发生,我们必须给编译器一点提示,表明我们不希望它们互相持有。一般来说我们习惯希望 "被动" 的一方不要去持有 "主动" 的一方。在这里 b.a 里对 A 的实例的持有是由 A 的方法设定的,我们在之后直接使用的也是 A 的实例,因此认为 b 是被动的一方。可以将上面的 class B 的声明改为:

class B {
    weak var a: A? = nil
    deinit {
        println("B deinit")
    }
}

在 var a 前面加上了 weak,向编译器说明我们不希望持有 a。这时,当 obj 指向 nil 时,整个环境中就没有对 A 的这个实例的持有了,于是这个实例可以得到释放。接着,这个被释放的实例上对 b 的引用 a.b 也随着这次释放结束了作用域,所以 b 的引用也将归零,得到释放。添加 weak 后的输出:

A deinit
B deinit

总体说下来,我理解为在声明的时候,被动一方需要加上weak


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
_SIZE; j++) { if (map[i][j] == -1) { g.setColor(Color.BLACK); g.fillRect(i * CELL_SIZE, j * CELL_SIZE, CELL_SIZE, CELL_SIZE); } } } } /** * 绘weak_ptr一般不能单独使用,它需要和shared_ptr一起使用。因为weak_ptr本身只是一个指向制蛇 */ private void drawSnake(Graphics g) { for (int i = 0; i < snake.size(); i对象的弱引用,并不会增加对象的引用计数,也不会保证所指对象的生命周期。++) { int x = snake.get(i).x; int y = snake.get(i).y; g.setColor(Color.GREEN); g.fillRect(x * CELL_SIZE, y * CELL_SIZE, CELL_SIZE, CELL_SIZE); } } /** * 绘制食如果只使用weak_ptr来管理资源,当所指对象被释放时,就会产生悬空指针,从而物 */ private void drawFood(Graphics g) { if (food != null) { int x = food.x; 引发程序崩溃或者内存泄漏等问题。 weak_ptr通常是用来协助shared_ptr进行资源管理, int y = food.y; g.setColor(Color.RED); g.fillRect(x * CELL_SIZE, y * CELL_SIZE, CELL_SIZE, CELL特别是用来解决shared_ptr的循环引用问题。当两个或多个shared_ptr相互引用时_SIZE); } } /** * 移动蛇 */ private void moveSnake() { // 记录蛇,会形成一个循环引用,导致所指对象无法被释放,从而产生内存泄漏尾 Point tail = snake.get(snake.size() - 1); // 移动蛇身 for (int i =。这时候可以使用weak_ptr来破除循环引用,将其中一个shared_ptr转换为weak_ptr,使得 snake.size() - 1; i > 0; i--) { snake.get(i).x = snake.get(i - 1).它不再持有对象的所有权,从而避免内存泄漏。 另外需要注意的是,weak_ptr不能x; snake.get(i).y = snake.get(i - 1).y; } // 移动蛇头 switch (direction) { case KeyEvent.VK_LEFT: snake.get(0).x--; break; case KeyEvent.VK_RIGHT: 直接访问所指对象,而需要用lock()方法将其转换为shared_ptr后,才能访问所指 snake.get(0).x++; break; case KeyEvent.VK_UP: snake.get(0).y--; break; 对象。如果所指对象已经被释放,lock()方法将返回一个空shared_ptr,表示对象不存在。 因此, case KeyEvent.VK_DOWN: snake.get(0).y++; break; } // 判断是否吃到食物 一般情况下,我们都需要将weak_ptr和shared_ptr结合使用,通过shared_ptr来管理资源的生命周期,通过weak_ptr来避免循环引用问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值