Advertise Books Events Forum News Social Networking Support Us

sdkIQ for iPhone
($4.99)

Shape Up
($0.99)

Your First iPhone App
($1.99)

iVidCam Free
(free)

Kid Art
($0.99)

iPUBQUIZ
(£1.19)

ArtStudio
($3.99)

Want your application or service advertised on iPhone Dev SDK?

Go Back   iPhone Dev SDK Forum > iPhone SDK Development Forums > iPhone SDK Development

Reply
 
LinkBack Thread Tools Display Modes
Old 03-23-2009, 12:28 PM   #1 (permalink)
Registered Member
 
Join Date: Feb 2009
Location: Los Angeles
Posts: 36
Send a message via AIM to kallipigous
Default Passing arrays back and forth to functions

Is there any reason why I can't pass an NSarray from a view controller to a function in the app delegate and have it return another NSarray back to me?

It's working fine but even when I release the array after I send it I get lots of memory leaks showing up in instruments.
kallipigous is offline   Reply With Quote
Old 03-23-2009, 01:16 PM   #2 (permalink)
Lost in a sea of code
 
BostonMerlin's Avatar
 
Join Date: Apr 2008
Location: Boston
Posts: 388
Default

there is no technical limitation. we would have to see how your instantiating the array and the calls your making.

John
__________________
----------------------------------------------------------------------
I love being a dad, flying airplanes and writing code.
----------------------------------------------------------------------
Follow me on Twitter: @BostonMerlin
Feed your brain on Twitter: @iPhoneDev101
----------------------------------------------------------------------
iPhone Apps:
BostonMerlin is offline   Reply With Quote
Old 03-23-2009, 08:50 PM   #3 (permalink)
Registered Member
 
Join Date: Feb 2009
Location: Los Angeles
Posts: 36
Send a message via AIM to kallipigous
Default Here's some context

Quote:
Originally Posted by BostonMerlin View Post
there is no technical limitation. we would have to see how your instantiating the array and the calls your making.

John

Let me see if I can break this down. The overall thrust is that I have a large sql database with locations in the world. I pull up an array of countries in one table and the user selects one, then I go back to the next sql database and pull up a list of cities in that country (different view controller). I'm basically using the same code in both view controllers (cities and countries), they call the app delegate that has two functions that creates sql calls and return the data. I then process that list into a table and organize it into an NSmutableArray based on the first letter. This basically uses code adapted from the Iphone developers cookbook (to automatically allocate cities to the sections based on their first letter).

The same mechanism on the retrieve countries works fine with no leaks, the second produces lots of leaks depending on the number of records returned from the database. The only difference was that the second version sent an array of data to the app delegate function (which table to use, which country was selected) and I wondered if it was somehing to do with the array. However I've just adapted it so that it sends one NSString instead and it still throws a wobbly. The strange thing is:
1) the number of leaks is not consistent in instruments. it can vary by a number each time I run it the same way
2) the number of leaks seems to not be related directly to the number of records returned, it is proportionate, but not directly.
3) I only see the leaks when I leave the list, either by choosing a city in the table or by hitting back on the nav bar.

Here is viewdidload:

- (void)viewDidLoad
{
HeliosAppDelegate *delegate = (HeliosAppDelegate *)[[UIApplication sharedApplication] delegate];
NSArray *array = [NSArray alloc];
array= [delegate grabCitiesWorld:country];
listData = array;
[self createSectionList];
[array release];
}


Here is the code for putting the array into the sectioned list

// Build a section/row list from the alphabetically ordered word list

- (void) createSectionList
{
sectionArray = [[NSMutableArray alloc] init];
// Build an array with 26 sub-array sections
for (int i = 0; i < 26; i++) [sectionArray addObject:[[NSMutableArray alloc] init]];
// Add each word to its alphabetical section

for (NSString *cities in listData)
{
if ([word length] == 0) continue;
// determine which letter starts the name
NSString *firstletter = [[word substringToIndex:1] uppercaseString];
NSRange range = [ALPHA rangeOfString:firstletter];
// Add the name to the proper array
[[sectionArray objectAtIndex:range.location] addObject:city];
}


}


#define ALPHA @"ABCDEFGHIJKLMNOPQRSTUVWXYZ"

and to prove I'm releasing the array


- (void)dealloc {
[super dealloc];
[sectionArray release];
}



Now in the app delegate here is the retrieve the list of cities code.

***app delegate

- (NSMutableArray *)grabCitiesWorldNSString *)country {
NSMutableArray *cities = [[NSMutableArray alloc] init];
NSString *path = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"CityData.sqlite"];

if (sqlite3_open([path UTF8String], &database) == SQLITE_OK) {
NSString *longsql = [NSString stringWithFormat:@"SELECT city FROM %@ where country = '%@' ORDER BY City", table, country];
const char *sql = [longsql UTF8String];
sqlite3_stmt *statement;
if (sqlite3_prepare_v2(database, sql, -1, &statement, NULL) == SQLITE_OK) {
while (sqlite3_step(statement) == SQLITE_ROW) {
NSString *cityname = [NSString stringWithUTF8Stringchar *) sqlite3_column_text(statement, 0)];
[cities addObject:cityname];
}
}
// "Finalize" the statement - releases the resources associated with the statement.
sqlite3_finalize(statement);
} else {
// Even though the open failed, call close to properly clean up resources.
sqlite3_close(database);
NSAssert1(0, @"Failed to open database with message '%s'.", sqlite3_errmsg(database));
// Additional error handling, as appropriate...
}
sqlite3_close(database);
return cities;
[cities release];
}


Any pointers (no pun intended) gratefully received.


