iphone - 应用程序对 NSFetchRequests 没有响应

标签 iphone ios core-data nsmanagedobjectcontext nsfetchrequest

Core Data Model

我的第一个 iPad 应用程序中有上述 CoreData 模型。我正在 TableViewController 中构建一个过滤系统,如下所示。问题是,每当我更改 UI、切换点击按钮的开关时,我的 UI 都会在一两秒内无响应。我运行一个非常长的函数,重新创建照片的获取请求,然后运行更多计数获取以确定是否应启用该控件。我只是不知道如何以一种有意义的方式将其分解以防止挂起。即使我需要添加一秒钟左右的旋转 View ,我也对此感到满意。只是想摆脱滞后。

正如我所提到的,这是我第一次尝试 iOS 开发,因此我将不胜感激任何建议......

enter image description here

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

}

最佳答案

好的,这里有几件事需要考虑。

首先,我会考虑为主要搜索字段创建索引。如果没有索引,每次搜索都是线性的,因为它必须检查每条记录的值。索引将导致更快的搜索时间。

其次,我在复合谓词中进行排序时会非常小心。它将根据顺序过滤它们。因此,您希望首先创建最快、最多的过滤谓词。尽快修剪可能的解空间。

通过对前 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 检查,并将其放在后面。如果您只是检查这个谓词来切换该开关,并且您只关心是否有 0 个或多个,为什么还要使用整个复合谓词呢? “切换”是否会相对于所有其他字段打开/关闭?

如果您继续这样做,请记住,它将首先执行所有其他谓词(有些是引用)。尽量让它们保持良好的顺序,以便尽可能快地过滤。

第三,使用引用很方便,但成本很高。您可以通过单独查询这些对象,然后使用复合谓词来过滤内存中的对象来获得更好的性能。

第四,您应该在单独的线程中执行所有这些查询。这很容易做到,但具体方法取决于您当前的 ManagedObjecttContext 安排。您是否有单一的 MOC、父/子关系、UIManagedDocument?基本上,您可以创建一个单独的 MOC,并调用 PerformBlock 来执行提取。事实上,您可以使用多个 MOC 同时异步触发所有这些获取。

然后,您可以在完成后调用主线程。

最后,您可能需要考虑对数据库进行非规范化。它会导致您使用更多空间,但获取速度会更快。具体来说,关系字段...您可以将照片/提要标签与照片本身放在一起。这样,在搜索时,您无需执行额外的联接即可获取这些记录。

所以,这不是一个简单的答案,而是实现每一个,看看你的性能是否没有显着提高(更不用说你的 UI 响应能力)。

关于iphone - 应用程序对 NSFetchRequests 没有响应,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10389887/

相关文章:

ios - 需要删除一个 subview

iphone - https 连接是否自动使用 ssl 加密数据

ios - CollectionView 内的按钮不可点击

ios - 如何在 Swift 中获取类的方法

ios - 如何使用Objective C从iOS的核心数据中过滤数据

ios - 在 NSManagedObject 类中初始化 @NSManaged 变量?

objective-c - 如何对 NSArrayController (子类)进行单元测试?

iphone - 在iPhone中播放视频网址

iphone - ShareKit 准备好用于现实世界的应用程序了吗?

iphone - ScrollView 子 uiscrollviews uiimageview viewForZoomingInScrollView