4

ストーリーボードのUINavigationControllerプッシュセグエで実現されたドリルダウンの詳細を備えた単純なUIViewTableがあります。詳細ビューを表示しているときに、テーブルビューコントローラーの割り当てが解除されているように見えることがあります。そのため、有名なものが表示されます。

[MyViewController controllerWillChangeContent:]: message sent to deallocated instance

よりよく説明すると、データを非同期でロードし、終了するとすぐにテーブルを埋めるNSOperationキューがあります。データが正しく取得され、テーブルが埋められます。詳細ビューでは、セルをクリックして、prepareForSegueメソッドでNSManagedObjectIDを宛先コントローラーに渡します。詳細ビューに変更を加えると、フェッチされたコントローラーがデリゲートを失うか、コントローラーであるデリゲート自体の割り当てが解除されます。クラッシュを引き起こします。

フェッチされた結果コントローラーは、プロパティとして宣言されます。

@property(nonatomic,strong) NSFetchedResultsController *fetchedResultsController;

次に、これがviewDidLoadから始まるすべてがどのように機能しているかです。

- (void)viewDidLoad {
    [super viewDidLoad];
    [self loadDataAsynchronously];
}

-(void)loadDataAsynchronously {

    NSOperationQueue *queue = [NSOperationQueue new];
    NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self
                                                                            selector:@selector(loadData)
                                                                              object:nil];

    [queue addOperation:operation];
}


-(void)loadData {

    NSFetchRequest *findAllEntities = [[NSFetchRequest alloc] init];
    [findAllEntities setEntity:ENTITY_DESC];

    NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:@"created" ascending:YES];
    [findAllEntities setSortDescriptors:[NSArray arrayWithObject:sort]];
    [findAllEntities setFetchBatchSize:20];

    [NSFetchedResultsController deleteCacheWithName:@"MyCache"];
    if(self.fetchedResultsController==nil) {
        self.fetchedResultsController = [[NSFetchedResultsController alloc] 
                                            initWithFetchRequest:findAllPlants
                                            managedObjectContext:MOC
                                            sectionNameKeyPath:nil 
                                            cacheName:@"MyCache"];

        self.fetchedResultsController.delegate=self;
    }
    NSError *error=nil;
    if (![FRC performFetch:&error]) {
        exit(EXIT_FAILURE);
    }

    [self.dataTableView performSelectorOnMainThread:@selector(reloadData) withObject:nil waitUntilDone:YES];    

}

このコードは機能し、ほとんどの場合、次のようなセグエと呼ばれる詳細ビュー内でも機能します。

-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {

    IP2SegueIdentifier segueIdentifier = [IP2Factory segueSolver:[segue identifier]];  
    MyDestinationViewController *dvc = [segue destinationViewController];
    NSIndexPath *indexPath = [TV indexPathForSelectedRow];
    dvc.entityID=[[self.fetchedResultsController objectAtIndexPath: indexPath] objectID];

}

宛先コントローラーはエンティティIDを正しく取得し、コンテキストを尋ねることによってオブジェクトを再構築します。次に、詳細ビューコントローラにいるときにエンティティに変更を加えることができ、ナビゲーション階層に戻るときにコンテキスト保存を実行します。この時点で、コンテキストの保存時にアプリケーションがクラッシュします。それほど頻繁ではありませんが、時々です。フェッチされた結果コントローラーは変更を認識し、既に割り当てが解除されているデリゲートに送信されるためです。

この時点で私はほとんど疑問を持っていません。私はiOS5とARCを使用しているので、コンパイラーはリリースとdeallocメソッドを(ほぼ)完全に制御できるはずです。また、単純なナビゲーション階層を備えたストーリーボードを使用しています。これにより、以前のViewControllerチェーン全体が保持されることが保証されます。

また、メモリリーク/ゾンビ分析のためにプロファイラーを実行しましたが、何も問題を見つけることができませんでした。それどころか、すべてのオブジェクト管理が正常であったことを嬉しく思いました。

現時点では推測が少ないので、確認し忘れた可能性のあるものや、コードに誤りがあると思われるものをお気軽にご指摘ください。

ありがとう

4

1 に答える 1

22

まず、ARCについてのメモ。ARCは自動ゼロ調整weakポインターを提供しますが、ポインターを自動ゼロ調整しませんassign。デリゲートにプロパティをNSFetchResultsController使用します(を参照)。assignNSFetchedResultsController.h

@property(nonatomic, assign) id< NSFetchedResultsControllerDelegate > delegate;

割り当てを解除する前に、代理人として自分自身をクリアするのは依然としてあなたの責任です。通常、これは次の場所で行いますdealloc

- (void)dealloc {
  _fetchedResultsController.delegate = nil;
}

また、自分を削除することもできます(代理人としての自分を削除することを含む)fetchedResultsControllerviewWillDisappear:通常、画面外にいるときにフェッチリクエストを残したくない場合があります。(そうする場合は、ビューコントローラーではなくモデルオブジェクトでフェッチを管理する必要があります。ビューコントローラーは、ビューが画面外にあるときはいつでも消えてしまう可能性があるためです。)

loadDataを作成するという点で奇妙ですがfindAllEntities、実際にはを使用しfindAllPlantsます。これはタイプミスですか、それともバグですか?ivarに個別のfindAllPlantsフェッチ要求がある場合、これも問題の原因である可能性があります。

于 2012-02-09T17:42:13.283 に答える