我正在查询IOS地址簿中的所有人,并将他们的图像存储在本地缓存中。一切都适用于小型地址簿 - 但由于内存压力,很多条目(> 1000)会使应用程序崩溃。
在调查问题之后,似乎ABPersonCopyImageData
为该图像分配内存,并返回引用数为2的CFDataRef photoData
。在释放数据CFRelease(photoData)
后,引用数保持为1 ,这表明ABAddressBookRef addressBook
保留了引用,可能是出于缓存的原因。内存消耗在整个循环中线性增加。
循环CFRelease(addressBook)
最终清理所有引用并释放内存。因此,一个黑客解决方案是定期发布地址簿并创建一个新的地址簿(每100项左右),但它有一些缺点。
是否有其他方法可以告诉地址簿释放对图像数据的引用?
- (void)testContacts {
ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, nil);
CFArrayRef allContacts = ABAddressBookCopyArrayOfAllPeople(addressBook);
CFIndex nPeople = ABAddressBookGetPersonCount(addressBook);
for (CFIndex idx = 0; idx < nPeople; idx++ ) {
ABRecordRef person = CFArrayGetValueAtIndex( allContacts, idx );
if (ABPersonHasImageData(person)) {
CFDataRef photoData = ABPersonCopyImageDataWithFormat(person, kABPersonImageFormatThumbnail);
if (photoData) {
// do something (eg. store data) - does not affect problem
CFRelease(photoData);
}
}
}
CFRelease(addressBook);
}
答案 0 :(得分:0)
我认为@autoreleasepool
确实会像Volker在评论中所说的那样帮助你的案子。
调用CFRelease
实际上并不释放内存,它只会减少对象的保留计数,就像release
在ARC之前一样。
因此,内存使用量会累积到自动释放池的下一次排放,这会在当前运行循环的当前周期结束时发生。
此外,请注意您可以使用免费桥接将所有权转移到ARC,从而提高效率:
- (void)testContacts {
ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, nil);
NSArray *allContacts = (__bridge_transfer NSArray*)ABAddressBookCopyArrayOfAllPeople(addressBook);
CFIndex nPeople = ABAddressBookGetPersonCount(addressBook);
for (CFIndex idx = 0; idx < nPeople; idx++ ) {
@autoreleasepool {
ABRecordRef person = (__bridge ABRecordRef)allContacts[idx];
if (ABPersonHasImageData(person)) {
NSData *photoData = (__bridge_transfer NSData*)ABPersonCopyImageDataWithFormat(person, kABPersonImageFormatThumbnail);
if (photoData) {
// do something (eg. store data) - does not affect problem
}
}
}
}
CFRelease(addressBook);
}
答案 1 :(得分:0)
好的,到目前为止定期发布addressBook
似乎是解决问题的唯一方法,请参阅下面的代码。
每100个条目发布会增加 8%的执行时间开销,但正如预期的那样会使内存几乎减少10倍 1000个电话簿条目(即35MB和在iPhone 4上15秒对4MB对比16.2秒
我希望任何人都可以提出更好的解决方案,直到那时我才会使用它。
void abImagesV3() {
ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, nil);
CFArrayRef allContacts = ABAddressBookCopyArrayOfAllPeople(addressBook);
CFIndex nPeople = ABAddressBookGetPersonCount(addressBook);
for (CFIndex idx = 0; idx < nPeople; idx++ ) {
ABRecordRef person = CFArrayGetValueAtIndex( allContacts, idx );
if (ABPersonHasImageData(person)) {
CFDataRef photoData = ABPersonCopyImageDataWithFormat(person, kABPersonImageFormatThumbnail);
if (photoData) {
// do something (eg. store data) - does not affect problem
CFRelease(photoData);
}
}
if (idx%100 == 99) {
CFRelease(addressBook);
CFRelease(allContacts);
addressBook = ABAddressBookCreateWithOptions(NULL, nil);
allContacts = ABAddressBookCopyArrayOfAllPeople(addressBook);
}
}
CFRelease(addressBook);
}