views:

615

answers:

3

Hi everybody,

I've a problem with Iphone programming... I've a NSMutableArray containing object of type "MyClass". MyClass has some parameters, like NSNumber and NSString.

I've followed the Apple Tutorial about coding and decoding object using NSCoding, but when I try to decode the file I've stored, the NSMutableArray contain object of type "MyClass", but with wrong parameters.

I think i've done all I've to do (including protocol, including methods like initwithencode etc), but it's still not working...

Any suggestions?

Thanks a lot! And sorry for my English XD

A: 

Maybe seeing a simplified example will help. If not, we might be better able to assist with some code examples.

@interface MyObject : NSObject <NSCoding>
{
    NSUInteger MyObject_myInteger;
    MyEnum MyObject_myEnum;
    NSMutableArray *MyObject_myArray;
}

@end

@implementation MyObject

/** Implentation of the NSCoding protocol. */

-(void)encodeWithCoder:(NSCoder *)encoder
{
    [encoder encodeInt:MyObject_myInteger forKey:@"myInteger"];
    [encoder encodeInt:MyObject_myEnum forKey:@"myEnum"];
    [encoder encodeObject:MyObject_myArray forKey:@"myArray"];
}

-(id)initWithCoder:(NSCoder *)decoder
{
    // Init first.
    self = [self init];

    MyObject_myInteger = [decoder decodeIntForKey:@"myInteger"];
    MyObject_myEnum = [decoder decodeIntForKey:@"myEnum"];
    MyObject_myArray = [decoder decodeObjectForKey:@"myArray"];

[MyObject_myArray retain]; // Array is autoreleased, so we need to retain.

    return self;
}

@end

When your code archives an object of this class, make sure that you do it correctly. For example:

NSMutableData *stateInfo = [NSMutableData data];
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:stateInfo];
[archiver autorelease];

// Encode our stateful objects.
[archiver encodeObject:MYAppDelegate_myObject forKey:@"myObject"];

// Wrap up the encoding process.  If you omit this, 
// then bad things will happen when you decode.
[archiver finishEncoding];

Good luck, and happy coding!

Zack
Tks for the fast reply! I've modified my class so now it's similar to your example, but still not working well..I'll try to give you better explanation... The main problem appears when I launch my app. When the program try to reload the object from the file, if i look into the NSMutableArray in debug mode, i find 1 object, but i can't see the value i was expecting, but unusually value (like 0x3d33b20).So I thought that the error is in the save/load part of the code.I'm using the code of Apple Tutorial,but do you have any example of this part of code?NB: MyClass contain NSString object...
Matte Gary
I have updated my answer above to include an illustration showing one way to create the archive. Make sure that your are including the finishingEncoding message. Omitting this may account for the problem you are seeing with the de-archived object graph.
Zack
The code i was using was similar to your... Studying the debug i noticed that, when i call the encode method, the debug returns me as value of the variable the description "out of scope", and when i decode the variable,it returns me not the value, but a kind of description, like CFS_Xcode_NSStringSummary... i think there is a problem of memory management,like if i'm trying to decoding something which is actually somewhere else... Also when i decode, the 3 variables that i'm trying to decode, return the same value (i think is an address,the one like 0x3d39e80...)even if the key was different...
Matte Gary
I get new info! When i debug, if i look step by step into the method "initWithCoder" the value of the variable decoded are correct!! but exiting this method i lose everything, and outside this they become no more readable correctly...
Matte Gary
Sounds like you are not retaining objects that are auto-released. Have you retained all decoded objects (like NSStrings) inside initWithCoder?
Zack
i'll try to explain better with some code in the next comment...
Matte Gary
I would think the encode of an NSMutableArray will be decoded as an NSArray so you would need to do [NSMutableArray arrayWithArray:[decoder ...]];
epatel
just tried, but not that... even in this case, debugging i read in the description of the variable NSCFarray*...
Matte Gary
A: 

My class is something like this:

TorneoSingolo.h

@interface TorneoSingolo : NSObject <NSCoding> {
...
NSString *Descrizione;
...
}

- (void)encodeWithCoder:(NSCoder *)encoder;
- (id)initWithCoder:(NSCoder *)decoder;

@property(nonatomic, retain) NSString *Descrizione;

@end

TorneoSingolo.m

@implementation TorneoSingolo

@synthesize Descrizione;

- (void)encodeWithCoder:(NSCoder *)encoder

{
    [encoder encodeObject:self.NomeTorneo forKey:@"OBJ_NomeTorneo"];
    [encoder encodeObject:NomeSquadra forKey:@"OBJ_NomeSquadra"];
    [encoder encodeObject:self.Descrizione forKey:@"OBJ_Descrizione"];
    [encoder encodeInt: [self.NumPortieri intValue] forKey:@"OBJ_numportieri"];         
}

