A NSMutableArray is destroying my life! - iphone

IET ANOTHER EDIT (to increase strangeness)
EDITED to show the relevant part of the code
Hi. There's a strange problem with an NSMutableArray which I'm just not understanding...
Explaining:
I have a NSMutableArray, defined as a property (nonatomic, retain), synthesized, and initialized with 29 elements.
realSectionNames = [[NSMutableArray alloc] initWithCapacity:29];
After the initialization, I can insert elements as I wish and everything seems to be working fine.
While I'm running the application, however, if I insert a new element in the array, I can print the array in the function where I inserted the element, and everything seems ok.
However, when I select a row in the table, and I need to read that array, my application crashes. In fact, it cannot even print the array anymore.
Is there any "magical and logical trick" everybody should know when using a NSMutableArray that a beginner like myself can be missing?
Thanks a lot.
I declare my array as
realSectionNames = [[NSMutableArray alloc] initWithCapacity:29];
I insert objects in my array with
[realSectionNames addObject:[category categoryFirstLetter]];
although I know i can also insert it with
[realSectionNames insertObject:[category categoryFirstLetter] atIndex:i];
where the "i" is the first non-occupied position.
After the insertion, I reload the data of my tableView. Printing the array before or after reloading the data shows it has the desired information.
After that, selecting a row at the table makes the application crash. This realSectionNames is used in several UITableViewDelegate functions, but for the case it doesn't matter. What truly matters is that printing the array in the beginning of the didSelectRowAtIndexPath function crashes everything (and of course, doesn't print anything). I'm pretty sure it's in that line, for printing anything he line before works (example):
NSLog(#"Anything");
NSLog(#"%#", realSectionNames);
gives the output:
2010-03-24 15:16:04.146 myApplicationExperience[3527:207] Anything
[Session started at 2010-03-24 15:16:04 +0000.]
GNU gdb 6.3.50-20050815 (Apple version gdb-967) (Tue Jul 14 02:11:58 UTC 2009)
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i386-apple-darwin".sharedlibrary apply-load-rules all
Attaching to process 3527.
Still not understanding what kind of stupidity I've done this time... maybe it's not too late to follow the career of brain surgeon?
following an answer, i printed
NSLog(#"self=%x", self);
NSLog(#"self=%#", self);
NSLog(#"realSectionNames=%x", realSectionNames);
gives the exact same results in every function (from delegate or not).
NSLog(#"realSections = %#", realSectionNames);
prints well in my viewWillAppear, in the didSelectRowAtIndexPath, and crashes in viewForHeaderInSection. No threading, by the way...
So, without knowing what to do, I'm trying "things"... I changed all references of realSectionNames to self.realSectionNames
printing in the viewForHeaderInSection gives the following problem:
2010-03-24 16:01:44.067 myApplication[4104:207] numberOfSectionsInTableView result -> 1
2010-03-24 16:01:44.068 myApplication[4104:207] viewForHeaderAtSection
2010-03-24 16:01:44.068 myApplication[4104:207] self=3d13470
2010-03-24 16:01:44.068 myApplication[4104:207] self=<RootViewController: 0x3d13470>
2010-03-24 16:01:44.069 myApplication[4104:207] self.realSectionNames=3b12830
2010-03-24 16:01:44.070 myApplication[4104:207] realSections = (
<__NSArrayReverseEnumerator: 0x3b50500>
)
2010-03-24 16:01:44.070 myApplication[4104:207] *** -[__NSArrayReverseEnumerator length]: unrecognized selector sent to instance 0x3b50500
2010-03-24 16:01:44.071 myApplication[4104:207] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSArrayReverseEnumerator length]: unrecognized selector sent to instance 0x3b50500'
2010-03-24 16:01:44.072 myApplication[4104:207] Stack: (
31073371,
2572170505,
31455291,
31024758,
30877378,
276908,
26404,
3227182,
4544033,
4551926,
4550923,
3267462,
3207973,
3249408,
25927,
3222086,
3205252,
459178,
30857920,
30854216,
39163413,
39163610,
2949039
)
What is a NSArrayReverseEnumerator??? And why is it being mean to me???

Note to everybody (and specially to self)...
I'm a dumb, dumb boy.
I was releasing an element which I inserted in the array.
Sorry for the questions, thanks for the answers...

When an application crashes, the Xcode Console will report the error that caused the application to crash. You will want to edit your question to include this error message, along with relevant source code, as this will help others answer your question.
I suspect your selected table row is trying to point to an index in the array, which does not exist. If so, you're trying to reference a part of the array that does not exist. This causes your application to throw an exception and quit.
Remember that an NSMutableArray can be initialized to a certain capacity, but until items are inserted, it does not actually hold any objects. The -initWithCapacity: method only sets aside a certain amount of memory, but there are no placeholders or nil entries for 1 through n indices.

1) Could we know the error message ? and the relevant part of the code ?
2) Here is the proper way to declare your array :
self.realSectionNames = [[NSMutableArray arrayWithCapacity:29];

I suspect your TableView's delegate isn't hooked up correctly, or is connected to an invalid object. Are any of your other delegate methods working? Try putting these at the top of didSelectRowAtIndexPath to check the pointer values:
NSLog(#"self=%x", self);
NSLog(#"self=%#", self);
NSLog(#"realSectionNames=%x", realSectionNames);

Related

Passing UISearchBar.text to another function

It's my first time working with a UISearchBar and so far it went fine, but now after clicking "search" I'm trying to pass the text to another function that handles the search.
[_searchModel searchIssueForTerm:searchBar.text inIssue:_dataModel.currentIssue];
This just throws me an error, saying:
-[CFString copyWithZone:]: message sent to deallocated instance 0x7dee900
But when I use this, it just works
[_searchModel searchIssueForTerm:#"test" inIssue:_dataModel.currentIssue];
Why is this hapenning? In my - (void)searchIssueForTerm:(NSString *)searchTerm inIssue:(Issue *)issue I store the NSString in another variable, why would it get deallocated then?
After searching for a while I managed to fix it by just saving the variable somewhere else, seems it was getting lost along the way.
_currentSearchTerm = [[NSString alloc] initWithString:searchTerm];

Changing Core Data Items crashes App

I am running into another problem with my Iphone App which I just can't solve myself.
I implemented a kind of organizer functionality in my latest app.
There one can create appointments which are displayed in a tableview and persisted in a CoreDataStore. I use 4 classes:
an overview where appointments are displayed
a view with textfields to put in Values for place and name of appointment (create/edit view)
a view with DatePicker to define start- and enddate
a controller which handles creation and deletion of items using this methods:
The code:
-(void)createAppointmentObjectWithDate:(NSDate *)
appointmentDate name:(NSString *)appointmentName
description:(NSString *)appointmentDescription
eDate:(NSDate *)appointmentEndDate
{
NSManagedObjectContext *managedObjectContext = [[CoreDataManager sharedManager] managedObjectContext];
AppointmentObject *newAppointmentObject = [NSEntityDescription insertNewObjectForEntityForName:AppointmentObjectEntityName
inManagedObjectContext:managedObjectContext];
newAppointmentObject.appointmentName = appointmentName;
newAppointmentObject.appointmentDescription = appointmentDescription;
newAppointmentObject.appointmentDate = [appointmentDate earlierDate:appointmentEndDate];
newAppointmentObject.appointmentEndDate = [appointmentEndDate laterDate:appointmentDate];
}
-(void)deleteAppointmentObject:(AppointmentObject *)appointmentObject triggeredByUser:(BOOL)byUser{
NSManagedObjectContext *managedObjectContext = [[CoreDataManager sharedManager] managedObjectContext];
[managedObjectContext deleteObject:appointmentObject];
}
But all kind of crazy stuff is happening which makes my app crash with "SICBART" message:
2010-10-13 17:35:04.630 didacta[109:307] Serious application error. Exception was caught during Core Data change processing.
This is usually a bug within an observer of NSManagedObjectContextObjectsDidChangeNotification.
-[CALayer controllerWillChangeContent:]: unrecognized selector sent to instance 0x19f150 with userInfo (null)
2010-10-13 17:35:05.118 didacta[109:307] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[CALayer controllerWillChangeContent:]: unrecognized selector sent to instance 0x19f150'
errors appear while doing this:
creating new Appointment and pressing "Done" (should trigger creation and pop overview)
changing appointments and pressing "Done" (should send changes and pop overview)
tapping on an appointment in overview ( should pop create/edit view and hand over values)
deleting an item
sometimes I can even delete an appointment but then the order of the items in the tableview is somehow gotten twisted so the index of the tableview isn't pointing to the index of the appointment anymore.
Right.
-[CALayer controllerWillChangeContent:]: unrecognized selector sent to instance 0x19f150 with userInfo (null)
That's your error. You have an NSFetchedResultsController whose delegate is a CALayer. This sounds like the original delegate was deallocated and a CALayer was allocated using the same region of memory. The fix is to find the offending -dealloc and add something like self.myFetchedResultsController.delegate = nil; self.myFetchedResultsController = nil; assuming you're using properties.
You can sometimes help debugging things like this by enabling zombies (go to Project → Edit Current Executable or so, select Environment, add the NSZombieEnabled environment variable, and set its value to "YES" or so; uncheck the checkbox when you've finished debugging). Zombies cause an exception when a message is sent to a deallocated object. (Zombies are not deallocated by default, so your application will effectively leak; remember to uncheck the checkbox!).
"unrecognized selector" makes it sound like maybe your data model doesn't contain some of the Entity attributes that you're trying to use. For example, maybe you're trying to set an attribute of the object that doesn't exist.
Try creating a breakpoint at newAppointmentObject.appointmentName = appointmentName; and step through it to see at what point the error occurs.

Exception when running on simulator 3.2

I'm testing my app on simulator 3.1.3, it runs fine.
When it come to simulator 3.2, it crashes right from the beginning:
2010-06-24 16:35:29.208 MyTestApp[6991:207] *** Terminating app due to uncaught exception 'NSGenericException', reason: 'This coder requires that replaced objects be returned from initWithCoder:'
2010-06-24 16:35:29.213 MyTestApp[6991:207] Stack: (
46195275,
2520474889,
46194715,
46194554,
6387912,
6392266,
5568184,
6388086,
6386450,
6392266,
5564974,
5573454,
3555255,
3560368,
3586056,
3567777,
3599431,
52998524,
45735996,
45731912,
3559044,
3591649,
10824,
10678
)
As far as I know, I do not use the "initWithCoder" method (do not really know what this is though).
How can I know where the exception is thrown so I could have a better understanding of what is causing the problem?
ps: I have added an exception in Breakpoint: objc_exception_throw (with location libobjc.A.dylib, strangely I had to enter the location manually, I expected xcode to find it for me when I added objc_exception_throw). But still the same trace and no more information.
This page should provide some helpful info: Debugging Tips for Objective-C
Of particular interest is the console command info line *. Every one of those numbers listed by the exception log is an address on the stack. Those bottom ones in the 10,000 and below range are usually located in the app's main method, for example. The highest ranged addresses tend to represent the default libraries.
Using the command info line *10678 would likely return some info about a specific line in the main method, which doesn't help very much. Normally the trick is to find the highest address before the default libraries begin. I'm unsure how much this will help your problem in particular, seeing as there's a huge gap between the expected small addresses and the next highest up. In any case, start with the smallest address above the bottom two (3555255 from what I can see in the log you posted) and see if it returns a line from one of your own code files. If it does, check the one above it, and so on until you find the last address from your own code. Hope this helps.
I figured out the reason of this error. I had created an object of type NSDictionnary within IB and it seems it was not the correct way to do this. Instead I have programmatically created this same object in XCode and this works fine. Seems like it was some kind of persistent problem.

issue with array

Getting error in this line :
NSString *nmm =[narr objectAtIndex:1];
error shows :
'NSRangeException', reason: '*** -[NSCFArray objectAtIndex:]: index
(1) beyond bounds (1)'
It looks like your array only got one value (which you can accesss at index 0, not index 1).
You should probably start by checking the contents of narr at run time. It sounds like the contents aren't what you would expect them to be at the desired point in execution. Right before the line you posted in your question, use an NSLog call to log the contents of the array like this:
NSLog(#"Contents of array: %#", narr);
Then run the app and check the console after the error arises. Put some time into learning how to use NSLog, breakpoints, and the GDB console - they will end up saving you lots of frustration when debugging.
Your comments on unset's answer raise another point: Why are you storing multiple pieces of data inside the same string? Wouldn't it be easier to separate name, lname and id into separate strings and place each into its own array cell? Then you could access them using [narr objectAtIndex:] without having to worry about parsing the string every time you need one of those pieces of information.

Multiple UITableViews displaying the same data

I am working on an app that has several views selected using a tab bar. One tab displays a list of data using a table view. Another tab uses a table view (and a navigation controller) so the user can add items to/delete items from the list.
If the user switches to the first tab after adding or deleting items on the other tab, the app crashes. (I'm still working on trying to glean anything useful from the debugger output).
New (as of 22 Aug) info:
In light of some of the comments below, I've been doing some exploring and I think I must not be allocating objects correctly. Below is a stack trace, but note I am not always getting the same error. Eg, sometimes I get an uncaught exception, sometimes just the debugger steps in; when it is an uncaught exception, it's not always the same one.
2009-08-22 15:20:34.254 Mexico[29531:20b] * -[_NSIndexPathUniqueTreeNode isEqualToString:]: unrecognized selector sent to instance 0x562ff0
2009-08-22 15:20:34.255 Mexico[29531:20b] * Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[_NSIndexPathUniqueTreeNode isEqualToString:]: unrecognized selector sent to instance 0x562ff0'
2009-08-22 15:20:34.257 Mexico[29531:20b] Stack: (
2504683691,
2423127611,
2504712874,
2504706220,
2504706418,
817812590,
19892,
816386359,
816387412,
816468754,
816411067,
836579268,
836579060,
836577406,
836576671,
2504177986,
2504184740,
2504187000,
827745792,
827745989,
816114848,
816160924
)
[Session started at 2009-08-22 15:20:34 -0400.]
Loading program into debugger…
GNU gdb 6.3.50-20050815 (Apple version gdb-962) (Sat Jul 26 08:14:40 UTC 2008)
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i386-apple-darwin".warning: Unable to read symbols for "/System/Library/Frameworks/UIKit.framework/UIKit" (file not found).
warning: Unable to read symbols from "UIKit" (not yet mapped into memory).
warning: Unable to read symbols for "/System/Library/Frameworks/CoreGraphics.framework/CoreGraphics" (file not found).
warning: Unable to read symbols from "CoreGraphics" (not yet mapped into memory).
Program loaded.
sharedlibrary apply-load-rules all
Attaching to program: `/Users/matt/Library/Application Support/iPhone Simulator/User/Applications/0F9033CE-39BB-4589-B791-5E473D991789/Mexico.app/Mexico', process 29531.
(gdb) bt
#0 0x954a6f54 in ___TERMINATING_DUE_TO_UNCAUGHT_EXCEPTION___ ()
#1 0x906dfe3b in objc_exception_throw ()
#2 0x9542da53 in CFRunLoopRunSpecific ()
#3 0x9542dc78 in CFRunLoopRunInMode ()
#4 0x31566600 in GSEventRunModal ()
#5 0x315666c5 in GSEventRun ()
#6 0x30a4eca0 in -[UIApplication _run] ()
#7 0x30a5a09c in UIApplicationMain ()
#8 0x00001ffc in main (argc=1, argv=0xbfffe108) at /Users/matt/Coding/iPhone/Mexico/main.m:13
(gdb)
Here is some (I think) relevent code. First, the function that invokes a view to add a new entry:
- (IBAction)addButtonWasPressed {
AddPlayerViewController *apvController;
apvController = [[AddPlayerViewController alloc]
initWithNibName:#"AddPlayerView" bundle:nil];
apvController.rvController = self;
[self.navigationController pushViewController:apvController animated:YES];
[apvController release];
}
Then in the add player view:
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
[rvController addPlayerNamed:textField.text];
[textField resignFirstResponder];
[self.navigationController popViewControllerAnimated:YES];
return YES;
}
Finally RosterViewController is a subclass of UITableViewController and has the following method:
- (void)addPlayerNamed:(NSString *)name {
Player *player = [[Player alloc] initWithName:name];
[players addObject:player];
// insert NSLogS
NSIndexPath *indexPath;
indexPath = [NSIndexPath indexPathForRow:[players indexOfObject:player] inSection:0];
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:NO];
}
If I iterate over players and print out the names using NSLog statements (see commented spot above), then the app crashes after I add a second entry. If I take the NSLog statements out, then it won't crash until later.
If i had to guess, i would say you are probably sharing the data objects (data sources) in your two table views and deleting from one is affecting the other, and you try to do something with the released object in the other view which is causing the app to crash, or something along those lines.
Here's how I'd do it:
Create a class that implements the UITableViewDataStore delegate methods. Assign it to something accessible from both viewcontrollers. A good place is up at the UIApplication level so you can get to it via something like:
MyDataStore* store = ((MyApplication *)[UIApplication sharedApplication]).{myDataStore}
For backing store this class would keep the actual values in an array, a dictionary, a SQLite database, or something like that.
Assign this class to each table's dataSource property. Since it's owned by the app it shouldn't be released until the app is finished. So you'll want to make sure the object is accessible via a property with a "retain" attribute.
Each time the user adds something you'll want to add it to this object then call reloadData on the table to refresh the table.
For each of those views implement the viewWillAppear method. This gets called whenever that view comes into view. Inside it make a call to the table reloadData as well. This way when the user switches from one view to another, before the new view is shown, it will have refreshed itself with the new data.
If you assign the datastore object to the application then it owns it for the lifetime of the app so you probably don't want to set it to autorelease. If memory becomes an issue then you'll want to save the backing stuff to SQLite or CoreData.
You're probably already doing all this but this is the general pattern for creating shared data between views. Maybe it'll help jog something.