I'm querying all persons from the IOS address book and store their image in a local cache. Everything works fine for small address books - however a lot of entries (>1000) crash the app due to memory pressure.
After investigating the issue it seems that the ABPersonCopyImageData
allocates memory for that image, and returns a CFDataRef photoData
with a refcount of 2. After releasing the data CFRelease(photoData)
the refcount stays at 1, which suggests that the ABAddressBookRef addressBook
keeps a reference, probably for caching reasons. The memory consumption linearly increases through the whole loop.
After the loop CFRelease(addressBook)
finally cleans up all references and frees up the memory. So one hack-ish solution is to periodically release the address book and create a new one (every 100 items or so), but it has some downsides.
Is there another way to tell the address book to release the reference to the image data?
- (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);
}