回到 runloop 讨论,这就是我在基本NSOperation
实现中通常如何解决这个问题:
// create connection and keep the current runloop running until
// the operation has finished. this allows this instance of the operation
// to act as the connections delegate
_connection = [[NSURLConnection alloc] initWithRequest:[self request]
delegate:self];
while(!self.isFinished) {
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate distantFuture]];
}
我关闭isFinished
,我通过设置器保持更新isCancelled
和isFinished
。下面以isCancelled
setter 为例:
- (void)setIsCancelled:(BOOL)isCancelled {
_isCancelled = isCancelled;
if (_isCancelled == YES) {
self.isFinished = YES;
}
}
也就是说,我支持一些关于为什么这是必要的问题。如果您在找到位置之前不需要启动某些东西,为什么不在主线程上启动您的位置管理器,等待适当的委托回调,然后启动后台操作呢?
更新:更新的解决方案
虽然最初的答案通常是正确的,但我已经完全实施了一个解决方案,它确实需要对您管理运行循环的方式进行轻微更改。也就是说,所有代码都可以在 GitHub 上找到 - https://github.com/nathanhjones/CLBackgroundOperation。这是该方法的详细说明。
Tl;博士
改变
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate distantFuture]];
至
[[NSRunLoop currentRunLoop] runMode:NSRunLoopCommonModes
beforeDate:[NSDate distantFuture]];
细节
在您的操作界面中定义以下三个属性。我们将表明这些操作是并发的,因此我们将手动管理它们的状态。在 GitHub 上的解决方案中,这些是NJBaseOperation
.
@property(nonatomic,assign,readonly) BOOL isExecuting;
@property(nonatomic,assign,readonly) BOOL isFinished;
@property(nonatomic,assign,readonly) BOOL isCancelled;
在您的操作实现中,您需要像这样使这些读写:
@interface NJBaseOperation ()
@property(nonatomic,assign,readwrite) BOOL isExecuting;
@property(nonatomic,assign,readwrite) BOOL isFinished;
@property(nonatomic,assign,readwrite) BOOL isCancelled;
@end
接下来,您需要综合上面定义的三个属性,以便您可以覆盖设置器并使用它们来管理您的操作状态。这是我通常使用的,但有时会setIsFinished:
根据我的需要在方法中添加一些额外的语句。
- (void)setIsExecuting:(BOOL)isExecuting {
_isExecuting = isExecuting;
if (_isExecuting == YES) {
self.isFinished = NO;
}
}
- (void)setIsFinished:(BOOL)isFinished {
_isFinished = isFinished;
if (_isFinished == YES) {
self.isExecuting = NO;
}
}
- (void)setIsCancelled:(BOOL)isCancelled {
_isCancelled = isCancelled;
if (_isCancelled == YES) {
self.isFinished = YES;
}
}
最后,为了不必手动发送 KVO 通知,我们将实现以下方法。这是有效的,因为我们的属性被命名为isExecuting
,isFinished
和isCancelled
。
+ (BOOL) automaticallyNotifiesObserversForKey:(NSString *)key {
return YES;
}
现在运营基础已经处理好,是时候淘汰定位的东西了。您需要覆盖main
并在其中启动您的位置管理器并指示当前运行循环继续运行,直到您另有说明。这确保您的线程可以接收位置委托回调。这是我的实现:
- (void)main {
if (_locationManager == nil) {
_locationManager = [[CLLocationManager alloc] init];
_locationManager.delegate = self;
_locationManager.desiredAccuracy = kCLLocationAccuracyBest;
[_locationManager startUpdatingLocation];
}
while(!self.isFinished) {
[[NSRunLoop currentRunLoop] runMode:NSRunLoopCommonModes
beforeDate:[NSDate distantFuture]];
}
}
您应该会收到一个委托回调,此时您可以根据位置进行一些工作,然后完成操作。这是我的实现,计数到 10,000,然后清理。
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {
NSLog(@"** Did Update Location: %@", [locations lastObject]);
[_locationManager stopUpdatingLocation];
// do something here that takes some length of time to complete
for (int i=0; i<10000; i++) {
if ((i % 10) == 0) {
NSLog(@"Loop %i", i);
}
}
self.isFinished = YES;
}
GitHub 上的源代码包括一个dealloc
实现,它只是记录它正在被调用,还观察operationCount
我的变化NSOperationQueue
并记录计数 - 以指示它何时回落到 0。希望有帮助。如果您有任何问题,请告诉我。