我从Facebook Connect获取一些数据(使用FBConnect Objective-C 2.0框架),我正在NSOperation中完成所有这些工作。它在NSOperation中,因为我还运行了其他几个操作,这就是其中之一。
问题是所有FBConnect调用都是异步的。因此,NSOperation的主要方法快速完成,操作标记为已完成。
有没有办法克服这个问题?看来FBConnect中没有同步选项!
非常感谢,
麦克
答案 0 :(得分:24)
以下是一个完整的例子。在您的子类中,在异步方法完成后,调用[self completeOperation]
以转换到已完成状态。
@interface AsynchronousOperation()
// 'executing' and 'finished' exist in NSOperation, but are readonly
@property (atomic, assign) BOOL _executing;
@property (atomic, assign) BOOL _finished;
@end
@implementation AsynchronousOperation
- (void) start;
{
if ([self isCancelled])
{
// Move the operation to the finished state if it is canceled.
[self willChangeValueForKey:@"isFinished"];
self._finished = YES;
[self didChangeValueForKey:@"isFinished"];
return;
}
// If the operation is not canceled, begin executing the task.
[self willChangeValueForKey:@"isExecuting"];
[NSThread detachNewThreadSelector:@selector(main) toTarget:self withObject:nil];
self._executing = YES;
[self didChangeValueForKey:@"isExecuting"];
}
- (void) main;
{
if ([self isCancelled]) {
return;
}
}
- (BOOL) isAsynchronous;
{
return YES;
}
- (BOOL)isExecuting {
return self._executing;
}
- (BOOL)isFinished {
return self._finished;
}
- (void)completeOperation {
[self willChangeValueForKey:@"isFinished"];
[self willChangeValueForKey:@"isExecuting"];
self._executing = NO;
self._finished = YES;
[self didChangeValueForKey:@"isExecuting"];
[self didChangeValueForKey:@"isFinished"];
}
@end
答案 1 :(得分:6)
将FBConnect
来电置于“start
”,而不是“main
”,并管理“isFinished
”{“1}}”属性。 (并返回“isExecuting
”的YES
有关详细信息,请参阅Apple有关撰写concurrent NSOperations的文档。
答案 2 :(得分:0)
如果没有其他要求,请理解这一点:NSOperation
的行为没有任何魔术。 NSOperationQueue
仅使用关键值观察来监视操作。之所以不那么容易的唯一原因是所使用的密钥与Objective-C 2.0约定所说的密钥不同,因此标准的合成设置器将无法使用。
结果是,当您定义NSOperation
子类时,需要提供asynchronous
,executing
和finished
。最后两个需要您的帮助才能正常工作。
听起来复杂吗?不是,只是细节。整个过程中的每个步骤都很简单且有意义,但在您正确理解所有步骤之前,它实际上不会起作用。
首先,标题:
//
// MyOperation.h
#import <Foundation/Foundation.h>
@interface MyOperation : NSOperation
@property(readonly, getter=isAsynchronous) BOOL asynchronous;
@property(readonly, getter=isExecuting) BOOL executing;
@property(readonly, getter=isFinished) BOOL finished;
@end
您当然可以在此处将executing
和finished
定义为readwrite
,因此在实现中无需将它们重新定义为readwrite
。但是我想知道只有我的操作可以更改它们的状态。
现在执行。这里有一些步骤:
finished
和executing
属性重新定义为读/写。executing
和finished
的实现,以手动提供正确的KVO消息传递(因此isExecuting
,setExecuting:
,isFinished
和{{1} })。setFinished:
为executing
和finished
密钥提供存储。@synthesize
(请注意,此代码可能会滚动一点。)
asynchronous
实际上不需要在设置器中检查//
// MyOperation.m
#import "MyOperation.h"
@interface MyOperation()
@property(readwrite) BOOL executing;
@property(readwrite) BOOL finished;
@end
@implementation MyOperation
// Provide your own start.
- (void)start {
if (self.cancelled) {
self.finished = YES;
return;
}
NSLog(@"Starting %@", self);
self.executing = YES;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
NSLog(@"Finished %@", self);
self.executing = NO;
self.finished = YES;
});
}
// The rest of this is boilerplate.
- (BOOL)isAsynchronous {
return YES;
}
@synthesize executing = _executing;
- (BOOL)isExecuting {
@synchronized(self) {
return _executing;
}
}
- (void)setExecuting:(BOOL)executing {
@synchronized(self) {
if (executing != _executing) {
[self willChangeValueForKey:@"isExecuting"];
_executing = executing;
[self didChangeValueForKey:@"isExecuting"];
}
}
}
@synthesize finished = _finished;
- (BOOL)isFinished {
@synchronized(self) {
return _finished;
}
}
- (void)setFinished:(BOOL)finished {
@synchronized(self) {
if (finished != _finished) {
[self willChangeValueForKey:@"isFinished"];
_finished = finished;
[self didChangeValueForKey:@"isFinished"];
}
}
}
@end
。通过调用executing != _executing
,盲目更改值,然后调用willChangeValueForKey
,可以自动提供正确的行为。但是这种情况意味着您可以在赋值上设置一个断点,并且只有在值更改后才停止,并且我发现这在实践中调试我的操作非常有用。
我还看到通过在didChangeValueForKey
和executing
属性之上提供自定义状态来实现此目的。当然,这可以很好地工作,并且在某些方面会更好……但是比这个示例还需要更多的KVO知识,这已经足够了。
最后,请注意,操作开始后,我还没有添加对cancel的支持。为此,您必须覆盖finished
(或者更正确地说,观察cancel
的值)并进行处理。这会使我的简单isCancelled
示例变得复杂很多。
我在命令行控制台应用程序中运行了此代码,方法是将15个操作添加到start
为5的队列中,然后等待队列完成使用maxConcurrentOperationCount
的操作(这就是为什么我使用背景在我的waitUntilAllOperationsAreFinished
中排队dispatch_after
)。这是输出:
start
答案 3 :(得分:0)
这个怎么样?
//
// Operation.m
#import "Operation.h"
@interface Operation()
@property (nonatomic, strong) dispatch_semaphore_t semaphore;
@end
@implementation Operation
- (void)main {
[self doWorkWithCompletion:^{
dispatch_semaphore_signal(self.semaphore);
}];
dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);
}
....
@end