Search code examples
objective-ciosnsstringnsscannersubstring

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.


Solution

  • 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.