0

コア データ モデル

私の最初の iPad アプリには上記の CoreData Model があります。以下に示すように、TableViewController でフィルタリング システムを構築しています。問題は、UI を変更するたびに、ボタンをタップするスイッチを切り替えると、UI が 1 ~ 2 秒間応答しなくなることです。写真のフェッチ リクエストを再作成する非常に長い関数を実行し、さらにカウント フェッチを実行して、コントロールを有効にする必要があるかどうかを判断します。ハングを防ぐ意味のある方法でこれを分解する方法がわかりません。1 秒ほど回転するビューを追加する必要がある場合でも、それで満足しています。ラグをなくしたいだけです。

前述したように、これは iOS 開発の最初の試みなので、何か提案をいただければ幸いです...

ここに画像の説明を入力

-(void) refilterPhotos {
/*
*   First section builds the NSCompoundPredicate to use for searching my CoreData Photo objects. 
    Second section runs queries so 0 result controls can be disabled.
*/
subpredicates = [[NSMutableArray alloc] init];

NSPredicate *isNewPredicate;
if(newSwitch.on) {
    isNewPredicate = [NSPredicate predicateWithFormat:@"is_new == 1"];
} else {
    isNewPredicate = [NSPredicate predicateWithFormat:@"is_new == 0"];
}

[subpredicates addObject:isNewPredicate];

//Photo Types
PhotoType *photoType;
NSPredicate *photoTypePredicate;
for (UISwitch *photoSwitch in photoSwitches) {
     PhotoType * type = (PhotoType *) photoSwitch.property;
    if([type.selected boolValue] == YES) {
        NSLog(@"photo_type.label == %@", type.label);
        photoType = type;
        photoTypePredicate = [NSPredicate predicateWithFormat:@"photo_type.label == %@", type.label];
        break;
    }
}

//Feed Types
FeedType *feedType;
NSPredicate *feedTypePredicate;
for (UISwitch *feedSwitch in feedSwitches) {
    FeedType * type = (FeedType *) feedSwitch.property;
    if([type.selected boolValue] == YES) {
        NSLog(@"feed_type.label == %@", type.label);
        feedType = type;
        feedTypePredicate = [NSPredicate predicateWithFormat:@"feed_type.label == %@", type.label];
        break;
    }
}

//Markets
NSArray *filteredMarkets = [model.availableMarkets filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"selected == 1"]];
for (Market *market in filteredMarkets) {
    [subpredicates addObject:[NSPredicate predicateWithFormat:@"ANY markets.name == %@", market.name]];
}


//Tags
NSArray *filteredTags = [model.availableTags filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"selected == 1"]];
for (Tag *tag in filteredTags) {
    NSLog(@"ANY tags.name == %@",tag.name);
    [subpredicates addObject:[NSPredicate predicateWithFormat:@"ANY tags.name == %@", tag.name]];
}

if(photoTypePredicate)
    [subpredicates addObject:photoTypePredicate];
if(feedTypePredicate)
    [subpredicates addObject:feedTypePredicate];
NSPredicate *finished = [NSCompoundPredicate andPredicateWithSubpredicates:subpredicates];//Your final predicate

model.availablePhotos = [model fetchPhotoswithPredicate:finished];
[[self parentViewController] setTitle:[NSString stringWithFormat:@"%d items",[model.availablePhotos count]]];

NSLog(@"FILTERED PHOTOS:::: %d", [model.availablePhotos count]);
[gridVC reloadGrid];   

/**
 *  Filtering Section Here, I'm running count requests for each grouping of controls to ensure if they're selected, results will be returned.
 *  If zero results, I'll disable that control. For the switch-based controls, I need to removed them before running my fetches since there can only be
 *  one switch value per photo.
 */



//Have to remove the existing type predicate since they're exlcusive values
[subpredicates removeObject:isNewPredicate];

//New Toggle
NSPredicate *newRemainderPredicate = [NSPredicate predicateWithFormat:@"is_new == %d",newSwitch.on?0:1];
[subpredicates addObject:newRemainderPredicate];

if([model countPhotoswithPredicate:[NSCompoundPredicate andPredicateWithSubpredicates:subpredicates]]<1) {
    [newSwitch setEnabled:NO];
} else {
    [newSwitch setEnabled:YES];
}

[subpredicates removeObject:newRemainderPredicate];
[subpredicates addObject:isNewPredicate];



[subpredicates removeObject:photoTypePredicate];
//Photo Type Toggles
NSArray *remainderPhotoTypes = [photoSwitches filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"on == NO"]];
for( UISwitch*control in remainderPhotoTypes) {
    PhotoType *remainderPhotoType = (PhotoType*)control.property;

    [subpredicates addObject:[NSPredicate predicateWithFormat:@"photo_type == %@", remainderPhotoType]];
    if([model countPhotoswithPredicate:[NSCompoundPredicate andPredicateWithSubpredicates:subpredicates]]<1) {
        //NSLog(@"PHOTOTYPE OFF %@", remainderPhotoType.label);
        control.enabled = NO;
    } else {
        //NSLog(@"PHOTOTYPE ON %@ count = %d", remainderPhotoType.label, [[model fetchPhotoswithPredicate:[NSCompoundPredicate andPredicateWithSubpredicates:subpredicates]] count]);
        control.enabled = YES;
    }
    remainderPhotoType.enabled = [NSNumber numberWithBool:control.enabled];
    [subpredicates removeObject:[NSPredicate predicateWithFormat:@"photo_type == %@", remainderPhotoType]];
}
if(photoTypePredicate)
[subpredicates addObject:photoTypePredicate];



