Memory leak with CFArray in ARC - iphone

I've made an iPhone app using ARC that accesses every entry in the address book, and then every address for every person. The data is stored in CFArrays, which are toll-free bridged to NSArrays. The code is below.
ABAddressBookRef addressBook = ABAddressBookCreate();
CFArrayRef arrayRef = ABAddressBookCopyArrayOfAllPeople(addressBook);
NSArray *peopleArray =[(__bridge NSArray *) arrayRef copy];
CFRelease(arrayRef);
arrayRef = nil;
for(id personId in peopleArray)
{
ABRecordRef person = (__bridge ABRecordRef) personId;
//process other attributes of the address book
ABMultiValueRef multi = ABRecordCopyValue(person, kABPersonAddressProperty);
CFArrayRef addressRef = ABMultiValueCopyArrayOfAllValues(multi);
NSArray *addressArray = [(__bridge NSArray *) addressRef copy];
for(NSDictionary *address in addressArray)
{
//process the addresses
}
CFRelease(addressRef);
addressRef = nil;
}
From what I've researched on the internet and in Apple's Memory Management guides, this looks like the proper way to do it. The problem is when I got to run the code, it halts on "CFRelease(addressRef)", highlighted green with text "Thread 1" (not sure what this error means). I've also tried putting the CFRelease before the for loop, but the same issue occurs.
If I remove the CFRelease, it compiles, but there is a memory leak at the creation of addressArray. Does anyone know how to solve this problem? I can't seem to figure it out using ARC.

Instead of NSArray *peopleArray =[(__bridge NSArray *) arrayRef copy]; CFRelease(arrayRef);, use NSArray *peopleArray = CFBridgingRelease(arrayRef). This transfers ownership of the object to ARC.

Whenever you see "Copy" in a method name you should use (__bridge_transfer <ObjectType> *)
Then ARC will be responsible for releasing the object.
So your code will look like:
NSArray *peopleArray = (__bridge_transfer NSArray *)ABAddressBookCopyArrayOfAllPeople(addressBook);

Related

Memory Leak in the below simple code

I have a piece of code & I want to understand the memory leak possibility in this. I have gone through the Apple documentation here!
The code here process a array from parameter & stores the dictionary into a temporary array.
+ (void)setLinkedProfiles:(NSArray *)profileData {
NSMutableArray *returnArray = [[NSMutableArray alloc]init];
if([profileData count] > 0) {
for(NSDictionary *dict in profileData) {
NSDictionary *tempDict = #{
#"verifiedEmail" : ([[dict objectForKey:#"verifiedEmail"] isKindOfClass:[NSNull class]]) ? #"": [dict objectForKey:#"verifiedEmail"],
#"identifier" : [dict objectForKey:#"identifier"],
};
[returnArray addObject:tempDict];
}
}
[SharedApp sharedUserData].linkedProfiles = returnArray;
}
I suspect that the line of code SharedApp sharedUserData].linkedProfiles = returnArray might create a memory leak.
I want to understand why & in which scenario?
Can some one help me in understanding this ?
Thank you for reading & understanding my problem.
To clear things up:
Your line
NSMutableArray *returnArray = [[NSMutableArray alloc]init];
gives you ownership of that array. You are responsible to release it when you're done with it.
So adding
[returnArray release];
as the last line would work. You could use autorelease instead, or even work with an autoreleased array from the beginning, i.e.
NSMutableArray *returnArray = [NSMutableArray array];
linkedProfiles should be a strong reference (i.e. strong, or copy).
Although I strongly suggest to switch to ARC, understanding of the underlying memory management might come handy.

potential leak of an object stored into address book

I got issue potential leak of an object stored into address book, how to resolve it.
I create address book locally and then copyArrayofAllpeople to people1 array. people1 array used outside of method. So where should I release addressbookref. Please make suggestions.
This is my code
ABAddressBookRef addressBook = ABAddressBookCreate();
people1 = (__bridge NSArray *)ABAddressBookCopyArrayOfAllPeople(addressBook);
Use CFRelease() method.
ABAddressBookRef addressBook = ABAddressBookCreate();
people1 = (__bridge NSArray *)ABAddressBookCopyArrayOfAllPeople(addressBook);
CFRelease(addressBook);
I think it will be helpful to you.

contact notes are not transferred when exporting/importing via the built-in vCard representation methods

