【问题标题】:Trouble with ARC and background dispatch queuesARC 和后台调度队列的问题
【发布时间】:2012-11-11 02:39:06
【问题描述】:

自从我安装了最新的 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 中的字符串不是在后台任务中定义的,而是在我强制到主队列的单独方法中定义的。

在这方面我能得到的任何帮助都会很棒。

这里有一些相关的sn-ps代码:

这是来自主应用委托:

- (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

再一次,我能得到的任何帮助都会很棒。提前致谢。

【问题讨论】:

  • 开始创建一个克隆项目,该项目使用 xcode 4.5.2 的 10.8 出现问题的基本元素。如果没有程序的 KVO 观察部分,它可以正常工作。下一步是添加它(它有点复杂,看看它是否仍然有效)。
  • 呃...完成了复制,复制的代码现在可以正常工作了。我确实想知道这是否必须对一些背景标志​​做一些事情,当这个项目被引入最新的 Xcode 时,这些标志没有被切换。复制的代码是在最新的 Xcode 中从头开始完成的,这就是它运行正常的原因。叹息。

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


【解决方案1】:

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

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

【讨论】:

    【解决方案2】:

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

    希望一切顺利。

    【讨论】:

    • 在后台块之外尝试了 dispatch_release 并作为后台块的最后一条语句,但均未成功。这源于升级后立即发生的相同问题。在升级之前,我将该声明作为后台线程中的最后一件事,它运行良好。你能告诉我为什么程序会因为我在上面留下的小 sn-p 转储而崩溃吗?
    • 我将目标更改为 OS 10.8,但由于 ARC,编译器不会接受任何一条语句,我仍然遇到同样的问题。如果您有任何想法,我什至可以捕获问题的直接原因,这将很有帮助。
    • 哦,是的,对于 10.8 和 ios6,他们已经删除了 dispatch_release。所以,我错了,你的情况不需要 dispatch_release。
    猜你喜欢
    • 2012-01-26
    • 1970-01-01
    • 2018-03-15
    • 2017-10-26
    • 2023-03-28
    • 1970-01-01
    • 1970-01-01
    • 2011-12-06
    • 1970-01-01
    相关资源
    最近更新 更多