- iOS/Objective-C 元类和类别
- objective-c - -1001 错误,当 NSURLSession 通过 httpproxy 和/etc/hosts
- java - 使用网络类获取 url 地址
- ios - 推送通知中不播放声音
我有一个应用程序,其中一个长时间运行的进程(> 1 分钟)被放置在 NSOperationQueue(队列 A)上。当队列 A 操作运行时,UI 完全响应,完全符合预期。
但是,我有一种用户可以执行的不同类型的操作,它在完全独立的 NSOperationQueue(队列 B)上运行。
当 UI 事件触发队列 B 上的操作放置时,它必须等到队列 A 上当前正在执行的操作完成之后。这发生在 iPod Touch (MC544LL) 上。
相反,我希望看到的是,放置在队列 B 上的任何操作都会或多或少地立即开始与队列 A 上的操作并行执行。这是我在模拟器上看到的行为。
我的问题分为两部分:
注意:通过为队列 A/B 使用 GCD 队列,我可以准确地获得我想要的行为,因此我知道我的设备能够支持我正在尝试做的事情。但是,我真的非常想使用 NSOperationQueue,因为这两个操作都需要可取消。
我有一个简单的测试应用程序:
ViewController 是:
//
// ViewController.m
// QueueTest
//
#import "ViewController.h"
@interface ViewController ()
@property (strong, nonatomic) NSOperationQueue *slowQueue;
@property (strong, nonatomic) NSOperationQueue *fastQueue;
@end
@implementation ViewController
-(id)initWithCoder:(NSCoder *)aDecoder
{
if (self = [super initWithCoder:aDecoder]) {
self.slowQueue = [[NSOperationQueue alloc] init];
self.fastQueue = [[NSOperationQueue alloc] init];
}
return self;
}
-(void)viewDidLoad
{
NSLog(@"View loaded on thread %@", [NSThread currentThread]);
}
// Responds to "Slow Op Start" button
- (IBAction)slowOpStartPressed:(id)sender {
NSBlockOperation *operation = [[NSBlockOperation alloc] init];
[operation addExecutionBlock:^{
[self workHard:600];
}];
[self.slowQueue addOperation:operation];
}
// Responds to "Fast Op Start" button
- (IBAction)fastOpStart:(id)sender {
NSBlockOperation *operation = [[NSBlockOperation alloc] init];
[operation addExecutionBlock:^{
NSLog(@"Fast operation on thread %@", [NSThread currentThread]);
}];
[self.fastQueue addOperation:operation];
}
-(void)workHard:(NSUInteger)iterations
{
NSLog(@"SlowOperation start on thread %@", [NSThread currentThread]);
NSDecimalNumber *result = [[NSDecimalNumber alloc] initWithString:@"0"];
for (NSUInteger i = 0; i < iterations; i++) {
NSDecimalNumber *outer = [[NSDecimalNumber alloc] initWithUnsignedInteger:i];
for (NSUInteger j = 0; j < iterations; j++) {
NSDecimalNumber *inner = [[NSDecimalNumber alloc] initWithUnsignedInteger:j];
NSDecimalNumber *product = [outer decimalNumberByMultiplyingBy:inner];
result = [result decimalNumberByAdding:product];
}
result = [result decimalNumberByAdding:outer];
}
NSLog(@"SlowOperation end");
}
@end
我在第一次按下“Slow Op Start”按钮后大约 1 秒后按下“Fast Op Start”按钮后看到的输出是:
2012-11-28 07:41:13.051 QueueTest[12558:907] View loaded on thread <NSThread: 0x1d51ec30>{name = (null), num = 1}
2012-11-28 07:41:14.745 QueueTest[12558:1703] SlowOperation start on thread <NSThread: 0x1d55e5f0>{name = (null), num = 3}
2012-11-28 07:41:25.127 QueueTest[12558:1703] SlowOperation end
2012-11-28 07:41:25.913 QueueTest[12558:3907] Fast operation on thread <NSThread: 0x1e36d4c0>{name = (null), num = 4}
如您所见,第二个操作直到第一个操作完成后才开始执行,尽管事实上这是两个独立的(并且可能是独立的)NSOperationQueues。
我已阅读 Apple Concurrency Guide , 但找不到描述这种情况的任何内容。我还阅读了两个关于相关主题的 SO 问题( link , link ),但似乎都没有触及我所看到的问题的核心(先发制人)。
我尝试过的其他事情:
这个问题经过了多次编辑,这可能会使某些评论/答案难以理解。
最佳答案
我怀疑您遇到的问题是两个操作队列都在底层默认优先级调度队列上执行它们的 block 。因此,如果几个慢速操作在快速操作之前排队,那么您可能会看到这种行为。
为什么不为慢速操作设置 NSOperationQueue 实例,使其在任何给定时间只执行一个操作(即将此队列的 maxConcurrentOperationCount 设置为 1),或者如果您的操作都是 block ,那么为什么不直接使用 GCD 队列?例如
static dispatch_queue_t slowOpQueue = NULL;
static dispatch_queue_t fastOpQueue = NULL;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
slowOpQueue = dispatch_queue_create("Slow Ops Queue", NULL);
fastOpQueue = dispatch_queue_create("Fast Ops Queue", DISPATCH_QUEUE_CONCURRENT);
});
for (NSUInteger slowOpIndex = 0; slowOpIndex < 5; slowOpIndex++) {
dispatch_async(slowOpQueue, ^(void) {
NSLog(@"* Starting slow op %d.", slowOpIndex);
for (NSUInteger delayLoop = 0; delayLoop < 1000; delayLoop++) {
putchar('.');
}
NSLog(@"* Ending slow op %d.", slowOpIndex);
});
}
for (NSUInteger fastBlockIndex = 0; fastBlockIndex < 10; fastBlockIndex++) {
dispatch_async(fastOpQueue, ^(void) {
NSLog(@"Starting fast op %d.", fastBlockIndex);
NSLog(@"Ending fast op %d.", fastBlockIndex);
});
}
至于根据您关于需要操作取消工具等的评论使用 NSOperationQueue,您可以尝试:
- (void)loadSlowQueue
{
[self.slowQueue setMaxConcurrentOperationCount:1];
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"begin slow block 1");
[self workHard:500];
NSLog(@"end slow block 1");
}];
NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"begin slow block 2");
[self workHard:500];
NSLog(@"end slow block 2");
}];
[self.slowQueue addOperation:operation];
[self.slowQueue addOperation:operation2];
}
因为我认为您添加到慢队列操作的两个 block 正在默认队列上并行执行并阻止您的快速操作被调度。
编辑:
如果您仍然发现默认的 GCD 队列令人窒息,为什么不创建一个 NSOperation 子类来执行 block 而不使用 GCD 来处理您的慢速操作,这仍然会给您带来声明式的便利,无需为每个操作,但使用常规 NSOperation 的线程模型。例如
#import <Foundation/Foundation.h>
typedef void (^BlockOperation)(NSOperation *containingOperation);
@interface PseudoBlockOperation : NSOperation
- (id)initWithBlock:(BlockOperation)block;
- (void)addBlock:(BlockOperation)block;
@end
然后是实现:
#import "PseudoBlockOperation.h"
@interface PseudoBlockOperation()
@property (nonatomic, strong) NSMutableArray *blocks;
@end
@implementation PseudoBlockOperation
@synthesize blocks;
- (id)init
{
self = [super init];
if (self) {
blocks = [[NSMutableArray alloc] initWithCapacity:1];
}
return self;
}
- (id)initWithBlock:(BlockOperation)block
{
self = [self init];
if (self) {
[blocks addObject:[block copy]];
}
return self;
}
- (void)main
{
@autoreleasepool {
for (BlockOperation block in blocks) {
block(self);
}
}
}
- (void)addBlock:(BlockOperation)block
{
[blocks addObject:[block copy]];
}
@end
然后在你的代码中你可以做这样的事情:
PseudoBlockOperation *operation = [[PseudoBlockOperation alloc] init];
[operation addBlock:^(NSOperation *operation) {
if (!operation.isCancelled) {
NSLog(@"begin slow block 1");
[self workHard:500];
NSLog(@"end slow block 1");
}
}];
[operation addBlock:^(NSOperation *operation) {
if (!operation.isCancelled) {
NSLog(@"begin slow block 2");
[self workHard:500];
NSLog(@"end slow block 2");
}
}];
[self.slowQueue addOperation:operation];
请注意,在此示例中,添加到同一操作的任何 block 都将按顺序执行而不是并发执行,以同时执行为每个 block 创建一个操作。这比 NSBlockOperation 的优势在于您可以通过更改 BlockOperation 的定义将参数传递到 block 中 - 在这里我传递了包含操作,但您可以传递任何其他需要的上下文。
希望对您有所帮助。
关于objective-c - 在一个 NSOperationQueue 上抢占 NSOperation 并将 NSOperation 放置到一个单独的 NSOperationQueue 上?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13568756/
是否可以将数据从 NSOperation 传递到下一个 NSOperation 使用的依赖链? 谢谢 克里斯 最佳答案 是的。当前NSOperation可以通过 dependencies 访问它的依赖
我有两个 NSOperation 负责下载和解析。下载操作成功后,我收到一些 NSData 我想将该数据设置为解析操作要使用的数据: init(context: NSManagedObjectCont
我的应用程序获取当前设备位置,将其发布到我的服务器,并返回要在 TableView 中显示的字典。目前我正在使用 CLLocationManager 委托(delegate)方法和 AFJSONReq
我正在编写一个网络连接的应用程序,它需要执行多个异步请求来加载依赖树中较低层所需的数据。 图 1. 出于可视化目的,考虑一个 ASIHTTPRequests A、B、C、D、E 和 F 的示例: A的
我有一个相当简单但昂贵的任务需要在后台运行,这是标准的 NSOperation 情况。我还需要确保该操作支持取消并适当停止。鉴于该要求,哪种方法更好:只是将昂贵的方法调用包装在 NSInvocatio
今天,我在尝试“概括”我的“CoreData 导入操作”时遇到了一个奇怪的问题。看来如果我创建 NSOperation 的通用子类 main() func 不会被调用。 简单的例子: class My
我需要帮助了解如何适当处理以下用例: 假设我正在编写一个聊天应用: 启动应用 请求服务器 (AFHTTPRequestOperation) 给我一个所有新消息的列表 遍历这些消息以查看我是否需要下载任
我想在当前执行的线程上同步执行一个 NSOperation。我可以只调用 [NSOperation start] 吗?这是否总是在当前正在执行的线程中运行? 另一种方法是创建一个 NSOperatio
我有一个应用程序,其中一个长时间运行的进程(> 1 分钟)被放置在 NSOperationQueue(队列 A)上。当队列 A 操作运行时,UI 完全响应,完全符合预期。 但是,我有一种用户可以执行的
我正在使用UIActivityItemProvider子类提供自定义数据。但是有时获取数据失败,并且我不想展示事件(例如消息编写器)。在[self cancel]方法中尝试了return nil;和i
根据关于 NSOperation 的 Apple 文档,我们必须覆盖 main非并发操作的方法和start并发操作的方法。但为什么? 最佳答案 首先,请记住,“并发”和“非并发”在 NSOperati
我正在开发一个 iPad 应用程序。它使用 NSOperation 在后台下载某些内容,并由 NSOperationQueue 处理。我发现,除非我向 NSOperation 添加保留,否则在执行操作
我将创建一系列 NSOperation 并在队列中运行它们。 它们都是按顺序运行的,一次运行一个。 这些操作将从网络获取数据并创建和保存核心数据管理对象。 如何处理应用程序退出的情况?由于操作在分离线
我在 NSOperation 和观察者方面遇到问题。 我有一个 tabbarcontroller 和一个 splashController。我希望启动画面加载并下载文件,并且在下载文件时使 tabba
我正在从 Facebook Connect 获取一些数据(使用 FBConnect Objective-C 2.0 框架),并且我在 NSOperation 中完成所有这些操作。它位于 NSOpera
我正在从NavigationController的 subview 中调用NSOperation。 MyOperation *op = [[MyOperation alloc] target:self
我是iPhone新手。在哪里可以找到NSOperationQueue和NSOperation的示例?NSOperationQueue和NSOperation与线程相比有什么优势? 谢谢 最佳答案 阅读
我对 NSOperations 有疑问。一切正常,但有时(我不知道为什么)操作 block 被简单地跳过。我错过了什么吗?操作怎么可能不是 NSLogging “输入操作”?以下是 viewDidLo
我一直在搜索,但只能找到委托(delegate)模式的想法来从 NSOperation 传回数据。我有一个 NSOperation 在完成该 NSOperation 后下载数据我希望它传递回将其下载的
我正在使用后台操作来发出 URL 请求。如果操作返回错误,如何通知我,然后将相同的操作类型重新添加到队列中以重试? 最佳答案 您可以使用 NSNotification 来实现这一点。创建通知并在请求失
我是一名优秀的程序员,十分优秀!