matthewpalmer
matthewpalmer

Reputation: 2649

Avoid Re-Initialising my Table View's Data Source (Core Data)

So I've made a little app that functions well, I'm just not sure on a few best practices/efficiency. The app has a Core Data source which is loaded into a table view. Items can be added to the table view from a text field. The issue is with the line [_tvds init] in the cellForRowAtIndexPath method of my TVViewController. I think this line will cause the _tvds instance variable to reinstantiate when I scroll up and down the table - will this cause poor performance? I'm still new to iOS and Objective C, so feel free to give me pointers on how to refactor my code. It may seem unnecessary to separate the view controller and the data store (with Core Data methods etc), but I was trying to practice my MVC design. Here's the code (I've left out the App Delegate and xib files - they're standard stuff). The app works, but it doesn't seem optimal to my beginner mind.

TVViewController.h

#import <UIKit/UIKit.h>
#import "TVDataSource.h"
#import "TVAppDelegate.h"
@interface TVViewController : UIViewController <UITableViewDataSource, UITableViewDelegate,UITextFieldDelegate>

@property (strong, nonatomic) IBOutlet UITableView *tableView;
@property (nonatomic, retain) TVDataSource *tvDS;
@property (weak, nonatomic) IBOutlet UITextField *addSomethingTextField;
- (IBAction)goButton:(id)sender;

@end

TVViewController.m

#import "TVViewController.h"

@interface TVViewController ()
@end
@implementation TVViewController

- (void)viewDidLoad
{
[super viewDidLoad];
_tvDS = [[TVDataSource alloc]init];
NSLog(@"%@",[_tvDS dataArray]);

NSLog(@"%@",[[_tvDS dataArray] valueForKey:@"name"]);


[_addSomethingTextField setDelegate:self];

}

- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
// If you're serving data from an array, return the length of the array:

return [[_tvDS dataArray] count];
}

// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

static NSString *CellIdentifier = @"Cell";

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
    cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
            }

// Set the data for this cell:

cell.textLabel.text = @"dfjklsl";
NSInteger myInt = [indexPath row];
[_tvDS init];
NSObject *myObj = [[_tvDS dataArray] objectAtIndex:myInt];

[[cell textLabel]setText:[myObj valueForKey:@"name"]];

// NSArray *toBeDisplayed = [_tvDS getAnArray];


// [[cell textLabel]setText:[toBeDisplayed objectAtIndex:myInt]];

// set the accessory view:
cell.accessoryType =  UITableViewCellAccessoryNone;

return cell;
}
 - (IBAction)goButton:(id)sender {
 NSString *textFieldContents = [_addSomethingTextField text];
[[_tvDS dataArray] addObject:textFieldContents];
[_tvDS addItem:textFieldContents];
[_addSomethingTextField setText:@""];

[[self tableView] reloadData];
NSLog(@"%u",[[_tvDS dataArray] count]);



}
-(BOOL)textFieldShouldReturn:(UITextField *)textField
{
[self goButton:textField];
[_addSomethingTextField resignFirstResponder];
//NSLog(@"%@", [_addSomethingTextField text]);
return YES;
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSUInteger row = [indexPath row];

NSLog(@"%@", [[_tvDS dataArray] objectAtIndex:row]);


}

@end

TVDataSource.h

#import <Foundation/Foundation.h>
#import "TVAppDelegate.h"
@interface TVDataSource : NSObject
@property (nonatomic, retain) NSMutableArray *dataArray;
@property (nonatomic, retain) NSManagedObjectContext *context;
@property (nonatomic, retain) NSFetchRequest *request;
@property (nonatomic, retain) NSError *error;
//@property (nonatomic, strong) NSArray *obj
//@property (nonatomic, weak) NSManagedObjectContext *context;
//-(void)saveInput;
-(void)addItem:(id)input;


@end

TVDataSource.m

#import "TVDataSource.h"

@implementation TVDataSource

- (id)init
{
    self = [super init];
    TVAppDelegate *appDelegate = [[UIApplication sharedApplication]delegate];
    NSManagedObjectContext *context = [appDelegate managedObjectContext];
    NSEntityDescription *entityDesc = [NSEntityDescription entityForName:@"Todo" inManagedObjectContext:context];
    NSFetchRequest *request = [[NSFetchRequest alloc]init];
    [request setEntity:entityDesc];
    // NSPredicate *pred = [NSPredicate predicateWithFormat:@"(name = %@)", [_name text]];

    NSManagedObject *matches = nil;
    NSError *error;
    NSArray *objects = [context executeFetchRequest:request error:&error];

    if ([objects count]==0) {
        NSLog(@"nothing there");
    } else {
        matches = objects[0];

        [self setDataArray:[objects mutableCopy]];
        NSLog(@"initialise %@",[self dataArray]);
        //NSLog(@"%@",[_dataArray valueForKey:@"name"]);

    }
    return self;

}
-(void)addItem:(id)input
{
    TVAppDelegate *appDelegate = [[UIApplication sharedApplication]delegate];

    NSManagedObjectContext *context = [appDelegate managedObjectContext];
    NSManagedObject *newTask;
    newTask = [NSEntityDescription insertNewObjectForEntityForName:@"Todo" inManagedObjectContext:context];
    [newTask setValue:input forKey:@"name"];

    if ([input isEqualToString:@""]) {
        NSLog(@"CONTENTS BLANK");

    } else {
        NSLog(@"Contents were %@", input);
        NSLog(@"goButton: was called");
        NSError *error;
        [context save:&error];
    }
}

@end

Thanks for the help guys. I think it will be something simple.

Edit:

Should have been clearer of the things I've tried/issues I'm having. The reason I have to do the init again is so I can repopulate from the Core Data store when I add something new from the text field. If I remove the second init then when I add a new item and try to scroll down to it I get a SIGABRT error. I believe this error is because I'm adding the contents of the text field to the array which doesn't have a 'name key'. I'm not sure how to fix this. I've considered making a working array just for the view controller to display to the user, then have this update to the Core Data store. Is this a good solution or is there a better way?

Upvotes: 0

Views: 197

Answers (1)

lnafziger
lnafziger

Reputation: 25740

You are already initing your data source in your viewDidLoad method, so you shouldn't need it when you are returning a cell.

I would remove it altogether, and see if you have any problems that are caused by that. If your class is correct then you are right, it is simply wasting time every time that it is called.

Since it is not adding it to your data source without calling init you could call init only when you add a new object. Or, even better, fix your addItem method so that it works without needing to reinitialize by adding this line:

[self.dataArray addObject:input];

Upvotes: 2

Related Questions