【问题标题】:"Variable used within its own initial value" when trying to enable weak in initialization尝试在初始化中启用弱时“在其自己的初始值内使用的变量”
【发布时间】:2016-07-08 07:41:08
【问题描述】:

我正在使用包含块的初始化程序创建我的 NSOperation 子类:

let concurrentOperation = ABOConcurrentOperation {[weak weakOp = concurrentOperation] in
    ...
}

不幸的是,这不起作用,因为我总是收到错误消息 Variable used within its own initial value,这对我来说很有意义,但是......我怎样才能实现将 concurrentOperation 作为内部弱引用?

【问题讨论】:

  • 似乎我确实得到了一个保留周期,因为操作没有被释放 - 请参阅我对@AMomchilov 答案的评论
  • 回想起来,问题在于您显然是在尝试引用闭包中的操作。解决此问题的方法是让操作本身将对自身的引用作为参数传递。请参阅下面我的答案中的修订讨论。

标签: memory-management swift2


【解决方案1】:

如果您需要对块中操作的引用,您应该将其作为参数传递给闭包,然后您就不需要weak 引用。闭包完成后,它将自动解析引用。例如,考虑以下情况:

let queue = NSOperationQueue()

let concurrentOperation = ABOConcurrentOperation() { operation in
    print("\(NSDate()): starting operation")
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 5 * Int64(NSEC_PER_SEC)), dispatch_get_main_queue()) {
        print("\(NSDate()): finishing operation")
        operation.completeOperation()
    }
}

queue.addOperation(concurrentOperation)

而且,我将该闭包定义为标准闭包:

private var block: ((AsynchronousOperation) -> ())?    // FYI, I use optional in case the caller accidentally introduces a strong reference cycle, I can resolve that when the operation completes.

如果您的子类在deinit 中打印某些内容:

/// a subclass that will just confirm that `deinit` is called

class ABOConcurrentOperation: AsynchronousBlockOperation {
    deinit {
        print("deinit")
    }
}

你会看到发生了什么:

2016-07-07 21:20:54 +0000:开始运行
2016-07-07 21:21:01 +0000:完成操作
取消初始化

供您参考,这是上面使用的示例AsynchronousOperation 类:

/// Asynchronous Operation base class
///
/// This class performs all of the necessary KVN of `isFinished` and
/// `isExecuting` for a concurrent `NSOperation` subclass. So, to developer
/// a concurrent NSOperation subclass, you instead subclass this class which:
///
/// - must override `main()` with the tasks that initiate the asynchronous task;
///
/// - must call `completeOperation()` function when the asynchronous task is done;
///
/// - optionally, periodically check `self.cancelled` status, performing any clean-up
///   necessary and then ensuring that `completeOperation()` is called; or
///   override `cancel` method, calling `super.cancel()` and then cleaning-up
///   and ensuring `completeOperation()` is called.

public class AsynchronousOperation : NSOperation {

    override public var asynchronous: Bool { return true }

    private let stateLock = NSLock()

    private var _executing: Bool = false
    override private(set) public var executing: Bool {
        get {
            return stateLock.withCriticalScope { _executing }
        }
        set {
            willChangeValueForKey("isExecuting")
            stateLock.withCriticalScope { _executing = newValue }
            didChangeValueForKey("isExecuting")
        }
    }

    private var _finished: Bool = false
    override private(set) public var finished: Bool {
        get {
            return stateLock.withCriticalScope { _finished }
        }
        set {
            willChangeValueForKey("isFinished")
            stateLock.withCriticalScope { _finished = newValue }
            didChangeValueForKey("isFinished")
        }
    }

    /// Complete the operation
    ///
    /// This will result in the appropriate KVN of isFinished and isExecuting

    public func completeOperation() {
        if executing {
            executing = false
        }

        if !finished {
            finished = true
        }
    }

    override public func start() {
        if cancelled {
            finished = true
            return
        }

        executing = true

        main()
    }

    override public func main() {
        fatalError("subclasses must override `main`")
    }
}

/// Asynchronous Operation base class
///
/// This class lets you perform asynchronous block operation. Make sure that the
/// the provided `block` calls `completeOperation`, or else this operation will 
/// never finish.

public class AsynchronousBlockOperation : AsynchronousOperation {

    private var block: ((AsynchronousOperation) -> ())?

    init(block: (AsynchronousOperation) -> ()) {
        self.block = block
        super.init()
    }

    override public func main() {
        block?(self)
    }

    override public func completeOperation() {
        block = nil

        super.completeOperation()
    }

}

extension NSLock {

    /// Perform closure within lock.
    ///
    /// An extension to `NSLock` to simplify executing critical code.
    ///
    /// - parameter block: The closure to be performed.

    func withCriticalScope<T>(@noescape block: Void -> T) -> T {
        lock()
        let value = block()
        unlock()
        return value
    }
}

【讨论】:

    【解决方案2】:

    代码(正如您编写的那样)提出了“先有鸡还是先有蛋”的场景。试试这个:

    var concurrentOperation: ((foo) -> bar)! //insert correct type annocation here
    
    concurrentOperation = ABOConcurrentOperation {
        //use concurrentOperation here
    }
    

    【讨论】:

    • 但是如果我在那里使用concurrentOperation,那么操作不会被释放......
    • @swalkner 不一定。如果闭包结束而没有再次调用自己,ARC 应该清理引用。但是,请务必对其进行测试。
    猜你喜欢
    • 2021-06-12
    • 1970-01-01
    • 2016-06-30
    • 2017-04-19
    • 1970-01-01
    • 2020-05-09
    • 2020-11-16
    • 1970-01-01
    相关资源
    最近更新 更多