简体   繁体   中英

Cocoa: Why set 'nil' prior to the method call?

I have been working though the examples in Using a Directory Enumerator in the Apple Low-Level File sections.

Here is a code snippet:

for (NSURL *url in enumerator) {

// Error-checking is omitted for clarity.

NSNumber *isDirectory = nil;
[url getResourceValue:&isDirectory forKey:NSURLIsDirectoryKey error:NULL];

if ([isDirectory boolValue]) {

    NSString *localizedName = nil;
    [url getResourceValue:&localizedName forKey:NSURLLocalizedNameKey error:NULL];

    NSNumber *isPackage = nil;
    [url getResourceValue:&isPackage forKey:NSURLIsPackageKey error:NULL];

    if ([isPackage boolValue]) {
        NSLog(@"Package at %@", localizedName);
    }
    else {
        NSLog(@"Directory at %@", localizedName);
    }
}

}

Why are localizedName , isPackage , and isDirectory being set to nil prior to the relevant calls to the getResourceValue method? Is this just an over abundance of caution or is this required?

As I read the docs for getResourceValue:forKey:error: it seems redundant:

Return Value YES if value is successfully populated; otherwise, NO.

Discussion value is set to nil if the requested resource value is not defined for the URL. In this case, the method still returns YES.

Am I missing something?

This code is not checking whether the getResourceValue:forKey:error: method actually succeeds. Instead it is assuming that the passed variable is either set nil or left unchanged when the method fails. This may not be a warranted assumption; the behavior on failure does not seem to be documented.

Were they not initialized beforehand, "left unchanged" would cause undefined behavior via the use of uninitialized stack variables.

It's good practice to never leave variables undefined. Also, I think you'll get a compiler warning if you do, and it's easier to make sure you have no bad warnings if you just fix them all.

If you don't set an object variable to nil, its value is undefined. If you subsequently access that variable without setting its value first, your app could misbehave or conceivably throw an exception or crash.

If you set the value to nil first then this can't happen. It's always good practice to initialize local object variables to nil. This does not apply to instance variables as they are guaranteed to always be initialized to nil.

File this under C NULL pointer type stuff.

Here is an example:

#import <Foundation/Foundation.h>

void test(void){
    NSString *test1, *test2, *test3;

    test1=nil;
    // Note: NSString would USUALLY be aloc then init before use...
    [test1 isEqual:@"static object string"];
    [test2 isEqual:@"static string"];
    [test3 isEqual:test1];

}

int main (int argc, const char * argv[])
{
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    test();

    [pool drain];
    return 0;
}

Now place a breakpoint on test1=nil in xcode and run. On my machine, I get:

test1=(NSString *) 0x0 <nil>
test2=(NSString *) 0x0 <nil>
test3=(NSString *) 0x7fff84db7dd7 Invalid

If you run it, you will get EXC_BAD_ACCESS and termination when it tries to execute [test3 isEqual:test1] . You can use the nil values of test1 and (accidental?) nil value of test2 but use the random value in test3: death.

Now change that line to test1=test2=test3=nil; Run it again. Works great.

An important concept: You can validly send a message to nil in Objective-C.

Just as in may be good practice to assign a C pointer to a safe value when not in use, nil is a safe value in Objective-C until it is assigned to something else. For the programmer who wrote the example -- it was probably muscle memory. Unnecessary in the particular case, but safer than a random value.

it would be redundant if the return value of -getResourceValue was checked. As it isn't in the above code the nil is necessary.

id aLocalVar;
// 1
[ob maybeAssignAValueToPassedInPointer:&aLocalVar];
// 2

In the above code above at //1 aLocalVar points to a random bit of junk in memory - could be anything.

As we dont know if -maybeAssignAValueToPassedInPointer: did or didn't assign a value or not we dont know at //2 if aLocalVar still contains junk (which may be a Number, or a String, or whatever, but junk non the less) or a valid value. We may crash if we try to use it, or we might have an incorrect value.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM