收费桥过度使用

时间:2013-03-13 16:35:33

标签: ios automatic-ref-counting core-foundation

我最近将一个庞大的图书馆迁移到了ARC,一个免工具部分令人头疼。这是代码:

+ (NSString *)getKeychainItem:(NSString *)identifier
{
    NSString *fullIdentifier = [NSString stringWithFormat:@"%@%@", kIdentifierPrefix, identifier];

    NSMutableDictionary *queryKeychain;
    OSStatus status = noErr;

    queryKeychain = [NSMutableDictionary dictionary];

    // Set the public key query dictionary.
    [queryKeychain setObject:(__bridge id)kSecClassGenericPassword
                      forKey:(__bridge id)kSecClass];

    // Get the key.
    CFDataRef data;
    CFDictionaryRef queryKeychainCF = (__bridge CFDictionaryRef)queryKeychain;
    status = SecItemCopyMatching(queryKeychainCF, (CFTypeRef *)&data);

    NSData *passwordData = (__bridge_transfer NSData *)data;

    NSString *password;

    if (status == noErr)
    {
        password = [[NSString alloc] initWithBytes:[passwordData bytes]
                                            length:[passwordData length]
                                          encoding:NSUTF8StringEncoding];

    }
    else if (status != errSecItemNotFound)
    {
        NSLog(@"Error getting keychain item %@ -- OSStatus: %lu", identifier, status);
    }

    return password;
}

这应该是非常简单的,但是,passwordData对象被过度释放,我不知道为什么,堆栈跟踪是this。如果我只是将passwordData设置为nil而不执行__bridge__transfer,则不会崩溃。关于为什么的任何想法?

非常感谢!

3 个答案:

答案 0 :(得分:2)

我自己没有使用__bridge_transfer,但是如果你将“passwordData”更改为:

NSData *passwordData = (NSData *)data;

XCode为您提供了两条建议。

不要转让所有权(核心基金会必须释放它):

NSData *passwordData = (__bridge NSData *)data;

转让所有权(ARC接管):

NSData *passwordData = (NSData *)CFBridgingRelease(data);

__bridge_transfer可能是同一回事,但我使用CFBridgingRelease调用时遇到了麻烦,这是XCode推荐的。

将任何内容设置为nil实际上不会释放任何内容,除非ARC正在管理内存。您永远不希望将Core Foundation对象设置为nil,除非您已使用Core Foundation明确发布它,或将所有权转移到ARC。

您拥有的另一个选择是在返回之前执行CFRelease(data)并使用正常的__bridge

这完全基于SecItemCopyMatching正在为您提供数据副本而不释放它的假设。 NewCopy是通常表明这一点的关键字。您可以在不同点使用CFGetRetainCount(data)进一步调试以验证计数。

我还注意到没有使用fullIdentifier。这是整个功能吗?

您也可以使用initWithData:encoding:代替initWithBytes:length:encoding

答案 1 :(得分:0)

您需要使用

CFDataRef投射到NSData
 NSData *passwordData = (NSData *)data;

或只是在不转让所有权的情况下使用网桥

NSData* passwordData = (__bridge NSData*) data;

答案 2 :(得分:0)

原来问题出在另一个对象上,附上正确的代码:

CFDictionaryRef queryKeychainCF = (__bridge_retained CFDictionaryRef)queryKeychain;
status = SecItemCopyMatching(queryKeychainCF, (CFTypeRef *)&data);   
NSData *passwordData = (__bridge_transfer NSData *)data;

尝试所有解决方案后,开始在ARC下阅读SecItemCopyMatching方法的错误并进入此answer