2014-01-05 2 views
0

У меня есть два разных контроллера вида, отображающих почти тот же запрос на выборку. Один из них отображает больше информации, чем другой, а также используется для редактирования данных. Второй - только для отображения.iOS 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 
+0

Что вы имеете в виду, когда говорите, что контроллер «замедляется»? Значительно ли отстает таблица? И сколько записей мы говорим здесь? Учитывая эффективность FRC (Fetched Results Controller) при извлечении записей из модели Core Data, я подозреваю, что ваша проблема лежит где-то, кроме запуска второго FRC. –

+0

Да, это заметно заметно. Сейчас у меня всего 3 записи (всего 5 атрибутов, нет только строк и чисел), и он уже отстает. Чем больше я вставляю, тем больше будет отставание. – MichiZH

+0

Тогда это, вероятно, не ваш FRC. Вы делаете что-то необычное с вашим рисунком UITableViewCell? –

ответ

1

Похоже, вы создаете создание UIImage в методе cellForRowAtIndexPath, что приведет к замедлению.

Лучше создать фон 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 вызывается в фоновом потоке, но фактическое назначение происходит в основном потоке. Вам нужно сделать так:, потому что вам не разрешено обновлять элементы пользовательского интерфейса в любом месте, кроме основного потока.

+0

Спасибо за ваш тщательный ответ. Отличное улучшение для моего кода, я думаю, и я все это реализовал. Однако для моей реальной проблемы это не помогло :-(Лаг все еще там. Я действительно не знаю, почему ... Я не думаю, что клетки медленно загружаются ... и самое смешное, что это отставание происходит только тогда, когда Я переключусь на этот второй контроллер представления, используя тот же выбор.Если я переключусь на любой другой контроллер вида (и никогда не открываю другой), он не замедляется .. – MichiZH

+0

Можете ли вы разместить свой код для второго контроллера представления? например, что-то может быть там. –

+0

Да, только что сделал :-) Добавлено полный второй контроллер. Я действительно не знаю, что мне не хватает. Мое приложение не очень сложно, я никогда не сохраняю никаких изображений в coredata, однако многие вещи пользовательского интерфейса (UISwitch, AlertView, selectin image) и переупорядочение строк как обсуждается здесь. Мне обычно нравится искать проблемы и оптимизировать свое приложение ... но здесь я не знаю, куда идти дальше. – MichiZH

Смежные вопросы