最新の10.8.2オペレーティングシステムをインストールし、Xcodeを4.5.2にアップグレードしてから、長い間機能していた自分が作成したアプリケーションで問題が発生し始めました。
アプリケーションには、文字列の配列であるivarNSMutableArrayを構築するバックグラウンドキューでそれ自体を実行するメソッドを持つクラス(CalculateTimeFiles)があります。この配列とステータスカウンターは、NSMutableArrayからメッセージを取り出し、ウィンドウ内のスクロールテキストビューボックスに表示する2番目のクラス(RunResultWindow)によって監視されます。メッセージはCalculateTimeFilesによって生成されるため、RunResultWindowはメッセージを取得してテキストビューにドロップし、CalculateTimeFilesが現在何をしているかをユーザーに知らせます。
このプロセスは長い間正常に機能していましたが、この新しいバージョンのXcodeでは、ARCがディスパッチキューに対して有効になっていると思います(これは新しいことだと思います)。コードはXcodeで正常に実行されますが、アプリケーションをXcodeの外部にエクスポートしてそこで実行すると、爆発します。私が見つけたのは、バックグラウンドのディスパッチキュータスクの最後に何かが発生し、それが原因ですべてがおかしくなっているということです。Xcode自体がCalculateTimeFilesのivarに対してある程度の可観測性を確立し、何かが消えないようにしているため、Xcodeで機能していると思います。
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で機能しているものです。ARCがこれをカバーするようになったため、dispatch_releaseメソッド呼び出しを削除したことに注意してください。
@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
繰り返しますが、私がこれに乗ることができるどんな助けも素晴らしいでしょう。前もって感謝します。