table view memory leak? - ios

I have some memory issues using table view in iOS.
I have a screen setup like this , so there is one View Controller going to another with table view.
Every time I go in the controller with table view and go back my memory keeps incrementing by about 1 MB.
So memory usage gets big in a few screen changes
Any ideas to why this is happening?
Thanks
Code for the view controller with table view:
view did load method:
questionGroups = [[NSMutableArray alloc] init];
// Do any additional setup after loading the view.
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *path = [paths objectAtIndex:0];
NSString *databasePath = [path stringByAppendingPathComponent:#"databaseApp.sql"];
sqlite3 *database = NULL;
if(sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK){
const char *sql = "SELECT * FROM question_groups";
sqlite3_stmt *statement;
if(sqlite3_prepare_v2(database, sql, -1, &statement, NULL) == SQLITE_OK){
while (sqlite3_step(statement) == SQLITE_ROW) {
NSMutableArray *questionType = [[NSMutableArray alloc] init];
NSString *groupName = [NSString stringWithUTF8String:(char *)sqlite3_column_text(statement, 1)];
NSString *groupId = [NSString stringWithUTF8String:(char *)sqlite3_column_text(statement, 0)];
[questionType addObject:groupId];
[questionType addObject:groupName];
[questionGroups addObject:questionType];
questionType = nil;
}
sqlite3_finalize(statement);
statement = nil;
}
sqlite3_close(database);
}
database = NULL;
//populiranje liste
self.table.delegate = self;
self.table.dataSource = self;
cell creating:
static NSString *CellIndentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIndentifier];
if(cell == nil){
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIndentifier];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
NSInteger row = indexPath.row;
cell.textLabel.text = [NSString stringWithFormat:#"%# - %#",questionGroups[row][0],questionGroups[row][3]];

Related

UITableView is not scrolling down

I created an ios app in which i have a table with dynamic cell generation. When i try to scroll down to the table. It returns to the top of the table. My code as follows:
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if(!self.customCell){
self.customCell = [self.tableView dequeueReusableCellWithIdentifier:#"CustomCell"];
}
//Cell configuration
int quoteIndex = indexPath.row % [vendor count];
self.customCell.description.text = message[quoteIndex];
//Cell Layout
[self.customCell.description sizeToFit];
//Height of cell
float height = (CGRectGetMaxY(self.customCell.description.frame) +5);
return height + 1;
}
Here the cellForRowAtIndexPath:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
CustomTableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:#"CustomCell"];
const char *dbpath = [_databasePath UTF8String];
sqlite3_stmt *statement;
if (sqlite3_open(dbpath, &_beaconDB) == SQLITE_OK)
{
NSString *querySQL = [NSString stringWithFormat:
#"SELECT vendor_name, enter_message, enter_image, vendor_image, received_date, time_interval FROM beacons WHERE id=%#", [unique objectAtIndex:indexPath.row]];
const char *query_stmt = [querySQL UTF8String];
if (sqlite3_prepare_v2(_beaconDB,
query_stmt, -1, &statement, NULL) == SQLITE_OK)
{
if (sqlite3_step(statement) == SQLITE_ROW)
{
title = [[NSString alloc]
initWithUTF8String:
(const char *) sqlite3_column_text(
statement, 0)];
description = [[NSString alloc]
initWithUTF8String:(const char *)
sqlite3_column_text(statement, 1)];
NSString *fullImage = [[NSString alloc]
initWithUTF8String:(const char *)
sqlite3_column_text(statement, 2)];
[fullImg insertObject:fullImage atIndex:indexPath.row];
NSString *vendorImage = [[NSString alloc]
initWithUTF8String:(const char *)
sqlite3_column_text(statement, 3)];
[thumbnailImg insertObject:vendorImage atIndex:indexPath.row];
NSString *receivedDate = [[NSString alloc]
initWithUTF8String:(const char *)
sqlite3_column_text(statement, 4)];
NSString *timeInterval = [[NSString alloc] initWithUTF8String:(const char *) sqlite3_column_text(statement, 5)];
BeaconAdTime *beaconAdTime = [[BeaconAdTime alloc] init];
NSDate *updatedTime = [beaconAdTime updatedDateTime:receivedDate andInterval:[timeInterval intValue]];
int count = 0;
do
{
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat: #"MMM-dd hh:mm a"];
BeaconAdTime *beaconAdTime = [[BeaconAdTime alloc] init];
NSString *presentDateTime = [beaconAdTime presentDateTime];
NSDate *presentDateConvert = [dateFormatter dateFromString:presentDateTime];
count++;
if([updatedTime compare:presentDateConvert] == NSOrderedAscending || count == 1)
{
cell.title.text = title;
cell.description.text = description;
cell.receivedDate.text = receivedDate;
NSString *imgURL = fullImg[indexPath.row];
NSURL *imageURL = [NSURL URLWithString:imgURL];
UIImage *image = nil;
image = [UIImage imageWithData:[NSData dataWithContentsOfURL: imageURL]];
cell.vendorImage.image = image;
NSUserDefaults *userDefauts = [NSUserDefaults standardUserDefaults];
[userDefauts setObject:fullImg forKey:#"fullImage"];
[userDefauts synchronize];
UILocalNotification *localNotif = [[UILocalNotification alloc] init];
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:localNotif];
[[NSUserDefaults standardUserDefaults] setObject:data forKey:[NSString stringWithFormat:#"%d", indexPath.row]];
NSString *uid = [NSString stringWithFormat:#"%d", indexPath.row];
NSDictionary *infodict = [NSDictionary dictionaryWithObject:uid forKey:#"id"];
localNotif.userInfo = infodict;
NSDate *fireTime = [[NSDate date] addTimeInterval:0];
localNotif.fireDate = fireTime;
localNotif.alertBody = description;
[[UIApplication sharedApplication] scheduleLocalNotification:localNotif];
if(count>1)
{
break;
}
}
}
while (updatedTime!=nil);
}
sqlite3_finalize(statement);
}
sqlite3_close(_beaconDB);
}
// cell.title.text = vendor[indexPath.row];
//
// cell.vendorImage.image = [UIImage imageNamed:#"gg.jpg"];
return cell;
}
Thanks in advance.
You should not dequeue cells in height for row at index path. Also, self.customCell implies that you are working with just one cell. This is a very strange and unusual pattern.
Also, all those sqlite calls for each recycled cell is very inefficient. Instead, fetch the data you need into an array and work with that.
Also, avoid allocating formatters etc. in your cell method. Instead, use a static variable.

NSMutableArray addObject from sqlite database not working

I have an NSMutableArray that I want to populate with an sqlite database and display the data in a table view. The app builds and shows "database opened" in the console but just shows an empty tableview, I had it working perfectly but suddenly it seems to have stopped working. here is the code for adding the data to the array:
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize entries;
- (void)viewDidLoad
{
[super viewDidLoad];
//[entries addObject:#"object1"];
//[entries addObject:#"object2"];
//[entries addObject:#"object3"];
//[entries addObject:#"object4"];
[self openDB];
entries = [[NSMutableArray alloc] init];
NSString *sql = [NSString stringWithFormat:#"SELECT * FROM speakers ORDER BY name"];
sqlite3_stmt *statement;
if(sqlite3_prepare_v2(db, [sql UTF8String], -1, &statement, nil)==SQLITE_OK)
{
while (sqlite3_step(statement)==SQLITE_ROW) {
char *field2 = (char *) sqlite3_column_text(statement,1);
NSString *field2Str = [[NSString alloc]initWithUTF8String:field2];
NSString *str = [[NSString alloc]initWithFormat:#"%#", field2Str];
[entries addObject:str ];
}
}
}
//file path to db
-(NSString *) filePath {
NSArray *paths = NSSearchPathForDirectoriesInDomains
(NSDocumentDirectory, NSUserDomainMask, YES);
return [[paths objectAtIndex:0] stringByAppendingPathComponent:#"speakerdb.sqlite"];
}
//open the db
-(void)openDB {
if (sqlite3_open([[self filePath] UTF8String], &db) != SQLITE_OK){
sqlite3_close(db);
NSAssert(0,#"error preparing statement: %s", sqlite3_errmsg(db));
}else{
NSLog(#"database opened");
}
}
The NSMutableArray is defined in the header file as follows:
#import <UIKit/UIKit.h>
#import "sqlite3.h"
#import "speakerViewController.h"
#interface ViewController : UIViewController <UITableViewDelegate, UITableViewDataSource>{
IBOutlet UITableView *theTable;
sqlite3 *db;
}
#property (nonatomic,retain) NSMutableArray *entries;
#end
Here is the code for adding the data to the table view:
-(NSInteger) numberOfSectionsInTableView:(UITableView *)tableView
{
//return the number of sections.
return 1;
}
-(NSInteger)tableView:(UITableView *) tableView numberOfRowsInSection:(NSInteger)section
{
//Return the number of rows in the section
return [entries count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
//Congigure the cell...
cell.textLabel.text = [entries objectAtIndex:indexPath.row];
return cell;
}
The data shows in the table view if I add objects to the array manually (commented out lines shown in the viewDidLoad method) but doesnt seem to work from the sqlite database.
Any help would be much appreciated.
Edit: I'm using xcode 5.1.1
Try to debug your code... Put a break point on NSString *field2Str = [[NSString alloc]initWithUTF8String:field2]; row, and check that field2 is not empty...
try to open db like this:
NSString *sqLiteDb = [[NSBundle mainBundle] pathForResource:#"speakerdb"
ofType:#"sqlite"];
if (sqlite3_open([sqLiteDb UTF8String], &_database) != SQLITE_OK) {
NSLog(#"Failed to open database!");
}
And one thing is missing from your code:
sqlite3_finalize(statement);
use it like this:
if(sqlite3_prepare_v2(db, [sql UTF8String], -1, &statement, nil)==SQLITE_OK)
{
while (sqlite3_step(statement)==SQLITE_ROW) {
char *field2 = (char *) sqlite3_column_text(statement,1);
NSString *field2Str = [[NSString alloc]initWithUTF8String:field2];
NSString *str = [[NSString alloc]initWithFormat:#"%#", field2Str];
[entries addObject:str ];
}
sqlite3_finalize(statement);
}

How to filter NSDictonary in UISearchBar?

In my app i am using UITableView To display List of items And UISearchBar to Filter the List Of items. All the data, i am reading it from sqlite3.
Following is my code:
below code is used to retrieve the data from Local DB and save the data into NSMutableDictionary called dict and the the dict is added into NSMutableArray
arr_AllTableData.
-(void)dataFromLocalDB
{
const char *dbpath = [databasePath UTF8String];
sqlite3_stmt *statement;
if (sqlite3_open(dbpath, &PSATestDB) == SQLITE_OK)
{
NSString *querySQL = [NSString stringWithFormat:#"SELECT * FROM Test"];
const char *query_stmt = [querySQL UTF8String];
if (sqlite3_prepare_v2(PSATestDB, query_stmt, -1, &statement, NULL) == SQLITE_OK)
{
while (sqlite3_step(statement) == SQLITE_ROW)
{
companyName = [[NSString alloc]initWithUTF8String:(const char *) sqlite3_column_text(statement, 9)];
[arr_Name addObject:companyName];
address = [[NSString alloc]initWithUTF8String:(const char *) sqlite3_column_text(statement, 10)];
[arr_Address addObject:address];
number = [[NSString alloc]initWithUTF8String:(const char *)sqlite3_column_text(statement, 3)];
[arr_TelephoneNo addObject:number];
url = [[NSString alloc]initWithUTF8String:(const char *)sqlite3_column_text(statement, 4)];
[arr_WebAddress addObject:url];
_dict = [[NSMutableDictionary alloc] init];
[_dict setValue:arr_Name forKey:#"Name"];
[_dict setValue:arr_Address forKey:#"Address"];
[_dict setValue:arr_TelephoneNo forKey:#"Number"];
[_dict setValue:arr_WebAddress forKey:#"WebAddress"];
[arr_AllTableData addObject:_dict];
}
sqlite3_finalize(statement);
sqlite3_close(PSATestDB);
}
}
}
Following code is used to display data in UITableView
#pragma mark - UITableView Data source
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (isFiltered == YES)
{
return [filtered_Name count];
}
else
{
return [arr_AllTableData count];
}
}
-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *CellIdentifier = #"listCell";
PSAListCell *List = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
if (isFiltered == YES)
{
List.lbl_name.text = [filtered_Name objectAtIndex:indexPath.row];
}
else
{
List.lbl_name.text = [[_dict objectForKey:#"Name"] objectAtIndex:indexPath.row];
List.lbl_address.text = [[_dict objectForKey:#"Address"] objectAtIndex:indexPath.row];
List.lbl_ContactNO.text = [[_dict objectForKey:#"Number"] objectAtIndex:indexPath.row];
List.lbl_WebAddress.text = [[_dict objectForKey:#"WebAddress"] objectAtIndex:indexPath.row];
}
return List;
}
This is my UISearch Bar implemantion code:
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
if (searchText.length == 0)
{
// set our boolean flag
isFiltered = NO;
}
else
{
//set our boolean flag
isFiltered = YES;
// Alloc And init our filter NSMutable array
filtered_Name = [[NSMutableArray alloc]init];
//fast enumeration
NSMutableArray *test = [[NSMutableArray alloc]init];
for (NSDictionary *dictionary in arr_AllTableData)
{
NSArray *array = [dictionary objectForKey:#"Name"];
[test addObjectsFromArray:array];
}
for (NSString * name in test)
{
NSRange nameRang = [name rangeOfString:searchText options:NSCaseInsensitiveSearch];
if (nameRang.location != NSNotFound)
{
[filtered_Name addObject:name];
}
}
}
[_ListTable reloadData];
}
I want to filter the NSDictionary which contains the data and want to display to the filter data on UITableView and if the data is not available in the dictionary then it need to call a web service?
Can anyone please help me out with this?
you can use NSPredicate to filter content from arr_AllTableData based upon your input string.
you can find filter array with predicate in below link.
http://alexeckermann.com/blog/legacy/filtering-nsarray-with-nspredicates

How to add JSON data to a NSMutableArray?

Im trying to read rows from my SQLite DB and then convert the data to JSON and put it into an NSMutableArray. I then want to loop over the array and print the data into my table view.
This is what i do to load the data from SQLite:
entries = [[NSMutableArray alloc] init];
NSString *sql = [NSString stringWithFormat:#"SELECT * FROM chatHistory GROUP BY channelID ORDER BY time DESC"];
sqlite3_stmt *statement;
if(sqlite3_prepare_v2([box db], [sql UTF8String], -1, &statement, nil) == SQLITE_OK) {
while(sqlite3_step(statement) == SQLITE_ROW) {
char *field1 = (char *) sqlite3_column_text(statement, 0);
NSString *channelID = [[NSString alloc] initWithUTF8String:field1];
char *field2 = (char *) sqlite3_column_text(statement, 0);
NSString *sender = [[NSString alloc] initWithUTF8String:field2];
char *field3 = (char *) sqlite3_column_text(statement, 0);
NSString *message = [[NSString alloc] initWithUTF8String:field3];
char *field4 = (char *) sqlite3_column_text(statement, 0);
NSString *recipient = [[NSString alloc] initWithUTF8String:field4];
char *field5 = (char *) sqlite3_column_text(statement, 0);
NSString *time = [[NSString alloc] initWithUTF8String:field5];
NSString *messageArray = [[NSString alloc] initWithFormat:#"[{ \"channelID\":\"%#\", \"sender\":\"%#\", \"message\":\"%#\", \"recipient\":\"%#\", \"time\":\"%#\"}]", channelID, sender, message, recipient, time];
// Convert to JSON object:
NSArray *jsonObject = [NSJSONSerialization JSONObjectWithData:[messageArray dataUsingEncoding:NSUTF8StringEncoding]
options:0 error:NULL];
[entries addObject:jsonObject];
Now here is how i try to add it to my table view:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"ConvCell";
ConvCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[ConvCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
NSArray *tableData = [self.entries valueForKey:#"message"];
NSLog(#"Message=%#", tableData);
cell.visitorName.text = (NSString *) [self.entries objectAtIndex:indexPath.row]; // This trows an error
cell.visitorAvatar.image = [UIImage imageNamed:#"hello.png"];
cell.messageTime.text = #"19.10";
return cell;
}
The error im getting is:
exception 'NSInvalidArgumentException', reason: '-[__NSCFArray length]: unrecognized selector sent to instance 0xa0cfc40'
Any ideas what I'm doing wrong?
Your use of JSON is interesting, it's just a short code way of creating dictionaries and nesting them in arrays. There are more containers than you really need and you could just create the dictionaries without all of the JSON wrangling.
That said, your actual error is because this line:
cell.visitorName.text = (NSString *) [self.entries objectAtIndex:indexPath.row];
should be
cell.visitorName.text = (NSString *) [tableData objectAtIndex:indexPath.row];
because you have those nested arrays are you aren't digging right to the bottom of them...
Your setup is not ideal. That being said...
You are casting your array of messages to a string.
[self.entries objectAtIndex:indexPath.row]
should return an array, not a string. What you want is something like this:
NSArray* jsonArray = [self.entries objectAtIndex:indexPath.row];
NSDictionary *dictionary = [jsonArray lastObject];
cell.visitorName.text = dictionary[#"sender"];
You can see that wrapping your one dictionary into an array does not really make any sense. Instead you could leave out the [...] in your JSON template. Then you would have
NSDictionary *dictionary = [self.entries objectAtIndex:indexPath.row];
BTW, the proper way to do this is to have a custom object Message with appropriate properties that you set right in your database fetch.

Show Contact list on table view of iPhone

I have a view controller where I have an add contact button and a table view. On clicking the add contact button a new view controller opens where there are a few textfields and labels and a button(Submit) which adds the textfield data to the database. I want that as I press the Submit button, the table view in the previous controller should contain the name and contact no. of the contact that was submitted. The addContact method is as follws:
-(IBAction)addContact{
sqlite3_stmt *statement;
const char *dbpath = [mDatabasePath UTF8String];
if (sqlite3_open(dbpath, &mDiary) == SQLITE_OK)
{
NSString *insertSQL1=[NSString stringWithFormat:#"INSERT INTO CONTACTS VALUES(\"%#\",\"%#\",\"%#\",\"%#\",\"%#\")", mName.text, mContactno.text,mCemail.text,mClatitude.text,mClongitude.text];
const char *insert_stmt1 = [insertSQL1 UTF8String];
sqlite3_prepare_v2(mDiary, insert_stmt1,
-1, &statement, NULL);
NSString *tempName = [NSString stringWithFormat:#"%#",mName.text];
NSString *tempNo = [NSString stringWithFormat:#"%#",mContactno.text];
tempName = [tempName stringByAppendingString:tempNo];
[phoneContacts addObject:tempName];
tableView.delegate = self;
tableView.dataSource = self;
[tableView reloadData];
if (sqlite3_step(statement) == SQLITE_DONE)
{ mStatus.text=#"Contact added";
mName.text=#"";
mContactno.text=#"";
mCemail.text=#"";
mClatitude.text=#"";
mClongitude.text=#"";
}
else {
mStatus.text = #"Failed to add contact";
}
sqlite3_finalize(statement);
sqlite3_close(mDiary);
}
}
The table view methods are:
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [self.phoneContacts count];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *simpleTableIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
}
cell.textLabel.text = [self.phoneContacts objectAtIndex:indexPath.row];
return cell;
}
Nothing is getting shown in the table view. Why?
I've added a method populateContactTable:
-(IBAction)populateContactTable:(id)sender
{
sqlite3_stmt *statement;
const char *dbpath = [mDatabasePath UTF8String];
if (sqlite3_open(dbpath,&mDiary)== SQLITE_OK) {
NSString *selectSQL = [NSString stringWithFormat:#"SELECT * FROM CONTACTS"];
const char *query_stmt = [selectSQL UTF8String];
if (sqlite3_prepare_v2(mDiary,
query_stmt, -1, &statement, NULL) == SQLITE_OK)
{
while(sqlite3_step(statement) == SQLITE_ROW)
{
tempContactName = [[NSString alloc]initWithUTF8String:(const char *) sqlite3_column_text(statement, 0)];
tempContactNo = [[NSString alloc] initWithUTF8String:(const char *) sqlite3_column_text(statement,1)];
tempContactName = [tempContactName stringByAppendingString:#" "];
tempContactName = [tempContactName stringByAppendingString:tempContactNo];
[phoneContacts addObject:tempContactName];
tableView.delegate = self;
tableView.dataSource = self;
[tableView reloadData];
// NSLog(#"%#",userName);
// return tempContactName;
}
}
sqlite3_finalize(statement);
}
sqlite3_close(mDiary);
}
this method is called via a button in the root view controller. Still no data from the database is getting shown on the table.
Here's a snap of the storyboard:

Resources