[subpredicates removeObject:feedTypePredicate];
//Feed Type Toggles
NSArray *remainderFeedTypes = [feedSwitches filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"on == NO"]];
for( UISwitch*control in remainderFeedTypes) {
    PhotoType *remainderFeedType = (PhotoType*)control.property;

    [subpredicates addObject:[NSPredicate predicateWithFormat:@"feed_type == %@", remainderFeedType]];
    if([model countPhotoswithPredicate:[NSCompoundPredicate andPredicateWithSubpredicates:subpredicates]]<1) {
        control.enabled = NO;
    } else {
        control.enabled = YES;

    }
    remainderFeedType.enabled = [NSNumber numberWithBool:control.enabled];
    [subpredicates removeObject:[NSPredicate predicateWithFormat:@"feed_type == %@", remainderFeedType]];
}
if(feedTypePredicate)
[subpredicates addObject:feedTypePredicate];



NSArray *remainderMarkets = [[model availableMarkets] filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"selected == 0"]];
//Markets..many-to-many so I don't remove the existing predicate
for( Market *remainderMarket in remainderMarkets) {
    [subpredicates addObject:[NSPredicate predicateWithFormat:@"ANY markets == %@", remainderMarket]];
     NSInteger countForTag = [model countPhotoswithPredicate:[NSCompoundPredicate andPredicateWithSubpredicates:subpredicates]];

    if(countForTag<1) {
        remainderMarket.enabled = [NSNumber numberWithInt:0];
    } else {
        remainderMarket.enabled = [NSNumber numberWithInt:1];
    }
    [subpredicates removeObject:[NSPredicate predicateWithFormat:@"ANY markets == %@", remainderMarket]];
}



NSArray *remainderTags = [[model availableTags] filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"selected == 0"]];
//TAGS..many-to-many so I don't remove the existing predicate 
int tagCounter = 0;
for( Tag *remainderTag in remainderTags) {
    [subpredicates addObject:[NSPredicate predicateWithFormat:@"ANY tags == %@", remainderTag]];
    NSInteger countForTag = [model countPhotoswithPredicate:[NSCompoundPredicate andPredicateWithSubpredicates:subpredicates]];
    if(countForTag<1) {
        NSLog(@"TAG OFF %@", remainderTag.name);
        remainderTag.enabled = 0;
    } else {
        NSLog(@"TAG ON %@ count = %d", remainderTag.name, countForTag);
        remainderTag.enabled = [NSNumber numberWithInt:1];
    }
    [subpredicates removeObject:[NSPredicate predicateWithFormat:@"ANY tags.name == %@", remainderTag.name]];
    tagCounter ++;
} 
//Update the controls with this new data
[self.tableView reloadData];    

}

4

1 に答える 1

1

OK、ここで考慮すべきことがいくつかあります。

まず、主要な検索フィールドのインデックスを作成することを検討します。インデックスがないと、各レコードの値をチェックする必要があるため、各検索は直線的になります。インデックスを使用すると、検索時間が大幅に短縮されます。

2 つ目は、複合述語での順序付けには細心の注意を払うことです。順序に基づいてそれらをフィルタリングします。したがって、最も高速で、ほとんどのフィルタリング述語を最初に作成する必要があります。可能な解空間をできるだけ早くトリムします。

最初の 1 から 3 の述語で使用する属性にインデックスを付けることで、多くのことを得ることができます。下部にあるように、カウントを照会するときは、同じ複合述語を使用していることに注意してください。あなたは本当にそれをしたいですか?また、このコードでは

//Have to remove the existing type predicate since they're exlcusive values
[subpredicates removeObject:isNewPredicate];
//New Toggle
NSPredicate *newRemainderPredicate = [NSPredicate predicateWithFormat:@"is_new == %d",newSwitch.on?0:1];
[subpredicates addObject:newRemainderPredicate];

is_new チェックを前面から取り外し、背面に配置しています。この 1 つの述語をチェックしてそのスイッチを切り替えるだけで、0 個以上あるかどうかだけを確認する場合は、複合述語全体を使用する必要があります。「トグル」は、他のすべてのフィールドに対してオン/オフになりますか?

これを続ける場合は、最初に他のすべての述語を実行することを覚えておいてください (一部は参照です)。できるだけ早くフィルタリングできるように、それらを適切な順序に保つようにしてください。

第 3 に、参照の使用は便利ですが、コストがかかります。これらを個別にクエリし、複合述語を使用してメモリ内オブジェクトをフィルター処理することで、パフォーマンスを向上させることができます。

第 4 に、これらすべてのクエリを別のスレッドで実行する必要があります。これは非常に簡単に実行できますが、正確な方法は現在の ManagedObjecttContext の配置によって異なります。単一の MOC、親子関係、UIManagedDocument はありますか? 基本的に、別の MOC を作成し、performBlock を呼び出してフェッチを実行できます。実際、複数の MOC を使用して、これらすべてのフェッチを同時に非同期に起動できます。

次に、それらが完了したら、メインスレッドを呼び出すだけです。

最後に、データベースの非正規化を検討することをお勧めします。より多くのスペースを使用することになりますが、フェッチははるかに高速になります。具体的には、関係フィールド...写真/フィードラベルを写真自体に入れることができます。そうすれば、検索時にそれらのレコードを取得するために余分な結合を行う必要がなくなります。

したがって、単純な答えではありませんが、これらをそれぞれ実装して、パフォーマンスが大幅に向上しないかどうかを確認してください (UI の応答性は言うまでもありません)。

于 2012-04-30T22:39:48.083 に答える