Comparing Unmanaged to nil

2019-07-03 21:52发布

问题:

The code is copied from this blog post: http://matthewpalmer.net/blog/2014/06/21/example-ios-keychain-swift-save-query/

I used to have this code:

// Search for the keychain items
let status: OSStatus = SecItemCopyMatching(keychainQuery, &dataTypeRef)

// The following line crashes with an EXEC_BAD_ACCESS if dataTypeRef is nil
var opaque = dataTypeRef!.toOpaque()

When I run the above using a new account value in the keychain query the dataTypeRef will be nil which leads to an EXEC_BAD_ACCESS.

I tried to circumvent this by checking if dataTypeRef is nil like this:

var opaque = COpaquePointer.null()
if (dataTypeRef != nil) {
    opaque = dataTypeRef!.toOpaque()    // This line should not be executed.
}

While the debugger shows that dataTypeRef is nil it will still enter the if clause and crash.

Could anyone explain what is going on here? I do have experience in Objective-C but I can't figure out what's going on here.

回答1:

As far as I know, the toOpaque/fromOpaque dances are no longer necessary, and you can simply use optional binding. This can be combined with the cast to NSData using optional chaining:

let status: OSStatus = SecItemCopyMatching(keychainQuery, &dataTypeRef)
if let retrievedData = dataTypeRef?.takeRetainedValue() as? NSData {
    contentsOfKeychain = NSString(data: retrievedData, encoding: NSUTF8StringEncoding)
}

Note also that takeRetainedValue() is the correct choice here because you "own" the item returned by SecItemCopyMatching (it has "Copy" in its name).

But actually you should check the return value first:

let status: OSStatus = SecItemCopyMatching(keychainQuery, &dataTypeRef)
if status == errSecSuccess {
    if let retrievedData = dataTypeRef?.takeRetainedValue() as? NSData {
        contentsOfKeychain = NSString(data: retrievedData, encoding: NSUTF8StringEncoding)
    }
}