具有依赖关系的 NSOperationQueue 错误

Posted

技术标签:

【中文标题】具有依赖关系的 NSOperationQueue 错误【英文标题】:NSOperationQueue bug with dependencies 【发布时间】:2013-10-30 20:22:17 【问题描述】:

我正在使用 NSOperation 和 NSOperationQueue 执行一系列操作,所有操作都相互依赖(2 对 1、3 对 2 等)。创建操作后,我设置了依赖项。队列完成时我遇到了问题:程序在代码的 _release 部分崩溃,显然是在 NSOperations 被释放时。请注意,它们都在队列的最后被释放,因为只有在最后一个依赖于倒数第二个之后,它们才能被释放。如果我删除任何依赖项,代码运行良好。 如果我将 waitUntilFinished: 更改为 NO,它会崩溃,如果是 YES,它不会。

我已将问题隔离到以下代码不使用我的任何自定义类。默认情况下,NSOperation 是一个完全不做任何事情的类。然而,当所有操作都完成后,这仍然会崩溃。因此,看来我没有正确使用 NSOperationQueue 但看不出有什么问题。我在 10.9 上运行,我注意到 Maverick 10.9 通常比 10.8 对这些问题更敏感。

我用一个菜单项从主线程调用这个方法:

- (void) testOperations:(id)object  


        NSOperationQueue* queue = [ [ NSOperationQueue alloc ] init ];

        NSMutableArray* array = [ NSMutableArray array ];
        for ( int i = 0; i < 10000; i++)
            [ array addObject: [[[ NSOperation alloc ] init ] autorelease ] ];

        for ( int i = 1; i < [ array count ]; i++)
            [[ array objectAtIndex:i ] addDependency:[array objectAtIndex:i-1]]; // remove this and no crash

        [ queue addOperations: array waitUntilFinished:NO ]; // Change to YES, no crash
        [ queue autorelease ]; // or release, it does not make a difference, in fact leaking the memory makes no difference: the code crashes when the queue is removing the NSOperations

每次都会崩溃: bool objc::DenseMapBase >, objc_object*, unsigned long, objc::DenseMapInfo, true>: (EXC_BAD_ACCESS)

完整的堆栈是:

#0  0x9104d81b in objc::DenseMapBase<objc::DenseMap<objc_object*, unsigned long, true, objc::DenseMapInfo<objc_object*> >, objc_object*, unsigned long, objc::DenseMapInfo<objc_object*>, true>::find(objc_object* const&) ()
#1  0x910384e3 in _objc_rootReleaseWasZero ()
#2  0x9104d5d9 in -[NSObject release] ()
#3  0x99e41224 in CFRelease ()
#4  0x99e56277 in -[__NSArrayM dealloc] ()
#5  0x9104d5ef in -[NSObject release] ()
#6  0x97f62b22 in -[__NSOperationInternal dealloc] ()
#7  0x9104d5ef in -[NSObject release] ()
#8  0x97f62ac8 in -[NSOperation dealloc] ()
#9  0x9104d5ef in -[NSObject release] ()
#10 0x99e41224 in CFRelease ()
#11 0x99e56277 in -[__NSArrayM dealloc] ()
#12 0x9104d5ef in -[NSObject release] ()
#13 0x97f62b22 in -[__NSOperationInternal dealloc] ()
#14 0x9104d5ef in -[NSObject release] ()
#15 0x97f62ac8 in -[NSOperation dealloc] ()
#16 0x9104d5ef in -[NSObject release] ()
#17 0x99e41224 in CFRelease ()
#18 0x99e56277 in -[__NSArrayM dealloc] ()
#19 0x9104d5ef in -[NSObject release] ()
#20 0x97f62b22 in -[__NSOperationInternal dealloc] ()
#21 0x9104d5ef in -[NSObject release] ()
#22 0x97f62ac8 in -[NSOperation dealloc] ()
#23 0x9104d5ef in -[NSObject release] ()
#24 0x99e41224 in CFRelease ()
#25 0x99e56277 in -[__NSArrayM dealloc] ()
#26 0x9104d5ef in -[NSObject release] ()
#27 0x97f62b22 in -[__NSOperationInternal dealloc] ()
#28 0x9104d5ef in -[NSObject release] ()
#29 0x97f62ac8 in -[NSOperation dealloc] ()
#30 0x9104d5ef in -[NSObject release] ()
#31 0x99e41224 in CFRelease ()
#32 0x99e56277 in -[__NSArrayM dealloc] ()
#33 0x9104d5ef in -[NSObject release] ()
#34 0x97f62b22 in -[__NSOperationInternal dealloc] ()
#35 0x9104d5ef in -[NSObject release] ()
#36 0x97f62ac8 in -[NSOperation dealloc] ()
#37 0x9104d5ef in -[NSObject release] ()
#38 0x99e41224 in CFRelease ()
#39 0x99e56277 in -[__NSArrayM dealloc] ()
#40 0x9104d5ef in -[NSObject release] ()
#41 0x97f62b22 in -[__NSOperationInternal dealloc] ()
#42 0x9104d5ef in -[NSObject release] ()
#43 0x97f62ac8 in -[NSOperation dealloc] ()
#44 0x9104d5ef in -[NSObject release] ()
#45 0x99e41224 in CFRelease ()
#46 0x99e56277 in -[__NSArrayM dealloc] ()
#47 0x9104d5ef in -[NSObject release] ()
#48 0x97f62b22 in -[__NSOperationInternal dealloc] ()
#49 0x9104d5ef in -[NSObject release] ()
#50 0x97f62ac8 in -[NSOperation dealloc] ()
#10722  0x9104d5ef in -[NSObject release] ()
#10723  0x97f62b22 in -[__NSOperationInternal dealloc] ()
#10724  0x9104d5ef in -[NSObject release] ()
#10725  0x97f62ac8 in -[NSOperation dealloc] ()
#10726  0x9104d5ef in -[NSObject release] ()
#10727  0x99e41224 in CFRelease ()
#10728  0x99e56277 in -[__NSArrayM dealloc] ()
#10729  0x9104d5ef in -[NSObject release] ()
#10730  0x97f62b22 in -[__NSOperationInternal dealloc] ()
#10731  0x9104d5ef in -[NSObject release] ()
#10732  0x97f62ac8 in -[NSOperation dealloc] ()
#10733  0x9104d5ef in -[NSObject release] ()
#10734  0x99e41224 in CFRelease ()
#10735  0x99e56277 in -[__NSArrayM dealloc] ()
#10736  0x9104d5ef in -[NSObject release] ()
#10737  0x97f62b22 in -[__NSOperationInternal dealloc] ()
#10738  0x9104d5ef in -[NSObject release] ()
#10739  0x97f62ac8 in -[NSOperation dealloc] ()
#10740  0x9104d5ef in -[NSObject release] ()
#10741  0x99e41224 in CFRelease ()
#10742  0x99e56277 in -[__NSArrayM dealloc] ()
#10743  0x9104d5ef in -[NSObject release] ()
#10744  0x97f62b22 in -[__NSOperationInternal dealloc] ()
#10745  0x9104d5ef in -[NSObject release] ()
#10746  0x97f62ac8 in -[NSOperation dealloc] ()
#10747  0x9104d5ef in -[NSObject release] ()
#10748  0x99e41224 in CFRelease ()
#10749  0x99e56277 in -[__NSArrayM dealloc] ()
#10750  0x9104d5ef in -[NSObject release] ()
#10751  0x97f62b22 in -[__NSOperationInternal dealloc] ()
#10752  0x9104d5ef in -[NSObject release] ()
#10753  0x97f62ac8 in -[NSOperation dealloc] ()
#10754  0x9104d5ef in -[NSObject release] ()
#10755  0x99e41224 in CFRelease ()
#10756  0x99e56277 in -[__NSArrayM dealloc] ()
#10757  0x9104d5ef in -[NSObject release] ()
#10758  0x97f62b22 in -[__NSOperationInternal dealloc] ()
#10759  0x9104d5ef in -[NSObject release] ()
#10760  0x97f62ac8 in -[NSOperation dealloc] ()
#10761  0x9104d5ef in -[NSObject release] ()
#10762  0x99e41224 in CFRelease ()
#10763  0x99e56277 in -[__NSArrayM dealloc] ()
#10764  0x9104d5ef in -[NSObject release] ()
#10765  0x97f62b22 in -[__NSOperationInternal dealloc] ()
#10766  0x9104d5ef in -[NSObject release] ()
#10767  0x97f62ac8 in -[NSOperation dealloc] ()
#10768  0x9104d5ef in -[NSObject release] ()
#10769  0x99e41224 in CFRelease ()
#10770  0x99e56277 in -[__NSArrayM dealloc] ()
#10771  0x9104d5ef in -[NSObject release] ()
#10772  0x97f62b22 in -[__NSOperationInternal dealloc] ()
#10773  0x9104d5ef in -[NSObject release] ()
#10774  0x97f62ac8 in -[NSOperation dealloc] ()
#10775  0x9104d5ef in -[NSObject release] ()
#10776  0x99e41224 in CFRelease ()
#10777  0x99e56277 in -[__NSArrayM dealloc] ()
#10778  0x9104d5ef in -[NSObject release] ()
#10779  0x97f62b22 in -[__NSOperationInternal dealloc] ()
#10780  0x9104d5ef in -[NSObject release] ()
#10781  0x97f62ac8 in -[NSOperation dealloc] ()
#10782  0x9104d5ef in -[NSObject release] ()
#10783  0x99e41224 in CFRelease ()
#10784  0x99e56277 in -[__NSArrayM dealloc] ()
#10785  0x9104d5ef in -[NSObject release] ()
#10786  0x97f62b22 in -[__NSOperationInternal dealloc] ()
#10787  0x9104d5ef in -[NSObject release] ()
#10788  0x97f62ac8 in -[NSOperation dealloc] ()
#10789  0x9104d5ef in -[NSObject release] ()
#10790  0x99e41224 in CFRelease ()
#10791  0x99e56277 in -[__NSArrayM dealloc] ()
#10792  0x9104d5ef in -[NSObject release] ()
#10793  0x97f62b22 in -[__NSOperationInternal dealloc] ()
#10794  0x9104d5ef in -[NSObject release] ()
#10795  0x97f62ac8 in -[NSOperation dealloc] ()
#10796  0x9104d5ef in -[NSObject release] ()
#10797  0x99e41224 in CFRelease ()
#10798  0x99e56277 in -[__NSArrayM dealloc] ()
#10799  0x9104d5ef in -[NSObject release] ()
#10800  0x97f62b22 in -[__NSOperationInternal dealloc] ()
#10801  0x9104d5ef in -[NSObject release] ()
#10802  0x97f62ac8 in -[NSOperation dealloc] ()
#10803  0x9104d5ef in -[NSObject release] ()
#10804  0x99e41224 in CFRelease ()
#10805  0x99e56277 in -[__NSArrayM dealloc] ()
#10806  0x9104d5ef in -[NSObject release] ()
#10807  0x97f62b22 in -[__NSOperationInternal dealloc] ()
#10808  0x9104d5ef in -[NSObject release] ()
#10809  0x97f62ac8 in -[NSOperation dealloc] ()
#10810  0x9104d5ef in -[NSObject release] ()
#10811  0x99e41224 in CFRelease ()
#10812  0x99e56277 in -[__NSArrayM dealloc] ()
#10813  0x9104d5ef in -[NSObject release] ()
#10814  0x97f62b22 in -[__NSOperationInternal dealloc] ()
#10815  0x9104d5ef in -[NSObject release] ()
#10816  0x97f62ac8 in -[NSOperation dealloc] ()
#10817  0x9104d5ef in -[NSObject release] ()
#10818  0x99e41224 in CFRelease ()
#10819  0x99e56277 in -[__NSArrayM dealloc] ()
#10820  0x9104d5ef in -[NSObject release] ()
#10821  0x97f62b22 in -[__NSOperationInternal dealloc] ()
#10822  0x9104d5ef in -[NSObject release] ()
#10823  0x97f62ac8 in -[NSOperation dealloc] ()
#10824  0x9104d5ef in -[NSObject release] ()
#10825  0x99e41224 in CFRelease ()
#10826  0x99e56277 in -[__NSArrayM dealloc] ()
#10827  0x9104d5ef in -[NSObject release] ()
#10828  0x97f62b22 in -[__NSOperationInternal dealloc] ()
#10829  0x9104d5ef in -[NSObject release] ()
#10830  0x97f62ac8 in -[NSOperation dealloc] ()
#10831  0x9104d5ef in -[NSObject release] ()
#10832  0x99e41224 in CFRelease ()
#10833  0x99e56277 in -[__NSArrayM dealloc] ()
#10834  0x9104d5ef in -[NSObject release] ()
#10835  0x97f62b22 in -[__NSOperationInternal dealloc] ()
#10836  0x9104d5ef in -[NSObject release] ()
#10837  0x97f62ac8 in -[NSOperation dealloc] ()
#10838  0x9104d5ef in -[NSObject release] ()
#10839  0x99e41224 in CFRelease ()
#10840  0x99e56277 in -[__NSArrayM dealloc] ()
#10841  0x9104d5ef in -[NSObject release] ()
#10842  0x97f62b22 in -[__NSOperationInternal dealloc] ()
#10843  0x9104d5ef in -[NSObject release] ()
#10844  0x97f62ac8 in -[NSOperation dealloc] ()
#10845  0x9104d5ef in -[NSObject release] ()
#10846  0x99e41224 in CFRelease ()
#10847  0x99e56277 in -[__NSArrayM dealloc] ()
#10848  0x9104d5ef in -[NSObject release] ()
#10849  0x97f62b22 in -[__NSOperationInternal dealloc] ()
#10850  0x9104d5ef in -[NSObject release] ()
#10851  0x97f62ac8 in -[NSOperation dealloc] ()
#10852  0x9104d5ef in -[NSObject release] ()
#10853  0x99e41224 in CFRelease ()
#10854  0x99e56277 in -[__NSArrayM dealloc] ()
#10855  0x9104d5ef in -[NSObject release] ()
#10856  0x97f62b22 in -[__NSOperationInternal dealloc] ()
#10857  0x9104d5ef in -[NSObject release] ()
#10858  0x97f62ac8 in -[NSOperation dealloc] ()
#10859  0x9104d5ef in -[NSObject release] ()
#10860  0x99e41224 in CFRelease ()
#10861  0x99e56277 in -[__NSArrayM dealloc] ()
#10862  0x9104d5ef in -[NSObject release] ()
#10863  0x97f62b22 in -[__NSOperationInternal dealloc] ()
#10864  0x9104d5ef in -[NSObject release] ()
#10865  0x97f62ac8 in -[NSOperation dealloc] ()
#10866  0x9104d5ef in -[NSObject release] ()
#10867  0x99e41224 in CFRelease ()
#10868  0x99e56277 in -[__NSArrayM dealloc] ()
#10869  0x9104d5ef in -[NSObject release] ()
#10870  0x97f62b22 in -[__NSOperationInternal dealloc] ()
#10871  0x9104d5ef in -[NSObject release] ()
#10872  0x97f62ac8 in -[NSOperation dealloc] ()
#10873  0x9104d5ef in -[NSObject release] ()
#10874  0x99e41224 in CFRelease ()
#10875  0x99e56277 in -[__NSArrayM dealloc] ()
#10876  0x9104d5ef in -[NSObject release] ()
#10877  0x97f62b22 in -[__NSOperationInternal dealloc] ()
#10878  0x9104d5ef in -[NSObject release] ()
#10879  0x97f62ac8 in -[NSOperation dealloc] ()
#10880  0x9104d5ef in -[NSObject release] ()
#10881  0x99e41224 in CFRelease ()
#10882  0x99e56277 in -[__NSArrayM dealloc] ()
#10883  0x9104d5ef in -[NSObject release] ()
#10884  0x97f62b22 in -[__NSOperationInternal dealloc] ()
#10885  0x9104d5ef in -[NSObject release] ()
#10886  0x97f62ac8 in -[NSOperation dealloc] ()
#10887  0x9104d5ef in -[NSObject release] ()
#10888  0x99e41224 in CFRelease ()
#10889  0x99e56277 in -[__NSArrayM dealloc] ()
#10890  0x9104d5ef in -[NSObject release] ()
#10891  0x97f62b22 in -[__NSOperationInternal dealloc] ()
#10892  0x9104d5ef in -[NSObject release] ()
#10893  0x97f62ac8 in -[NSOperation dealloc] ()
#10894  0x9104d5ef in -[NSObject release] ()
#10895  0x99e41224 in CFRelease ()
#10896  0x99e56277 in -[__NSArrayM dealloc] ()
#10897  0x9104d5ef in -[NSObject release] ()
#10898  0x97f62b22 in -[__NSOperationInternal dealloc] ()
#10899  0x9104d5ef in -[NSObject release] ()
#10900  0x97f62ac8 in -[NSOperation dealloc] ()
#10901  0x9104d5ef in -[NSObject release] ()
#10902  0x99e41224 in CFRelease ()
#10903  0x99e56277 in -[__NSArrayM dealloc] ()
#10904  0x9104d5ef in -[NSObject release] ()
#10905  0x97f62b22 in -[__NSOperationInternal dealloc] ()
#10906  0x9104d5ef in -[NSObject release] ()
#10907  0x97f62ac8 in -[NSOperation dealloc] ()
#10908  0x9104d5ef in -[NSObject release] ()
#10909  0x99e41224 in CFRelease ()
#10910  0x99e56277 in -[__NSArrayM dealloc] ()
#10911  0x9104d5ef in -[NSObject release] ()
#10912  0x97f62b22 in -[__NSOperationInternal dealloc] ()
#10913  0x9104d5ef in -[NSObject release] ()
#10914  0x97f62ac8 in -[NSOperation dealloc] ()
#10915  0x9104d5ef in -[NSObject release] ()
#10916  0x97f49cca in __NSOQSchedule_f ()
#10917  0x9c1c9e21 in _dispatch_async_redirect_invoke ()
#10918  0x9c1c53a6 in _dispatch_client_callout ()
#10919  0x9c1c7467 in _dispatch_root_queue_drain ()
#10920  0x9c1c8732 in _dispatch_worker_thread2 ()
#10921  0x960c2dab in _pthread_wqthread ()

