I have a UITableViewController with an NSMutableArray in the header like this:
NSMutableArray *someArray;
Also I have the property declared:
#property (nonatomic, retain) NSMutableArray *someArray;
In the .m file I load the array in the method:
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
someArray = [[NSMutableArray alloc] init];
[self loadArrayData];
[tableview reloadData];
}
The table populates great, adding is no problem, but when I try to delete a row, the app crashes on the following line:
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
[someArray removeObjectAtIndex:indexPath.row];
//update table etc..
If I use the debugger, I can see the array has some objects, and when I Log indexPath.row I get a value which is inside the array size.
I don't understand why it is crashing on this line... Who can help me?
The console outputs:
* Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSArrayI removeObjectAtIndex:]: unrecognized selector sent to instance 0x6193890'
I had a very similar problem and I was initiating my array with CoreData like this:
self.allContacts = (NSMutableArray*)[cdc.managedObjectContext executeFetchRequest:fetchRequest error:&error];
trying to cast the result from CoreDataController fetch request to NSMutableArray.
Turns out I need the function called "mutableCopy" at the end of fetch request.
self.allContacts = (NSMutableArray*)[[cdc.managedObjectContext executeFetchRequest:fetchRequest error:&error] mutableCopy];
I don't think cast to (MutableArray *) is required but I have it just in case.
Hope this helps.
I had the same issue a week back. What I did was this:
Declared a NSMutableArray* globalArray as a global within the same tableViewController.m implementation file. Then inside
if (editingStyle == UITableViewCellEditingStyleDelete) {
globalArray = [[NSMutableArray alloc] initWithArray:someArray];
[globalArray removeObjectAtIndex:[indexPath row]];
}
And then I copied back this globalArray into someArray before the reloadData call. Works.
not sure if that cause one of your issues, but if you have property for someArray then use self.someArray instead of just someArray .
Of course that after using self, you should auto release it like this :
self.someArray = [[[NSMutableArray
alloc] init] autorelease];
(call this inside your viewController
init method)
then try to :
[self.someArray removeObjectAtIndex:indexPath.row];
2.
try to call [self loadArrayData]; inside viewDidLoad.
It should be called prior to cellForRowAtIndexPath, and that way you won't need to call reloadData
if that still won't help, then post the code of loadArrayData
Related
I'm making an app which tries to read the information from the .plist file (put there parsed JSON).
The reading from file flows nice: got the array of dictionaries, but while trying to display it on tableview, the problems start. The initial view is loaded properly, but when I start scrolling, the app crashes.
#define DOCUMENTS [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject]
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *filePathDocArray = [DOCUMENTS stringByAppendingPathComponent:#"filters.plist"];
NSString *filePathBundleArray = [[NSBundle mainBundle] pathForResource:#"filters" ofType:#"plist"];
if (![[NSFileManager defaultManager] fileExistsAtPath:filePathDocArray]) {
[[NSFileManager defaultManager] copyItemAtPath:filePathBundleArray toPath:filePathDocArray error:nil];
NSLog(#"File saved");
} else {
NSLog(#"File already exists");
filters = [NSArray arrayWithContentsOfFile:filePathDocArray];
}
}
Here I get all the info I need into filters array (checked by looping). Then:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [filters count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *myIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:myIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:myIdentifier];
}
NSInteger crow = indexPath.row;
NSDictionary *story = [filters objectAtIndex: crow];
cell.textLabel.text = [story objectForKey:#"Name"];
cell.detailTextLabel.text = [story objectForKey:#"Description"];
return cell;
}
#end
When the app starts everething is OK: I see the normel table view, but when I start scrolling it crashes
After series of breakpoint debugs I evaluated, that after the applications starts on Simulator, the link on array filters screws, so when I try to populate the next cell, the story dictionary can't be properly created.
What sort of problem it can be?
Here the console report:
2012-09-22 13:37:43.545 JSONExample[4559:207] -[__NSCFString
objectAtIndex:]: unrecognized selector sent to instance 0x6a083c0
2012-09-22 13:37:43.547 JSONExample[4559:207] *** Terminating app due
to uncaught exception 'NSInvalidArgumentException', reason:
'-[__NSCFString objectAtIndex:]: unrecognized selector sent to
instance 0x6a083c0'
are you using ARC??
if not then autorelease the cell first!!
and if filters is not a retain property.. kindly make it one and synthesize it and again if ARC not used then release it in the dealloc block.. that should do it i suppose..
-[NSCFString objectAtIndex:]: unrecognized selector seems to be the same problem.
Your 'filters' variable is an NSString/CFString at the time you call objectAtIndex: - not an array, as you would assume. The solution given in the question linked to is to retain your filters array whenever it's set.
If you wote property for the filters like #property (nonatomic, retain) NSArray *filters; you need to write like self.filters = [NSArray arrayWithContentsOfFile:filePathDocArray];
Else you need to write like filters = [[NSArray arrayWithContentsOfFile:filePathDocArray] retain];
You need to keep in mind another thing..
if you do this step
NSDictionary *story = [[filters objectAtIndex: crow]retain];
every time your cellForRowAtIndexPath is called the retain count would increase by 1. But you will delloc it only once.
So there will be memory leaks in your applications.
I suggest you go through the memory management guide once. its a small document. at max it will take one day of yours. But will feel more confident of what to do and what not to do.
http://developer.apple.com/library/ios/documentation/CoreFoundation/Conceptual/CFMemoryMgmt/CFMemoryMgmt.pdf
Cheers!! And Happy Coding!!
This is my first post on stackoverflow.com so please be kind (rewind) ;)
I have a navigation based application whose purpose is to display blog posts (title) in a Table View (with JSON).
The problem I ran into occurred when a cell got out of the screen and then back in.
I was getting a EXC_BAD_ACCESS (because I sent a message to a deallocated instance), so I struggled to understand where it came from and I finally found a solution. But the fact is I don't exactly understand how the problem occurs. That's why I need someone to enlighten me, I think this is fundamental understanding !
When the connection to the JSON web service has finished loading, I parse the JSON code to obtain a list of blog posts (recentPosts), then I create a BlogArticle object for each post (blogArticle), store it in a MutableArray iVar (allEntries) and insert a row in the Table View :
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
[connection release];
NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
[responseData release];
NSError *error;
SBJsonParser *json = [[SBJsonParser new] autorelease];
NSDictionary *recentPostsData = [json objectWithString:responseString error:&error];
[responseString release];
NSArray *recentPosts = [recentPostsData objectForKey:#"posts"];
int i = 0;
for (NSDictionary *post in recentPosts) {
BlogArticle *blogArticle = [[BlogArticle alloc] initWithDictionary:post];
[allEntries insertObject:blogArticle atIndex:i];
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:i inSection:0]] withRowAnimation:UITableViewRowAnimationRight];
i++;
}
}
Here's the initialisation of the BlogArticle object which turned to be the origin of the problem :
- (id)initWithDictionary:(NSDictionary *)article
{
if (self = [super init])
{
// title = [[[article valueForKey:#"title"] gtm_stringByUnescapingFromHTML] copy];
// title = [[NSString alloc] initWithString:[[article valueForKey:#"title"] gtm_stringByUnescapingFromHTML]];
title = [[article valueForKey:#"title"] gtm_stringByUnescapingFromHTML];
}
return self;
}
So every Objective-C programmer who isn't as noobish as me is able to tell that title is never allocated before being assigned. If I uncomment one of the two lines above it will work. The program crashes exactly when I try to initialize a cell with that title variable, here :
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
NSLog(#"indexPath.row = %i", indexPath.row);
// Configure the cell.
BlogArticle *article = [allEntries objectAtIndex:indexPath.row];
cell.textLabel.text = article.title;
return cell;
}
Now, what I need to understand is why it does compile/work without allocating the iVar and where exactly it causes trouble (or where exactly the content of title is released causing the program to crash).
Any good resource (noob friendly) about memory management in iOS environment would be much appreciated.
Thanks in advance :)
This line
title = [[article valueForKey:#"title"] gtm_stringByUnescapingFromHTML];
is allocating an autoreleased string. Essentially, think of it that an autoreleased string will get released at the end of the method (though it can last longer, it's useful to think of it that way).
You know the string is autoreleased because the name of the method gtm_stringByUnescapingFromHTML does not start with alloc, new, copy or mutableCopy.
You can add retain to this to stop it getting autoreleased:
title = [[[article valueForKey:#"title"] gtm_stringByUnescapingFromHTML] retain];
Now you own the string, and it will not get released until you say so.
The best summary I know of is Apple's own documentation here.
Well, the problem is, that you have to initialize your object, if you want to manage the memory of it on your own. Why should you manage now the memory of title?
Quite simple:
Every object reference, that is stored in an Array, Set, Dictionary etc. is managed by the Array, Dictionary and Set.
If you now just use this reference (by writing: "title = ...") in your cell, you will add the reference also to the cell. And now the cell is also responsible for the object-reference. So if the tableView wants to release your cells, which will happen from time to time to save memory, the cell will release your title-object. And this would cause the NSDitionary to be quite sad, since the NSDictionary wants to take care about the objects stored within itself.
So you could write the following in the tableView-method:
cell.textLabel.text = [article.title retain];
Or the commented lines of your own method.
That means, you will "raise" the storage-level of your object up and if it gets released, the storage level itself will be decreased by one.
If the storage-level will reach zero, it will be completely released (that should happen, if your tablecell is released AND your NSDIctionary)
I hope i could help you a bit :)
Hi i received 2000 of datas from udp and display the values in tableview .What is the easiest method to do this ?
Now i am using Two nsthreads and one thread for receive data via udp and stores it in NSMutableDictionary.Another thread update the tableview using these Dictionary values. But it crashes my app.
Here is some code i used
I stored Received values like this
NSMutableDictionary *dictItem
CustomItem *item = [[CustomItem alloc]init];
item.SNo =[NSString stringWithFormat:#"%d",SNo];
item.Time=CurrentTime;
[dictItem setObject:item forKey:[NSString stringWithFormat:#"%d",SNo]];
[item release];
Delegate method i used and i used CustomTableCells to display data as column vice.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [dictItem count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *identifier = #"CustomTableCell";
CustomTableCell *cell = (CustomTableCell *)[tableView dequeueReusableCellWithIdentifier:identifier];
if (cell == nil)
{
cell = [[[CustomTableCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier] autorelease];
}
NSArray *keys = [dictItem allKeys];
CustomItem *item = [dictItem objectForKey:[keys objectAtIndex:indexPath.row]];
cell.SNo.text = item.SNo;
cell.Time.text = item.Time;
return cell;
}
The errror is
Terminating app due to uncaught exception 'NSGenericException', reason: '*** Collection was mutated while being enumerated.'
2010-07-23 02:33:07.891 Centrak[2034:207] Stack: (
42162256,
43320108,
42161198,
43372629,
41719877,
41719345,
9948,
3276988,
3237662,
3320232,
3288478,
71153942,
71153189,
71096786,
71096114,
71296742,
41650770,
41440069,
41437352,
51148957,
51149154,
2925426
)
terminate called after throwing an instance of 'NSException'
Can anyone help me ?
Thanks in advance.......
You probably have to use locking because when you access your dictionary from table view, it perhaps being mutated with other thread.
Try to look at NSLock docs. Before mutating your dictionary do [myLock lock]; and after mutating do [myLock unlock];. Similar in other thread: before enumerating dictionary do [myLock lock]; and after getting all values do [myLock unlock];.
myLock is a NSLock object and must be shared between your threads.
Mutable collections are not thread-safe by nature, so if you use them with multiple threads, you have to create an immutable copy first. For example, if you want to iterate through all the keys in your NSMutableDictionary, you would do this (assuming your NSMutableDictionary is called mutableDictionary):
NSDictionary *dictionary = [NSDictionary dictionaryWithDictionary:mutableDictionary];
for(id key in dictionary) {
// Do anything you want to be thread-safe here.
}
If you don't want to copy the dictionary, I suppose you could use locks or simply the #synchronized directive as follow:
#synchronized(mutableDictionary) {
// Do anything you want to be thread-safe here.
}
For more information, please have a look at the Apple documentation regarding multithreading and thread-safe objects: http://developer.apple.com/mac/library/documentation/cocoa/conceptual/Multithreading/ThreadSafetySummary/ThreadSafetySummary.html
I'm writing an application to show some news from a portal. The news are fetched using a JSON file from the Internet and then stored into a NSMutableArray using the CoreData Model. Obviously a user can't delete the news from the JSON file on the Internet, but he can hide them locally. The problems come out here, where I have the following code:
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
if( !moc ){
moc = [[NewsFetcher sharedInstance] managedObjectContext];
}
[[dataSet objectAtIndex:indexPath.row] setEliminata:[NSNumber numberWithBool:YES]];
NSError *error;
if( ![moc save:&error] ){
NSLog( #"C'è stato un errore!" );
}
[dataSet removeObjectAtIndex:indexPath.row];
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:YES];
}
The line:
[dataSet
removeObjectAtIndex:indexPath.row];
cause my apps to crash with the following error:
2010-07-12 19:08:16.021
ProvaVideo[284:207] * -[_PFArray
removeObjectAtIndex:]: unrecognized
selector sent to instance 0x451c820
2010-07-12 19:08:16.022
ProvaVideo[284:207] * Terminating
app due to uncaught exception
'NSInvalidArgumentException', reason:
'*** -[_PFArray removeObjectAtIndex:]:
unrecognized selector sent to instance
0x451c820'
I'm trying to understand why it doesn't work but I can't.
If I re-launch the app, the new is correctly logically cancelled.
Any suggestions?? Thanks in advance.
Interface:
#interface ListOfVideo : UITableViewController <NSFetchedResultsControllerDelegate> {
NSMutableArray *dataSet;
}
#property (nonatomic, retain) NSMutableArray *dataSet;
// In the .m file:
#synthesize dataSet;
Initialization in viewDidLoad:
dataSet = (NSMutableArray *) [[NewsFetcher sharedInstance]
fetchManagedObjectsForEntity:#"News"
withPredicate:predicate
withDescriptor:#"Titolo"];
[dataSet retain];
updateDatabase ... this is when checking for new news from the net, I add them in the MutableArray:
[dataSet addObject:theNews];
Your NewsFetcher returns you an immutable array, not a mutable instance. Use the following instead for initialization:
NSArray *results = [[NewsFetcher sharedInstance]
fetchManagedObjectsForEntity:#"News"
withPredicate:predicate
withDescriptor:#"Titolo"];
dataSet = [results mutableCopy];
An expression like A *a = (A*)b; only casts the pointer to a different type - it doesn't convert/change the actual type of the instance it points to.
Verify that dataSet is an NSMutableArray. The exception is getting thrown because it doesn't respond to removeObjectAtIndex.
jdot is correct dataSet must be NSMutableArray..
you must do it this way..
dataSet = [NSMutableArray arrayWithArray:[[NewsFetcher sharedInstance]
fetchManagedObjectsForEntity:#"News"
withPredicate:predicate
withDescriptor:#"Titolo"]];
Now the dataSet is a mutable instance of the array you got from NewsFetcher and it won't crash on removing objects.
i got an NSArray which gets filled in the init Method of my UITableViewController.
i use this object in "didSelectRowAtIndexPath" for pushing another tableviewcontroller.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
ablogSingleCatTableViewController *singleCatTableViewController = [[ablogSingleCatTableViewController alloc] initWithStyle:UITableViewStylePlain category:[categories objectAtIndex:indexPath.row]];
[[self navigationController] pushViewController:singleCatTableViewController animated:YES];
[singleCatTableViewController release];
}
this works a few times when i start my application. after selecting a row and getting back to the main uitableview controller at a rondom point my application crashes after selecting a row.
with some nslogs i found out, that it crashes if i try to use my "categories" object.
so
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"before");
NSLog(#"cats: %#", categories);
NSLog(#"after");
ablogSingleCatTableViewController *singleCatTableViewController = [[ablogSingleCatTableViewController alloc] initWithStyle:UITableViewStylePlain category:[categories objectAtIndex:indexPath.row]];
[[self navigationController] pushViewController:singleCatTableViewController animated:YES];
[singleCatTableViewController release];
}
with that code my application crashes after "before" ... "after" never shows up.
i dont know why my "categories" object is crashing my application ?!
my categories object is defined in my header file and has a #property (nonatomic, retain). i synthesize it and releasing it in my dealloc method.
anyone has an idea?
// edit:
some more details here, because of the comments:
Debugger Console says: "Program received signal: “EXC_BAD_ACCESS”.
i create the category array like this:
- (void)initCategories {
NSString *path = [[NSBundle mainBundle] pathForResource:#"Categories" ofType:#"plist"];
[self setCategories:[[NSArray alloc] initWithContentsOfFile:path]];
}
calling this method in my initwithstyle method
[self initCategories];
my other custom initializing method looks something like this:
- (id)initWithStyle:(UITableViewStyle)style category:(NSDictionary*)cat {
if (self = [super initWithStyle:style]) {
currentCategory = cat;
items = [[NSMutableArray alloc] init];
self.title = [currentCategory objectForKey:#"TITLE"];
//XLog("%#", currentCategory);
}
return self;
}
ok, first thing is ;
[self setCategories:[[NSArray alloc] initWithContentsOfFile:path]];
you have a leak here. just use
categories = [[NSArray alloc] initWithContentsOfFile:path];
Crash occurs in here;
currentCategory = cat;
you have to retain, use;
currentCategory = [cat retain];
These are the problems I see in posted code, if you have not any mistake in the rest of the program, it should be fine with these fixes.
If you are creating your array something like this:
NSArray *tmpArray = [[NSArray alloc] initWithBlah ...];
Make sure that you assign it using the synthesized getter by using this code:
self.categories = tmpArray;
[tmpArray release];
If you do:
categories = tmpArray;
[tmpArray release];
the instance variable will not be retained at all.