gpt4 book ai didi

ios - [NSBlockOperation addExecutionBlock :]: blocks cannot be added after the operation has started executing or finished

转载 作者:搜寻专家 更新时间:2023-10-31 22:20:46 26 4
gpt4 key购买 nike

我试图在完成或取消后重新启动 NSBlockOperation,但出现错误?任何人都知道错误在哪里?谢谢

let imageURLs = ["http://www.planetware.com/photos-large/F/france-paris-eiffel-tower.jpg",
"http://adriatic-lines.com/wp-content/uploads/2015/04/canal-of-Venice.jpg",
"http://algoos.com/wp-content/uploads/2015/08/ireland-02.jpg",
"http://bdo.se/wp-content/uploads/2014/01/Stockholm1.jpg"]

class Downloader {

class func downloadImageWithURL(url:String) -> UIImage! {

let data = NSData(contentsOfURL: NSURL(string: url)!)
return UIImage(data: data!)
}
}

class ViewController: UIViewController {

@IBOutlet weak var imageView1: UIImageView!
var indeX = 0

let operation1 = NSBlockOperation()
var queue = NSOperationQueue()

override func viewDidLoad() {
super.viewDidLoad()
}
@IBAction func didClickOnStart(sender: AnyObject) {
queue = NSOperationQueue()

operation1.addExecutionBlock { () -> Void in

for _ in imageURLs {
if !self.operation1.cancelled {
let img1 = Downloader.downloadImageWithURL(imageURLs[self.indeX])
NSOperationQueue.mainQueue().addOperationWithBlock({
self.imageView1.image = img1

print("indeX \(self.indeX)")
self.indeX++
})

}
}
}
queue.addOperation(operation1)
}

@IBAction func didClickOnCancel(sender: AnyObject) {
self.queue.cancelAllOperations()
print(operation1.finished)
}
}

Output

