views:

870

answers:

3

Hi

I was trying to archive an object into a plist file and load it later to fill a tableView. It seems that the file gets archived correctly but I get a bad access when trying to get the value out of the file.

Am I doing something wrong?

This is where I save it

// Create some phonebook entries and store in array
NSMutableArray *book = [[NSMutableArray alloc] init];
Phonebook *chris = [[Phonebook alloc] init];
chris.name = @"Christian Sandrini";
chris.phone = @"1234567";
chris.mail = @"[email protected]";
[book addObject:chris];
[chris release];

Phonebook *sacha = [[Phonebook alloc] init];
sacha.name = @"Sacha Dubois";
sacha.phone = @"079 777 777";
sacha.mail = @"[email protected]";
[book addObject:sacha];
[sacha  release];

Phonebook *steve = [[Phonebook alloc] init];
steve.name = @"Steve Solinger";
steve.phone = @"079 123 456";
steve.mail = @"[email protected]";
[book addObject:steve];
[steve release];

[NSKeyedArchiver archiveRootObject:book toFile:@"phonebook.plist"];

And here I try to get it out of the file and save it back into an array

- (void)viewDidLoad {
    // Load Phone Book
    NSArray *arr = [NSKeyedUnarchiver unarchiveObjectWithFile:@"phonebook.plist"];

    self.list = arr;

    [arr release];
    [super viewDidLoad];
}

The part where I try to build a cell

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *PhoneBookCellIdentifier = @"PhoneBookCellIdentifier";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:PhoneBookCellIdentifier];

    if ( cell == nil )
    {
        cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:PhoneBookCellIdentifier] autorelease];
    }

    NSUInteger row = [indexPath row];
    Phonebook *book = [self.list objectAtIndex:row];
    cell.textLabel.text = book.name;   
    cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;

    return cell;
}

Here the bad access error

Current language: auto; currently objective-c Assertion failed: (cls), function getName, file /SourceCache/objc4_Sim/objc4-427.5/runtime/objc-runtime-new.mm, line 3990. Assertion failed: (cls), function getName, file /SourceCache/objc4_Sim/objc4-427.5/runtime/objc-runtime-new.mm, line 3990. Assertion failed: (cls), function getName, file /SourceCache/objc4_Sim/objc4-427.5/runtime/objc-runtime-new.mm, line 3990. Assertion failed: (cls), function getName, file /SourceCache/objc4_Sim/objc4-427.5/runtime/objc-runtime-new.mm, line 3990.

+1  A: 

Just to expand on that a fraction: unarchiveObjectWithFile will return an autoreleased pointer. You don't locally retain it so you shouldn't release. Because you do, the object is subsequently deallocated and by the time you come to use it by calling book.name, it's not there.

(I'm assuming the self.list property is retaining appropriately so that the object will be kept around as long as you don't release here. If not, you'll need to fix that too.)

walkytalky
You are the man ;-) Removing the [arr release] solved the problem. Thanks a lot!
Chris
A: 

I Have a similar problem but un-forunately I don't think the solution is the same, I have an instance class to hold my app Settings, the class itself is a retained property of the main App Delegate, this is created via a

@interface AppDelegate : NSObject <UIApplicationDelegate> {
    Settings *settings;
    [...] 
}

@property (nonatomic, retain) Settings *settings;
[...]

This is created in the applicationDidFinishLaunching event as:

settings = [Settings LoadSettings];

If I comment out the above line then the app works fine every time, however if I pull the oject back from persisted settings using NSCoder and NSKeyedUnarchiver, the SIGARBT error is thrown as a NSCFString selector is being sent for what is encoded as a boolean property? The Settings Class is defined as an NSObject which implements the protocol.

@interface Settings : NSObject <NSCoding>

As I say, creating an instance of the settings class is fine, there is no issue, saving it seems OK as well as checking the returning class from the LoadSettings method shows the right values, only after exiting the method does the expected bool value seem to be getting sent to the load method as an NSCFString

- (id)initWithCoder:(NSCoder *)decoder {
    if (self = [super init]) {
            [...]
        self.animateMenus = [decoder decodeBoolForKey:@"animateMenus"];
    }

    return self;
}

- (void)encodeWithCoder:(NSCoder *)encoder {
    [...]
    [encoder encodeBool:animateMenus forKey:@"animateMenus"];
}

Once the settings have been loaded the property in question is used like this:

SettingsViewController *settingsView = [[SettingsViewController alloc] initWithNibName:@"SettingsView" bundle:nil];
[self presentModalViewController:settingsView animated:[AppDelegate instance].settings.animateMenus];
[settingsView release];

**The animateMenus member of the settings class will now throw the following:

-[NSCFString animateMenus]: unrecognized selector sent to instance 0xc712570 2010-10-15 11:12:51.828 App[900:207] * Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSCFString animateMenus]: unrecognized selector sent to instance 0xc712570'

Whereas, taking the 'settings = [[Settings alloc] LoadSettings];' call out of the app startup removes the issue (but then always uses the app defaults)?

Load and Save Methods:

+ (Settings*) LoadSettings { 
    Settings *s = nil;

    @try {
        NSData *data = [[NSUserDefaults standardUserDefaults] objectForKey:@"settings"];

        if (data == nil) {
            s = [[Settings alloc] init];
            [s Initialise];
            [s SaveSettings];
        }
        else
            s = (Settings*)[NSKeyedUnarchiver unarchiveObjectWithData:data];
    }
    @catch (NSException * e) {
        NSLog(@"Error Loading Settings\n%@", [e reason]);
    }
    @finally {
        return s;
    }
}

// Saves the settings dictionary to the user's device documents folder..
- (void) SaveSettings { 
    NSData *data = [NSKeyedArchiver archivedDataWithRootObject:self];
    [[NSUserDefaults standardUserDefaults] setObject:data forKey:@"settings"];
}
Neilster
A: 

Neilster, seems like you have a problem accessing the correct AppDelegate. Assumi g you are trying to access your implementation of AppDelegate in a UIViewController or some other controller class, you should either assign the AppDelegate in interface builder to your implementation or create an ivar/property for your AppDelegate and assign it to the instance of your implementation when you initialize your class.

sean woodward