我在我的UIView子类中UITextView logView
(尝试了atomic
和nonatomic
)和NSOperationQueue uploadQueue
属性。
我在我的NSOperationQueue
媒体资源operationsCount
上添加了KVO,如下所示:
[[self uploadQueue] addObserver:self
forKeyPath:@"operationCount"
options:(NSKeyValueObservingOptionNew |
NSKeyValueObservingOptionOld)
context:NULL];
观察者功能如下所示:
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
{
if ([object isKindOfClass: [NSOperationQueue class]]) {
NSLog(@"Keypath is: %@ change dictionary is: %@", keyPath, change);
NSInteger testnew = [[change objectForKey: @"new"] integerValue];
NSInteger testold = [[change objectForKey: @"old"] integerValue];
if (testnew > testold) {
[[self logView] setText: [NSString stringWithFormat: @"Uploading %d files", testnew]];
objc_setAssociatedObject([self logView], @"max_value_of_uploads", [change objectForKey: @"new"], OBJC_ASSOCIATION_COPY);
} else {
NSInteger value = [objc_getAssociatedObject([self logView], @"max_value_of_uploads") integerValue];
[[self logView] setText: [NSString stringWithFormat: @"Uploaded %d of %d files", testnew, value]];
}
}
}
uploadQueue
填充的工作原理如下:
...
NSDictionary *iter;
NSEnumerator *enumerator = [objects objectEnumerator];
while (iter = [enumerator nextObject]) {
Uploader *op = [[Uploader alloc] initWithFileName: [iter objectForKey: @"fileName"]
FileID: [[iter objectForKey: @"fileID"] integerValue]
AndSessionID: [self sess]];
//Uploader *op = [[Uploader alloc] init];
[[self uploadQueue] addOperation: op];
...
没有if-else
阻止,一切正常:我有关于队列中操作次数的NSLog消息,数字是否正常等等。
但是对于那个if-else
块,我会崩溃,看起来像这样:
bool _WebTryThreadLock(bool), 0x10617fb0: Tried to obtain the web lock from a thread other than the main thread or the web thread. This may be a result of calling to UIKit from a secondary thread. Crashing now...
1 WebThreadLock
2 -[UITextView setText:]
3 -[SincViewController observeValueForKeyPath:ofObject:change:context:]
4 NSKeyValueNotifyObserver
5 NSKeyValueDidChange
6 -[NSObject(NSKeyValueObservingPrivate) _changeValueForKey:key:key:usingBlock:]
7 -[NSObject(NSKeyValueObservingPrivate) _changeValueForKey:usingBlock:]
8 ____NSOQDelayedFinishOperations_block_invoke_0
9 -[NSObject(NSKeyValueObservingPrivate) _changeValueForKey:key:key:usingBlock:]
10 -[NSObject(NSKeyValueObservingPrivate) _changeValueForKey:usingBlock:]
11 __NSOQDelayedFinishOperations
12 _dispatch_after_timer_callback
13 _dispatch_source_invoke
14 _dispatch_queue_invoke
15 _dispatch_worker_thread2
16 _pthread_wqthread
17 start_wqthread
它在else
块中崩溃了。
而且,我没有看到我的logView有任何变化。
感谢您将来的帮助和回复。
更新: performSelectorOnMainThread
设置文字时保存了我。
答案 0 :(得分:4)
更新UI时,请确保您在主线程中。可能的解决方案如下:
dispatch_async(dispatch_get_main_queue(), ^{
if (testnew > testold) {
[[self logView] setText: [NSString stringWithFormat: @"Uploading %d files", testnew]];
objc_setAssociatedObject([self logView], @"max_value_of_uploads", [change objectForKey: @"new"], OBJC_ASSOCIATION_COPY);
} else {
NSInteger value = [objc_getAssociatedObject([self logView], @"max_value_of_uploads") integerValue];
[[self logView] setText: [NSString stringWithFormat: @"Uploaded %d of %d files", testnew, value]];
}
});
请注意,使用dispatch_async
或dispatch_sync
取决于您所希望的实现,但在后一种情况下,除非您检查自己所处的线程,否则您将面临死锁风险。
另见:NSOperation, observer and thread error 有人建议将您的UI代码移动到单独的方法:
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
[self performSelectorOnMainThread:@selector(dataLoadingFinished:) withObject:nil waitUntilDone:YES];
}
..这是一条很好的替代路线。
答案 1 :(得分:1)
我建议你在这里使用委托模式。您似乎将非常先进的技术与一些松散的代码混合在一起,这可能会很快导致问题。我认为你不需要关联对象。
您的错误发生是因为您正在从后台线程访问UI。你应该在performSelectorOnMainThread:withObject:waitUntilDone:或块中包装UI调用(setText :)。