- Java 双重比较
- java - 比较器与 Apache BeanComparator
- Objective-C 完成 block 导致额外的方法调用?
- database - RESTful URI 是否应该公开数据库主键?
我试图在完成或取消后重新启动 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)
最佳答案
让我概述一系列备选方案。第一个只是解决您问题中的直接战术问题,后两个是进一步改进,越来越复杂。
您不能在操作开始后调用 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()
}
}
值得注意的是,连续加载图像会导致不必要的缓慢。您可以同时加载它们,例如:
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()
}
}
即使那样也有问题。另一个问题是,当您“取消”时,它可能会继续尝试下载当前正在下载的资源,因为您没有使用可取消的网络请求。
更好的方法是将下载(将通过 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/
为了避免在 Objective-C 中使用 NSBlockOperation 时发生内存泄漏,我们必须将变量声明为 weak 以便能够引用 block 内的 block 操作(如果需要则取消),通常如
我创建了一个实现一些方法的类。这些方法由另一个类调用,并通过 NSBlockOperation 进行管理。 我的 NSBlockOperation 工作正常,我在尝试计算变量时遇到问题: EXC_BA
我正在学习 NSOperations 和 NSOperationQueue。 我有一组 NSBlockOperation:“UPLOAD”和“DELETE”。删除必须在执行前等待上传完成。 我希望发生
在检查我的代码时,我发现在很多地方我都假设调用 [NSBlockOperationInstance start]; 将在主线程上启动此操作。我不知道我为什么这么想,但无论如何我都不应该这么确定。我检查
我正在进入 NSBlockOperation 并且我有一些问题。值得注意的是,documentation对于 addExecutionBlock 说: Discussion The specified
由于我目前正在处理 Web 服务调用和数据库检索,所以我偶然发现了 NSBlockOperation 未启动的问题。 但是,首先要稍微解释一下。主要目标是拥有一个由以下操作组成的操作队列: 操作 #1
我在引用 block 本身内的“NSBlockOperation”时遇到麻烦。我需要检查操作是否被取消,并且似乎在启用 ARC 的项目中运行时,对“searchOperation”的任何访问都会泄漏。
我正在尝试在后台线程上发出网络请求,并且我决定使用 NSBlockOperations。我正在使用 ADNKit处理我的获取请求。这是代码: - (void)reloadPosts { dis
我扩展了 NSOperationQueue 以允许添加带有特定 NSString 作为标识符的 NSBlockOperation。 标识符值保存在用作注册表的 NSMutableArray 中。这就是
我有一个长时间运行的循环,我想使用 NSOperation 在后台运行。我想使用一个 block : NSBlockOperation *operation = [NSBlockOperation b
我有一个问题。我有以下代码: NSBlockOperation *op=[NSBlockOperation blockOperationWithBlock:^{ [[ClassA sh
我有一个 UITableView,其中每个部分都包含一行,每一行都包含一个 UICollectionView。 UITableView 是数据源并委托(delegate)给 Core Data 数据库
大家好,我很沮丧,因为我想改进代码,但我没有得到好的结果,这是我的代码 NSBlockOperation *blockOperation1 = [NSBlockOperation blockOpera
我必须从我的 RestAPI 同步一堆信息。我必须进行 6 次 RestAPI 调用才能完成工作。我用 Blocks 设计了 API 调用,如果有则返回 NSError。其中 3 个调用应该嵌套执
在我的项目中,我使用 NSBlockOperation 在后台线程上运行一个操作: var operationQueue = NSOperationQueue() var iop = NSBlockO
通常,当您创建一个 NSOperation 子类时,您负责在 -main 方法中创建和释放一个 NSAutoreleasePool。 当你使用 NSBlockOperation 时,你需要在 bloc
Foundation Framework 中共有三个操作类(NSOperation、NSInvocationOperation 和NSBlockOperation)。 我已经阅读了 concurren
我有一个应用程序目前使用 NSURLConnection 进行绝大多数网络。我想搬到 NSURLSession 因为 Apple 告诉我这是要走的路。 我的应用只是通过 + (NSData *)sen
我正在调试我的应用程序的一个有趣问题。 该应用程序针对iOS6.1,并且使用ARC。这是一些背景,因为它太大,无法复制粘贴到 SO 中。 有一个 Dashboard 类,它有一个 subview Co
我在使用 NSBlockOperation 并尝试访问 EKEventStore 时遇到问题。这是我的代码: 这段代码异步运行 NSBlockOperation *wordsOp =
我是一名优秀的程序员,十分优秀!