ARC и CFRelease?
Я немного смущен. Везде, где я читал, предположим, что при использовании ARC вам все равно нужно освободить основные объекты фундамента, что имеет смысл, ARC не справляется с ними. Тем не менее, у меня есть метод, который использует некоторые методы/объекты CF, которые я использовал CFRelease
, но это привело к сбою приложения. Раскомментирование моего CFRelease
устраняет проблему, но затем я предполагаю, что у меня есть утечка памяти?
Может кто-нибудь объяснить, какие вещи нужно выпускать, а какие нет или что-то еще не так с этим кодом?
+ (NSString *) fileExtensionForMimeType:(NSString *)type
{
CFStringRef mimeType = (__bridge CFStringRef)type;
CFStringRef uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, mimeType, NULL);
CFStringRef extension = UTTypeCopyPreferredTagWithClass(uti, kUTTagClassFilenameExtension);
NSString *ext = (__bridge NSString *)extension;
// CFRelease(mimeType);
// CFRelease(uti);
// CFRelease(extension);
return ext;
}
Три закомментированных вызова CFRelease
исправляют проблему, как упоминалось, но я знаю, что это неправильно. Что я должен делать?
Ответы
Ответ 1
Вы не можете освободить mimeType
, потому что вы не владеете им. Вы не передали права собственности на __bridge
.
Вы должны освободить uti
, так как вы его создали.
Вы также должны освободить extension
, так как вы создали его, но это, вероятно, вызовет проблемы с ext
. Вместо этого переведите право собственности на ext
.
Я бы предложил следующее:
+ (NSString *) fileExtensionForMimeType:(NSString *)type {
CFStringRef mimeType = (__bridge CFStringRef)type;
CFStringRef uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, mimeType, NULL);
CFStringRef extension = UTTypeCopyPreferredTagWithClass(uti, kUTTagClassFilenameExtension);
NSString *ext = (__bridge_transfer NSString *)extension;
// CFRelease(mimeType); // not owned
if (uti) CFRelease(uti);
// CFRelease(extension); // ownership was transferred
return ext;
}
Ответ 2
Отъезд WWDC 2012 - Современный Objective-C, в котором описываются новые рекомендации для объектов Core Foundation и ARC. Это примерно 37:35 в этом видео. Короче говоря, функции Core Foundation с Copy
или Create
в имени создают объект, который передал право собственности на ваше приложение, и ваше приложение несет ответственность за его освобождение.
В любом случае, если право собственности передано с помощью метода Core Foundation с Copy
или Create
в имени, вы можете либо вручную отпустить его с помощью CFRelease
, когда вы закончите с ним, либо, что проще, вы может передать право собственности в ARC и позволить ему позаботиться об этом. Исторически для передачи права собственности на ARC мы использовали __bridge_transfer
, но теперь они рекомендуют CFBridgingRelease
(хотя последний является всего лишь макросом для первого). И, очевидно, если у вас есть объект Core Foundation, который вы извлекли с помощью другого механизма, отличного от функции с Copy
или Create
в имени, вы не должны ни CFRelease
, ни передать право собственности в ARC.
В качестве иллюстрации этот метод выполняет то, что вы хотите:
+ (NSString *) fileExtensionForMimeType:(NSString *)type {
NSString *uti = CFBridgingRelease(UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType,
(__bridge CFStringRef)type,
NULL));
return CFBridgingRelease(UTTypeCopyPreferredTagWithClass((__bridge CFStringRef)uti,
kUTTagClassFilenameExtension));
}
Ответ 3
В общем, я думаю, вы должны попытаться прокомментировать первую строку CFRelease (mimeType) и раскомментировать следующие две строки: CFRelease (uti) и CFRelease (расширение). Вы передаете бесплатный мост для ввода NSString, и ARC будет обрабатывать выпуск. Но uti и расширение создаются/копируются как CFString. ARC не будет знать, как с ними справиться (помните, что ARC - это функция компилятора для NSObject), поэтому вам нужно их освободить.