0

I'm having issues placing a custom object (WSWCMPost) into an NSMutableArray and then accessing the data stored in it later. Below is the relevant code.

Here is "WSWCMPost.h"

#import <Foundation/Foundation.h>

@interface WSWCMPost : NSObject
{
    NSString *postBody;
    NSString *postTitle;
    NSString *postID;
}
@property (nonatomic, retain) NSString *postBody, *postTitle, *postID;

- init;
- (id)initWithID: (NSString*)ID AndBody: (NSString*)body AndTitle: (NSString*)title;
- (NSString*)postBody;
- (NSString*)postTitle;
- (NSString*)postID;

Here is "WSWCMPost.m"

#import "WSWCMPost.h"

@implementation WSWCMPost

@synthesize postBody, postTitle, postID;

- (id)init {
    self = [super init];
    if(self) {
        postID = @"none";
        postBody = @"none";
        postTitle = @"none";
    }
}

- (id)initWithID: (NSString*)ID AndBody: (NSString*)body AndTitle: (NSString*)title {
    postTitle = title;
    postID = ID;
    postBody = body;
}

@end

And here is the "viewDidLoad" method that is causing my issues

    - (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    self.detailViewController = (WSWCMDetailViewController *)[[self.splitViewController.viewControllers lastObject] topViewController];

    // getting an NSString
    NSLog(@"Pulling saved blogs...");
    NSUserDefaults *currentDefaults = [NSUserDefaults standardUserDefaults];
    NSData *dataRepresentingSavedArray = [currentDefaults objectForKey:@"wswcmt1"];
    if (dataRepresentingSavedArray != nil)
    {
        NSArray *oldSavedArray = [NSKeyedUnarchiver unarchiveObjectWithData:dataRepresentingSavedArray];
        if (oldSavedArray != nil)
            _objects = [[NSMutableArray alloc] initWithArray:oldSavedArray];
        else
            _objects = [[NSMutableArray alloc] init];
    }
    NSLog(@"Pulled saved blogs...");

    NSLog(!_objects ? @"Yes" : @"No");
    @try {
        NSLog(@"_objects description: %@",[_objects description]);
        NSLog(@"_objects[0] postID: %@",[[_objects objectAtIndex:0] postID]);
    }
    @catch (NSException *exception) {
        NSLog(@"Caught exception %@", exception);
        NSLog(@"Objects doesnt exist, allocating memory...");
        _objects = [[NSMutableArray alloc] init];
        WSWCMPost *testPost = [[WSWCMPost alloc] initWithID:@"noID" AndBody:@"noBody" AndTitle:@"noTitle"];

        [_objects insertObject:testPost atIndex:0];
        [[NSUserDefaults standardUserDefaults] setObject:[NSKeyedArchiver archivedDataWithRootObject:_objects] forKey:@"wswcmt1"];
    }

    if (!_objects ) {
        NSLog(@"Objects doesnt exist...");
        _objects = [[NSMutableArray alloc] init];
        WSWCMPost *testPost = [[WSWCMPost alloc] initWithID:@"dne" AndBody:@"Dne" AndTitle:@"DNe"];

        [_objects insertObject:testPost atIndex:0];
        [[NSUserDefaults standardUserDefaults] setObject:[NSKeyedArchiver archivedDataWithRootObject:_objects] forKey:@"wswcmt"];
    }
    [self refreshButton:nil];
}

And finally, here is the output

2012-06-25 22:39:49.345 WSWCM[4406:907] Pulling saved blogs...
2012-06-25 22:39:49.352 WSWCM[4406:907] Pulled saved blogs...
2012-06-25 22:39:49.355 WSWCM[4406:907] Yes
2012-06-25 22:39:49.356 WSWCM[4406:907] _objects description: (null)
2012-06-25 22:39:49.358 WSWCM[4406:907] _objects[0] postID: (null)
2012-06-25 22:39:49.360 WSWCM[4406:907] Objects doesnt exist...
2012-06-25 22:39:49.363 WSWCM[4406:907] Refresh Triggered...

I think that is all of the relevant code. If i forgot anything let me know please. This issue has been bothering me for hours...

TJC
  • 717
  • 3
  • 12

3 Answers3

1

While I'm not positive why it's giving you NSStrings instead of just blowing up normally, the problem seems to stem from the fact that your custom class, WSWCMPost, does not conform to the NSCoding protocol. Make sure that your custom objects implement this protocol if you want to store them in NSUserDefaults, since it doesn't know how to serialize the data otherwise.

To be more exact, you'll have to add these methods to your class implementation:

- (id)initWithCoder:(NSCoder *)coder {
    self = [self initWithID:[coder decodeObjectForKey:@"id"] AndBody:[coder decodeObjectForKey:@"body"] AndTitle:[coder decodeObjectForKey:@"title"]];
    return self;
}

- (void)encodeWithCoder:(NSCoder *)coder {
    [encoder encodeObject:postID forKey:@"id"];
    [encoder encodeObject:postBody forKey:@"body"];
    [encoder encodeObject:postTitle forKey:@"title"];
}

This will allow the data to be serialized by NSCoder. Once you've done this, you should clear all the information currently stored by NSUserDefaults to make sure that it doesn't contain any more NSStrings, but then everything should work properly. Of course, you'll have to update these two methods if you change the data stored by your WSWCMPost object.