-(id)initWithCoder:(NSCoder *)decoder
{
    self = [self init];

    Descrizione = [decoder decodeObjectForKey:@"OBJ_Descrizione"];

    [Descrizione retain];

    return self;
}

The method that call the loading function works like this:

ElencoTornei2 = [[NSMutableArray alloc] init];

NSData *data;
NSKeyedUnarchiver *unarchiver;
NSString *archivePath = [rootPath stringByAppendingPathComponent:@"ElencoTornei.archive"];

if ([[NSFileManager defaultManager] fileExistsAtPath:archivePath]) {
    data = [NSData dataWithContentsOfFile:archivePath];
    unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];

    ElencoTornei2 = [unarchiver decodeObjectForKey:@"MyElencoTornei"];
    [unarchiver finishDecoding];
    [unarchiver release];

    [ElencoTornei2 retain];
}

I can't understand where i'm doing something wrong, like reinitialize an object...

please help me... =)

Matte Gary
It seems like you might not have a properly prepared archive. Can you post the code that creates the archive (viz., the code that uses NSKeyedArchiver's class method archivedDataWithRootObject:, or it's instance method encodeObject:)?
Zack
ok, i'll copy and attach in the next comment... tks so much for your help anyway...
Matte Gary
A: 

Here it is:

NSString *rootPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *archivePath = [rootPath stringByAppendingPathComponent:@"ElencoTornei.archive"];
BOOL result;

NSMutableData *stateInfo = [NSMutableData data];
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:stateInfo];
[archiver autorelease];

[archiver encodeObject:ElencoTornei forKey:@"MyElencoTornei"];
[archiver finishEncoding];

if (![[NSFileManager defaultManager] fileExistsAtPath:archivePath]) {
    [[NSFileManager defaultManager] createFileAtPath:rootPath contents:stateInfo attributes:nil];
}
else {
    result = [stateInfo writeToFile:archivePath atomically:YES];
}

ElencoTornei (the object that has to be encoded) is a static NSMutableArray contained in the class that is containing this part of code... i didn't write any overload of the method encodewithcoder for this specific object because i hope it use the appropriated one...

Matte Gary
As long as all objects contained in ElencoTornei implement NSCoding, then NSMutableArray doesn't need any help encoding. You might consider instead using the following encoding convenience methods instead: If you need to append/prepend other data to the archive before saving:`NSData *stateInfo = [NSKeyedArchiver archivedDataWithRootObject:payload];`If you just need to write the array to file (sounds like your case):`BOOL success = [NSKeyedArchiver archiveRootObject:ElencoTornei toFile:rootPath];`
Zack
I want to ensure that I understand your problem correctly. You observe that, while stepping through initWithCoder:, your object-graph decodes correctly into memory (specifically, your NSMutableArray ElencoTornei, which is stored as a static object, contains multiple objects). However, once the program execution leaves initWithCoder:, ElencoTornei's contains three objects, each of which points to the same memory location? Does that sound right?
Zack
Yes... when I decode a NSMutableArray, inside the InitWithCoder of every single obj i read the correct value, outside this method, so where the NSMutableArray exists, i can't read it at all...I've found a solution for this problem, with a trick... but now i can save an load value in the xcode emulator, but on the iPhone it doesn't work... Have you any idea about this?The trick is just about calling the MyClass function -(void)InitWithMyClass:(MyClass)obj, passing to it the self object...
Matte Gary
Sorry, the trick was not that, the trick was that i call a method of the static class "MyArrayHandler", which has the duty of adding an obj to the array. I call this method from the iniwithcoder, the only place where i can read the right value of the obj, passing to it the self obj...The non-loading/saving problem in the device instead of the emulator still exists...
Matte Gary
Out of curiosity, where are you defining your static variable for your NSMutableArray (I don't see it in any of the code listings)? If you want to access it's contents outside of instance/class method, you need to declare it outside of your implementation. For example, `static NSMutableArray ElencoTornei` should go *before* your `@implementation` line in your .m file. If you don't put it there, you won't be able to access ElencoTornei from outside of the method/function that you defined it in.
Zack
Yes, i declare it before the @implementation...A scheme of this part of program should be this:"GestioneDB" static class, with ElencoTornei's definition as static array,and static methods:"SalvaVariabili" save variables;"AggiungiTorneo: (TorneoSingolo*)newtorneo" add obj to the array;"GetElencoTornei" used to return the array.The trick i'm using is to call the function "AggiungiTorneo" from inside of method "InitWithCoder"in TorneoSingolo.m,like this:-(id)initWithCoder:(NSCoder*)decoder{ ... [GestoreDB AggiungiTorneo:self];}this works in the emulator, but not on the device...
Matte Gary