How and where to allocate memory? - iphone

I narrowed down my problem to not allocating memory properly. I have a NSMutableArray called subjects as a member of a Category. When I populate the category with the array of subjects it doesn't store.
I am not sure how and where to allocate the memory for subjects, which is what I believe my problem is.
Here is the snippet:
Category *cat = [[Category alloc] init];
cat.category_title = [result_categories stringForColumn:#"CATEGORY"];
QuotesAppDelegate *appDelegate = (QuotesAppDelegate *)[[UIApplication sharedApplication] delegate];
for (Subject *sb in appDelegate.subjects){
if([cat.category_title isEqualToString:sb.category_title]){
[cat.subjects addObject:sb];
NSLog(#"Adding subject: %# cat.subjects.count=%i", sb.title, cat.subjects.count);
}
}
(The above log does print cat.subject names but it doesn't print the subjects.count)

Have you allocate memory to cat.subjects like
cat.subject = [[NSMutableArray alloc] init];
or
subject = [[NSMutableArray alloc] init];
in your Category class ? if not then do it. This might be the one of the problem.
Update
first of all change your code from Category *cat = [Category alloc]; to Category *cat = [[Category alloc] init];
and change your init as
- (id)init {
self = [super init];
if (self) {
subjects = [[NSMutableArray alloc] init];
}
return self;
}

Related

Is there a way to shorten my CountryViewController.m without hard coding?

I would like to know if there's a way to shorten my CountryViewController.m because I will be adding another else if statement every time I add a new country.
I am using a UITableViews that pushes a new table view.
RootViewController.m
- (void)viewDidLoad
{
    [super viewDidLoad];
    
    self.tableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStyleGrouped];
    
    ASIA = [[NSMutableArray alloc] init];
    EU = [[NSMutableArray alloc] init];
    [ASIA addObject: #"China"];
    [ASIA addObject: #"Japan"];
    [ASIA addObject: #"Korea"];
    [EU addObject: #"France"];
    [EU addObject: #"Italy"];
    [EU addObject: #"Switzerland"];
    self.title = #"Continent";
}
CountryViewController.m
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear: animated];
if ([self.title isEqualToString: #"China"]) {
_listOfCities = [[NSMutableArray alloc] init];
[_listOfCities addObject: #"Beijing"];
[_listOfCities addObject: #"Hong Kong"];
}
else if ([self.title isEqualToString: #"Japan"]){
_listOfCities = [[NSMutableArray alloc] init];
[_listOfCities addObject: #"Tokyo"];
}
else if ([self.title isEqualToString: #"Korea"]){
_listOfCities = [[NSMutableArray alloc] init];
[_listOfCities addObject: #"Seoul"];
}
else if ([self.title isEqualToString: #"France"]){
_listOfCities = [[NSMutableArray alloc] init];
[_listOfCities addObject: #"Paris"];
[_listOfCities addObject: #"Versailles"];
}
else if ([self.title isEqualToString: #"Italy"]){
_listOfCities = [[NSMutableArray alloc] init];
[_listOfCities addObject: #"Rome"];
}
else if ([self.title isEqualToString: #"Switzerland"]){
_listOfCities = [[NSMutableArray alloc] init];
[_listOfCities addObject: #"Bern"];
}
[self.tableView reloadData];
}
A simple way is to use a property list and include that as a bundle resource (i.e. just add it to your project).
For example, if your property list looks like this:
You can then load it with
countries = [NSDictionary dictionaryWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"CountryData" ofType:#"plist"]];
and use it as a normal dictionary or array, i.e.
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear: animated];
NSString* countryName = self.title;
NSDictionary* country = [countries objectForKey:countryName];
NSArray* cities = [country objectForKey:#"Cities"];
NSString* continent = [country objectForKey:#"Continent"];
_listOfCities = cities;
[self.tableView reloadData];
}
Plist files can be edited easily in XCode.

How can i release the entries object

- (id)init {
if (self == [super init]) {
entries = [[NSMutableArray alloc] init];
NSString *file = [[NSBundle mainBundle] pathForResource:#"qanda" ofType:#"db"];
sqlite3 *database = NULL;
if (sqlite3_open([file UTF8String], &database) == SQLITE_OK) {
sqlite3_exec(database, "select * from qanda order by question", LoadEntriesCallback, entries, NULL);
}
sqlite3_close(database);
}
return self;
}
leak near entries object: instance variable used while 'self' is not set to the result of [ super of self[init ...]]
leak near self: returning while 'self' is not set to the result of [ super of self[init ...]]
You should have
self = [super init];
not
self == [super init];
The most common pattern to use is:
self = [super init];
if (self) {
// Initialisation code
}
return self;
In your example you are not assigning the result of [super init] to anything and it is left hanging, hence the leak and the message.
If you want to release entries as per the question in you title:
entries = [[[NSMutableArray alloc] init] autorelease];
or
entries = [[NSMutableArray alloc] init];
and
[entries release];
Before the return or in your dealloc.
You can release entries by replacing :
entries = [[NSMutableArray alloc] init];
with :
entries = [[[NSMutableArray alloc] init] autorelease];
But i think the question should be : is this a good way of getting the contents from the db and frankly i think your code would be better if you moved the reading content from the db part to a loadData method.
You should autorelease it while creating.
entries = [[[NSMutableArray alloc] init] autorelease];
Try replacing
entries = [[NSMutableArray alloc] init];
with
self.entries = [[[NSMutableArray alloc] init]autorelease];
and replacing
sqlite3_exec(database, "select * from qanda order by question", LoadEntriesCallback, entries, NULL);
with
sqlite3_exec(database, "select * from qanda order by question", LoadEntriesCallback, self.entries, NULL);

Memory management advice - how to handle Zombies?

In the below sample code, I am a bit lost as to why I am getting a NZombie on the line:
[Category getInitialDataToDisplay:[self getDBPath]];
I have looked through SO posts and other documentation but am new to objective-c and am spinning my wheels on this.
- (void)applicationDidFinishLaunching:(UIApplication *)application {
//Copy database to the user's phone if needed.
[self copyDatabaseIfNeeded];
// Init the Array
activeCategories = [[NSMutableArray alloc] init];
activeSubjects = [[NSMutableArray alloc] init];
categories = [[NSMutableArray alloc] init];
subjects = [[NSMutableArray alloc] init];
quotes = [[NSMutableArray alloc] init];
quoteMaps = [[NSMutableArray alloc] init];
//Initialize the Category array.
NSMutableArray *tempArray = [[NSMutableArray alloc] init];
self.categories = tempArray;
[tempArray release];
//Once the db is copied, get the initial data to display on the screen.
[Category getInitialDataToDisplay:[self getDBPath]];
//populate active subjects and categories:
activeCategories = [self getActiveCategories];
activeSubjects = [self getActiveSubjects];
// sort data
NSSortDescriptor *categorySorter;
NSSortDescriptor *subjectSorter;
categorySorter = [[NSSortDescriptor alloc]initWithKey:#"category_title" ascending:YES];
subjectSorter = [[NSSortDescriptor alloc]initWithKey:#"title" ascending:YES];
NSArray *sortDescriptorsCat = [NSArray arrayWithObject:categorySorter];
NSArray *sortDescriptorsSub = [NSArray arrayWithObject:subjectSorter];
[self.categories sortUsingDescriptors:sortDescriptorsCat];
[self.subjects sortUsingDescriptors:sortDescriptorsSub];
[categorySorter release];
[subjectSorter release];
// Configure and show the window
[window addSubview:[navigationController view]];
[window makeKeyAndVisible];
}
...
- (void)dealloc {
[activeSubjects release];
[activeCategories release];
[categories autorelease];
[subjects autorelease];
[quotes autorelease];
[quoteMaps autorelease];
[navigationController release];
[window release];
[super dealloc];
}
Here is the getInitialDataToDisplay:
+ (void) getInitialDataToDisplay:(NSString *)dbPath {
// Use this section to bring in database and populate the array
FMDatabase *database = [FMDatabase databaseWithPath:dbPath];
[database open];
QuotesAppDelegate *appDelegate = (QuotesAppDelegate *)[[UIApplication sharedApplication] delegate];
//appDelegate.categories = [appDelegate.categories sortedArrayUsingSelector:#selector(caseInsensitiveCompare:)];
//POPULATE THE SUBJECT
FMResultSet *result_subjects = [database executeQuery:#"select * from SUBJECT"];
while([result_subjects next]) {
NSInteger primaryKey = [result_subjects intForColumn:#"SUBJECT_ID"];
Subject *sub = [[Subject alloc] initWithPrimaryKey:primaryKey];
sub.title = [result_subjects stringForColumn:#"SUBJECT"];
sub.category_title = [result_subjects stringForColumn:#"CATEGORY"];
sub.active = [result_subjects intForColumn:#"ACTIVE"];
sub.isDirty = NO;
[appDelegate.subjects addObject:sub];
[sub release];
}
FMResultSet *result_categories = [database executeQuery:#"select distinct category from SUBJECT"];
while([result_categories next]) {
Category *cat = [[Category alloc] init];
cat.category_title = [result_categories stringForColumn:#"CATEGORY"];
NSLog(#"loading category: %#", cat.category_title);
QuotesAppDelegate *appDelegate = (QuotesAppDelegate *)[[UIApplication sharedApplication] delegate];
for (Subject *sb in appDelegate.subjects){
if([cat.category_title isEqualToString:sb.category_title]){
[cat.subjects addObject:sb];
NSLog(#" Adding subject: %# cat.subjects.count=%i", sb.title, cat.subjects.count);
}
}
[appDelegate.categories addObject:cat];
[cat release];
}
//POPULATE THE QUOTES
FMResultSet *result_quotes = [database executeQuery:#"select * from QUOTE"];
while([result_quotes next]) {
Quote *sub = [Quote alloc];
sub.quote_id = [result_quotes stringForColumn:#"QUOTE_ID"];
sub.quote_date = [result_quotes stringForColumn:#"DATE"];
sub.title = [result_quotes stringForColumn:#"DESC1"];
sub.desc2 = [result_quotes stringForColumn:#"DESC2"];
sub.excerpt = [result_quotes stringForColumn:#"EXCERPT"];
sub.note = [result_quotes stringForColumn:#"NOTES"];
sub.isDirty = NO;
[appDelegate.quotes addObject:sub];
[sub release];
}
//POPULATE THE QUOTE_MAPS
FMResultSet *result_quote_map = [database executeQuery:#"select * from QUOTE_MAP"];
while([result_quote_map next]) {
QuoteMap *sub = [QuoteMap alloc];
sub.quote_id = [result_quote_map stringForColumn:#"QUOTE_ID"];
sub.quote_map_id = [result_quote_map stringForColumn:#"QUOTE_MAP_ID"];
sub.subject_id = [result_quote_map stringForColumn:#"SUBJECT_ID"];
sub.isDirty = NO;
[appDelegate.quoteMaps addObject:sub];
[sub release];
}
[database close];
NSLog(#"Count of categories: %i", appDelegate.categories.count);
NSLog(#"Count of subjects: %i", appDelegate.subjects.count);
NSLog(#"Count of quotes: %i", appDelegate.quotes.count);
NSLog(#"Count of quoteMaps: %i", appDelegate.quoteMaps.count);
}
Here is the getDbPath:
- (NSString *) getDBPath {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory , NSUserDomainMask, YES);
NSString *documentsDir = [paths objectAtIndex:0];
return [documentsDir stringByAppendingPathComponent:#"reference.db"];
}
Sometimes, the best thing to do is build->analyze ( cmd shift b ). This will point out your bug right away in almost all cases.
Best of luck!
categories = [[NSMutableArray alloc] init];
.
.
//Initialize the Category array.
NSMutableArray *tempArray = [[NSMutableArray alloc] init];
self.categories = tempArray;
[tempArray release];
u've setup categories, then setup the tempArray, replaced the one in categories with it thus making a leak, then released the temp arrayObject, which what categories is now also pointing on, so unless "self.categories" is a retained property it will be a zombie. there seems to be something wrong there.
I may need to see some more of your code (the property declarations and their synthesis to make sure.
is the Zombie called on "getInitialDataToDisplay" or on "getDBPath"
try dividing it on 2 lines to know pin point more
I think you have not declare Category as retained property in .h file.If not, add following line in your .h file
#property (nonatomic, retain) NSArray Category;
And synthesize the property in .m as-
#synthesize Category;
I think it will help ....

Sectioned tableview problem

I have a database app i am making and i can extract the results from a sqlite database and put them in the table, however i need to make them sort alphabetically in sections.
So now i have this code
AArray = [[NSMutableArray alloc] init];
BArray = [[NSMutableArray alloc] init];
CArray = [[NSMutableArray alloc] init];
DArray = [[NSMutableArray alloc] init];
EArray = [[NSMutableArray alloc] init];
FArray = [[NSMutableArray alloc] init];
GArray = [[NSMutableArray alloc] init];
HArray = [[NSMutableArray alloc] init];
IArray = [[NSMutableArray alloc] init];
JArray = [[NSMutableArray alloc] init];
KArray = [[NSMutableArray alloc] init];
LArray = [[NSMutableArray alloc] init];
MArray = [[NSMutableArray alloc] init];
NArray = [[NSMutableArray alloc] init];
OArray = [[NSMutableArray alloc] init];
PArray = [[NSMutableArray alloc] init];
QArray = [[NSMutableArray alloc] init];
RArray = [[NSMutableArray alloc] init];
SArray = [[NSMutableArray alloc] init];
TArray = [[NSMutableArray alloc] init];
UArray = [[NSMutableArray alloc] init];
VArray = [[NSMutableArray alloc] init];
WArray = [[NSMutableArray alloc] init];
XArray = [[NSMutableArray alloc] init];
YArray = [[NSMutableArray alloc] init];
ZArray = [[NSMutableArray alloc] init];
and i have code to move each item from the database into the relevant array, this all works fine.
I then have this code:
All = [NSMutableDictionary dictionaryWithObjectsAndKeys:AArray,#"A",BArray,#"B",CArray,#"C",DArray,#"D",EArray,#"E",FArray,#"F",GArray,#"G",HArray,#"H",IArray,#"I",JArray,#"J",KArray,#"K",LArray,#"L",MArray,#"M",NArray,#"N",OArray,#"O",PArray,#"P",QArray,#"Q",RArray,#"R",SArray,#"S",TArray,#"T",UArray,#"U",VArray,#"V",WArray,#"W",XArray,#"X",YArray,#"Y",ZArray,#"Z",nil];
AllArray = [NSArray alloc];
AllArray = [AllArray initWithArray:[[All allKeys]sortedArrayUsingSelector:#selector(compare:)]];
and this
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
return [AllArray count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
NSArray *Array = [All objectForKey:[AllArray objectAtIndex:section]];
return [Array count];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
int sectionindex = section;
return [AllArray objectAtIndex:sectionindex];
}
When i run it works fine, however if i scroll up and down a few times the app crashes without any error message.
It is something to do with the sections as it crashes when i add them in, but i just cannot see what im doing wrong
Everythings declared in the h file and i #property and #synthesize the following
#property (nonatomic,retain) NSMutableDictionary *All;
#property (nonatomic,retain) NSArray *AllArray;
#property (nonatomic, retain) UITableView *TableView;
If anyone could help me it would be really great!
Thanks!
Your dictionary "All" does not seem to be retained. You appear to assign the dictionary object directly to the instance variable, not to the property (in which case you would have used self.All).
If your app crashes without a message, then make sure to enable the Breakpoints button in Xcode's toolbar. This will run the app in the debugger, which will give you more helpful information about the crash. Setting NSZombieEnabled to YES also helps.
Take a look here:
Documentation
The simplest approach is, to provide a sort selector (see the link for details):
sortedArray = [anArray sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)];

Initializing a class. Can you see any problems with this?

-(id)init {
if (self = [super init]) {
self.name = [[NSString alloc] init];
self.type = [[NSString alloc] init];
self.phoneNumber = [[NSString alloc]init];
self.webAddress = [[NSString alloc] init];
NSMutableArray *pricesArray = [[NSMutableArray alloc] init];
NSMutableArray *poolsArray = [[NSMutableArray alloc] init];
self.prices = pricesArray;
self.pools = poolsArray;
[pricesArray release];
[poolsArray release];
//Create the address dictionaries
NSMutableDictionary *addressItems = [[NSMutableDictionary alloc] initWithObjectsAndKeys:#"", KAddressStreet1Key, #"", KAddressStreet2Key, #"", KAddressBoroughKey, #"", KAddressCityKey, #"", KAddressCountyKey, #"", KAddressPostCodeKey, #"" ,KAddressCountryKey, nil];
//Add dictionary to the array to contain address values
self.address = addressItems;
[addressItems release];
}
return self;
}
I'm currently doing a massive round of debugging thanks to EXC_BAD_ACCESS errors.. grr.
Does the code above seem fine and logical for a class init method? Basically I'm getting the EXC_BAD_ACCESS errors when I release both the pools (mutable array and the dictionary).
How are your properties declared? If they are not declared with retain then most of your objects will be deallocated at the end of this method.
You're leaking objects with each allocation for the string properties. Other than that, I don't notice anything wrong. How are the AddressXKeys defined?