完整的崩溃上下文是(崩溃行的粗体):

libobjc.A.dylib`objc::DenseMapBase<objc::DenseMap<objc_object*, unsigned long, true, objc::DenseMapInfo<objc_object*> >, objc_object*, unsigned long, objc::DenseMapInfo<objc_object*>, true>::find(objc_object* const&):
0x9104d800:  pushl  %ebp
0x9104d801:  movl   %esp, %ebp
0x9104d803:  pushl  %esi
0x9104d804:  subl   $20, %esp
0x9104d807:  leal   -8(%ebp), %eax
0x9104d80a:  movl   %eax, 8(%esp)
0x9104d80e:  movl   16(%ebp), %eax
0x9104d811:  movl   %eax, 4(%esp)
0x9104d815:  movl   12(%ebp), %esi
0x9104d818:  movl   %esi, (%esp)
**0x9104d81b:  calll  0x9104d9b6                ; bool objc::DenseMapBase<objc::DenseMap<objc_object*, unsigned long, true, objc::DenseMapInfo<objc_object*> >, objc_object*, unsigned long, objc::DenseMapInfo<objc_object*>, true>::LookupBucketFor<objc_object*>(objc_object* const&, std::__1::pair<objc_object*, unsigned long> const*&) const**
0x9104d820:  movl   12(%esi), %ecx
0x9104d823:  shll   $3, %ecx
0x9104d826:  addl   (%esi), %ecx
0x9104d828:  movl   8(%ebp), %edx
0x9104d82b:  testb  %al, %al
0x9104d82d:  je     0x9104d836                ; objc::DenseMapBase<objc::DenseMap<objc_object*, unsigned long, true, objc::DenseMapInfo<objc_object*> >, objc_object*, unsigned long, objc::DenseMapInfo<objc_object*>, true>::find(objc_object* const&) + 54
0x9104d82f:  movl   -8(%ebp), %eax
0x9104d832:  movl   %eax, (%edx)
0x9104d834:  jmp    0x9104d838                ; objc::DenseMapBase<objc::DenseMap<objc_object*, unsigned long, true, objc::DenseMapInfo<objc_object*> >, objc_object*, unsigned long, objc::DenseMapInfo<objc_object*>, true>::find(objc_object* const&) + 56
0x9104d836:  movl   %ecx, (%edx)
0x9104d838:  movl   %ecx, 4(%edx)
0x9104d83b:  addl   $20, %esp
0x9104d83e:  popl   %esi
0x9104d83f:  popl   %ebp
0x9104d840:  ret    $4
0x9104d843:  nop    

我尝试使用预先创建的队列,这没有区别。显然,由于依赖关系,这段代码是 XCode 5.0 32 位的问题。

编辑:事实上,我可以进一步隔离问题。 XCOde 5.0 在 10.9 上的一个空的 Cocoa 应用程序项目在 ARC 和单个方法上会崩溃。如果您的计算机上没有, 将 4269 增加到更大的值:

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification

    NSOperationQueue* aQueue = [[ NSOperationQueue alloc ] init ];
    NSMutableArray* array = [ NSMutableArray array ];
    for ( int i = 0; i < 4269; i++)
        [ array addObject: [ [NSOperation alloc ] init  ]];

    for ( int i = 1; i < [ array count ]; i++)
        [[ array objectAtIndex:i ] addDependency:[array objectAtIndex:i-1]];

    [ aQueue addOperations: array waitUntilFinished:NO ];

编辑 2:

请注意,在一个干净的新项目中,以下代码会崩溃(ARC ON,但我认为这无关紧要)。我希望队列保持强引用(保留)NSOperation。

    NSOperationQueue 被永久保存,永远不会被释放。 没有临时 NSMutableArray。 操作一添加就开始运行

代码如下:

static NSOperationQueue* aQueue = nil;

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification

    aQueue = [[ NSOperationQueue alloc ] init ];
    NSOperation* lastOp = nil;
    for ( int i = 0; i < 5000 ; i++) 
        NSOperation* newOp = [ NSBlockOperation blockOperationWithBlock:^ ; ];
        if ( lastOp != nil )
            [ newOp addDependency: lastOp ];

        [ aQueue addOperation:newOp ];

        lastOp = newOp;
    


【问题讨论】:

如果您删除 [queue autorelease] 会发生什么? 您能发布崩溃的完整堆栈跟踪吗? 我用堆栈编辑了帖子,你会看到我认为是 NSOperation 版本的启动。 bbum:删除队列上的自动释放以进行释放或只是泄漏不会改变任何东西。这仍然崩溃。 很奇怪...我能够重现该问题。一些观察:1)似乎限制是大约 1500 次操作。 2) 将 waitUntilFinished 更改为 YES 也会阻止程序崩溃。似乎它可能是一个多线程问题。 godel9:你试过 ARC 吗?我拥有的代码库并不允许我打开 ARC。另外,您是否在一个干净、独立的 XCode 项目中重现?我从没想过调查手术的数量。我想知道这是否与完成所有操作所需的时间或队列中的操作数量有关。同样,我坚持我遇到的许多问题都是 10.9 特有的。 【参考方案1】:

新理论

好的,这是关于发生了什么的另一种理论:

1) 每个NSOperation 对象都有一个指向所有依赖项的强指针,可能使用数组。

2) NSOperation 释放dealloc 中的引用,而不是在依赖操作完成时释放。

3) 因此,您会得到一个引用链,它会阻止任何东西被释放,直到最后一个操作完成,它没有任何引用它的东西。

4) 由于队列和数组是自动释放的,它们对NSOperation 对象的引用消失了。

5)当最后一个操作完成时,它会释放一个字符串releasedealloc,数组迭代调用它,我在这里推测,会导致堆栈溢出。

我认为解决方案可能是手动保留对NSOperation 对象的引用,并在它们全部完成后故意以相反的顺序(从后到先)释放它们。不是最优雅的解决方案,但如果我的理论是正确的,它应该可以工作。

【讨论】:

不幸的是,它没有。您可以从代码中删除该自动释放或释放并泄漏 NSOPerationQueue (或使用预分配队列),无论哪种方式,错误仍然存​​在。让它消失的是 waitUntilFinished: 如果否,它会崩溃。如果是,则不是。问题是当操作完成并且依赖的长链使得可以将它们从队列中删除(文档指出如果另一个操作依赖于它们,NSOperations 不会从队列中删除)。当最后一个强制时,被删除并崩溃。 @Daniel 我有一个关于发生了什么的新理论。我现在无法访问我的开发环境,所以你必须让我知道它是否有效。 我相信这是正确的。我应该注意到大量的回溯。 +1 这个理论看起来很有道理。事实上,我将发布一个带有似乎证实这一点的代码的答案。非常感谢,godel9! @Daniel 很高兴为您提供帮助,如果此答案解决了您的问题,请单击答案旁边的复选标记将其标记为已接受。请参阅:How does accepting an answer work? 了解更多信息【参考方案2】:

基于 Daniel 和 Godel9 的分析,可以通过在执行时清理 NSOperation 依赖项来解决该问题:

@implementation MyOperation 

- (void) main 

  ...
  NSArray* dependencies = self.dependencies;
  for (NSOperation* op in dependencies)
    [self removeDependency:op];


@end

确实,当一个操作执行时,它的依赖数组中的所有操作都完成了。不再需要在它们上保留指针。

【讨论】:

【参考方案3】:

看来 Godel9 是对的:当最后一个操作完成时,依赖项中的引用链会带来一连串的 dealloc(我知道但不知道这会导致问题)。如果该链中有足够多的对象,则会发生崩溃。为了确认这一点,我编写了一个带有自定义对象(Holder)的程序,该对象保留了另一个对象,该对象保留了另一个对象等......并且我自动释放所有对象。就释放/保留计数而言,这是完全有效的:除第一个之外的所有对象的保留计数都高于 1。在下一个自动释放周期中,第一个对象被自动释放,并且是唯一不被任何人引用的对象。它的释放触发了一系列的 dealloc。对于足够多的对象,这最终会崩溃,堆栈跟踪类似于我原来的问题。我很惊讶这会因 BAD_EXEC 故障而崩溃。我期待一些类似于 STACK_OVERFLOW 的东西(如果存在,我不知道)。

事后看来,这很明显(他们说事后看来是 20/20)。不过,我感谢 godel9 在 NSOperationQueue 中的依赖关系上下文中指出问题。

此外,Godel9 提出的解决方案(保留一个包含所有对象引用的数组)如果对象从末尾释放,则将起作用,因为最后一个对象没有指向任何东西,也不会触发级联的 dealloc。

这是代码(Foundation Tool 项目)我不确定为什么计数需要这么高,但崩溃发生在相同的位置:

#import <Foundation/Foundation.h>

@interface Holder : NSObject

@property (strong) id object;
@property (assign) int i;

- (void) dealloc;
@end

@implementation Holder

- (void) dealloc

    [ _object release ];
    [ super dealloc ];


@end

int main(int argc, const char * argv[])

    @autoreleasepool 
        Holder* lastObject = nil;

        for ( int i = 0; i < 1000000; i++) 
            Holder* anObject = [[[ Holder alloc ] init] autorelease ];

            if ( lastObject != nil )
                lastObject.object = anObject;

            lastObject = anObject;
        
    


【讨论】:

【参考方案4】:

几年过去了......所以我不知道是否有人需要解决方案。 崩溃是由级联释放的堆栈溢出引起的。所以解决办法之一就是及时打破这个版本。

@implementation SafeOperation

- (void)dealloc 
    __block NSArray<NSOperation *> *dependencies = nil;

    @autoreleasepool 
        dependencies = self.dependencies;
        if ([dependencies count] == 0) 
            return;
        

        for (NSOperation *op in dependencies) 
            [self removeDependency:op];
        
    

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^
         dependencies = nil;
    );


@end

【讨论】:

【参考方案5】:

您的数组正在从您下方释放。如果对数组的引用超出了方法的范围(一直到 10,000),我运行您的示例没有问题。这也解释了为什么如果您设置waitUntilFinished:YES,您的代码运行良好。

这并不总是以较小的数字崩溃的原因是数组占用的内存尚未重新分配给其他东西。

另外,NSOperation 是一个abstract 类;你不应该实例化它。

这是对您的代码的修改,不会崩溃(需要 ARC)

static NSMutableArray* array;
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification

    NSOperationQueue* aQueue = [[ NSOperationQueue alloc ] init ];
    aQueue.maxConcurrentOperationCount = 1;
    array = [ NSMutableArray arrayWithCapacity:10000];

    @autoreleasepool 
        for ( int i = 0; i < 10000; i++)
            [ array addObject: [NSBlockOperation blockOperationWithBlock:(void (^)(void))^
                NSLog(@"executing %d", i);
            ]];
    
    for ( int i = 1; i < [ array count ]; i++)
        [[ array objectAtIndex:i ] addDependency:[array objectAtIndex:i-1]];

    [ aQueue addOperations: array waitUntilFinished:NO ];

据我所知,您只是想保证您的操作的串行执行。为什么不直接做aQueue.maxConcurrentOperationCount = 1;

【讨论】:

我不认为这是正确的:你可以泄漏队列,这很好。我还使用了另一个分配在其他地方的队列,但它仍然崩溃。在其他 cmets 中,您会看到 NSBlockOperation 可以与空块一起使用,但它仍然会崩溃。 NSOperation 是抽象的,可以实例化,它什么也不做。在帖子的最后,我添加了一个崩溃的方法项目,即使您释放或不释放队列。你也可以在没有可变数组的情况下重写代码,但它仍然会崩溃,至少在我的机器上是这样。 @Daniel 是的,我正在使用您的示例代码并在 ARC 的 10.9 上运行良好。我指的是NSOperations 数组,不是 队列。除非我对方法调用之外的数组有强引用,否则它会崩溃。 如果没有数组,这会在一个空的 Cocoa App 项目中崩溃- (void)applicationDidFinishLaunching:(NSNotification *)aNotification NSOperationQueue* aQueue = [[ NSOperationQueue alloc ] init ]; NSOperation* lastOp = nil; for ( int i = 0; i &lt; 5000 ; i++) NSOperation* newOp = [ NSBlockOperation blockOperationWithBlock:^ NSLog(@"%d", i); ]; if ( lastOp != nil ) [ newOp addDependency: lastOp ]; [ aQueue addOperation:newOp ]; lastOp = newOp; 文档说不能保证顺序的原因是您可以有多个并发操作以及依赖关系。如果您只将队列用于这些相同的操作而没有依赖关系并将 maxConcurrentOpperation 设置为 1,那么操作将按 FIFO 顺序进行。 在我的代码中使用 NSOperation 和 NSOperationQueue 的全部意义在于将一系列相互依赖的操作排队。我遇到了崩溃并将其缩小到上面显示的代码。如果我不能使用依赖项,那么这是有问题的。可能只是我用错了,但在这一点上,我没有看到问题。【参考方案6】:

您在子级之前删除父级,您会遇到崩溃。

在树状结构中很常见,其中叶子知道(但不保留,而不是绕圈子)。

在这里看不到错误

【讨论】:

这正在成为一个长线程,但是有一些例子表明,对数组和队列的强引用,代码仍然会崩溃。查看问题中的第二个编辑。

以上是关于具有依赖关系的 NSOperationQueue 错误的主要内容,如果未能解决你的问题,请参考以下文章

gcd和NSOperationQueue区别

GCD与NSOperationQueue

多线程

NSOperationQueue 串行 FIFO 队列

领域驱动设计:如何设计具有依赖关系的关系聚合

避免具有异步数据依赖关系的事件链