views:

101

answers:

4

I made a class. This is the h file.

//  MyClass.h
#import <Foundation/Foundation.h>


@interface MyClass : NSObject <NSCoding> {
    NSString *string1;
    NSString *string2;

}
@property (nonatomic, retain) NSString *string1;
@property (nonatomic, retain) NSString *string2;

@end

This is the m file.

//  MyClass.m
#import "MyClass.h"


@implementation MyClass
@synthesize string1, string2;

- (void)encodeWithCoder:(NSCoder *)coder;
{
    if (self = [super init]){
        [coder encodeObject:string1 forKey:@"string1"];
        [coder encodeObject:string2 forKey:@"string2"]; 
    }

}

- (id)initWithCoder:(NSCoder *)coder;
{
    self = [[MyClass alloc] init];
    if (self != nil)
    {
        string1 = [coder decodeObjectForKey:@"string1"];
        string2 = [coder decodeObjectForKey:@"string2"];

    }   
    return self;
}

- (void)viewDidUnload {
    self.string1 = nil;
    self.string2 = nil;
}


- (void)dealloc {
    [super dealloc];
    [string1 release];
    [string2 release];
}


@end

I created an array of these objects like this:

MyClass *object1 = [[MyClass alloc] init];
object1.string1 = @"object1 string1";
object1.string2 = @"string1 string2";
MyClass *object2 = [[MyClass alloc] init];
object2.string1 = @"object2 string1";
object2.string2 = @"object2 string2";
theArray = [[NSMutableArray alloc] initWithObjects:object1, object2, nil];

Then I saved the array like this:

  [[NSUserDefaults standardUserDefaults] setObject:[NSKeyedArchiver archivedDataWithRootObject:theArray] forKey:@"savedArray"];

Then I loaded the array from disk like this.

NSUserDefaults *currentDefaults = [NSUserDefaults standardUserDefaults];
    NSData *dataRepresentingSavedArray = [currentDefaults objectForKey:@"savedArray"];
    if (dataRepresentingSavedArray != nil)
    {
        NSArray *oldSavedArray = [NSKeyedUnarchiver unarchiveObjectWithData:dataRepresentingSavedArray];
        if (oldSavedArray != nil)
        {
            theArray = [[NSMutableArray alloc] initWithArray:oldSavedArray];
        }
        else {

            theArray = [[NSMutableArray alloc] init];
        }


    }

The program crashes when it gets to this line in - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath

cell.textLabel.text = [[theArray objectAtIndex:indexPath.row] string1];

Why does it crash there? It doesn't crash if I don't load the array from NSUserDefaults. However, I don't see anything I did wrong with saving or loading the array.

Edit: I can also make it crash with this line of code:

NSLog(@"%@", [[theArray objectAtIndex:0] string1]) ;
+1  A: 

I didn't read all of your code, so I am not sure if this is the problem, but in dealloc you should call [super dealoc] only after releasing the instance variables.

Florin
That didn't fix it.
awakeFromNib
+3  A: 

A few things jump out at me

  • you're calling init in encodeWithCoder:. since you're not initializing anything here you should not be changing self
  • you're calling alloc in initWithCoder:. Once you're in an init method you shouldn't have to call alloc, just call self = [super init].
  • you have a viewDidUnload method. Even if this were called in an NSObject subclass, you probably don't want to get rid of your data when the view is unloaded.
  • you're calling [super dealloc] at the beginning of your dealloc method. It should go at the end.
cobbal
What should I do about your first point? Should I just delete if (self = [super init])? I tried that, and it didn't work. I made changes according to your other points and none of them fixed it.
awakeFromNib
A: 

If it crashes due to being out of bounds, check that your UITableViewController correctly implements

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView

and

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView

to reflect the NSArray you're using as data source.

Ciryon
No, that's not it, I already checked for that. See my edit.
awakeFromNib
+6  A: 

In addition to cobbal's excellent points, your initWithCoder: method isn't using setters and therefore the strings aren't being retained. When you try to access string1 at your crash line, that string has probably already been released. Do this in initWithCoder: instead:

self.string1 = [coder decodeObjectForKey:@"string1"];
self.string2 = [coder decodeObjectForKey:@"string2"];

To address cobbal's first point, don't do any init at all in encodeWithCoder:. Just encode your object. Check out Apple's Archives and Serializations Programming Guide for Cocoa for more details about encoding and decoding objects.

James Huddleston
Thanks, that solved it! Why aren't setters and getters used if the strings have already been synthesized?
awakeFromNib
@awakeFromNib because self.string1 is actually syntactic sugar for [self setString1]. Writing string1 cannot call that method, how could it know it is a method?
ustun
The @synthesize statement effectively writes the setter and getter methods for you; it's still up to you to call them. As ustun points out, self.string1=@"z" gets replaced by [self setString1:@"z"] whereas string1=@"z" just sets string1 without calling the setter.
James Huddleston