Cheers
kallipigous is offline   Reply With Quote
Old 03-23-2009, 10:21 PM   #4 (permalink)
Registered Member
 
Join Date: Feb 2009
Location: Los Angeles
Posts: 36
Send a message via AIM to kallipigous
Default

Quote:
Originally Posted by kallipigous View Post

...Any pointers (no pun intended) gratefully received.


Cheers

By the way, Instruments is saying there are 71 leaks in grabCitiesWorld
and 45 in createSectionList. To give a sense of proportion.
kallipigous is offline   Reply With Quote
Old 03-24-2009, 11:08 AM   #5 (permalink)
Pro. Game Developer
iPhone Dev SDK Supporter
 
Join Date: Feb 2009
Location: żLa Islas Hermosas?
Posts: 1,421
Default

Code:
// Build a section/row list from the alphabetically ordered word list
- (void) createSectionList
{
	sectionArray = [[NSMutableArray alloc] init];
	// Build an array with 26 sub-array sections
	for (int i = 0; i < 26; i++) [sectionArray addObject:[[NSMutableArray alloc] init]];	
	// the line above will normally cause a leak
	// Add each word to its alphabetical section
	
	for (NSString *cities in listData)
	{
		if ([word length] == 0) continue;
		// determine which letter starts the name
		NSString *firstletter = [[word substringToIndex:1] uppercaseString];
		NSRange range = [ALPHA rangeOfString:firstletter];
		// Add the name to the proper array
		[[sectionArray objectAtIndex:range.location] addObject:city]; 
	} 
}
It looks here like you're creating a 2-dimensional array by allocating one array (sectionArray), then allocating individual arrays as elements of that first array. On the offending line, the [[NSMutableArray alloc] init] returns instances with retaincount (rc) = 1, and adding it to the array makes rc=2. In your dealloc method, you release sectionArray, which automatically sends a release to all entries in the array, but that will only make rc=1 in those array objects, thus causing a leak.

You can fix this in at least two different ways. (1) Before you release sectionArray, you can iterate it and manually call release on each element, or (2) change the [[NSMutableArray alloc] init] in your code to [[[NSMutableArray alloc] init] autorelease]. (Note: I usually choose the latter approach, for reasons obvious. )

Code:
- (void)dealloc
{
	[super dealloc];	
	[sectionArray release];
	// just a style note here - typically you clean up everything in your class BEFORE calling the [super dealloc]
}

Code:
- (NSMutableArray *)grabCitiesWorld:(NSString *)country
{
	NSMutableArray *cities = [[NSMutableArray alloc] init];
	NSString *path = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"CityData.sqlite"];	
	
    if (sqlite3_open([path UTF8String], &database) == SQLITE_OK) {				
		NSString *longsql =  [NSString stringWithFormat:@"SELECT city FROM %@ where country = '%@' ORDER BY City", table, country];
		const char *sql = [longsql UTF8String];
   		sqlite3_stmt *statement;		
		if (sqlite3_prepare_v2(database, sql, -1, &statement, NULL) == SQLITE_OK) {
            while (sqlite3_step(statement) == SQLITE_ROW) {     
				NSString *cityname = [NSString stringWithUTF8String:(char *) sqlite3_column_text(statement, 0)];
							[cities addObject:cityname];					
            }
        }
        // "Finalize" the statement - releases the resources associated with the statement.
        sqlite3_finalize(statement);
    } else {
        // Even though the open failed, call close to properly clean up resources.
        sqlite3_close(database);
        NSAssert1(0, @"Failed to open database with message '%s'.", sqlite3_errmsg(database));
        // Additional error handling, as appropriate...
    }
	sqlite3_close(database);
	return cities;
	[cities release];
	// BIG problem here
}
Hopefully, the problem here is obvious now. You're returning from the function before the [cities release] executes, so the release is never done. In a function, once any return statement it hit, the function ends. Period.

But you're thinking that you've allocated an array in this function -- how do you return a valid array, yet clean it up properly? Like this:
Code:
	return [cities autorelease];
This will return an array that the caller can use, but will automatically be released via the autorelease pool and not cause a leak.

Hopefully this helps. Let us know how things progress.
Kalimba is offline   Reply With Quote
Old 03-24-2009, 11:48 AM   #6 (permalink)
Registered Member
 
Join Date: Feb 2009
Location: Los Angeles
Posts: 36
Send a message via AIM to kallipigous
Default

Quote:
Originally Posted by Kalimba View Post

Hopefully this helps. Let us know how things progress.
That is a marvelous reply. Many, many thanks. And it all makes perfect sense.

I didn't want to release the array before I sent it back but it makes sense that the return is the last thing it does. Thanks for autorelease. That does the trick splendidly

It occurred to me that the way the array is filled with allocated arrays was causing the problem but when I ran the test code from the book it worked fine with no leaks.

I suspect that's because it was such a simple program the view was never switched out and I didn't notice. There's a better implementation of this automatic creation of indexes based on first letter in the apple table examples that I may try as well.

Again thanks.

Last edited by kallipigous; 03-24-2009 at 12:12 PM.
kallipigous is offline   Reply With Quote
Reply

Bookmarks

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are On


Enter the iPhone App Challenge!  Win $500!
» Advertisements
» Stats
Members: 24,181
Threads: 38,954
Posts: 170,900
Top Poster: smasher (2,565)
Welcome to our newest member, Zephyros
Powered by vBadvanced CMPS v3.1.0

All times are GMT -5. The time now is 08:31 AM.
Powered by vBulletin® Version 3.8.0
Copyright ©2000 - 2010, Jelsoft Enterprises Ltd.
Search Engine Friendly URLs by vBSEO 3.2.0