0

This problem might be related to Instance variable not retaining value in iOS application.

My application has a table view that's supposed to be populated with data from a plist. The plist is question is determined based on a defaults setting.

After setting the contents of the instance variable, a count on it shows that it contains multiple records. However, if I then later try to count this instance variable when drawing the table view cells, it shows no records.

--

I declared a "providerData" NSDictionary instance variable in my class header:

#import <UIKit/UIKit.h>

@interface ViewController : UITableViewController {

    NSDictionary *providerData;

}

@property (nonatomic, retain) NSDictionary *providerData;

- (void)loadProviderDataSource:(NSString *)providerName;

@end

In my implementation I use viewDidLoad to read a dictionary from a plist file, check the value of "provider_preference", and then call [self loadProviderDataSource:providerPreference]:

- (void)viewDidLoad
{
    [super viewDidLoad];

    // Retrieve the user's provider preference from defaults:
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];

    // Set a default value for provider preference if the user didn't explicitly set one:
    NSString *providerPreference = [[NSString alloc] initWithString:@"foo"];

    if ([defaults stringForKey:@"provider_preference"]) {
        providerPreference = [defaults stringForKey:@"provider_preference"];
    }

    // Load provider data from the relevant plist:
    [self loadProviderDataSource:providerPreference];
}

This all works fine (using NSLog I can see that the plist file is read correctly and that a correct providerPreference value is passed to loadProviderDataSource.

The implementation for loadProviderDataSource is as follows:

- (void)loadProviderDataSource:(NSString *)providerName
{
    // Set the filename and path for the data source:
    NSString *dataSourceFileName = [[NSString alloc] initWithFormat:@"data-%@.plist",providerName];
    NSString *dataSourcePath = [[NSBundle mainBundle] bundlePath];
    dataSourcePath = [dataSourcePath stringByAppendingPathComponent:dataSourceFileName];

    // Read the provider data:
    self.providerData = [[NSDictionary alloc] initWithContentsOfFile:dataSourcePath];

    NSLog(@"provider data entry count 1: %d", [self.providerData count]);
}

At this point the NSLog shows me that there are 2 entries in providerData.

However, when I have to set the cells for my table view, the NSLog in the following code shows me that providerData has 0 entries:

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

    NSLog(@"provider data entry count 2: %d", [self.providerData count]);

    return cell;
}

In loadProviderDataSource I've tried using:

self.providerData = [[NSDictionary alloc] initWithContentsOfFile:dataSourcePath];

and:

[self setProviderData:[[NSDictionary alloc] initWithContentsOfFile:dataSourcePath]];

... neither work and I still end up with this in my log:

provider data entry count 1: 2
provider data entry count 2: 0
provider data entry count 2: 0

Any ideas?

Community
  • 1
  • 1
Willem
  • 1
  • 1
  • Where are you calling `loadProviderDataSource` and how are you loading the `ViewController` instance? – Deepak Danduprolu Jul 15 '11 at 09:22
  • Unless you are using ARC, you are leaking with this line: `self.providerData = [[NSDictionary alloc] initWithContentsOfFile:dataSourcePath];`. You need to add an `autorelease` command. – FreeAsInBeer Jul 15 '11 at 09:26
  • 1
    Are you sure that providerData is non-nil in cellForRow – Warren Burton Jul 15 '11 at 09:29
  • @Deepak: In my implementation I use viewDidLoad to read a dictionary from a plist file, check the value of "provider_preference", and then call: `[self loadProviderDataSource:providerPreference];` My application's MainWindow.xib interface has a Navigation Controller with inside it my ViewController (which is a subclass of UITableViewController). The Navigation Controller that ViewController is in, is my Root View Controller so it loads as soon as the application does. – Willem Jul 15 '11 at 09:31
  • @FreeAsInBeer: Thanks! I'll add the autorelease. – Willem Jul 15 '11 at 09:32
  • @Willem is `providerData` set elsewhere? Can you add the `viewDiadLoad` method? – Deepak Danduprolu Jul 15 '11 at 09:38
  • +1 on Warren's question. Check to make sure self.providerData isn't nil (somehow) in `tableView:cellForRowAtIndexPath:`. – cduhn Jul 15 '11 at 09:44
  • @Deepak: The only place providerData is set, is in loadProviderDataSource. I've updated the original post with the content of viewDidLoad now. – Willem Jul 15 '11 at 09:46
  • @Warren, @cduhn: It is nil in `tableView:cellForRowAtIndexPath`, I don't see why, though. The only place I assign data to it is in loadProviderDataSource. Might it be that there's something wrong with the declaration of my providerData instance variable and it's only retaining its data for local scope within each method somehow? – Willem Jul 15 '11 at 09:49
  • Have you checked for release ? Are you releasing the providerData anywhere ? – Raxit Jul 15 '11 at 09:52
  • Try to override the setProviderData: method and put a break point over there. This way we can find how it is being set to nil. – Ilanchezhian Jul 15 '11 at 09:56
  • @Raxit: No, I'm not explicitly releasing it anywhere. On that issue, am I correct in saying that my @property declaration's "nonatomic, retain" settings will cause it to never be autoreleased? AFAIK you need to manually enable Automatic Reference Counting and autorelease, right? – Willem Jul 15 '11 at 09:57
  • @Gomathi: No luck, the setProviderData mutator is only called once, from loadProviderDataSource... – Willem Jul 15 '11 at 10:09
  • @Willem what does `NSLog(@"%@", self);` in both `loadProviderDataSource:` and `tableView:cellForRowAtIndexPath:` log? – Deepak Danduprolu Jul 15 '11 at 10:12
  • @Deepak: `loadProviderDataSource self: ` and `tableView:cellForRowAtIndexPath self: `. – Willem Jul 15 '11 at 10:16

1 Answers1

3

Based on NSLog(@"%@", self); in both the method which shows up as follows

loadProviderDataSource

self: <ViewController: 0x83739b0> 

tableView:cellForRowAtIndexPath

self: <ViewController: 0x8373170>

It looks like you are creating a second instance of ViewController somehow.

I understand one of them is created using XIB. Verify whether you are creating another one programmatically.

Deepak Danduprolu
  • 44,595
  • 12
  • 101
  • 105
  • My word. I wasn't creating another instance programmatically, but in playing around in Interface Builder I had added two objects: A "Table View Controller" object within the "Navigation Controller", and a "Blank Object". Both had been set to my class "ViewController". The one was set as the dataSource for the UITableView, the other wasn't. I've removed the extra object now and everything works 100%, thanks so much! :D – Willem Jul 15 '11 at 10:39