iOS NSFetchedResultsController:是否可以在不同的View Controller中为同一个fetch使用相同的缓存?

时间:2014-01-05 13:45:32

标签: ios core-data nsfetchedresultscontroller

我有两个不同的视图控制器显示几乎相同的获取请求。其中一个显示的信息比另一个更多,也用于编辑数据。第二个只是展示。

有没有办法加速,因为两者都显示完全相同的数据?我在我的代码中寻找一些东西,这使得第一次显示第二个视图控制器后整个重新排序很慢。换句话说:最初在我的主视图控制器中重新排序非常快。然后你只显示第二个视图控制器并切换回来,然后主视图控制器中的重新排序变慢。我有理由认为这是因为他们使用相同的提取。

我在两个viewDidLoad方法中启动了获取请求,如下面的代码片段所示。第一个是我的主视图控制器,也是启动应用程序时显示的第一个:

- (void)setupFetchedResultsController
{
    self.managedObjectContext = ((AppDelegate *)[[UIApplication sharedApplication] delegate]).managedObjectContext;

    NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"MainCategory"];
    request.sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:@"position" ascending:YES]];

    self.fetchedResultsController = [[NSFetchedResultsController alloc]initWithFetchRequest:request
                                                                       managedObjectContext:self.managedObjectContext
                                                                         sectionNameKeyPath:nil                                                                                  cacheName:@"MainCategoryCache"];
}

这是我的第二个:

- (void)setupFetchedResultsController
{
    self.managedObjectContext = ((AppDelegate *)[[UIApplication sharedApplication] delegate]).managedObjectContext;

    NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"MainCategory"];
    request.sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:@"position" ascending:YES]];

    self.fetchedResultsController = [[NSFetchedResultsController alloc]initWithFetchRequest:request
                                                                       managedObjectContext:self.managedObjectContext
                                                                         sectionNameKeyPath:nil
                                                                                  cacheName:@"NetCache"];
}

以下是我的主视图控制器的加载和cellForRowAtIndexPath视图:

- (void)viewDidLoad
{
    [super viewDidLoad];
    [self setupFetchedResultsController];

    //Edit/Done button
    UIBarButtonItem *editButton = [[UIBarButtonItem alloc] initWithTitle:NSLocalizedString(@"Edit", nil) style:UIBarButtonItemStyleBordered target:self action:@selector(editTable:)];
    [self.navigationItem setRightBarButtonItem:editButton];

    //Budget at bottom
    self.sumTitleLabel.text = [NSString stringWithFormat:@"%@%@:",NSLocalizedString(@"Budget", nil),NSLocalizedString(@"PerMonth", nil)];
    self.sumLabel.text = [[DatabaseFetches budgetPerMonthForManagedObjectContext:self.managedObjectContext] getLocalizedCurrencyString];

    //Layout
    self.view.backgroundColor = [UIColor clearColor];
    [self styleTableView];
    [self styleBudgetTotal];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    //Data model and cell setup
    static NSString *CellIdentifier = @"MainCategoryCell";
    MainCategoryTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
    MainCategory *mainCategory = [self.fetchedResultsController objectAtIndexPath:indexPath];

    //Clear background color
    cell.backgroundColor = [UIColor clearColor];

    //Setup the cell texts
    cell.title.text = mainCategory.name;

    int numberOfSubcategories = [[mainCategory getNumberOfSpendingCategories] integerValue];
    if (numberOfSubcategories == 1) {
        cell.subcategories.text = [NSString stringWithFormat:@"%i %@", numberOfSubcategories, NSLocalizedString(@"subcategory", nil)];
    } else {
        cell.subcategories.text = [NSString stringWithFormat:@"%i %@", numberOfSubcategories, NSLocalizedString(@"subcategories", nil)];
    }

    cell.costs.text = [[mainCategory getMonthlyCostsOfAllSpendingCategories] getLocalizedCurrencyString];

    //Delegation
    cell.title.delegate = self;

    //Format text
    cell.title.font = [Theme tableCellTitleFont];
    cell.title.textColor = [Theme tableCellTitleColor];
    cell.title.tag = indexPath.row;
    cell.subcategories.font = [Theme tableCellSubTitleFont];
    cell.subcategories.textColor = [Theme tableCellSubTitleColor];
    cell.costs.font = [Theme tableCellValueFont];
    cell.costs.textColor = [Theme tableCellValueColor];

    //Icon
    UIImage *icon;
    if(!mainCategory.icon){
        icon = [UIImage imageNamed:@"DefaultIcon.png"];
    } else {
        icon = [UIImage imageNamed:mainCategory.icon];
    }
    [cell.iconButton setImage:icon forState: UIControlStateNormal];
    cell.icon.image = icon;
    cell.iconButton.tag = indexPath.row;


    //Background image
    cell.cellBackground.image = [[UIImage imageNamed:@"content-bkg"] resizableImageWithCapInsets:UIEdgeInsetsMake(10, 10, 10, 10)];

    //Selection
    //cell.title.highlightedTextColor = cell.title.textColor;
    cell.subcategories.highlightedTextColor = cell.subcategories.textColor;
    cell.costs.highlightedTextColor = cell.costs.textColor;

    return cell;
}

完成第二个视图控制器:

@interface NetIncomeViewController()
@property (nonatomic, strong) NSManagedObjectContext *managedObjectContext;
@end

@implementation NetIncomeViewController

@synthesize incomeTitleLabel = _incomeTitleLabel;
@synthesize incomeValueTextField = _incomeValueTextField;
@synthesize incomeBackground = _incomeBackground;
@synthesize incomeValueBackground = _incomeValueBackground;
@synthesize netIncomeTitleLabel = _netIncomeTitleLabel;
@synthesize netIncomeValueLabel = _netIncomeValueLabel;
@synthesize netIncomeBackground = _netIncomeBackground;
@synthesize netIncomeValueBackground = _netIncomeValueBackground;
@synthesize managedObjectContext = _managedObjectContext;

#pragma mark Initializer and view setup
- (void)setupFetchedResultsController
{
    self.managedObjectContext = ((AppDelegate *)[[UIApplication sharedApplication] delegate]).managedObjectContext;

    NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"MainCategory"];
    request.sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:@"position" ascending:YES]];

    self.fetchedResultsController = [[NSFetchedResultsController alloc]initWithFetchRequest:request
                                                                       managedObjectContext:self.managedObjectContext
                                                                         sectionNameKeyPath:nil
                                                                                  cacheName:nil];
}

#pragma mark View lifecycle
- (void)awakeFromNib{
    [super awakeFromNib];

    //Title (necessary here because otherwise the tab bar would be only localized after first click
    //on the respective tab bar and not from beginning
    self.title = NSLocalizedString(@"Net Income", nil);
}

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    [self updateNetIncome];
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    [self setupFetchedResultsController];

    //Edit/Done button
    UIBarButtonItem *editButton = [[UIBarButtonItem alloc] initWithTitle:NSLocalizedString(@"Edit", nil) style:UIBarButtonItemStyleBordered target:self action:@selector(editIncome:)];
    [self.navigationItem setRightBarButtonItem:editButton];

    //Get or set and get the gross income
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    NSNumber *grossIncome = [defaults objectForKey:@"income"];
    if (!grossIncome) {
        [defaults setDouble:0 forKey:@"income"];
        [defaults synchronize];
        grossIncome = [defaults objectForKey:@"income"];
    }
    self.incomeValueTextField.enabled = NO;
    self.incomeValueTextField.delegate = self;
    self.incomeValueTextField.keyboardType = UIKeyboardTypeDecimalPad;
    self.incomeValueTextField.text = [grossIncome getLocalizedCurrencyString];

    [self styleTableViewAndIncome];
}


