Objective c 连接到NSOperationQueue operationsCount的observeValueForKeyPath中崩溃

Objective c 连接到NSOperationQueue operationsCount的observeValueForKeyPath中崩溃,objective-c,ios,multithreading,key-value-observing,nsoperationqueue,Objective C,Ios,Multithreading,Key Value Observing,Nsoperationqueue,我在UIView子类中有UITextView日志视图(尝试了原子的和非原子的)和NSOperationQueue uploadQueue属性。 我在我的NSOperationQueue属性的operationcount上添加了KVO,如下所示: [[self uploadQueue] addObserver:self forKeyPath:@"operationCount" options:(NSKeyValueObservingOptio

我在UIView子类中有
UITextView日志视图
(尝试了
原子的
非原子的
)和
NSOperationQueue uploadQueue
属性。 我在我的
NSOperationQueue
属性的
operationcount
上添加了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]];
        }
    }
}
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
观察者函数如下所示:

[[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]];
        }
    }
}
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
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
block,一切正常:我得到了关于队列中操作数的NSLog消息,这些操作数正常,等等。 但是有了
if-else
块,我得到了崩溃,它看起来像这样:

[[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]];
        }
    }
}
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
块中崩溃。 此外,我没有看到我的日志视图有任何变化。 感谢您以后的帮助和回复


更新:
在设置保存的文本时执行selectoronmainthread

我建议您在此处使用委托模式。您似乎将非常先进的技术与一些松散的代码混合在一起,这可能会很快导致问题。例如,我认为您不需要关联对象


发生错误的原因是您正在从后台线程访问UI。您应该将UI调用(setText:)包装在performSelectorOnMainThread:内,并使用Object:waitUntilDone:或一个块。

更新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\u async
dispatch\u sync
取决于所需的实现,但在后一种情况下,除非检查所处的线程,否则可能会出现死锁

另见: 有人建议将UI代码移动到单独的方法:

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
  [self performSelectorOnMainThread:@selector(dataLoadingFinished:) withObject:nil waitUntilDone:YES];
}

。。这是一个很好的替代路线。

请看:这就是写错的地方,是的。但除了主线程之外,我并没有在任何线程中使用观察器函数。我认为问题在于KVO的内部机制,也许它使用了额外的线程?无论如何,
performSelectorOnMainThread
在设置我保存的文本时。无论在哪个线程中发生更改,都将调用观察者。您使用的是NSOperationQueue,所以是的,它将在后台。但我无法获取它。NSOperation实例从队列中退出后发生的更改。NSOperationQueue就是这样,它“存在”在主线程中。我的意思是,事件显然发生在所谓的后台线程中,但它本身的更改必须发生在主线程中。我错在哪里?谢谢。阅读链接的答案。苹果甚至告诉你不要这样做。我读过。但除了相信我,我想了解他们。