Alexis King
  • 43,109
  • 15
  • 131
  • 205
  • Thank you. I will go try to implement that and if it solves my issues, I will mark this as solved. Should this fix the Unrecognized Selector issue as well? – TJC Jun 26 '12 at 02:09
  • @TJC Yes, that should fix your issues. I've added more information to the post as well to clarify things. – Alexis King Jun 26 '12 at 02:32
  • I've updated the code in the original post, but now my objects all seem to be null. I also included some data storage suggestions from below – TJC Jun 26 '12 at 02:44
  • @TJC You're using the wrong key in the third time. You need to store the data to `wswcmt1`, but you're using `wswcmt` in the final case. – Alexis King Jun 26 '12 at 02:49
  • Caught that myself a second ago, but when i try to use `[[_objects objectAtIndex:0] postID]` I still get unrecognized selector issues. – TJC Jun 26 '12 at 02:53
  • `-[__NSCFString postID]: unrecognized selector sent to instance 0x1e582970` its the error, so yes it is the same. – TJC Jun 26 '12 at 03:32
0

Your output definitely shows what was wrong in your code.

2012-06-25 21:51:07.691 WSWCM[4049:907] -[__NSCFString postID]: unrecognized selector sent to instance 0x1d003e80
2012-06-25 21:51:07.696 WSWCM[4049:907] Caught exception -[__NSCFString postID]: unrecognized selector sent to instance 0x1d003e80

These two lines tell you that NSString object does not recognize selector postID. This hint should be enough to find out where you need to see in depth.

See this Storing custom objects in an NSMutableArray in NSUserDefaults for more information.

Community
  • 1
  • 1
REALFREE
  • 4,378
  • 7
  • 40
  • 73
  • Yes, but why doesnt it recognize those? I've looked all over the place and I don't understand what I did wrong – TJC Jun 26 '12 at 02:14
  • Why? because NSString object doesn't have selector postID. See this http://stackoverflow.com/questions/2315948/how-to-store-custom-objects-in-nsuserdefaults it might help your case. – REALFREE Jun 26 '12 at 02:17
  • thanks for the info, however I am now getting null errors for my description and postID. I updated the original posts ViewDidLoad and output. – TJC Jun 26 '12 at 02:45
0

Another thing to mention, you're having collisions with your getters/setters and their respective instance variables. So your implementation is:

interface

@interface WSWCMPost : NSObject
{
    NSString *postBody; // don't need to do these anymore for properties
    NSString *postTitle;
    NSString *postID;
}
@property (nonatomic, retain) NSString *postBody, *postTitle, *postID;

implementation

@implementation WSWCMPost

@synthesize postBody, postTitle, postID;

- (id)init {
    self = [super init];
    if(self) {
        postID = @"none";  // not prefixing your variables with 'self' so they are not getting retained
        postBody = @"none";
        postTitle = @"none";
    }
}

@end

Here's how you should be writing those out:

interface

/** NOTE: No need to specify your instance variables here anymore, just the properties */
@interface WSWCMPost : NSObject

@property (nonatomic, retain) NSString *postID;
@property (nonatomic, retain) NSString *postTitle;
@property (nonatomic, retain) NSString *postBody;

implementation

@implementation WSWCMPost

/** Now you specify the corresponding instance variable name alongside the property name */
@synthesize postBody=_postBody, postTitle=_postTitle, postID=_postID;

- (id)init {
    self = [super init];
    if(self) {
        self.postID = @"none";  //getting retained
        self.postBody = @"none";
        self.postTitle = @"none";
    }
}

That would definitely cause data to be released too soon.

So the previous way you could type in self.postID or postID and the compiler wouldn't complain. The difference is when you type postID it is actually setting the member variable and not retaining it... where self.postID will release whatever it is currently set to and retain the new value if it's different.

By declaring your properties the new way, you have to either call the setter as self.postID or set the underlying instance variable as _postID. A lot of early iPhone books had you bang out properties that way and it just ends up causing all sorts of memory issues.

Hope this helps!

UPDATE!!!

You forgot to return self in your constructor ;) I bet that's it

- (id)init {
    self = [super init];
    if(self) {
        self.postID = @"none";  //getting retained
        self.postBody = @"none";
        self.postTitle = @"none";
    }
    return self; // THIS IS WHY, you're constructor doesn't return an instance of the class... add this please
}


- (id)initWithID: (NSString*)ID AndBody: (NSString*)body AndTitle: (NSString*)title {

    if(( self = [super init] ))
    { 
        self.postTitle = title;
        self.postID = ID;
        self.postBody = body;
    }
    return self;
}
jerrylroberts
  • 3,419
  • 23
  • 22
  • Still results in `-[__NSCFString postID]: unrecognized selector sent to instance 0x1fd404a0` errors on the `NSLog(@"_objects[0] postID: %@",[(WSWCMPost*)[_objects objectAtIndex:0] postID]);` line – TJC Jun 26 '12 at 03:31
  • You'll get memory leaks and EXC Bad Access errors if you continue to declare your properties that way. Just trying to save you the head ache. – jerrylroberts Jun 26 '12 at 03:38
  • Read my last update, I think I got it. The last line of your constructor should always be "return self;" but it's not in your code. – jerrylroberts Jun 26 '12 at 03:50
  • Can't believe I didn't notice it sooner hahaha. Glad I could help out :) – jerrylroberts Jun 26 '12 at 03:59