NSMutable array error & showAnnotations - objective-c

I have the following situation. I developed an app that frequently retrieves values for POI's that I want to show as annotations on a map. For this I wrote the method below, called after when a new set of POIs was retrieved:
-(void)showAnnotation{
[self removeAllPinsButUserLocation];
annotationArray = [[NSMutableArray alloc] init];
for (Poi *poi in [parser.pois allValues]) {
myAnnotation *arr = [[myAnnotation alloc] init];
arr.title = [NSString stringWithFormat:#"%# (%#)",poi.description, sensor.name];
arr.subtitle = [NSString stringWithFormat:#"%#, %# %#",poi.street, poi.postalcode, poi.city];
arr.coordinate = CLLocationCoordinate2DMake(poi.lattitude, poi.longitude);
[annotationArray addObject:arr];
arr = nil;
}
[self.mapView addAnnotations:annotationArray];
[self.mapView showAnnotations:annotationArray animated:NO];
}
Problem is that I get an error (Terminating app due to uncaught exception 'NSGenericException', reason: '* Collection <__NSArrayM: 0x14ebe9b0> was mutated while being enumerated.')
However only if I set animated:NO in the last line, but not when set to animated:YES...
Does anyone have an answer for this?
Thanks for replying!
Eelco

Related

"unrecognized selector sent to instance" for NSArrayI

I have the following code which gives the "unrecognized selector sent to instance" error for the NSArray. I've not been able to figure this out and feel its something simple I'm missing here. I can post more if needed.
Quote *myQuote;
NSArray *myQuotes = theSubject.quotes;
//START LOOP HERE
for (myQuote in myQuotes){
NSLog(#" excerpt = %#", myQuote.excerpt);
NSLog(#" desc2 = %#", myQuote.desc2);
NSLog(#" quote_date = %#", myQuote.quote_date);
NSLog(#" myQuote = %#", myQuote);
I believe the problem is in this function which returns an array of Quotes:
- (NSArray *) getQuotesFromSubId:(NSInteger )subId {
QuotesAppDelegate *appDelegate = (QuotesAppDelegate *)[[UIApplication sharedApplication] delegate];
self.quoteMaps = [appDelegate quoteMaps];
self.quotes = [appDelegate quotes];
//get the quote_ids from quote_map for this subject_id
NSString *stringOfSubjectId = [NSString stringWithFormat:#"%ld", (long)subId];
NSPredicate *filterSubjectId = [NSPredicate predicateWithFormat:#"subject_id == %#", stringOfSubjectId];
NSArray *quoteMapSection = [self.quoteMaps filteredArrayUsingPredicate:filterSubjectId];
NSMutableArray *quoteSection = [[NSMutableArray alloc] init];
NSArray *quoteToAdd = [[NSArray alloc] init];
for (QuoteMap *qm in quoteMapSection){
//get the quote_ids from quote_map for this subject_id
NSPredicate *filter = [NSPredicate predicateWithFormat:#"quote_id == %#", qm.quote_id];
quoteToAdd = [self.quotes filteredArrayUsingPredicate:filter];
[quoteSection addObject:quoteToAdd];
}
return quoteSection;
}
This is where I call it:
QuotesAppDelegate *appDelegate = (QuotesAppDelegate *)[[UIApplication sharedApplication] delegate];
NSArray *myQuotes = [appDelegate getQuotesFromSubId:selectedSubject.subject_id];
NSMutableArray *mArray = [appDelegate createMutableArray:myQuotes];
selectedSubject.quotes = mArray;
NSMutableArray *mutableArray = [appDelegate createMutableArray:myQuotes];
selectedSubject.quotes = mutableArray;
I got the following error
2016-02-23 00:24:20.383 Quotes[10631:3698114] -[__NSArrayI excerpt]: unrecognized selector sent to instance 0x15ebbeff0
2016-02-23 00:24:29.164 Quotes[10631:3698114] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSArrayI excerpt]: unrecognized selector sent to instance 0x15ebbeff0'
*** First throw call stack:
(0x182b55900 0x1821c3f80 0x182b5c61c 0x182b595b8 0x182a5d68c 0x100078b2c 0x1000642d0 0x187cb17f4 0x187cb1f8c 0x187b9fc90 0x187ba2e88 0x187977284 0x187883394 0x187882e90 0x187882d18 0x185259c00 0x10011dbb0 0x100123658 0x182b0cbb0 0x182b0aa18 0x182a39680 0x183f48088 0x1878b0d90 0x100040398 0x1825da8b8)
libc++abi.dylib: terminating with uncaught exception of type NSException
You are sending -excerpt to the members (myQuote) of myQuotes. The runtime says that NSArray (NSArrayI is an internal subclass) instances cannot understand -excerpt.
So the type of the member is NSArray. We cannot know, why you have instances of NSArray in the array MyQuotes, because we do not see that code. Likely that happened when you tried to add new quotes to the quotes property and incidentally added the whole array instead of its members.
To your edit:
This is wrong:
NSArray *quoteToAdd = [[NSArray alloc] init]; // This is an array. It identifier should be quote*s*ToAdd
// BTW: This above code is meaningless, because you do not need to create an array instance. Simply omit "[[NSArray alloc] init]". But this is not your problem.
for (QuoteMap *qm in quoteMapSection){
…
quoteToAdd = [self.quotes filteredArrayUsingPredicate:filter]; // filtered array returns an *array*
[quoteSection addObject:quoteToAdd]; // You add the *array* instead of the member of the array.
}
What you get back is an array. Then you add the array itself (not its members) to the existing array. As result you get an array that contains an array.
Simply change …
[quoteSection addObject:quoteToAdd];
… to:
[quoteSection addObjectsFromArray:quoteToAdd];
(And change the reference name to a plural form for better readability.)

UIRefreshControl - Terminating app due to uncaught exception

I implemented UIRefreshControl on a Table View inside a UITableViewController. Today my friend (first tester) was playing with the app and he was able to make crash by pulling to refresh and while it's refreshing, pulling on it again very quickly a couple of times. I know it's not a common thing to do but I can recreate it very easily. I'm guessing there's something wrong with the way I'm implementing the refresh control since I tried to recreate it with the Gmail app and it didn't crash.
This is the error I get:
[1008:907] * Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '* -[__NSCFConstantString stringByAppendingString:]: nil argument'
* First throw call stack:
(0x343f32a3 0x3c08d97f 0x343f31c5 0x34c5c5e1 0x496b5 0x3624654d 0x3622b313 0x362427cf 0x361fe803 0x35fa8d8b 0x35fa8929 0x35fa985d 0x35fa9243 0x35fa9051 0x35fa8eb1 0x343c86cd 0x343c69c1 0x343c6d17 0x34339ebd 0x34339d49 0x37efb2eb 0x3624f301 0x46da5 0x3c4c4b20)
libc++abi.dylib: terminate called throwing an exception
(lldb)
Under ViewDidLoad
// pull to refresh
UIRefreshControl *refreshControl = [[UIRefreshControl alloc] init];
[refreshControl addTarget:self action:#selector(updateTableView:) forControlEvents:UIControlEventValueChanged];
[self setRefreshControl:refreshControl];
And this is the updateTableView method:
- (void)updateTableView:(id)sender {
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:#"MMM d, h:mm:ss a"];
NSString *lastUpdated = [NSString stringWithFormat:#"Last updated on %#", [formatter stringFromDate:[NSDate date]]];
self.refreshControl.attributedTitle = [[NSAttributedString alloc] initWithString:lastUpdated];
if(topOptions.selectedSegmentIndex == 0) {
//fetch the feed
_feed = [[FetchData alloc] initFromURLWithString:#"some json file" completion:^(JSONModel *model, JSONModelError *err) {
[self.tableView reloadData];
[(UIRefreshControl *)sender endRefreshing];
}];
} else if (topOptions.selectedSegmentIndex == 1) {
//fetch the feed
_feed = [[FetchData alloc] initFromURLWithString:#"another json file" completion:^(JSONModel *model, JSONModelError *err) {
[self.tableView reloadData];
[(UIRefreshControl *)sender endRefreshing];
}];
}
}
I'm guessing (from the nil argument) that by doing the refresh request many times it reaches a point that it can't find a value? I'm kind of new at this so any idea is really appreciated.
If you need any other part of the code let me know.

iOS/Objective-C: Attempting to Scan a string for substrings which will be assigned to multiple NSStrings

I'm attempting to complete the Stanford iPhone Programming (FA10) assignement "Flickr Fetcher" -- so far things are going well, however I have come to an impasse:
I have successfully extracted the location of the "Top 100" pictures, which are formated in a string as "Country, State, City". I would like to create two NSStrings -- one being the country, the other string being the State and City. From where I can then do
cell.textLabel.text = countryString;
cell.detailTextLabel.text = stateCityString;
in my table view datasource methods.
From research on stackoverflow and the Apple Documentaion, NSScanner seems to be my best bet -- here is what I have so far...
- (void)viewDidLoad {
//Get the top 100 photos from Flickr
self.topPlacesArray = [FlickrFetcher topPlaces];
NSString *mainLabelString = [[NSString alloc] init];
NSString *stringFromArray = [[NSString alloc] init];
//This retrieves the string of the location of each photo
stringFromArray = [topPlacesArray valueForKey:#"_content"];
NSScanner *theScanner = [NSScanner scannerWithString:stringFromArray];
NSCharacterSet *commaSet = [[NSCharacterSet alloc] init];
commaSet = [NSCharacterSet characterSetWithCharactersInString:#","];
while ([theScanner isAtEnd] == NO) {
if ([theScanner scanUpToCharactersFromSet:commaSet intoString:&stringFromArray]) {
NSLog(#"%#",stringFromArray);
}
}
I'm just trying to see if the string properly substrings itself -- however I am getting a "SIGBART" at the beggining of the while loop, the error is this:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSArrayI length]: unrecognized selector sent to instance 0x8939eb0'
From all the documentation I have seen on NSScanner, it seems I have it set up properly, however, no matter what changes I do, it seems unable to even begin the loop.
What do I have to do to set up NSScanner properly, to avoid the "SIGABRT"? (for the record, i'm assuming "SIGABRT" is a segfault?). Thank you all for your time, you all are the best!
(Btw: I know this is not fully implemented yet for both country and state-city, i just want to get used to NSScanner, I will implement the rest once I get NSScanner under control)
EDIT 1: SosBorn! You are incredible! Thank you so much! So I have implemented this for my viewDidLoad:
- (void)viewDidLoad
{
self.topPlacesArray = [FlickrFetcher topPlaces];
NSArray *ArrayOfStrings = [[NSArray alloc] init];
NSArray *placeElements = [[NSArray alloc] init];
NSString *country = [[NSString alloc] init];
NSString *city = [[NSString alloc] init];
NSString *state = [[NSString alloc] init];
ArrayOfStrings = [topPlacesArray valueForKey:#"_content"];
for (NSString *place in ArrayOfStrings) {
placeElements = [place componentsSeparatedByString:#", "];
if ([placeElements count] == 3 && [placeElements objectAtIndex:0] != nil) {
city = [placeElements objectAtIndex:0];
[self.cityArray addObject:city];
state = [placeElements objectAtIndex:1];
[self.stateArray addObject:state];
country = [placeElements objectAtIndex:2];
[self.countryArray addObject:country];
NSLog(#"%#, %#, %#", city, state, country);
}
else {
NSLog(#"Did this work?");
}
}
[ArrayOfStrings release];
[placeElements release];
[country release];
[city release];
[state release];
[super viewDidLoad];
}
This worked like a complete charm BUT i'm having some bad access going on in the Delegate when trying to access self.window.rootViewController = self.viewController -- this doesn't make any-sense (i actually have a completely empty table, etc...) -- so i'm thinking I played with bad memory management with my substring-ing and now it gets in trouble with this delegate call.
Chuck, I was very interested in your comment as I was taught that the proper way to make variables is to call [myclass alloc] init]; and then release when you are done -- as I have. Of course my objective-C greenness is showing a bit... blush.
You all and this incredible community are such an asset to us Students -- thank you for all your time and dedication. The only path to progress is a path of cooperation!
EDIT 2: Ok -- now it's totally fixed with no terrible leaking problems. Chuck you were right! I had the pricniples of alloc init completely mixed up in my head -- here was my final solution:
NSMutableArray *array1 = [[NSMutableArray alloc] init];
NSMutableArray *array2 = [[NSMutableArray alloc] init];
NSMutableArray *array3 = [[NSMutableArray alloc] init];
self.cityArray = array1;
self.countryArray = array2;
self.stateArray = array3;
[array1 release];
[array2 release];
[array3 release];
NSArray *ArrayOfStrings = [topPlacesArray valueForKey:#"_content"];
NSArray *topPlaces = [NSArray arrayWithArray:ArrayOfStrings];
NSArray *topPlacesSorted = [topPlaces sortedArrayUsingSelector:#selector(compare:)];
ArrayOfStrings = topPlacesSorted;
for (NSString *place in ArrayOfStrings) {
NSArray *placeElements = [place componentsSeparatedByString:#", "];
if ([placeElements count] == 3 && [placeElements objectAtIndex:0] != nil) {
NSString *city = [placeElements objectAtIndex:0];
[self.cityArray addObject:city];
NSString *state = [placeElements objectAtIndex:1];
[self.stateArray addObject:state];
NSString *country = [placeElements objectAtIndex:2];
NSString *stateAndCountry = [NSString stringWithFormat:#"%#, %#", state, country];
[self.countryArray addObject:stateAndCountry];
NSLog(#"%#, %#, %#", city, state, country);
}
else {
NSLog(#"Nil Request");
}
Thank you again SosBorn, i was feeling like I had forgotten the basics of CS ಠ_ಠ.
The only thing that really bothers me is why do we have to initialize instance NSMutableArrays that way -- i found this was the only way to get them to actually work.
Not totally sure why it is crashing, but I think another approach to this would serve you better. You have a topPlacesArray, why not iterate through the array and process each array entry seperately? I am making some assumptions about the topPlacesArray, but it would look something like this:
for (NSString *place in topPlacesArray)
{
//Place is probably in this format: "Country, State, City"
NSArray *placeElements = [place componentsSeperatedByString:#","];
//This should give you an array with three elements. Country State and city.
NSString *country = [placeElements objectAtIndex:0];
NSString *cityState = [NSString stringWithFormat:#"%#, %#", country, cityState];
//Now you have your strings that you need. Do whatever you need to do with them.
//Add them to an array or set the value of a text label, etc.
}
Didn't take the time to handle memory management but you get the idea.

Stumped on a memory leak for OS X app

OK, I have a memory management problem that is driving me up a wall. At one point I swear this worked with no problems, but now it's leaking memory everywhere and I can't figure out why.
To begin with I'm starting an NSTask and then running a loop while the task is running.
NSTask *encodingTask = [[NSTask alloc] init];
NSFileHandle *taskStdout = [NSFileHandle fileHandleForWritingAtPath:encodingOutput];
[encodingTask setStandardOutput:taskStdout];
[encodingTask setStandardError:taskStdout];
NSString argString = [NSString stingWithString: #"some arguments"];
[encodingTask setArguments:taskArgs];
[encodingTask setLaunchPath:somePath];
[encodingTask launch];
while ([encodingTask isRunning]){
sleep(1);
[self encodeProgressTimer];
}
The encodeProgessTimer method is grabbing the last line from the stdOut and placing that in the menu bar:
- (void)encodeProgressTimer
{
if ([[NSUserDefaults standardUserDefaults] boolForKey:#"menuProgress"]) {
// Read the last line
NSString *fileData = [NSString stringWithContentsOfFile:encodingOutput encoding:NSASCIIStringEncoding error:nil];
NSArray *lines = [fileData componentsSeparatedByString:#"\r"];
NSString *lastLine = [lines objectAtIndex:[lines count] - 1];
NSString *percent;
NSString *eta;
BOOL dataFound = NO;
if ([lastLine length] == 71) {
dataFound = YES;
percentRange = (NSRange) {23,5};
etaRange = (NSRange) {61,9};
percent = [lastLine substringWithRange:percentRange];
eta = [lastLine substringWithRange:etaRange];
}
else if ([lastLine length] == 72) {
dataFound = YES;
percentRange = (NSRange) {23,5};
etaRange = (NSRange) {62,9};
percent = [lastLine substringWithRange:percentRange];
eta = [lastLine substringWithRange:etaRange];
}
else if ([lastLine length] == 70) {
dataFound = YES;
percentRange = (NSRange) {23,5};
etaRange = (NSRange) {60,9};
percent = [lastLine substringWithRange:percentRange];
eta = [lastLine substringWithRange:etaRange];
}
if (dataFound) {
NSMutableString *bottomStr = [[NSMutableString alloc]
initWithFormat:#"Encoding: %#%% - ETA %#", percent, eta];
[appDelegate setMenuTop:topString andBottom:bottomStr];
[bottomStr release];
}
}
}
It's my understanding that anything I'm not specifically allocating and initializing should be auto released when the method has completed, but that isn't the case. Memory usage goes up exponentially every second when this is called. If I look at my memory allocations the number of living CFstings goes through the roof. If I turn of encodeProgressTimer my problems go away. I tried adding an autorelease pool to encodeProgressTimer which made memory usage very stable, however after 20 minutes or so of running I get a EXC_BAD_ACCESS. Turning on Zombies turns that into:
*** -[NSConcreteAttributedString _drawCenteredVerticallyInRect:scrollable:]: message sent to deallocated instance 0x2bc756e0
I actually went through and changed each variable declaration into it's alloc/init counterpart and manually released them, but that didn't solve the problem either. At this point I'm pretty stumped.
Also for the sake of completeness the [appDelegate setMenuTop: andBottom:] method looks like this:
-(void) setMenuTop: (NSString *) top andBottom: (NSString *) bottom
{
if ([[NSUserDefaults standardUserDefaults] boolForKey:#"menuProgress"]) {
[statusItem setImage:nil];
NSMutableParagraphStyle *lineHeight = [[NSMutableParagraphStyle alloc] init];
[lineHeight setMaximumLineHeight:10.5];
[lineHeight setLineBreakMode:NSLineBreakByTruncatingMiddle];
OperationQueue *opQueue = [OperationQueue sharedQueue];
NSString *sBuffer = [[NSMutableString alloc] initWithFormat: #"%# (%i More)\n%#", top, [opQueue queueCount] - 1, bottom];
attributes = [[NSDictionary alloc] initWithObjectsAndKeys:[NSFont menuFontOfSize:9], NSFontAttributeName, lineHeight, NSParagraphStyleAttributeName, nil];
if (statusTitle)
[statusTitle release];
statusTitle = [[NSAttributedString alloc] initWithString: sBuffer attributes: attributes];
[statusItem setAttributedTitle: statusTitle];
[lineHeight release];
[sBuffer release];
[attributes release];
}
}
There will be loads of stuff in the autorelease pool, but you need to explicitly drain it for the memory to go away. Change your while loop as follows:
while ([encodingTask isRunning]){
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
sleep(1);
[self encodeProgressTimer];
[pool drain];
}
Other stuff: if you are running this on a thread, you can't update user interface items directly. You need to use something like performSelectorOnMainThread: to actualy update the UI. If you are not running this on a thread, you need to rethink your design. The whole UI of your application will freeze while the loop is running.
You may want to use properties here or nil out the reference.
if (statusTitle) {
[statusTitle release];
statusTitle = nil;
}

iPad app crashing with no crash log while reading from file

The basic structure of my program has the user select an item from a UITableView, which corresponds to a stored text file. The file is then read into an array and a dictionary, where the array has the keys (I know I can just get the keys from the dictionary itself, this isn't my question).
The view is then changed to a UISplitView where the master view has the keys, and the detail view has the items in the dictionary attributed to that key. In this case, it's a series of "Yes/No" questions that the user selects the answer to.
My problem is this: When I click on a cell in the UITableView (first screen), it works fine, the data is read in perfectly, and so on. When I go back to the UITableView and click on the same cell again, the program crashes. Here is the read-in-from-file method:
-(NSArray *)readFromFile:(NSString *)filePath{
// NSLog(#"Path was: %#", filePath);
NSString *file = [[NSString alloc] initWithContentsOfFile:filePath];
// NSLog(#"File was: %#", file);
NSScanner *fileScanner = [[NSScanner alloc] initWithString:file];
NSString *held;
NSString *key;
NSMutableArray *detailStrings;
NSMutableArray *keys = [[NSMutableArray alloc] init];
NSMutableDictionary *details = [[NSMutableDictionary alloc] init];
/**
This is where the fun stuff happens!
**/
while(![fileScanner isAtEnd]){
//Scan the string into held
[fileScanner scanUpToString:#"\r" intoString:&held];
NSLog(#"Inside the while loop");
// If it is a character, it's one of the Key points, so we do the management necessary
if ([[NSCharacterSet lowercaseLetterCharacterSet] characterIsMember:[[held lowercaseString] characterAtIndex: 0]]){
NSArray *checkers = [[NSArray alloc] initWithArray:[held componentsSeparatedByString:#"\t"]];
NSLog(#"Word at index 2: %#", [checkers objectAtIndex:2]);
if(detailStrings != nil){
[details setObject:detailStrings forKey:key];
[detailStrings release];
}
NSLog(#"After if statement");
key = [checkers objectAtIndex:2];
[keys addObject:(NSString *) key];
detailStrings = [[NSMutableArray alloc] init];
}
else if ([[NSCharacterSet decimalDigitCharacterSet] characterIsMember:[[held lowercaseString] characterAtIndex: 0]]){
NSArray *checkers = [[NSArray alloc] initWithArray:[held componentsSeparatedByString:#"\t"]];
NSLog(#"Word at index 1: %#", [checkers objectAtIndex:1]);
[detailStrings addObject:[checkers objectAtIndex:1]];
}
}
NSLog(#"File has been read in");
[details setObject:detailStrings forKey:key];
NSArray *contents = [[NSArray alloc] initWithObjects:(NSMutableArray *) keys, (NSMutableDictionary *) details, nil];
[detailStrings release];
return contents;
}
I've determined that the program crashes inside the
if(detailStrings != nil)
statement. I figure this is because I'm missing some memory management that I am supposed to be doing, but don't have the knowledge of where it's going wrong. Any ideas as to the problem, or why it is crashing without giving me a log?
detailStrings is not initialized when you enter the while loop. When you declare NSMutableArray *detailStrings; inside a method, detailStrings is not automatically set to nil. So when you do
if ( detailStrings != nil ) { .. }
it enters the if statement and since it is not initialized, it will crash when you access detailStrings.
Another thing is that detailStrings won't be initialized if it enters the else part of the loop first. That will cause a crash too. So based on your requirement, either do
NSMutableArray *detailStrings = nil;
or initialize it before you enter the while loop.
Deepak said truth. You should initialize detailStrings with nil first.
But there is second possible issue:
I recommend also to set nil after release, because in the next loop you may test nonexistent part of memory with nil.
if(detailStrings != nil){
[details setObject:detailStrings forKey:key];
[detailStrings release];
detailStrings = nil;
}
And the third possible issue: depending from incoming data you may go to the second part of IF statement first time and try to addObject into non-initialized array.
The fourth (hope last): you have memory leak with "checkers" arrays
Here's what I'm seeing:
//read in the file
NSString *file = [[NSString alloc] initWithContentsOfFile:filePath];
//create the scanner
NSScanner *fileScanner = [[NSScanner alloc] initWithString:file];
//declare some uninitialized stuff
NSString *held;
NSString *key;
NSMutableArray *detailStrings;
//initialize some stuff
NSMutableArray *keys = [[NSMutableArray alloc] init];
NSMutableDictionary *details = [[NSMutableDictionary alloc] init];
//begin loop
while(![fileScanner isAtEnd]){
//scan up to a newline
[fileScanner scanUpToString:#"\r" intoString:&held];
//see if you scanned a lowercase string
if ([[NSCharacterSet lowercaseLetterCharacterSet] characterIsMember:[[held lowercaseString] characterAtIndex: 0]]){
//make an array
NSArray *checkers = [[NSArray alloc] initWithArray:[held componentsSeparatedByString:#"\t"]];
//do a check... against an uninitialized value
if(detailStrings != nil){
//set a potentially uninitialized value into an array with an uninitialized key
[details setObject:detailStrings forKey:key];
At this point, you're pretty much hosed.
The fix:
properly initialize your variables
run the static analyzer
read the memory management programming guide