indeX 0
false
indeX 1
2016-07-20 02:00:26.157 ConcurrencyDemo[707:15846] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[NSBlockOperation addExecutionBlock:]: blocks cannot be added after the operation has started executing or finished'
*** First throw call stack:
(
0 CoreFoundation 0x000000010c94be65 __exceptionPreprocess + 165
1 libobjc.A.dylib 0x000000010e68bdeb objc_exception_throw + 48
2 Foundation 0x000000010cd369fe -[NSBlockOperation addExecutionBlock:] + 356
3 ConcurrencyDemo 0x000000010c766edd _TFC15ConcurrencyDemo14ViewController15didClickOnStartfS0_FPSs9AnyObject_T_ + 253
4 ConcurrencyDemo 0x000000010c767086 _TToFC15ConcurrencyDemo14ViewController15didClickOnStartfS0_FPSs9AnyObject_T_ + 54
5 UIKit 0x000000010d16a194 -[UIApplication sendAction:to:from:forEvent:] + 92
6 UIKit 0x000000010d56b7b7 -[UIBarButtonItem(UIInternal) _sendAction:withEvent:] + 152
7 UIKit 0x000000010d16a194 -[UIApplication sendAction:to:from:forEvent:] + 92
8 UIKit 0x000000010d2d96fc -[UIControl sendAction:to:forEvent:] + 67
9 UIKit 0x000000010d2d99c8 -[UIControl _sendActionsForEvents:withEvent:] + 311
10 UIKit 0x000000010d2d9b43 -[UIControl _sendActionsForEvents:withEvent:] + 690
11 UIKit 0x000000010d2d8af8 -[UIControl touchesEnded:withEvent:] + 601
12 UIKit 0x000000010d1d949b -[UIWindow _sendTouchesForEvent:] + 835
13 UIKit 0x000000010d1da1d0 -[UIWindow sendEvent:] + 865
14 UIKit 0x000000010d188b66 -[UIApplication sendEvent:] + 263
15 UIKit 0x000000010d162d97 _UIApplicationHandleEventQueue + 6844
16 CoreFoundation 0x000000010c877a31 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
17 CoreFoundation 0x000000010c86d95c __CFRunLoopDoSources0 + 556
18 CoreFoundation 0x000000010c86ce13 __CFRunLoopRun + 867
19 CoreFoundation 0x000000010c86c828 CFRunLoopRunSpecific + 488
20 GraphicsServices 0x0000000110f5ead2 GSEventRunModal + 161
21 UIKit 0x000000010d168610 UIApplicationMain + 171
22 ConcurrencyDemo 0x000000010c76906d main + 109
23 libdyld.dylib 0x000000010f19492d start + 1
24 ??? 0x0000000000000001 0x0 + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException
(lldb)

最佳答案

让我概述一系列备选方案。第一个只是解决您问题中的直接战术问题,后两个是进一步改进,越来越复杂。

  1. 您不能在操作开始后调用 addExecutionBlock。因此,只需创建一个新的 Operation

    例如:

    class ViewController: UIViewController {

    @IBOutlet weak var imageView1: UIImageView!

    weak var downloadOperation: Operation? // make this weak

    var queue = OperationQueue()

    let imageURLs: [String] = ...

    @IBAction func didClickOnStart(_ sender: Any) {
    downloadOperation?.cancel() // you might want to stop the previous one if you restart this

    let operation = BlockOperation {
    for (index, imageURL) in self.imageURLs.enumerate() {
    guard let cancelled = self.downloadOperation?.cancelled where !cancelled else { return }

    let img1 = Downloader.downloadImageWithURL(imageURL)
    OperationQueue.main.addOperation {
    self.imageView1.image = img1

    print("index \(index)")
    }
    }
    }
    queue.addOperation(operation)

    downloadOperation = operation
    }

    @IBAction func didClickOnCancel(_ sender: Any) {
    downloadOperation?.cancel()
    }

    }
  2. 值得注意的是,连续加载图像会导致不必要的缓慢。您可以同时加载它们,例如:

    class ViewController: UIViewController {

    @IBOutlet weak var imageView1: UIImageView!

    var queue: OperationQueue = {
    let _queue = OperationQueue()
    _queue.maxConcurrentOperationCount = 4
    return _queue
    }()

    let imageURLs: [String] = ...

    @IBAction func didClickOnStart(_ sender: Any) {
    queue.cancelAllOperations()

    let completionOperation = BlockOperation {
    print("all done")
    }

    for (index, imageURL) in self.imageURLs.enumerate() {
    let operation = BlockOperation {
    let img1 = Downloader.downloadImageWithURL(imageURL)
    OperationQueue.main.addOperation {
    self.imageView1.image = img1

    print("index \(index)")
    }
    }
    completionOperation.addDependency(operation)
    queue.addOperation(operation)
    }

    OperationQueue.main.addOperation(completionOperation)
    }

    @IBAction func didClickOnCancel(_ sender: Any) {
    queue.cancelAllOperations()
    }
    }
  3. 即使那样也有问题。另一个问题是,当您“取消”时,它可能会继续尝试下载当前正在下载的资源,因为您没有使用可取消的网络请求。

    更好的方法是将下载(将通过 URLSession 执行)包装在它自己的异步 Operation 子类中,并使其可取消,例如:

    class ViewController: UIViewController {
    var queue: OperationQueue = {
    let _queue = OperationQueue()
    _queue.maxConcurrentOperationCount = 4
    return _queue
    }()

    let imageURLs: [URL] = ...

    @IBAction func didClickOnStart(_ sender: Any) {
    queue.cancelAllOperations()

    let completion = BlockOperation {
    print("done")
    }

    for url in imageURLs {
    let operation = ImageDownloadOperation(url: url) { result in
    switch result {
    case .failure(let error):
    print(url.lastPathComponent, error)

    case .success(let image):
    OperationQueue.main.addOperation {
    self.imageView1.image = img1

    print("index \(index)")
    }
    }
    }
    completion.addDependency(operation)
    queue.addOperation(operation)
    }

    OperationQueue.main.addOperation(completion)
    }

    @IBAction func didClickOnCancel(_ sender: AnyObject) {
    queue.cancelAllOperations()
    }
    }

    在哪里

    /// Simple image network operation

    class ImageDownloadOperation: DataOperation {
    init(url: URL, session: URLSession = .shared, networkCompletionHandler: @escaping (Result<UIImage, Error>) -> Void) {
    super.init(url: url, session: session) { result in
    switch result {
    case .failure(let error):
    networkCompletionHandler(.failure(error))

    case .success(let data):
    guard let image = UIImage(data: data) else {
    networkCompletionHandler(.failure(DownloadError.notImage))
    return
    }

    networkCompletionHandler(.success(image))
    }
    }
    }
    }

    /// Simple network data operation
    ///
    /// This can be subclassed for image-specific operations, JSON-specific operations, etc.

    class DataOperation: AsynchronousOperation {
    var downloadTask: URLSessionTask?

    init(url: URL, session: URLSession = .shared, networkCompletionHandler: @escaping (Result<Data, Error>) -> Void) {
    super.init()

    downloadTask = session.dataTask(with: url) { data, response, error in
    defer { self.complete() }

    guard let data = data, let response = response as? HTTPURLResponse, error == nil else {
    networkCompletionHandler(.failure(error!))
    return
    }

    guard 200..<300 ~= response.statusCode else {
    networkCompletionHandler(.failure(DownloadError.invalidStatusCode(response)))
    return
    }

    networkCompletionHandler(.success(data))
    }
    }

    override func main() {
    downloadTask?.resume()
    }

    override func cancel() {
    super.cancel()

    downloadTask?.cancel()
    }
    }

    /// 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: Operation {

    override public var isAsynchronous: Bool { return true }

    private let stateLock = NSLock()

    private var _executing: Bool = false
    override private(set) public var isExecuting: Bool {
    get {
    stateLock.withCriticalScope { _executing }
    }
    set {
    willChangeValue(forKey: "isExecuting")
    stateLock.withCriticalScope { _executing = newValue }
    didChangeValue(forKey: "isExecuting")
    }
    }

    private var _finished: Bool = false
    override private(set) public var isFinished: Bool {
    get {
    stateLock.withCriticalScope { _finished }
    }
    set {
    willChangeValue(forKey: "isFinished")
    stateLock.withCriticalScope { _finished = newValue }
    didChangeValue(forKey: "isFinished")
    }
    }

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

    public func complete() {
    if isExecuting {
    isExecuting = false
    }

    if !isFinished {
    isFinished = true
    }
    }

    override public func start() {
    if isCancelled {
    isFinished = true
    return
    }

    isExecuting = true

    main()
    }

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

    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>(block: () throws -> T) rethrows -> T {
    lock()
    defer { unlock() }
    return try block()
    }
    }

关于ios - [NSBlockOperation addExecutionBlock :]: blocks cannot be added after the operation has started executing or finished,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38468535/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com