2016-07-07 4 views
0

ich meine NSOperation Unterklasse mit einem Initialisierer enthält, einen Block zu erstellen:„Variable innerhalb des eigenen Anfangswert verwendet“ bei dem Versuch, schwach in der Initialisierung ermöglichen

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

Leider funktioniert das nicht, wie ich arbeite immer bekomme die Fehlermeldung Variable used within its own initial value, die für mich Sinn macht, aber ... wie kann ich die concurrentOperation als schwache Referenz innerhalb erreichen?

+0

http://stackoverflow.com/a/30523666/3141234 – Alexander

+0

es scheint, als ob ich tun ein Zyklus beibehalten, wie der Betrieb nicht freigegeben bekommt - mein Kommentar über die Antwort sehen von @AMomchilov – swalkner

+0

Rückblickend ist das Problem, dass Sie anscheinend versuchen, die Operation in der Schließung. Die Lösung besteht darin, dass die Operation selbst einen Verweis auf sich selbst als Parameter übergibt. Siehe die überarbeitete Diskussion in meiner Antwort unten. – Rob

Antwort

0

Der Code (wie Sie es geschrieben haben) bringt ein "Huhn oder das Ei" -Szenario. Versuchen Sie folgendes:

var concurrentOperation: ((foo) -> bar)! //insert correct type annocation here 

concurrentOperation = ABOConcurrentOperation { 
    //use concurrentOperation here 
} 
+0

aber dann wird der Vorgang nicht freigegeben, wenn ich nur 'concurrentOperation' dort verwende ... – swalkner

+0

@swalkner Nicht unbedingt. Wenn die Schließung endet, ohne sich selbst erneut aufzurufen, sollte ARC die Referenz bereinigen. Testen Sie es jedoch. – Alexander

1

Wenn Sie einen Verweis auf den Betrieb im Block benötigen, können Sie es als Parameter an die Schließung passieren sollte und dann brauchen Sie keinen weak Referenz. Die Referenz wird automatisch aufgelöst, wenn die Schließung abgeschlossen ist. Betrachten wir zum Beispiel die folgende:

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) 

Und ich diesen Verschluss definiert ein Standard-Verschluss zu sein:

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. 

Wenn Sie Ihre Unterklasse, die etwas in deinit druckt:

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

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

Sie werden sehen, was passiert:

2016.07.07 21.20.54 0.000: Anfahrvorgang
2016.07.07 21.21.01 0.000: Schlichten
deinit

Zu Ihrer Information, das ist die Probe AsynchronousOperation Klasse verwendet oben:

/// 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 
    } 
}