使用函数作为闭包会保留self吗?

6
我遇到了追踪保留循环的问题。我认为这与我的事件订阅方式有关。伪代码如下:
override func viewDidLoad() {
   func handleEvent() {
     self.doSomething()
   }

   subscribe("eventName", block: handleEvent)
}

deinit {
    unsubscribe("eventName")
} 

这样做会导致self或我的ViewController出现保留循环吗?如果是的话,我该如何解决?如果我使用闭包,我可以使用[weak self],但由于我传递的是一个函数,是否有任何办法使用类似[weak self]的方式?


那个问题是关于闭包的,我在问如何使用函数作为闭包。无法在函数中使用weak self。 - Roland Rabien
读错你的代码了...我的错。等一下。 - GetSwifty
1
我们需要知道subscribe将传递的闭包用于何种目的,以确定是否存在保留循环。如果它只是调用闭包而不存储它或在另一个闭包中捕获它,然后将其存储(在self上或者self具有强引用的某个对象上),那么就没有保留循环。如果确实进行了存储/捕获,那么就存在保留循环。如果您能将代码最简化 [mcve],将会非常有帮助。 - Hamish
我在追踪一个保留循环方面遇到了麻烦。Instruments 告诉你什么? - matt
2个回答

3

简而言之,你的代码确实保留了一个引用。(handleEvent->viewDidLoad->self),http://blog.xebia.com/function-references-in-swift-and-retain-cycles/提供了一些避免这个问题的通用策略。我的建议是创建一个函数引用,而不是声明一个函数:

let eventHandler: () -> () = { [weak self] in 
    self?.doSomething() 
}
subscribe("eventName", block: eventHandler)

1
基本上这就是我要说的。对于 OP 的语法问题,答案是匿名函数(包括分配为变量值的函数)允许捕获列表语法,而命名函数声明则不允许。但是,无法确定 OP 的情况是否会导致保留循环;OP 没有提供足够的信息。它取决于 subscribe 做什么以及其 block 参数的命运是什么(正如 Hamish 所指出的那样)- 而 OP 已经隐瞒了那些信息。 - matt
@matt 显然这是事件处理,需要在deinit中取消订阅,暗示该事件可能/应该在实例消失后触发。存储函数引用将阻止这种情况的发生。 - GetSwifty
@GetSwifty,链接损坏了。 - LGP

3
如果您在类内部引用属性或方法,则会创建一个保留循环。
class SomeClass {
  val a: (block: (() -> ()) -> ()) = ...

  func run() {
     func b() {
        print("Hello, World!")
     }

     func c() {
        self.someMethod()
     }

     func d() { [weak self] 
        self?.someMethod()
     }

     a(block: b) // no retain cycle
     a(block: c) // retain cycle
     a(block: d) // no retain cycle
  }

  func someMethod() {
     print("Hello, World!")
  }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接