NSKeyedUnarchiver Help

Discussion in 'Mac Programming' started by Soulstorm, Dec 16, 2007.

  1. Soulstorm macrumors 68000

    Soulstorm

    Joined:
    Feb 1, 2005
    #1
    I have these 2 classes inside the following files:

    SFSelector.h
    Code:
    #import <Cocoa/Cocoa.h>
    
    
    @interface ModStatus: NSObject <NSCoding>{
    	
    	NSMutableDictionary *properties;
    }
    
    - (void)setProperties:(NSDictionary *)otherProperties;
    - (NSMutableDictionary *)properties;
    
    @end
    
    
    #pragma mark -
    
    enum selectedMod {FIRST_MOD=0, SECOND_MOD=1, THIRD_MOD=2};
    
    @interface ModSelector : NSObject <NSCoding>{
    	NSMutableArray *modStatuses;
    	int SELECTED_MOD;
    }
    - (id) initWithStatuses :(NSArray *)statuses;
    - (void)changeSelectedMod:(int)newSelection;
    - (void)setModStatuses:(NSArray *)newModStatuses;
    
    - (void)addModStatus:(ModStatus *)modStatus;
    - (void)removeModStatusAtIndex:(unsigned)index;
    
    - (ModStatus *)currentModStatus;
    - (ModStatus *)modStatusAtIndex:(unsigned)index;
    - (NSMutableArray *)modStatuses;
    - (unsigned)count;
    
    - (void)selectModWithName:(NSString *)name;
    - (void)setModSelector:(ModSelector *)aSelector;
    - (id)valueForCurrentModKey: (id)key;
    @end
    
    
    SFSelector.m
    Code:
    #pragma mark -
    #pragma mark ModStatus
    
    #import "SFSelector.h"
    
    @implementation ModStatus
    
    #pragma mark Inits, setters, getters
    
    - (id) init
    {
    	self = [super init];
    	if (self != nil) {
    		NSArray *values = [NSArray arrayWithObjects:@"modName", @"modLocation", @"preferenceFileLocation", @"isFirstTime",nil];
    		NSArray *keys = [NSArray arrayWithObjects:@"<not set>", @"<mod location not set>", @"<not set>", [NSNumber numberWithBool:YES], nil];
    		properties = [NSMutableDictionary dictionaryWithObjects:values forKeys:keys];
    	}
    	return self;
    }
    
    - (void)setProperties:(NSDictionary *)otherProperties
    {
    	[properties setDictionary:otherProperties];
    }
    
    - (NSMutableDictionary *)properties
    {
    	return properties;
    }
    
    #pragma mark NSCoding
    - (void)encodeWithCoder:(NSCoder *)coder
    {
    	[coder encodeObject:properties forKey:@"properties"];
    }
    
    - (id)initWithCoder: (NSCoder *) coder
    {
    	properties = [[coder decodeObjectForKey:@"properties"]retain];
    	return self;
    }
    
    #pragma mark dealloc
    - (void) dealloc
    {
    	[properties release];
    	[super dealloc];
    }
    
    @end
    
    
    //________________________________________________________
    #pragma mark -
    #pragma mark ModSelector
    
    #pragma mark Inits, setters, getters
    
    @implementation ModSelector
    - (id) init
    {
    	self = [super init];
    	if (self != nil) {
    		modStatuses = [[NSMutableArray alloc]init];
    		SELECTED_MOD = FIRST_MOD;
    	}
    	return self;
    }
    
    - (id) initWithStatuses :(NSArray *)statuses
    {
    	self = [super init];
    	if (self != nil) {
    		modStatuses = [[NSMutableArray alloc]init];
    		[modStatuses setArray:statuses];
    	}
    	return self;
    }
    
    - (void)setModStatuses:(NSArray *)newModStatuses
    {
    	[modStatuses setArray:newModStatuses];
    }
    
    - (void)addModStatus:(ModStatus *)modStatus
    {
    	[modStatuses addObject:modStatus];
    	NSLog(@"%i",[modStatuses count]);
    }
    
    - (void)removeModStatusAtIndex:(unsigned)index
    {
    	[modStatuses removeObjectAtIndex:index];
    }
    
    
    - (ModStatus *)currentModStatus
    {
    	return [modStatuses objectAtIndex:SELECTED_MOD];
    }
    
    -(void)selectModWithName:(NSString *)name
    {
    	int i;
    	for (i=0; i<[modStatuses count]; i++) {
    		//NSLog(@"ASD");
    		if ([name isEqualToString:[[[modStatuses objectAtIndex:i]properties]valueForKey:@"modName"]] == TRUE){
    			SELECTED_MOD = i;
    			break;
    		}
    	}
    }
    
    - (NSMutableArray *)modStatuses
    {
    	return modStatuses;
    }
    
    - (ModStatus *)modStatusAtIndex:(unsigned)index
    {
    	if ([self count] > index)
    		return [modStatuses objectAtIndex:index];
    	return nil;
    }
    
    - (void)changeSelectedMod:(int)newSelection
    {
    	SELECTED_MOD = newSelection;
    }
    
    - (void)setModSelector:(ModSelector *)aSelector
    {
    	[modStatuses setArray:[aSelector modStatuses]];
    	SELECTED_MOD = 0;
    }
    
    
    #pragma mark Getting mod parameters
    
    - (id)valueForCurrentModKey: (id)key
    {
    	ModStatus *currentObject = [modStatuses objectAtIndex:SELECTED_MOD];
    	return [[currentObject properties]valueForKey:key];
    }
    
    - (unsigned)count
    {
    	return [modStatuses count];
    }
    
    #pragma mark NSCoding
    
    - (void)encodeWithCoder:(NSCoder *)coder
    {
    	[coder encodeObject:modStatuses forKey:@"modStatuses"];
    }
    
    - (id)initWithCoder: (NSCoder *) coder
    {
    	if (modStatuses != nil)
    		[modStatuses release];
    	modStatuses = [[coder decodeObjectForKey:@"modStatuses"]retain];
    	return self;
    }
    
    #pragma mark dealloc
    - (void) dealloc
    {
    	[modStatuses release];
    	[super dealloc];
    }
    
    @end
    
    The problem is that I want to save/load data from those classes to a file. I will need to transform the classes to NSData first. However, this is a test code I have written to test wether the encoding and decoding is done correctly.

    Code:
    ModSelector *defaultModSelector = [[ModSelector alloc]init];
    ModStatus *status1 = [[ModStatus alloc]init];
    ModStatus *status2 = [[ModStatus alloc]init];
    [[status1 properties]setValue:@"FS2_Open" forKey:@"modName"];
    [[status2 properties]setValue:@"Beyond The Red Line" forKey:@"modName"];
    [mainModSelector addModStatus:status1];
    [mainModSelector addModStatus:status2];
    NSData *defaultModStatusData = [NSKeyedArchiver archivedDataWithRootObject:defaultModSelector];
    	
    	
    ModSelector *temp = [NSKeyedUnarchiver unarchiveObjectWithData:defaultModStatusData];
    NSLog(@"%i, %@",[temp count], [[[temp modStatusAtIndex:0]properties]valueForKey:@"modName"]);
    The above code does not work. After conversion to NSData, unarchiving will result in an empty class. However, if I do the initializations to the ModSelector's -init method (put 2 ModStatus objects inside the class) and then encode it using NSKeyedArchiver and decode it with NSKeyedUnarchiver, it will work correctly. Can anyone tell my why this happens?
     
  2. kainjow Moderator emeritus

    kainjow

    Joined:
    Jun 15, 2000
    #2
    One problem is you don't retain properties in ModStatus after you create it.
     
  3. Soulstorm thread starter macrumors 68000

    Soulstorm

    Joined:
    Feb 1, 2005
    #3
    Problem fixed. It was so simple that it is too embarrassing to tell it here. Sorry for wasting anyone's time. I won't program with only 2 hours of sleep. It blinds me :)
     
  4. kainjow Moderator emeritus

    kainjow

    Joined:
    Jun 15, 2000
    #4
    I saw your original post before you edited it. You ask why you need to retain it: don't fight the system. You are half-way there to mastering memory management and still don't understand it completely, but don't fight it. You will end up losing :). Trust me, sometimes you think it doesn't make sense, but if you only apply the rules sometimes, things WILL not work. If you always "obey" them and retain what you are given and release when you are done, it will work. But I'm glad you got it fixed :)
     
  5. Soulstorm thread starter macrumors 68000

    Soulstorm

    Joined:
    Feb 1, 2005
    #5
    I trust you saw my other posts with similar problems, and are saying those things. Yes, you are correct, I have developed many applications and some of them are used by thousands of people (I mean it), however, sometimes I stumble upon simple memory management problems. That's why I haven't enabled garbage collection, even if I'm developing on Leopard. I want to rely on my knowledge about memory management, because otherwise, those simple problems will someday return.

    However, the funny thing is that the problem was not on memory management. Look at this code:

    Code:
    ModSelector *defaultModSelector = [[ModSelector alloc]init];
    ModStatus *status1 = [[ModStatus alloc]init];
    ModStatus *status2 = [[ModStatus alloc]init];
    [[status1 properties]setValue:@"FS2_Open" forKey:@"modName"];
    [[status2 properties]setValue:@"Beyond The Red Line" forKey:@"modName"];
    [mainModSelector addModStatus:status1];
    [mainModSelector addModStatus:status2];
    NSData *defaultModStatusData = [NSKeyedArchiver archivedDataWithRootObject:defaultModSelector];
    	
    ModSelector *temp = [NSKeyedUnarchiver unarchiveObjectWithData:defaultModStatusData];
    NSLog(@"%i, %@",[temp count], [[[temp modStatusAtIndex:0]properties]valueForKey:@"modName"]);
    My problem was that the NSLog was displaying an empty class. This is normal, because I hadn't added anything to the "temp"class! I was accidentally adding things to the "defaultModSelector" class! I got angry when I noticed it. I didn't think of even searching this function, I thought there was something wrong with the rest of the program!
     
  6. dgdosen macrumors 65816

    dgdosen

    Joined:
    Dec 13, 2003
    Location:
    Seattle
    #6
    DOOOOHHHHHH!

    I didn't think I needed to have retain set, as the objects were in memory and I could access that nested information. But by the time the application terminated, they weren't.
     

Share This Page