ARC和后台调度队列出现问题



自从我安装了最新的10.8.2操作系统并将Xcode升级到4.5.2之后,我编写的一个应用程序开始出现问题,这个应用程序已经运行了很长时间。

该应用程序有一个类(CalculateTimeFiles),该类有一个在后台队列上运行的方法,该队列构建一个ivar NSMutableArray,该数组是一个字符串数组。此数组和状态计数器由第二个类(RunResultWindow)观察,该类将消息从NSMutableArray中删除,并在窗口内的滚动文本视图框中显示。由于消息是由CalculateTimeFiles生成的,RunResultWindow会将它们拾取并放到文本视图中,以便用户了解CalculateTime Files当前正在做什么。

这个过程已经运行了很长一段时间,但我想在这个新版本的Xcode中,ARC现在可以用于调度队列(我认为这是一个新事物)。代码在Xcode中运行良好,但当我将应用程序导出到Xcode之外并在那里运行时,代码会崩溃。我发现,在后台调度队列任务结束时发生了一些事情,导致整个任务失败。我认为它在Xcode中起作用,因为Xcode本身已经在CalculateTimeFiles中建立了对ivar的一些可观察性,并防止某些东西消失。

我已经确保(我认为)ivar NSMutableArray中的字符串不是在后台任务中定义的,而是在一个单独的方法中定义的。

在这件事上我能得到的任何帮助都会很棒。

以下是一些相关的代码片段:

这是来自主应用程序代理:

- (IBAction)runButtonPressed:(id)sender
{
......
......
CalculateTimeFiles* tempCalculateTimeFiles = [[CalculateTimeFiles alloc] init];
RunResultWindowController = [[RunResultWindow alloc]       
initWithWindowNibName:@"RunResultWindow"];
RunResultWindowController.localCalculateTimeFiles=tempCalculateTimeFiles;
[RunResultWindowController showWindow:self];
[self.outputfilestring1 becomeFirstResponder];
NSLog(@"before calculate time files");
[tempCalculateTimeFiles calculateOutputFiles:self];
NSLog(@"after calculate time files");

......
......
}

以下是RunResultWindow中的方法:

- (void)windowDidLoad
{
[super windowDidLoad];
NSWindow *wcWindow;
wcWindow = [self window];
[wcWindow makeKeyAndOrderFront:self];
NSString *teststring;
teststring = @"start output calculations";
[RunResultWindowTextView setString:teststring];
[RunResultWindowTextView display];
localCalculateTimeFiles.localRunResultWindow = self;
[localCalculateTimeFiles addObserver:self
forKeyPath:@"arraystatuscounter"
options:NSKeyValueObservingOptionNew
context:NULL];
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:
(NSDictionary *)change context:(void *)context
{    
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_async(mainQueue, ^{ 
NSInteger arrayCountFromDictionary;
NSString* localDisplayString;
NSString* localNewlinePlusDisplayString;
NSTextStorage *tempTextStorage;
tempTextStorage = [RunResultWindowTextView textStorage];
NSLog(@"in observeValueForKeyPath before display");
arrayCountFromDictionary = [[change objectForKey:NSKeyValueChangeNewKey] integerValue];
if (arrayCountFromDictionary != 0 ){
arrayCountFromDictionary--;
localDisplayString = [localCalculateTimeFiles.StatusStrings
objectAtIndex:arrayCountFromDictionary];
if (![localDisplayString compare: @"removeobservernow"]){
NSLog(@"planned removeobserver logic");
[localCalculateTimeFiles removeObserver:self forKeyPath:@"arraystatuscounter"];}
if ([localDisplayString compare: @"removeobservernow"]){
localNewlinePlusDisplayString = [@"n" 
stringByAppendingString:localDisplayString];
[tempTextStorage beginEditing];
[tempTextStorage replaceCharactersInRange:NSMakeRange([tempTextStorage length] -
1, 0) withString:localNewlinePlusDisplayString];
[tempTextStorage endEditing];
NSLog(@"string before display %@",localDisplayString);
[RunResultWindowTextView display];}};
NSLog(@"in observeValueForKeyPath after display");

});
}

以下是CalculateTimeFiles的作用。请注意,我删除了dispatch_release方法调用,因为ARC现在涵盖了以下内容:

@interface CalculateTimeFiles : NSObject {
NSMutableArray *StatusStrings;
NSInteger arraystatuscounter;
RunResultWindow *localRunResultWindow;
}
@property (nonatomic, retain) NSMutableArray *StatusStrings;
@property  NSInteger arraystatuscounter;
@property RunResultWindow *localRunResultWindow;
- (void) calculateOutputFiles:(id) parameterTimeGenieAppDelegate;
- (void) UpdateStatusTable:(NSString*) statusStringMessage;
@end

- (void) calculateOutputFiles:(TimeGenieAppDelegate*) parameterTimeGenieAppDelegate;
{
dispatch_queue_t backgroundQueue = dispatch_queue_create("Background Queue",NULL);
dispatch_async(backgroundQueue, ^{ 
.... does a bunch of stuff...
[self UpdateStatusTable:@"stop here a long time"];
[self UpdateStatusTable:@"new test string very first one"];
[self UpdateStatusTable:@"===================="];
[self UpdateStatusTable:@"new test string"];
[self UpdateStatusTable:@"processing complete"];
[self UpdateStatusTable:@"removeobservernow"];
NSLog(@"just before dispatch release");
// dispatch_release(backgroundQueue);
[NSThread sleepForTimeInterval: 3.0];
NSLog(@"just after dispatch release");
});
NSLog(@"just after thread done");
}

- (void) UpdateStatusTable:(NSString*) statusStringMessage
{
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_async(mainQueue, ^{
[StatusStrings addObject:[NSString stringWithString:statusStringMessage]];
[self setArraystatuscounter:[StatusStrings count]];
});
}

我假设(这可能是个坏主意)UpdateStatusTable中的addObject会创建一个新的字符串,当后台进程完成时,这个字符串不会消失。

我也有崩溃转储,但我真的不知道如何阅读它们,所以也许其中有一些有用的东西,但我不知道。其中唯一对我有意义的部分是:

Crashed Thread:  2  Dispatch queue: com.apple.root.default-overcommit-priority
Exception Type:  EXC_BAD_ACCESS (SIGSEGV)
Exception Codes: EXC_I386_GPFLT
Thread 2 Crashed:: Dispatch queue: com.apple.root.default-overcommit-priority
0   libobjc.A.dylib                 0x00007fff8ea3bf5e objc_release + 14
1   libobjc.A.dylib                 0x00007fff8ea3b230 (anonymous
namespace)::AutoreleasePoolPage::pop(void*) + 464
2   libdispatch.dylib               0x00007fff93eb2264 _dispatch_worker_thread2 + 410
3   libsystem_c.dylib               0x00007fff8e0b4cab _pthread_wqthread + 404
4   libsystem_c.dylib               0x00007fff8e09f171 start_wqthread + 13

再说一遍,我能在这方面得到的任何帮助都将是非常棒的。提前谢谢。

Arc不释放GCD对象,您需要在块的末尾自己释放它。请参阅下面我的编辑;

- (void) calculateOutputFiles:(TimeGenieAppDelegate*) parameterTimeGenieAppDelegate;
{
dispatch_queue_t backgroundQueue = dispatch_queue_create("Background Queue",NULL);
dispatch_async(backgroundQueue, ^{ 
.... does a bunch of stuff...
[self UpdateStatusTable:@"stop here a long time"];
[self UpdateStatusTable:@"new test string very first one"];
[self UpdateStatusTable:@"===================="];
[self UpdateStatusTable:@"new test string"];
[self UpdateStatusTable:@"processing complete"];
[self UpdateStatusTable:@"removeobservernow"];
NSLog(@"just before dispatch release");
[NSThread sleepForTimeInterval: 3.0];
NSLog(@"just after dispatch release");
});
dispatch_release(backgroundQueue); // dispatch_release should be here
NSLog(@"just after thread done");
}

希望一切顺利。

下载了Xcode 4.6的测试版本,这就解决了问题。那是令人沮丧的6周。

问题实际上是一个方法中的临时对象,该方法用于临时旧NSMutableArray中对象的地址。该对象是在该方法的"main"例程中声明的,然后在循环中重复使用,该循环将对象数组中项的地址搅乱到临时对象,以便可以使用它。当循环结束时,程序在xcode之外爆炸,因为(我认为)xcode本身已经建立了对临时对象的可观察性,并在循环(具有自己的作用域范围)被释放时阻止了它被释放。SOOOOOO。。。如果您遇到了只在Xcode之外发生的奇怪的释放问题,请考虑4.5.2可能是您的问题。

最新更新