0

I need to save and load the contents of an array of structs, but I know that Objective C is very particular about which data types you can read/write with.

Here is my struct:

struct SCourse 
{
    NSMutableArray* holes; // holds integers (pars)
    NSString*       name;
    int             size;
    BOOL            inUse; 
};

@interface CoursesManager : NSObject
{
    struct SCourse courses[5];
}

What are the data types I'll need to use? Do they each have different methods needed in order to read/write? I'm just looking for a non-complex way to get all the data I need to and from a file. I could do this quite easily in a language I'm more familiar with (C++), but some of the particulars of Objective-c are still lost on me.

EDIT: Solution (thanks for the help, everyone)

-(void)applicationWillResignActive:(UIApplication *)application  {

    // save the courses
    NSMutableArray* totalWriteArray = [[NSMutableArray alloc] initWithCapacity:MAX_COURSES];

    for (int i = 0; i < MAX_COURSES; ++i) 
    {
        struct SCourse saveCourse = [coursesManager GetCourseAtIndex:i];

        NSNumber* nInUse = [NSNumber numberWithBool:saveCourse.inUse];      
        NSNumber* nSize = [NSNumber numberWithInt:saveCourse.size];

        NSMutableArray* writeArray = [[NSMutableArray alloc] initWithCapacity:4];
        [writeArray addObject:nInUse];
        [writeArray addObject:nSize];
        [writeArray addObject:saveCourse.name];
        [writeArray addObject:saveCourse.holes];
        [totalWriteArray addObject:writeArray];
    }

    [totalWriteArray writeToFile:[self saveFilePath] atomically:YES]; 
}

And for the loading back in...

-(void)loadFile {

    NSString *myPath = [self saveFilePath];
    BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:myPath];
    if (fileExists) {
        NSMutableArray* totalReadArray = [[NSMutableArray alloc] initWithContentsOfFile:[self saveFilePath]];

        for (int i = 0; i < MAX_COURSES; ++i) 
        {
            struct SCourse loadCourse = [coursesManager GetCourseAtIndex:i];

            NSMutableArray* loadArray = [totalReadArray objectAtIndex:i];

            NSNumber* nInUse = [loadArray objectAtIndex:0];
            loadCourse.inUse = [nInUse boolValue];           

            NSNumber* nSize = [loadArray objectAtIndex:1];
            loadCourse.size = [nSize integerValue];

            NSString* inName = [loadArray objectAtIndex:2];
            loadCourse.name = inName;

            NSMutableArray* inHoles = [loadArray objectAtIndex:3];
            loadCourse.holes = inHoles;

            [coursesManager ReplaceCourseAtIndex:i With:loadCourse];
        }
    } 
}
3
  • It's not really too different from doing it in C/C++ is it? Extracting the values from the NSMutableArray isn't so different than from a std::vector is it... Commented Jun 28, 2012 at 15:55
  • I disagree. I thought about writing my own version of std::vector just for this small project, but I really don't have the time. NSMutable array requires you to convert things to an NSNumber before converting to an int (pointless waste of time, imho). Too many little steps in between most (including this) operations with their preferred data types and I don't have the time to hunt them all down. I'd like to get this done in an hour rather than 6. Commented Jun 28, 2012 at 16:03
  • You don't have to write std::vector; simply rename your source files to .mm to just use it as normal. There is a reason the Cocoa collection classes require you to use NSNumber; they only hold objects, not primitive types. Commented Jun 28, 2012 at 18:34

3 Answers 3

1

First thing first. You shouldn't use plain old C structures. The ARC memory management will not appreciate.

If you are familiar with C++, you should maybe use a C++ class instead, which will please the compiler and runtime. Depends on what you want to do.

Array. Use either NSArray or std::vector but please, no plain C arrays. Not sure how ARC will handle this but I suppose it will not appreciate much. Objective-C and C++ both provides all the tools you need to handle collections of whatever.

Serialization. You have several possibilities, one of them is NSCoder.

Last word, with the so called modern syntax, converting things into ObjC objects is quite easy.

BOOL b = YES;
int i = 10;
double d = 3.14;
char* s = "Pouf pouf";

You get the ObjC equivalents with the boxin' thingy:

NSNumber* bo = @( b );
NSNumber* io = @( i );
NSNumber* do = @( d );
NSString* so = @( s );
NSArray* ao = @[ @( i ), do ];
NSDictionary* = @{ @"num" : io, @"str" : @( s ) };

To write something in a file, in one gracious step:

[@{ @"bool" : bo, @"array" : @[ @"string", @10, @( 10 + 20 ) ] }
    writeToFile: @"path.plist" atomically: YES];

But the question remains, what are you trying to accomplish?

Sign up to request clarification or add additional context in comments.

9 Comments

These are good suggestions. What I'm trying to accomplish is save and load my array of courses when the game is closed and opened. I have everything setup for being able to save and load, I'm just a bit frustrated with the objective-c data types. A lot of it seems unnecessary to me. What is the reason for not using plain old C structures in your suggestion? I prefer to use arrays rather than linked lists so that my memory is allocated before hand.
Could you also add something about converting a BOOL to the correct data type and then a simple how you would write each data type to a file? I already have the filePath.
A BOOL is just a NSNumber. Added some lines there. Xcode 4.4+.
A BOOL is a signed char, not an NSNumber.
Good tips. If I use the NSMutableArray method "writeToFile" will it handle it's own size? ie - if i just write the hole arrays straight to the plist, will it handle different sizes on its own?
|
1

One easy approach is to store these arrays in an NSMutableDictionary object and use the method:

[mutableDict writeToFile:@"path/to/file" atomically:YES];

To store the data and:

NSMutableDictionary *anotherDict = [NSMutableDictionary dictionaryWithContentsOfFile:@"path/to/file"];

To read the contents back in.

2 Comments

Do the NSMutableDictionary and NSMutableArray types handle their own size when saving and loading? ie - if i use [myArray writeToFile:file automatically:YES];, and I load it in, the NSMutableArray will handle it's own size?
writeToFile:atomically: requires the objects in the array to be property list objects (NSString, NSArray, NSDictionary, NSDate, NSNumber, or NSData). This won't work with custom objects.
1

Here's what I'd suggest:

Make a custom class with the properties you want (.h file):

#import <Foundation/Foundation.h>

@interface CustomHolder : NSObject {
    NSString *last;
    NSString *first;
    NSString *middle;
}

@property (strong, nonatomic) NSString *last;
@property (strong, nonatomic) NSString *first;
@property (strong, nonatomic) NSString *middle;

@end

And then set the .m file up so that you can encode/decode the object

#import "CustomHolder.h"

@implementation CustomHolder

@synthesize last, first, middle;

- (void)encodeWithCoder:(NSCoder *)encoder 
{
    [encoder encodeObject:first forKey:@"first"];
    [encoder encodeObject:last forKey:@"last"];
    [encoder encodeObject:middle forKey:@"middle"]; 
}

- (id)initWithCoder:(NSCoder *)decoder 
{
    if (self = [super init]) 
    {
        self.first = [decoder decodeObjectForKey:@"first"];
        self.last = [decoder decodeObjectForKey:@"last"];
        self.middle = [decoder decodeObjectForKey:@"middle"]; 
    }
    return self;
}

@end

Then you can just [NSKeyedArchiver archiveRootObject:obj toFile:[self saveFilePath]] to save and [NSKeyedUnarchiver unarchiveObjectWithFile:[self saveFilePath]] to load

That's probably the most similar to using C-structs (especially because ARC doesn't let you use structs).

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.