Display NSMutableArray size in label string - iphone

I'm trying to display in a label on my main screen the size of NSMutableArray when the screen loads 1st time and then every time I hit the Add button, but I get errors such as "Expression result unused". I tried several options but still no success...
Please advise me with your inputs, thanks! :)
int arraySize;
NSMutableArray *arrRaceCars;
- (void)viewDidLoad
{
[super viewDidLoad];
arrRaceCars = [[NSMutableArray alloc]init];
arraySize = [self numberOfObjectsInArray:arrRaceCars]; // call for a method that should return the number of objects in array
self.lblCarsCount.text = #"%d cars in the race", &arraySize;
}
// ...part of the Add button validation; in case that everything is OK, the code below should add an object to the array and change the display of number of cars in the array in the label
else
{
self.carType = [segmentedSelectCar titleForSegmentAtIndex:segmentedSelectCar.selectedSegmentIndex];
self.carName = self.txtCarName.text;
self.carSpeed = [self.txtCarSpeed.text intValue];
car* newCar = [[car alloc]initCarWithName:carName carType:carType carMaxSpeed:carSpeed];
NSLog(#"Car type is: %#, Car name is: %#, Car speed is: %d", self.carType, self.carName, self.carSpeed);
[arrRaceCars addObject:newCar];
arraySize = [self numberOfObjectsInArray:arrRaceCars];
self.lblCarsCount.text = #"%d cars in the race", &arraySize; // this is the problematic line
[self alertMessage:#"addNewCar" :#"Your car has been added!" :nil :#"OK" :nil];
}
// this is the method that should return the number of objects within the array
-(int) numberOfObjectsInArray : (NSMutableArray*) arrayToCheck
{
return [arrayToCheck count];
}

Instead of
self.lblCarsCount.text = #"%d cars in the race", &arraySize; // this is the problematic line
you should be using:
self.lblCarsCount.text = [NSString stringWithFormat: #"%d cars in the race", [arrRaceCars count]]
The first line of code compiles, but the compiler doesn't know you want to do a string with a format (the "expression result unused warning") which is why you should put the explicit "stringWithFormat" method call in there.

Related

Performance issue creating Section Index Titles for UITableView

I'm displaying an array of contacts ( [[ContactStore sharedStore]allContacts] ) in a tableview and have divided the list into alphabetic sections. I have used the following code to return an array of the first letters of the contacts, and a dictionary of the number of entries per letter.
//create an array of the first letters of the names in the sharedStore
nameIndex = [[NSMutableArray alloc] init];
//create a dictionary to save the number of names for each first letter
nameIndexCount = [[NSMutableDictionary alloc]init];
for (int i=0; i<[[[ContactStore sharedStore]allContacts]count]; i++){
//Get the first letter and the name of each person
Contact *p = [[[ContactStore sharedStore]allContacts]objectAtIndex:i];
NSString *lastName = [p lastName];
NSString *alphabet = [lastName substringToIndex:1];
//If that letter is absent from the dictionary then add it and set its value as 1
if ([nameIndexCount objectForKey:alphabet] == nil) {
[nameIndex addObject:alphabet];
[nameIndexCount setValue:#"1" forKey:alphabet];
//If its already present add one to its value
} else {
NSString *newValue = [NSString stringWithFormat:#"%d", ([[nameIndexCount valueForKey:alphabet] intValue] + 1)];
[nameIndexCount setValue:newValue forKey:alphabet];
}
}
This works, however it is very slow when the array is large, I'm sure there's a better way to do this but I'm quite new to this so am not sure how. Are there any suggestions for a better way to do this?
Although Bio Cho has a good point, you might see an increase in performance by calling
[[ContactStore sharedStore]allContacts]
only once. For example:
nameIndex = [[NSMutableArray alloc] init];
nameIndexCount = [[NSMutableDictionary alloc] init];
/*
Create our own copy of the contacts only once and reuse it
*/
NSArray* allContacts = [[ContactStore sharedStore] allContacts];
for (int i=0; i<[allContacts count]; i++){
//Get the first letter and the name of each person
Contact *p = allContacts[i];
NSString *lastName = [p lastName];
NSString *alphabet = [lastName substringToIndex:1];
//If that letter is absent from the dictionary then add it and set its value as 1
if ([nameIndexCount objectForKey:alphabet] == nil) {
[nameIndex addObject:alphabet];
[nameIndexCount setValue:#"1" forKey:alphabet];
//If its already present add one to its value
} else {
NSString *newValue = [NSString stringWithFormat:#"%d", ([[nameIndexCount
valueForKey:alphabet] intValue] + 1)];
[nameIndexCount setValue:newValue forKey:alphabet];
}
}
Though I can't say for sure, I'd guess that repeatedly accessing your shared store is what's killing you. Maybe only accessing it once will give you what you need.
Consider storing your contacts in Core Data and using an NSFetchedResultsController.
The NSFetchedResultsController will only load a subset of the rows which are visible on the table view, thus preventing your user from having to wait for all the contacts to be sorted.
NSFetchedResultsController will also sort your contacts by an attribute (ie. first or last name), and you can set your section titles to be the first letter of the field you're sorting by.
Take a look at this question and this tutorial.

Adding items to NSMutableArray not working

I am trying to create an array of numbers that I will use later to determin the size of my tableviewcells.
However I am having some issues with the array, After my while statment it comes back as being NULL, yet when I log the values I am getting from my array of objects they are correct... and the if statement works perfectly.
This is my code
int count = 0;
// Cell heights
int smallCell = 69;
int largeCell = 120;
NSNumber *currentHeight = [[NSNumber alloc] init]; // allows int to be stored into NSArray
while (count < seriesSearchArray.count) {
myObj = (SeriesSearchResultItem*)[dataArrayOfObjects objectAtIndex:count];
if (![myObj.seriesNote isEqualToString:#""]) {
NSLog(#"%#", myObj.seriesNote);
currentHeight = [NSNumber numberWithInt:largeCell];
NSLog(#"%#", currentHeight); // correct value shown
[heightArray addObject:currentHeight];
}
else {
currentHeight = [NSNumber numberWithInt:smallCell];
NSLog(#"%#", currentHeight); // correct value shown
[heightArray addObject:currentHeight];
}
NSLog(#"%#", heightArray); // NULL Shown
count ++;
}
So thats if, I am trying to get the value from each of the objects in my array which works, the if statment works perfectly but then when I try to add them to my new array it always comes back as NULL.
Moving comment to answer
You need something like
heightArray = [NSMutableArray arrayWithCapacity:seriesSearchArray.count]

Filtering an NSArray from JSON?

I'm trying to implement a searchable tableview in my app, where when someone can search a location and get results. It looks something like this:
I'm getting my source from genomes.com which gives more then just cities, it also has parks, buildings, counties, etc. I want to just show locations which are cities.
The data is a JSON file which is parsed by JSONKit. The whole file comes in (maximum 20 objects) and then the searchable table view shows it. I'm not sure if I should parse the JSON file differently, or if I should make the table view show only the results needed. (Performance in this case is not an issue.). The JSON file gets converted to an NSArray.
Here is part of the array:
{
adminCode1 = MA;
adminCode2 = 027;
adminName1 = Massachusetts;
adminName2 = "Worcester County";
adminName3 = "";
adminName4 = "";
adminName5 = "";
continentCode = NA;
countryCode = US;
countryName = "United States";
elevation = 178;
fcl = A;
fclName = "country, state, region,...";
fcode = ADMD;
fcodeName = "administrative division";
geonameId = 4929431;
lat = "42.2000939";
lng = "-71.8495163";
name = "Town of Auburn";
population = 0;
score = "53.40083694458008";
timezone = {
dstOffset = "-4";
gmtOffset = "-5";
timeZoneId = "America/New_York";
};
toponymName = "Town of Auburn";
},
What I want to do is if the "fcl" (seen in the array) is equal to P, then I want it to show that in the table view. If the "fcl" is some other character, then I don't want it to be seen in the table view. I'm pretty sure that an if statement can do that, but I don't know how to get it so that it filters part of it.
Any help would be appreciated! Thanks
EDIT: As of now, this is the code to search:
- (void)delayedSearch:(NSString*)searchString
{
[self.geoNamesSearch cancel];
[self.geoNamesSearch search:searchString
maxRows:20
startRow:0
language:nil];
}
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
self.searchDisplayController.searchBar.prompt = NSLocalizedStringFromTable(#"ILGEONAMES_SEARCHING", #"ILGeoNames", #"");
[self.searchResults removeAllObjects];
// Delay the search 1 second to minimize outstanding requests
[NSObject cancelPreviousPerformRequestsWithTarget:self];
[self performSelector:#selector(delayedSearch:) withObject:searchString afterDelay:0];
return YES;
}
Your question is basically, how do you filter your array from a search bar string? If so, you can detect when the text changes via UISearchBarDelegate and then go through your array copying those objects that contain the string you are looking for, i.e.
This is the delegate method you want: searchBar:textDidChange:.
[filterArray removeAllObjects];
for(int i = 0; i < [normalArray count]; i++){
NSRange textRange;
textRange =[[[[normalArray objectAtIndex:i] objectForKey:#"name"] lowercaseString] rangeOfString:[searchBarString lowercaseString]];
//I wasn't sure which objectForKey: string you were looking for, just replace the one you want to filter.
if(textRange.location != NSNotFound)
{
[filterArray addObject:[normalArray objectAtIndex:i]];
}
}
filterTableView = YES;
[tableView reloadData];
Note the filterTableView bool value, this is so your tableView knows either to load normally or the filtered version you just made. You implement this in:
tableView:numberOfRowsInSection: //For number of rows.
tableView:cellForRowAtIndexPath: //For the content of the cells.
Hope this is what you were looking for.
NSMutableArray* filtered = [[NSMutableArray alloc] autorelease];
for (int i=0;i<[data count];i++)
{
NSDictionary* item=[data objectAtIndex:i];
if (#"P" == [item objectForKey:#"fcl"] )
{
[filtered addObject:item];
}
}
So every time the search field changes, you will compute a new array, and then reload your tableview. The number of rows will be the numbers of rows in your filtered array.
To compute the new array, you can do this (assuming an array of dictionaries):
NSString *searchString; // from the search field
NSMutableArray *array = [NSMutableArray arrayWithCapacity:[origArray count]];
for(NSDictionary *dict in origArray) {
NSString *val = [dict objectForKey:#"fcl"];
if([val length] >= searchString) {
NSString subString = [val substringToIndex:[searchString length]];
if([subString isEqualToString:val]) [array addObject:dict];
}
}
Each cell then will get its values from the new array.
Just put your json in a NSDictionary and simply do something like :
if ([[yourJson objectForKey:#"fcl"] stringValue] == #"A")
//doSomething

Spotted a leak in UITextView delegate method. Confused about solution

I've got a problem with an UITextView and one of its delegate methods in my navigation based app:
- (BOOL)textView:aView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
I managed to limit the max length of text the user can input using the above method. But I'm using a leaky array for that matter I think.
The problem is:
I want to save the amount of typed characters right in the very moment the user enters the last line of my textview. I then use that value to calculate the string length - which I compare to the textview's content size to set a limit. The code works fine - but since the method it's inside of is updating with every text input, I'm having trouble releasing the array in the right moment.
Here's some code:
if (numLines == 9)
{
if (!numCharsArray)
{
numCharsArray = [[NSMutableArray alloc] initWithCapacity:1]; // Stack trace gives this line 3,3% of the leak.
}
numChars = tView.text.length;
NSNumber *number = [[NSNumber alloc] initWithInteger:numChars]; // This line gets 77,3%.
[numCharsArray addObject:number]; // This line gets the rest, 24,3%.
[number release];
startChars = [[numCharsArray objectAtIndex:0] integerValue];
NSString *lastLine = [[NSString alloc]initWithString:[[tView text] substringFromIndex:startChars]];
CGSize lineSize = [lastLine sizeWithFont:tView.font forWidth:tView.contentSize.width lineBreakMode:UILineBreakModeWordWrap];
[lastLine release];
if (range.length > text.length)
{
return YES;
}
else if (numLines == 9 && lineSize.width >= tView.contentSize.width - 45)
{
return NO;
}
}
else
{
numCharsArray = nil;
/*
if(numCharsArray)
{
[numCharsArray release];
}
*/
}
I tried the out-commented statement above, but that gives me an app crash once I leave the last line of the textview. And as you can see in the code comments - without releasing the array I get a leak.
So how and where do I release that array correctly - keeping it safe while the user is on the last line?
Just replace with
first one
numCharsArray = [NSMutableArray array]; // you do not need to release
//explicitly as its autorelease numberWithInt
second one
NSNumber *number = [NSNumber numberWithInt:numChars]; //autorelease
NSString *lastLine = [[tView text] substringFromIndex:startChars];

Why doesn't this for loop execute?

I have a picker view controller to select a chemical source and possibly a concentration. If the source doesn't have concentrations, it just presents a single picker. It gets populated by an NSDictionary with source type names as keys and a custom model object I made called Chemical that has four properties, two NSString, one float and one BOOL.
When I trigger this with dictionary that has 2 components, I want to extract the four values from the Chemical that is represented. Note that I populate the picker with values from the first two properties, but not the float or BOOL. I run through the array for the key that's selected in the first component and check the string from the second component against the chemConcentration property from each of the Chemicals in the key/value array. When the chemConcentration matches, I know I have the right Chemical and I can get its properties to send back.
Whew!
The problem is that even though I know I get to the for loop, it seems to get skipped. The NSLog right before it prints, but the one inside doesn't. sourceConstant and sourceIsLiquid stay 0.0 and NO
- (IBAction)selectedSourceButton {
NSLog(#"selectedSourceButton pressed");
NSInteger sourceRow = [picker selectedRowInComponent:kSourceComponent];
NSString *selectedSource = [self.sources objectAtIndex:sourceRow];
NSArray *selectedChemicalGroup = [dictionaryOfSources objectForKey:selectedSource];
NSInteger concentrationRow = [picker selectedRowInComponent:kConcentrationComponent];
NSString *selectedConcentration = [[NSString alloc] init];
float selectedConstant = 0.0;
BOOL selectedIsLiquid = NO;
if (numberOfComponents == 2) {
NSLog(#"numberOfComponents = 2 if/then chosen"); // <-- This prints.
selectedConcentration = [self.concentrations objectAtIndex:concentrationRow];
NSLog(#"begin selectedConcentration for loop. Number of loops = %d", [selectedChemicalGroup count]); // <-- And so does this.
for (int i; i<[selectedChemicalGroup count]; i++) { // <-- But this doesn't seem to fire!
NSLog(#"selectedConcentration = %#, from selectedChemicalGroup = %#", selectedConcentration, [[selectedChemicalGroup objectAtIndex:i] chemConcentration]); // <-- Because this doesn't print.
if ([selectedConcentration isEqualToString:[[selectedChemicalGroup objectAtIndex:i] chemConcentration]]) {
selectedConstant = [[selectedChemicalGroup objectAtIndex:i] chemConstant];
selectedIsLiquid = [[selectedChemicalGroup objectAtIndex:i] chemIsLiquid];
}
}
}
else {
selectedConcentration = #"";
selectedConstant = [[selectedChemicalGroup objectAtIndex:0] chemConstant];
selectedIsLiquid = [[selectedChemicalGroup objectAtIndex:0] chemIsLiquid];
}
NSLog(#"selectedSourceButton source to return = %#, concentration = %#, sourceConstant = %1.7f, isLiquid = %d", selectedSource, selectedConcentration, selectedConstant, selectedIsLiquid);
if ([self.delegate respondsToSelector:#selector (sourcePickerViewController:didSelectSource:andConcentration:andConstant:andIsLiquid:)]) {
[self.delegate sourcePickerViewController:self didSelectSource:selectedSource andConcentration:selectedConcentration andConstant:selectedConstant andIsLiquid:selectedIsLiquid];
}
}
You need to initialize your variable i: for (int i = 0; ...
But there's a better way to do this, using "fast enumeration":
for (MyChemicalGroupClass *group in selectedChemicalGroup) {
if ([selectedConcentration isEqualToString:[group chemConcentration]]) {
...
}
}
Initialize loop count i
for (int i = 0; i<[selectedChemicalGroup count]; i++)
Do the following and you will understand why:
int i;
NSLog(#"%d", i);