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

时间:2012-11-22 16:51:04

标签: objective-c xcode4.5 observer-pattern grand-central-dispatch crash-dumps

因为我已经安装了最新的10.8.2操作系统并将Xcode升级到4.5.2,我已经开始遇到我写的一个已经工作了很长时间的应用程序的问题。

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

这个过程已经运行了很长时间但我想在这个新版本的Xcode中,ARC现在已经启用了调度队列(我认为这是一个新事物)。代码在Xcode中运行正常,但是当我在Xcode之外导出应用程序并在那里运行它时会爆炸。我发现的是,在后台调度队列任务结束时会发生一些事情,导致整个事情变得瘫痪。我假设它在Xcode中工作,因为Xcode本身已经为CalculateTimeFiles中的ivars建立了一些可观察性,并防止某些东西消失。

我已经确定(我认为)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

同样,我能得到的任何帮助都会很棒。提前谢谢。

2 个答案:

答案 0 :(得分:0)

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");

}

希望一切顺利。

答案 1 :(得分:0)

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

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