具有依赖关系的 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)当最后一个操作完成时,它会释放一个字符串release
,dealloc
,数组迭代调用它,我在这里推测,会导致堆栈溢出。
我认为解决方案可能是手动保留对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 < 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 错误的主要内容,如果未能解决你的问题,请参考以下文章