2011-10-19 21 views
9

所以我試圖將舊項目轉換爲自動引用計數。我正在嘗試使用xCode提供的轉換工具,但它表示要在轉換之前修復幾件事。我不知道如何解決這個錯誤。它是在鑰匙串文件的實現中。這個方法是返回錯誤的方法,具體來說就是SecItemCopyMatching。我得到的錯誤說:「不允許ARC使用指向'CFTypeRef *'(又名'const void **')的Objective-C指針的間接指針。我一直在尋找谷歌,蘋果文檔,和其他的廢話一堆,不能找到一個更好的辦法來獲取在鑰匙串現有的數據字典。任何幫助表示讚賞。謝謝!iPhone從鑰匙串獲取數據字典

-(NSMutableDictionary*)fetchDictionary { 

NSMutableDictionary *genericPasswordQuery = [self buildSearchQuery]; 

NSMutableDictionary *outDictionary = nil; 
OSStatus status = SecItemCopyMatching((__bridge_retained CFDictionaryRef)genericPasswordQuery, (CFTypeRef*)&outDictionary); 

if (DEBUG) printf("FETCH: %s\n", [[self fetchStatus:status] UTF8String]); 

if (status == errSecItemNotFound) return NULL; 
return outDictionary; 

}

回答

41

你並不需要禁用ARC爲此,您只需要將查詢的結果聲明爲CFDictionaryRef,然後在調用後將其轉換爲NSDictionary

/*1*/ CFDictionaryRef cfquery = (__bridge_retained CFDictionaryRef)genericPasswordQuery; 
/*2*/ CFDictionaryRef cfresult = NULL; 
/*3*/ OSStatus status = SecItemCopyMatching(cfquery, (CFTypeRef *)&cfresult); 
/*4*/ CFRelease(cfquery); 
/*5*/ NSDictionary *result = (__bridge_transfer NSDictionary *)cfresult; 

夫婦的言論:

  • 在第1行,我們把從可可土地的Core Foundation國家查詢。我們使用__bridge_retained來確保ARC在我們使用它時不會釋放和釋放對象。這種橋接模型保留了目標,所以爲了防止泄漏,必須在相應的地方使用相應的CFReleaseSecItemCopyMatching絕對不會爲我們發佈查詢,所以如果我們使用保留橋接,那麼我們需要釋放由此產生的Core Foundation對象。 (我們在第4行做的)
  • 第2,3和4行是使用Core Foundation類型的純C代碼,所以ARC將無事可做或抱怨它們。
  • 在第5行中,我們告訴ARC,SecItemCopyMatching已創建其結果,保留計數爲1,我們負責釋放。 (我們知道這是因爲它的名稱中有「Copy」)__bridge_transfer讓ARC知道這個責任,所以它可以自動爲我們做。
  • 請勿將由SecItemCopyMatching返回的不可變的Core Foundation字典強制轉換爲NSMutableDictionary;這只是錯誤的。此外,它反對一般的可可風格約定buildSearchQuery返回NSMutableDictionary。簡單的NSDictionary s在兩種情況下都可以正常工作。

拇指這裏的規則是要遵循一個CFRelease__bridge_retained需求的同時,從「複製」或「創建」功能的結果必須被強制轉換成使用__bridge_transfer可可的土地。

+0

可以ü建議我一些關於同樣的問題在這裏http://stackoverflow.com/questions/16780202/secitemcopymatching-still-leak-on-osx-under-arc – user170317

0

方法2:當您使用它一次,爲什麼需要保留或轉移?例如從底部一目瞭然的工作對我來說,測試,調試運行(memleaks)全部通過:)只有一兩件事沒有泄漏未公開autoretained變量時按鍵會被發現()

CFDictionaryRef keyAttributes = NULL; /* variable for store attributes */ 

NSMutableDictionary *credQuery = [NSMutableDictionary dictionary]; // credential Query 

/* Here you add some options for search your key */ 

OSStatus errGather = SecItemCopyMatching(
    (__bridge CFDictionaryRef)credQuery, 
    (CFTypeRef *)&keyAttributes 
); 

if (errGather == errSecSuccess) { 
    // Gather stored key 
    NSDictionary *keychainDict = (__bridge NSDictionary *)keyAttributes; 
    NSData *passData = keychainDict[(__bridge id<NSCopying>)kSecValueData]; // password 
    ... 
    /* work with gathered data :) */ 
    ... 
    CFRelease(keyAttributes); // (1) HERE. Release when CFType is retained really :) 
    credQuery = nil; 
    keychainDict = nil; 
    passData = nil; 
} 
2

方法3:讓ARC做繁重(或方法1和方法2的組合):

NSMutableDictionary* query = [NSMutableDictionary dictionaryWithDictionary: 
@{ 
    (__bridge id) kSecClass : (__bridge id) kSecClassGenericPassword, 
    (__bridge id) kSecAttrService : nssService, 
#if ! TARGET_IPHONE_SIMULATOR 
    (__bridge id) kSecAttrAccessGroup : @"PRODUCT.com.COMPANY.GenericKeychainSuite", 
#endif 

    (__bridge id) kSecMatchLimit : (__bridge id) kSecMatchLimitOne, 
    (__bridge id) kSecReturnAttributes : (__bridge id) kCFBooleanTrue, 
}]; 

if ([nssAccount length] != 0) 
    [query setObject:nssAccount forKey:(__bridge id) kSecAttrAccount]; 

CFDictionaryRef cfresult; 
auto err = ::SecItemCopyMatching((__bridge CFDictionaryRef)query, 
            (CFTypeRef*)&cfresult); 
if (err == errSecItemNotFound) 
    return std::wstring(); 
else if (err != noErr) 
    throw std::exception(); 

NSDictionary* result = (__bridge_transfer NSDictionary*) cfresult; 

SecItemCopyMatching不需要擁有輸入字典,所以__bridge是 足夠然後ARC繼續管理查詢的壽命。

通過將結果所有權轉讓給弧,它將管理結果的生命週期 ,並免除我們在所有代碼 路徑上記住CFRelease的需要。