- (void)styleTableViewAndIncome
{
    self.view.backgroundColor = [UIColor clearColor];
    self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;

    self.tableView.backgroundColor = [UIColor clearColor];

    self.incomeTitleLabel.textColor = [Theme budgetValueTitleColor];
    self.incomeTitleLabel.font = [Theme budgetValueTitleFont];
    self.incomeValueTextField.textColor = [Theme budgetValueColor];
    self.incomeValueTextField.font = [Theme budgetValueTitleFont];

    self.netIncomeTitleLabel.textColor = [Theme budgetValueTitleColor];
    self.netIncomeTitleLabel.font = [Theme budgetValueTitleFont];
    self.netIncomeValueLabel.textColor = [Theme budgetValueColor];
    self.netIncomeValueLabel.font = [Theme budgetValueFont];

    self.incomeTitleLabel.text = [NSString stringWithFormat:@"%@:",NSLocalizedString(@"Income", nil)];
    self.netIncomeTitleLabel.text = [NSString stringWithFormat:@"%@%@:",NSLocalizedString(@"Net", nil),NSLocalizedString(@"PerMonth", nil)];

    self.incomeBackground.image = [[UIImage imageNamed:@"content-bkg"] resizableImageWithCapInsets:UIEdgeInsetsMake(10, 10, 10, 10)];
    self.incomeValueBackground.image = [[UIImage imageNamed:@"price-bkg"] resizableImageWithCapInsets:UIEdgeInsetsMake(10, 10, 10, 10)];
    self.netIncomeBackground.image = [[UIImage imageNamed:@"content-bkg"] resizableImageWithCapInsets:UIEdgeInsetsMake(10, 10, 10, 10)];
}

#pragma mark Table view delegate methods
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"CategoryCostCell";
    CategoryCostTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];

    cell.backgroundColor = [UIColor clearColor];

    // Configure the cell layout
    MainCategory *mainCategory = [self.fetchedResultsController objectAtIndexPath:indexPath];

    // Configure the cell layout    
    cell.categoryBackground.image = [[UIImage imageNamed:@"content-bkg"] resizableImageWithCapInsets:UIEdgeInsetsMake(10, 10, 10, 10)];
    if(!mainCategory.icon){
        cell.categoryImage.image = [UIImage imageNamed:@"DefaultIcon.png"];
    } else {
        cell.categoryImage.image = [UIImage imageNamed:mainCategory.icon];
    }

    cell.categoryName.text = mainCategory.name;
    NSNumber *expenditures = [mainCategory getMonthlyCostsOfAllSpendingCategories];
    double expendituresDouble =-[expenditures doubleValue];
    if (expendituresDouble == 0) {
        expenditures = [NSNumber numberWithDouble: 0];
    } else {
        expenditures = [NSNumber numberWithDouble: expendituresDouble];
    }
    cell.categoryCosts.text = [expenditures getLocalizedCurrencyString];

    //Format the text
    cell.categoryName.font = [Theme tableCellSmallTitleFont];
    cell.categoryName.textColor = [Theme tableCellSmallTitleColor];
    cell.categoryCosts.font = [Theme tableCellSmallValueFont];
    cell.categoryCosts.textColor = [Theme tableCellSmallValueColor];

    return cell;
}

#pragma mark TextField delegate methods
-(BOOL)textFieldShouldBeginEditing:(UITextField *)textField
{
    if(self.editing){
        return YES;
    } else {
        return NO;
    }
}

-(void)textFieldDidBeginEditing:(UITextField *)textField
{
    textField.text = [NSString stringWithFormat:@"%.2f",[NSNumber getUnLocalizedCurrencyDoubleWithString:textField.text]];
}

-(void)textFieldDidEndEditing:(UITextField *)textField
{
    textField.text = [[NSNumber numberWithDouble:[textField.text doubleValue]] getLocalizedCurrencyString];
}

-(BOOL)textFieldShouldReturn:(UITextField *)textField
{
    //Handle the enter button
    [textField resignFirstResponder];
    [self endEdit];
    //Since this method is called AFTER DidEndEditing, we have to call editTable or in this case since we have put all the endEdit code in a seperate method this method directly in order to format the table back from edit mode to normal.
    return YES;
}


#pragma mark Edit/Add/Delete action
- (IBAction) editIncome:(id)sender
{
    if(self.editing)
    {
        [self endEdit];
    } else {
        [self beginEdit];
    }
}

- (void) beginEdit
{
    //Change to editing mode
    [super setEditing:YES animated:YES];
    //Exchange the edit button with done button
    [self.navigationItem.rightBarButtonItem setTitle:NSLocalizedString(@"Done", nil)];
    [self.navigationItem.rightBarButtonItem setStyle:UIBarButtonItemStyleDone];
    self.incomeValueTextField.borderStyle = UITextBorderStyleRoundedRect;
    self.incomeValueTextField.enabled = YES;

    self.incomeValueBackground.hidden = YES;
    self.incomeValueTextField.textColor = [Theme budgetValueTitleColor];
}

- (void) endEdit
{
    //Change to editing no
    [super setEditing:NO animated:YES];
    //Remove Done button and exchange it with edit button
    [self.navigationItem.rightBarButtonItem setTitle:NSLocalizedString(@"Edit", nil)];
    [self.navigationItem.rightBarButtonItem setStyle:UIBarButtonItemStylePlain];
    self.incomeValueTextField.borderStyle = UITextBorderStyleNone;
    self.incomeValueTextField.enabled = NO;
    self.incomeValueBackground.hidden = NO;
    self.incomeValueTextField.textColor = [Theme budgetValueColor];
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    [defaults setDouble:[NSNumber getUnLocalizedCurrencyDoubleWithString:self.incomeValueTextField.text] forKey:@"income"];
    [self updateNetIncome];
    //[defaults synchronize];
}

#pragma mark Net income
- (void) updateNetIncome {
    double grossIncome = [NSNumber getUnLocalizedCurrencyDoubleWithString:self.incomeValueTextField.text];
    double budget = [[DatabaseFetches budgetPerMonthForManagedObjectContext:self.managedObjectContext] doubleValue];
    double netIncome = grossIncome - budget;

    if(netIncome >0){
        self.netIncomeValueBackground.image = [[UIImage imageNamed:@"price-bkg_green"] resizableImageWithCapInsets:UIEdgeInsetsMake(10, 10, 10, 10)];
    } else if(netIncome <0) {
        self.netIncomeValueBackground.image = [[UIImage imageNamed:@"price-bkg_red"] resizableImageWithCapInsets:UIEdgeInsetsMake(10, 10, 10, 10)];
    } else {
        self.netIncomeValueBackground.image = [[UIImage imageNamed:@"price-bkg"] resizableImageWithCapInsets:UIEdgeInsetsMake(10, 10, 10, 10)];
    }

    self.netIncomeValueLabel.text = [[NSNumber numberWithDouble:netIncome] getLocalizedCurrencyString];
}

@end

1 个答案:

答案 0 :(得分:1)

看起来你正在使用cellForRowAtIndexPath方法创建UIImage,这会导致速度减慢。

最好将背景UIImage创建为实例变量并在viewDidLoad中设置其图像,然后在cellForRowAtIndexPath方法中引用它。像这样:

//Declare the instance variable
UIImage *backgroundImage

//Set it in viewDidLoad
self.backgroundImage = [[UIImage imageNamed:@"content-bkg"] resizableImageWithCapInsets:UIEdgeInsetsMake(10, 10, 10, 10)];

//Assign it in cellForRowAtIndexPath
cell.backgroundImage = self.backgroundImage;

对于必须为每一行设置的图标,您需要将实际图像加载移出主线程。您可以使用dispatch_async()或NSOperationQueue执行此操作。 (从这里拉出来:Understanding dispatch_async

dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
     UIImage *icon;
     if(!mainCategory.icon){
          icon = [UIImage imageNamed:@"DefaultIcon.png"];
     } else {
          icon = [UIImage imageNamed:mainCategory.icon];
     }
     dispatch_async(dispatch_get_main_queue(), ^(void){
          [cell.iconButton setImage:icon forState: UIControlStateNormal];
          cell.icon.image = icon;
          cell.iconButton.tag = indexPath.row;
     });
});

注意如何在后台线程中调用imageNamed方法,但实际的赋值发生在主线程上。您必须这样做,因为除了主线程之外,您不允许更新UI元素。