createFileAtPath & OCUnit & errno blues

2019-07-15 00:54发布

问题:

I looked here and there but to no avail.

Consider:

- (void) write: (NSString *) xId data:(NSData *) data forClass: (Class) c {
    NSFileManager * fm = [NSFileManager defaultManager] ;

    NSString * instancePath = [self instancePath:xId forClass: c] ;

    errno = 0 ;
    BOOL success = [fm createFileAtPath: instancePath
                               contents: data
                             attributes: nil] ;
    if (!success) {
        ::NSLog(@"Couldn't write to path: %@", instancePath) ;
        ::NSLog(@"Error was code: %d - message: %s", errno, strerror(errno));
    } else {
        ::NSLog(@"COULD write to path: %@", instancePath) ;
        ::NSLog(@"Error was code: %d - message: %s", errno, strerror(errno));
    }
}

Which then prints:

2013-03-22 18:59:27.177 otest[18490:303] COULD write to path: /Users/verec/Library/Application Support/iPhone Simulator/6.1/Documents/cal/ModelRepo/ModelRepo#0.sexp
2013-03-22 18:59:27.177 otest[18490:303] Error was code: 3 - message: No such process
2013-03-22 18:59:27.178 otest[18490:303] Couldn't write to path: /Users/verec/Library/Application Support/iPhone Simulator/6.1/Documents/cal/ModelContainer/20130322.sexp
2013-03-22 18:59:27.178 otest[18490:303] Error was code: 3 - message: No such process
  1. Why is it that errno is not 0 even though `success' is YES in the first case (the "COULD" case)
  2. Can anyone spot the difference in the actual path that makes success=YES in the first ("COULD") case but success=NO in the second ("Couldn't")?

This is while running a OCUnit test, Xcode 4.6.1 Simulator running iOS 6.1

I'm just puzzled :-(

回答1:

  • The errno variable is generally only set by system calls (and some library functions) if the call failed. It is not modified if a system call succeeded, and may contain a non-zero value from a previous error.

  • One should print or save errno immediately after a failed system call. In your case,

    NSLog(@"Couldn't write to path: %@", instancePath);
    

    actually modifies errno. ("No such process" is very unlikely to be the correct failure reason.)

  • For the same reason, you cannot assume that errno contains the correct value after createFileAtPath failed. It actually did in my test, but it is not documented that this method sets/preserves errno correctly.


回答2:

I'm writing this answer to address verec's error code in more detail with a code blob. He actually alluded to this in his comment on the accepted answer.

The error he got from createFileAtPath was 3 (ESRCH) - No Such Process

One reason this might be the case is that createFileAtPath will NOT create intermediate directories, so if a directory on the way to the path you want to create doesn't exist, it will fail with this error code.

Instead you have to use createDirectoryAtPath:withIntermediateDirectories:attributes:error: to first create the directories and then use createFileAtPath after successfully creating those.

NSString *fileParentFolderPath;
NSString *filePath;

//first create the directory, createFileAtPath can't create intermediate dirs
NSError *error;
if([[NSFileManager defaultManager] createDirectoryAtPath:fileParentFolderPath
        withIntermediateDirectories:YES attributes:nil error:&error]) {
    //then create the file
    if(![[NSFileManager defaultManager] createFileAtPath:filePath 
            contents:nil attributes:nil]) {
        NSLog(@"Faliure creating File error was code: %d - message: %s", errno, strerror(errno));
    };
} else {
    NSLog(@"Faliure creating dir w error: %@", error);
};