Array is suddenly empty - iphone

For my UITableView I use an array as a datasource. Everything works fine so far. However, I have the weird issue, that when I use the search field and enter a few characters, which I afterwards delete again, the underlying array is suddenly empty. Here the code snippets, which might be relevant to understand my issue:
Declaration in my .h
#interface dictionaryViewController : UIViewController <UITableViewDelegate>{
...
...
NSMutableArray *cardArray;
}
...
#property (retain) NSMutableArray *cardArray;
...
Usage in my .m code:
#synthesize cardArray;
...
- (void)viewDidLoad {
[super viewDidLoad];
self.cardArray = [[NSMutableArray alloc] initWithObjects:nil];
...
}
I populate the array with data from my SQL DB:
[self.cardArray addObject:[NSString stringWithFormat:#"%# - %#", aQuestion, anAnswer]];
And within the code read the content of the array like in the cellForRow method:
- (UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
...
thisCardIndex = [self.cardArray indexOfObject:cellValue];
...
}
At the end I release it, like this (I actually had some other issues with the release command, why I used the removeObjects instead):
[self.cardArray removeAllObjects];
self.cardArray=nil;
In the log I do not see an error. The debugger shows, however, that the code crashes with a SIGABRT and when setting breakpoints I see, that the cause is the empty cardArray.
Thanks for the support in advance.

ok, finally found the culprit, it was the [myArray release] (see commented line below). I have no clue, WHY though. This is a local array, which I define locally and also should be able to release immediatly again. And the interesting part is, that this code works as long as the search is narrowing. It only crashes, when the search field is empty again. VERY confusing, but maybe someone has an explanation?? Anyway, finally got it and it ru as expected.
- (void) searchTableView {
NSString *searchText = searchBar.text;
NSMutableArray *searchArray = [[NSMutableArray alloc] initWithObjects:nil];
for (NSDictionary *dictionary in self.listOfItems) {
NSArray *myArray = [dictionary objectForKey:#"Cards"];
[searchArray addObjectsFromArray:myArray];
// [myArray release];
}
// Counter is needed to get the index of the primary key to dislpay the card in editViewController
int aCounter=0;
for (NSString *sTemp in searchArray) {
NSRange titleResultsRange = [sTemp rangeOfString:searchText options:NSCaseInsensitiveSearch];
if (titleResultsRange.length > 0) {
[self.aCopyListOfItems addObject:sTemp];
NSInteger myPrimaryKey;
myPrimaryKey = [[self.cardIDArray objectAtIndex:aCounter] integerValue];
[self.aCopyOfCardIDArray addObject:[NSNumber numberWithUnsignedInteger: myPrimaryKey]];
}
}
[searchArray removeAllObjects];
searchArray = nil;
}

Related

UITableView application crashes when scrolling

Firstly, please forgive my English.
I am learning about iPhone SDK, Obj-C and trying to make UITableView app. But my app crashes when scrolling the table. I've searched but can't solve my problem, so I post my question here and look for your help.
TMTMemberListTableViewController.h
#interface TMTMemberListTableViewController : UITableViewController
{
NSMutableArray *familyMembers;
sqlite3 *database;
}
#property (nonatomic, retain) NSMutableArray *familyMembers;
- (void) configureCell:(TMTFamilyMemberCell *)cell withFamilyMember:(TMTFamily *)member;
#end
TMTMemberListTableViewController.m
#interface TMTMemberListTableViewController (Private)
- (void) loadMemberListFromDatabase;
#end
#implementation TMTMemberListTableViewController
#synthesize familyMembers;
- (void) loadMemberListFromDatabase
{
familyMembers = [[NSMutableArray alloc] init];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"easycook.sqlite"];
if (sqlite3_open([path UTF8String], &database) == SQLITE_OK)
{
const char *sql = "SELECT member_id from family";
sqlite3_stmt *statement;
if (sqlite3_prepare_v2(database, sql, -1, &statement, NULL) == SQLITE_OK)
{
while (sqlite3_step(statement) == SQLITE_ROW)
{
NSString *memberID = [NSString stringWithUTF8String:(char *) sqlite3_column_text(statement, 0)];
TMTFamily *member = [[TMTFamily alloc] initWithMemberID:memberID database:database];
[familyMembers addObject:member];
[member release];
}
}
sqlite3_finalize(statement);
}
else
{
sqlite3_close(database);
NSAssert1(0, #"Failed to open database with message '%s'.", sqlite3_errmsg(database));
}
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.clearsSelectionOnViewWillAppear = NO;
self.navigationItem.rightBarButtonItem = self.editButtonItem;
[self loadMemberListFromDatabase];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (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 [familyMembers count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//static NSString *MyIdentifier = #"MyIdentifier";
TMTFamilyMemberCell *cell = (TMTFamilyMemberCell *)[tableView dequeueReusableCellWithIdentifier:nil];
if (cell == nil)
{
cell = [[[TMTFamilyMemberCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil] autorelease];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
TMTFamily *member = [familyMembers objectAtIndex:indexPath.row];
// Set up the cell
[self configureCell:cell withFamilyMember:member];
[member release];
return cell;
}
- (void) configureCell:(TMTFamilyMemberCell *)cell withFamilyMember:(TMTFamily *)member
{
cell.familyMember = member;
cell.memberNameLabel.text = member.description;
}
- (void)dealloc
{
NSLog(#"Dealloc");
[familyMembers release];
[super dealloc];
}
When scrolling table, my app crashes with EXC_BAD_ACCESS at
[self configureCell:cell withFamilyMember:member];
and
cell.memberNameLabel.text = member.description;
This line is not incrementing retain counter:
TMTFamily *member = [familyMembers objectAtIndex:indexPath.row]
So the following line is your problem
[member release];
You cannot release object if you down't own it. Either retain it like that:
TMTFamily *member = [[familyMembers objectAtIndex:indexPath.row] retain];
or
TMTFamily *member = [[familyMembers objectAtIndex:indexPath.row] copy];
or remove mentioned release completelly.
First off, welcome to Stack Overflow and welcome to the world of iOS Development.
There are a bunch of problems with your code and if you have an Apple developer account, I highly suggest you head over to the 2012 WWDC Session Videos and look at the talk about Modern Objective C session. https://developer.apple.com/videos/wwdc/2012/
Looking at the code, your bad access could either be an already released object or an empty cell. To determine the cause, you need to enable NSZombies. To do so, click on your project name in the tool bar as shown here:
In this example, it's called PartialTransparentcy (spelling be damned). When you click that button, click the edit scheme button and you'lll see a list of options. You'll want to click enable Zombie objects. If you don't see it, press the Diagnostics tab.
Then run your code and it'll log the object that is giving your grief. Just remember that zombie objects are very expensive. If you submit an app with them enabled, you'll be automatically rejected.
The first thing I notice when I look at your cell initialization code is this:
TMTFamilyMemberCell *cell = (TMTFamilyMemberCell *)[tableView dequeueReusableCellWithIdentifier:nil];
You are sending in a nil identifier which negates that whole entire point of reusing cells. Instead, you are creating a new cell for each row instead of reusing older cells.
So uncomment this line:
static NSString *MyIdentifier = #"MyIdentifier";
and change the dequeue method to read like this:
TMTFamilyMemberCell *cell = (TMTFamilyMemberCell *)[tableView dequeueReusableCellWithIdentifier:MyIdentifier];
If you are building your cell in interface builder, you'll need to also give the cell an identifier. This may be the cause of the crash.
Next ... your sqllite code should be encapsulated in another object. There's no reason for the view controller to know about it. Also, you should really investigate core data. It defaults to sqllite backing store. It's also a pretty awesome technology.
And finally, take a look at ARC. Let the compiler handle the grunt work of memory management so that you can focus on the user experience of your application.
Hope that helps and good luck!

How to solve a error when I call the method [self somemethod]

I have this code:
// .m
- (void)viewDidLoad {
NSMutableArray *array = [[NSMutableArray alloc] init];
[self addToArray];
}
- (void)addToArray {
NSString *stringA;
[stringA isEqualToString:#"door"];
NSString *stringB;
[stringB isEqualToString:textField.text];
[array addObject:stringA];
if ([stringA isEqual:stringB]) {
[stringA isEqual:nil];
[tableView reloadData];
} else {
[array addObject:stringB];
[tableView reloadData];
}
}
When I call the method addToArray it keeps returning me an error called Thread 1: Program recived signal "EXC_BAD_ACCESS", and the debugger output says : Single stepping until exit from function objc_msgSend, which has no line number information. at the line [self addToArray]. Any idea of how to solve it? I have wasted to much time with it, please help me!
As was said by others, array should be an instance variable or property of the class, declared in the .h file:
#property (strong) NSMutableArray *array;
Or, without ARC:
#property (retain) NSMutableArray *array;
Now you #synthesize array; in your implementation file and can access it from anywhere. Then you can do:
- (void) viewDidLoad
{
self.array = [[NSMutableArray alloc] init];
[self addToArray];
}
You seem to assume that isEqualToString does an assignment. It doesn't, it checks strings for (textual) equality. Try this:
- (void) addToArray
{
NSString *stringA = #"door";
NSString *stringB = textField.text;
[array addObject: stringA];
if (![stringA isEqualToString: stringB])
[array addObject: stringB];
[tableView reloadData];
}
These two variables are uninitialized and will cause you big problems:
NSString *stringA;
[stringA isEqualToString:#"door"];
NSString *stringB;
[stringB isEqualToString:textField.text];
You have not assigned anything to either stringA or stringB. Besides the result of your call to isEqualToString is never used.
Two things I can notice in your code:
1) Make array a class variable, so you can access it from your -[addToArray] method. Better do this in your .h file, for example:
#interface MyViewController : UIViewController {
#private
// ...skipped...
NSMutableArray * array;
// ...rest of class skipped...
}
#end
Then, in your .m file the method should look like this:
// .m
- (void)viewDidLoad {
array = [[NSMutableArray alloc] init];
[self addToArray];
}
And don't forget to release the array:
- (void)dealloc {
[array release];
[super dealloc];
}
2) Do not mess up -[NSString isEqualToString:] method with simple assigment to a variable. So in your -[addToArray] method, for example, replace this:
NSString *stringA;
[stringA isEqualToString:#"door"];
with this:
NSString *stringA = #"door";
And this:
NSString *stringB;
[stringB isEqualToString:textField.text];
with this:
NSString *stringB = textField.text;
3) Check the logic of -[addToArray] method - it is not very clear what are you going achieve.

NSMutable Array: Getting "out of scope" Status After Mutable Copying

I have a SOAP service and I generated classes and functions on SudzC.com.
So I'm using the soap functions they generated, it returns an NSMutableArray with objects that are inherited by my custom class(which is generated by them, too).
So far everything's good. My values are getting into the array and I could see any property of any object with one condition: Only inside of the function that's handling the service.
Just to make it clear, here is the code:
- (void)viewDidLoad
{
SDZGeneratedWebService* service = [SDZGeneratedWebService service];
service.logging = YES;
[service callMyData:self action:#selector(callMyDataHandler:) dataId: 1];
[super viewDidLoad];
}
- (void) callMyDataHandler: (id) value {
// Handle errors
if([value isKindOfClass:[NSError class]]) {
NSLog(#"%#", value);
return;
}
// Handle faults
if([value isKindOfClass:[SoapFault class]]) {
NSLog(#"%#", value);
return;
}
// Do something with the NSMutableArray* result
NSMutableArray *result = (NSMutableArray *)value;
MyCustomClass *myObject = [result objectAtIndex:0];
NSLog(#"%#", myObject.myProperty); //Works Great
}
Like I said, so far everything's perfect. But I need to use the data outside of this function.
So in my .h file, I created an array like NSMutableArray *myDataArray;
When I intend to copy the result array to myDataArray, it copies the objects(I can see that the myDataArray.count value is equal to result array's) but all the objects are "out of scope". So I cannot use them.
I also tried to copy all objects by indexes in a for loop, nope, the objects are getting their values, but when I "addObject" to myDataArray, same, out of scope.
What is wrong here? Can't I generate an array of a custom class this way?
Edit: The code I'm generating myDataArray:
myDataArray = [[NSMutableArray alloc] init];
[myDataArray removeAllObjects];
for (int i=0; i<((NSMutableArray *)result).count; i++) {
MyCustomClass *myObject = [result objectAtIndex:i];
[myDataArray addObject:myObject];
[myObject release];
}
[self.tableView reloadData];
} //(End of callMyDataHandler function)
I before tried this way, too:
[myDataArray removeAllObjects];
duyurular = [result mutableCopy];
} //(End of callMyDataHandler function)
You can copy objects from one array to another using this method:
NSArray *source;
NSArray *dst = [[NSArray alloc] initWithArray:source];
In your code you should remove line: [myObject release]; and I would better call [((NSMutableArray *)result) count] rather then using dot notation.

How can I implement search Objective-c?

I have a record like this.
FirstName - LastName - PhoneNo - Address - Designation
A - Mick - 789367789 - New york - Professor
B - Jossef - 534647458 - USA - Doctor
C - Sha - 342576765 - USA - Doctor
D - Gee - 535346457 - USA - Business Man
......Like this
I am displaying one FirstName in UITableView with UISearchBar. Search is working fine it filters data according the keyword your typing in the SearchBar but suppose you are typing M it list all the items of M and after then when i click on the first item of M, it displays the Details of A rather then M on the next View.
I think you can understand my problem.
How can i resolve this how to pass multiple values in the Next view corresponding to record.?
Thanks,
This function i took from the same code of TableView(apple sample code with searchbar ).
and i modified this according to my logic.
- (void) searchTableView
{
NSString *searchText = searchBar.text;
NSMutableArray *searchArray = [[NSMutableArray alloc] init];
NSInteger TotalNoOfRecords=[self.SearchtableDataSource count];
for (int i=0;i<TotalNoOfRecords;i++)
{ NSDictionary *dictionary = [self.SearchtableDataSource objectAtIndex:i];
NSArray *array = [dictionary objectForKey:#"Title"];
NSString *arrayID= [dictionary objectForKey:#"ID"];
NSLog(#"Testing - Id-%d",arrayID);
[searchArrayID addObject:arrayID];
[searchArray addObject:array];
}
for (NSString *sTemp in searchArray)
{
NSRange titleResultsRange = [sTemp rangeOfString:searchText options:NSCaseInsensitiveSearch];
if (titleResultsRange.length > 0)
{
[copyListOfItems addObject:sTemp];
}
}
[searchArray release];
searchArray = nil;
}
This is my didSelectRowAtIndexPath code i know this need modification but i don't know how?
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSDictionary *dictionary = [self.SearchtableDataSource objectAtIndex:indexPath.row];
FirstName= [dictionary objectForKey:#"FirstName"];
LastName=[dictionary objectForKey:#"LastName"];
DetailViewController *ivc = [[DetailViewController alloc] initWithNibName:#"DetailViewController" bundle:[NSBundle mainBundle]];
ivc.FirstName = FirstName;
ivc.LastName=LastName;
ivc.Title=[dictionary objectForKey:#"Details of Person"];
[self.navigationController pushViewController:ivc animated:YES];
[ivc release];
}
Please help me out...
Thanks
Check that you are retrieving the value from copyListOfItems in -didSelectRowAtIndexPath.
EDIT:
You should clearly understand the logic first. SearchedTableDatasource contains the tableViews complete set of data to be loaded in the tableview. copyListofItems contains filtered items. So in didSelectRowAtIndexPath you should code as follows,
if(searching==YES)
{
//retrieve the values from copyListofItems array
}
else
{
//retrieve the values from SearchedTableDatasource array
}
And also you are using a void function for searching, if so declare the copyListofItems array as global, or else make the function to return a NSMutableArray instance.
Change your init method in didSelectrowatIndexPath method and pass the dictionary object and set the DetailViewController attributes there, i hope that will work as it worked for me.

remove rows of UITableView causes memory leak

I hope to delete all rows of an UITablewView
The UITableView is "atableview", its data source is "fileArray".
NSMutableArray *fileArray;
fileArray is NSMutableArray for object MYFileObj
#import <UIKit/UIKit.h>
NSMutableArray *fileArray;
#interface MYFileObj : NSObject {
NSString *fileName;
}
-(void) setFileName:(NSString *)s ;
-(NSString *) FileName ;
#end
I load fileArray at first, then call [atableview reloadData];
after do something, I hope to reload fileArray and redraw atableview, so I call
-(void) removeACell:(NSInteger)row;
{
NSUInteger _lastSection = 0;//[self numberOfSectionsInTableView:atableview];
NSUInteger _lastRow =row;// [atableview numberOfRowsInSection:_lastSection] - 1;
NSUInteger _path[2] = {_lastSection, _lastRow};
NSIndexPath *_indexPath = [[NSIndexPath alloc] initWithIndexes:_path length:2];
NSArray *_indexPaths = [[NSArray alloc] initWithObjects:_indexPath, nil];
[_indexPath release];
[atableview deleteRowsAtIndexPaths:_indexPaths withRowAnimation: UITableViewRowAnimationNone];
[_indexPaths release];
}
-(void) reloadList;
{
if([fileArray count]>0) //----the begining of the codes cause memory leak
{ //I hope to remove all rows and reload fileArray
NSInteger n=fileArray.count;
[atableview beginUpdates];
for(int i=n-1;i>=0;i--)
{
[fileArray removeObjectAtIndex:i];
[self removeACell:i];
}
[fileArray release];
[atableview endUpdates];
} //----the end of the codes cause memory leak
//load fileArray again
[atableview reloadData];
}
But I found this cause memory leak.
Welcome any comment.
Thanks
interdev
While there are times where you want to delete a row or rows manually, your code doesn't seem to be one of those cases because you're then turning around and calling reloadData on the tableview.
when things change in your table, make the changes to the backing data source first -- fileArray -- and then call reloadData and all will be fine.
Next, you don't have to remove objects from the array in a loop if you're emptying it completely: just use [fileArray removeAllObjects]; (actually, since you're releasing fileArray after your loop, you could reduce all that logic to [fileArray release]; and it will send a release to each of it's objects.
Not sure where your mem leak is -- there's plenty of code we can't see, but cleaning up the logic as described will help you out.