Error while checking if a file exists
Asked Answered
P

2

5

I'm trying to write to a plist file using writeToFile, before I write I check whether the file exists.

This is the code:

#import "WindowController.h"

@implementation WindowController

@synthesize contacts;

NSString *filePath;
NSFileManager *fileManager;

- (IBAction)addContactAction:(id)sender {

    NSDictionary *dict =[NSDictionary dictionaryWithObjectsAndKeys:
                         [txtFirstName stringValue], @"firstName",
                         [txtLastName stringValue], @"lastName",
                         [txtPhoneNumber stringValue], @"phoneNumber",
                         nil];

    [arrayContacts addObject:dict];

    [self updateFile];
}

- (void)awakeFromNib {
    NSString *rootPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
    filePath    = [rootPath stringByAppendingPathComponent:@"Contacts.plist"];
    fileManager = [NSFileManager defaultManager];

    contacts = [[NSMutableArray alloc] init];

    if ([fileManager fileExistsAtPath:filePath]) {

        NSMutableArray *contactsFile = [[NSMutableArray alloc] initWithContentsOfFile:filePath];
        for (id contact in contactsFile) {
            [arrayContacts addObject:contact];
        }
    }
}

- (void) updateFile {
    if ( ![fileManager fileExistsAtPath:filePath] || [fileManager isWritableFileAtPath:filePath]) {
        [[arrayContacts arrangedObjects] writeToFile:filePath atomically:YES];
    }
}

@end

When the addContactAction is executed I don't get any error but the program halts and it brings me to the debugger. When I press continue in the debugger I get:

Program received signal:  “EXC_BAD_ACCESS”.

But that's probably not important.

PS: I'm new to mac programming and I don't know what else to try since I don't get an error message that tells me what's going wrong.

The path to the file is:

/Users/andre/Documents/Contacts.plist

I earlier tried this(with the same result), but I read that you can only write to the documents folder:

/Users/andre/Desktop/NN/NSTableView/build/Debug/NSTableView.app/Contents/Resources/Contacts.plist

Does anyone have an idea or even an explanation why this happens?

Photoelectric answered 30/8, 2009 at 14:32 Comment(0)
T
2

You are setting filePath with the stringByAppendingPathComponent: method. That method returns an autoreleased object. (Autoreleased object is used after it has been (automatically) released, which could cause the bad access error.)

I think changing

[rootPath stringByAppendingPathComponent:@"Contacts.plist"];

into

[[rootPath stringByAppendingPathComponent:@"Contacts.plist"] retain];

will solve your troubles.

Thermobarograph answered 30/8, 2009 at 15:7 Comment(5)
Wow, that indeed fixed my problem, thanks! Could you tell me a bit more about what the problem was? I'm pretty sure I copied this line from the official reference. Thanks a lot!Longdrawnout
I recommend you take a look at this tutorial: cocoadevcentral.com/d/learn_objectivec. It has a line saying "For this tutorial, you can assume that an automatic object will go away at the end of the current function." (Automatic object being autoreleased object). The string you set 'disappears' after the awakeFromNib function, so the filePath variable references something that doesn't exist anymore, causing errors. Retaining it will not stop it from being autoreleased, but will make an extra copy which isn't released until you tell it to. (Which you should probably do in -dealloc.)Thermobarograph
André Hoffmann: Apple has a well-hidden but very good tutorial on Cocoa memory management on the ADC website. developer.apple.com/mac/library/documentation/General/…Keystone
Thanks! I thought in Obj-C 2.0 the garbage collector takes care of that? Or is it still good practice?Longdrawnout
The garbage collector does take care of that if you use garbage collection. It's optional, and off by default. And it doesn't exist on the iPhone, so if you want the code (especially if it's model code) to run in both places, you need to write it as either retain-counted or dual-mode (written for both retain-counting and GC).Keystone
M
9

First, I think you shouldn't instantiate an NSFileManager object. Instead you use the default file manager, like this:

[[NSFileManager defaultManager] fileExistsAtPath: filePath];

Then, could you specify at which line the program is breaking into the debugger?

Medullary answered 30/8, 2009 at 14:57 Comment(2)
Where can I see at which line it breaks? It only shows assembly code. Thanks for the hint with the fileManager..removed the instantiation, but didn't change a thing.Longdrawnout
If you crash inside Cocoa code, you'll see only assembly. On the left side of the Debugger window is a list of stack frames; pick one that's in your own code, and you'll see your source code, with the relevant line highlighted in red.Keystone
T
2

You are setting filePath with the stringByAppendingPathComponent: method. That method returns an autoreleased object. (Autoreleased object is used after it has been (automatically) released, which could cause the bad access error.)

I think changing

[rootPath stringByAppendingPathComponent:@"Contacts.plist"];

into

[[rootPath stringByAppendingPathComponent:@"Contacts.plist"] retain];

will solve your troubles.

Thermobarograph answered 30/8, 2009 at 15:7 Comment(5)
Wow, that indeed fixed my problem, thanks! Could you tell me a bit more about what the problem was? I'm pretty sure I copied this line from the official reference. Thanks a lot!Longdrawnout
I recommend you take a look at this tutorial: cocoadevcentral.com/d/learn_objectivec. It has a line saying "For this tutorial, you can assume that an automatic object will go away at the end of the current function." (Automatic object being autoreleased object). The string you set 'disappears' after the awakeFromNib function, so the filePath variable references something that doesn't exist anymore, causing errors. Retaining it will not stop it from being autoreleased, but will make an extra copy which isn't released until you tell it to. (Which you should probably do in -dealloc.)Thermobarograph
André Hoffmann: Apple has a well-hidden but very good tutorial on Cocoa memory management on the ADC website. developer.apple.com/mac/library/documentation/General/…Keystone
Thanks! I thought in Obj-C 2.0 the garbage collector takes care of that? Or is it still good practice?Longdrawnout
The garbage collector does take care of that if you use garbage collection. It's optional, and off by default. And it doesn't exist on the iPhone, so if you want the code (especially if it's model code) to run in both places, you need to write it as either retain-counted or dual-mode (written for both retain-counting and GC).Keystone

© 2022 - 2024 — McMap. All rights reserved.