Using the ABPersonCreateVCardRepresentationWithPeople method for export, and the ABPersonCreatePeopleInSourceWithVCardRepresentation for import, I have successfully transfered contact data between devices. However, the data in the contact's "notes" field isn't transfered.
Here's my export function:
+(NSData*)exportContactsToVcard:(NSArray*)contacts
{
NSMutableArray *people = [NSMutableArray arrayWithCapacity:contacts.count];
ABAddressBookRef ab = ABAddressBookCreate();
for (Contact *contact in contacts)
{
ABRecordRef person = ABAddressBookGetPersonWithRecordID(ab,contact.contactId);
[people addObject:(__bridge id)person];
}
NSData *vCard = (__bridge NSData*)ABPersonCreateVCardRepresentationWithPeople((__bridge CFArrayRef) people);
return vCard;
}
and part of my import function:
+(NSArray*)importContactsFromVcardData:(NSData*)vcardData
{
NSMutableArray *addedContactIds = [NSMutableArray array];
ABAddressBookRef addressBook = ABAddressBookCreate();
ABRecordRef defaultSource = ABAddressBookCopyDefaultSource(addressBook);
NSArray *createdPeople = (__bridge_transfer NSArray*)ABPersonCreatePeopleInSourceWithVCardRepresentation(defaultSource,(__bridge CFDataRef)vcardData);
CFErrorRef error = NULL;
for (id person in createdPeople)
{
error = NULL;
ABRecordRef personRecord = (__bridge ABRecordRef)person;
NSString *notes = (__bridge NSString *)ABRecordCopyValue(personRecord, kABPersonNoteProperty);
In the last line, notes is always nil, even if the contact had notes before it was exported. All the other standard contact fields seem to be in place.
For example, if I replace the last line with:
NSString *firstName = (__bridge NSString *)ABRecordCopyValue(personRecord, kABPersonFirstNameProperty);
the firstName string will hold the contact's first name.
Any idea how I can work around this, and get the contact notes?
Thanks.
For testing purposes, you can export a vCard from Address Book. Then drag it to TextEdit to look at the various fields.
At the bottom you'll find something like this:
NOTE:This is a note!
Also, see this link for info on the vCard format:
http://en.wikipedia.org/wiki/VCard
ABPersonCreateVCardRepresentationWithPeople will only transfer the important contact details of a vCard...thats my identifying in much tests with the vCard and the iOS import-export function. It has many mistakes, e.g. if you try to import it with social networks like facebook, it won't do it with (just look in my other thread THREADLINK )
But if you try to add some vCard Information over ABNewPersonViewController, it will work perfectly.

How to dissect and reorganize info in an NSDictionary

So I have an array of NSDictionaries, each NSDictionary has a bunch of key/value pairs pertaining to aspects of a photo (from Flickr).
I'm making an app that has a UITableViewController whose cells should be each of the different categories of the photos. So in pseudocode, I'm trying to construct a new NSDictionary (with keys being categories of photos, values being the NSDictionaries of the photos that contains that key). I'm iterating through each NSDictionary in the initial array, getting the category tags, and saying, if my new NSDict doesn't contain this key, make a new key to an empty array. Then add the current NSDict to that array. I'm getting consistent errors, not sure why.
Here's the diluted code.
photoList = [FlickrFetcher photosWithTags:[NSArray arrayWithObjects: #"CS193p_SPoT", nil]];
NSLog(#"%#", photoList);
categories = [[NSDictionary alloc] init];
NSArray *temp = [[NSArray alloc] init];
for (id obj in photoList) {
temp = [[obj objectForKey:#"tags"] componentsSeparatedByString:#" "];
for (id string in temp) {
if (![categories objectForKey:string]) {
NSMutableArray *arr = [[NSMutableArray alloc] init];
[categories setObject:arr forKey:string];
//[arr release];
}
NSMutableArray *photos = [categories objectForKey:string];
[photos addObject:obj];
[categories setObject:photos forKey:string];
}
}
Thanks!
NSDictionary doesn't have a method setObject:forKey:. You need an NSMutableDictionary.
self.categories = [NSMutableDictionary dictionary];
Other than that, please do use Joost's excellent rewrite of your code.
SIGABRT, just so you know, most likely means that an assertion somewhere failed. In this case, it may be an assertion all the way down in CoreFoundation*; CF checks for mutability when you try to access a dictionary like that and causes an interrupt if the object isn't mutable.
*I have just learned about the CF source's availability recently and have been looking through it, so this may be just "new thing" bias and incorrect.
I don't notice any errors (syntax-errors, that is) in your code, however here is an updated piece of code which has been implemented a bit cleaner (and without memory leaks)
self.photoList = [FlickrFetcher photosWithTags:[NSArray arrayWithObjects: #"CS193p_SPoT", nil]];
NSLog(#"%#", photoList);
self.categories = [NSDictionary dictionary];
for (NSDictionary *obj in photoList) {
NSArray *temp = [[obj objectForKey:#"tags"] componentsSeparatedByString:#" "];
for (NSString *string in temp) {
NSMutableArray *photos = [categories objectForKey:string];
if (!photos) {
photos = [NSMutableArray array];
[categories setObject:photos forKey:string];
}
[photos addObject:obj];
}
}
If it's not working please tell us the exact warning, and were it is caused.

Objective c, Memory Leak, reading from sqlite and assigning values to a NSDictionary and NSAarray

I have a list of shops in a ListController file.
I've setted up a sqlite db, in which i've stored 60 shops.
On the top of the list i have a search bar.
I've made a class called DataController, that is responsible to load and store db datas.
#interface DataController : NSObject {
sqlite3 *database;
NSArray *shops;
NSDictionary* dictionaryOfShops;
}
#property (nonatomic, retain) NSDictionary *dictionaryOfShops;
#property (nonatomic, retain) NSArray* shops;
-(void)initializeShops;
initializeShops method loads data from the db, and stores results into the 2 props in this way:
-(void)initializeShops{
[dictionaryOfShops release];
[shops release];
NSMutableDictionary *dictionary = [[[NSMutableDictionary alloc] init] autorelease];
if (sqlite3_open(....))
NSString *query = ....
if (sqlite3_prepare_v2(database, [query UTF8String],-1, &statement, nil) == SQLITE_OK)
{
while (sqlite3_step(statement) == SQLITE_ROW) {
int rId = sqlite3_column_int(statement, 0);
char *rName = (char *)sqlite3_column_text(statement, 1);
Shop* s = [[Shop alloc] init];
s.ID = rId;
if(sName != nil) s.Name = [NSString stringWithUTF8String:rName];
NSString *shopID = [[NSString alloc] initWithFormat:#"%d",s.ID];
[dictionary setObject:s forKey:shopID];
[shopID release];
[s release];
}
sqlite3_finalize(statement);
}
[query release];
dictionaryOfShops = [[NSDictionary alloc] initWithDictionary:dictionary];
shops = [[NSArray alloc] initWithArray:[dictionary allValues]];
dictionary = nil;
[dictionary release];
//Sorting
NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:#"Name" ascending:YES];
NSArray *sortedList =[self.shops sortedArrayUsingDescriptors:[NSArray arrayWithObject:sort]];
self.shops = sortedList;
[sort release];
}
The problem is that when user enters some text into the search
bar, I change the value of the query (adding LIKE....) and then call the initializeShops method again. This second time makes
so many leaks, (related to the Shop class properties) and
leaks also a NSDictionary and a NSArray.
Before posting this to you I've tried different solutions, but
at least this doesn't leaks anything the first time I call
initilizeShops.
I accept any suggestion, since I'm really stuck
on it.
MORE:
The really strange thing is memory management of my var dictionary and the 2 props shops and dictionaryOfShops. With this code
NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] init];
//add data to dictionary
dictionaryOfShops = [[NSDictionary alloc] initWithDictionary:dictionary];
shops = [[NSArray alloc] initWithArray:[dictionary allValues]];
[dictionary release]
Considering that dictionaryOfShops and shops are two properties (nonatomic,retain) synthesized, how can I change value to them without leaks?
The very first time I pass through this method nothing gets leaked, from the second time it starts to leak so many objects (the contents of the collections).
The first question is Why not just use Core Data? It is very likely going to be faster, will require less code, and will be significantly easier to maintain over time. To be blunt; SQLite is deceptively hard. Easy to get started, exceptionally difficult to get right.
In any case, the memory management of dictionary is wrong. It only isn't crashing because you swapped the order of the nil assignment and release as kennyTM suggested. I would suggest not creating an autoreleased dictionary.
Otherwise, the code as written seems pretty leakless at first glance. So:
Can you provide some more code?
Anything interesting memory wise
going on elsewhere?
Are you using threading at all (or
NSOperationQueue)?
Have you run under the Leaks
instrument and retrieved the
backtraces of allocation of the
specific objects being leaked?
dictionary = nil;
[dictionary release];
Please swap these 2 statements. In this form it means [nil release] which is a no-op.
Ok, I've found the error.
In my class Shop, i realize i didn't implement the method
-(void)dealloc
So when I release the old dictionary (to prepare for a new assignment), all the fields inside of it didn't get released.