Problem with NSMutableArray and valueforkey - iphone

I use a plist file to get a list of site which are displayed in a tableview
The plist looks like this:
<array>
<dict>
<key>site</key>
<string>http://yahoo.com</string>
<key>title</key>
<string>Yahoo</string>
</dict>
<dict>
<key>site</key>
<string>http://google.com</string>
<key>title</key>
<string>Google</string>
</dict>
//...etc
</array>
</plist>
I display this without problem:
NSMutableArray *array = [[NSMutableArray alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"TestData" ofType:#"plist"]];
[self setContentsList:array];
*The problem is when I try to search in the content and I want to get the valueforkey #"site" from the search result to use it in didSelectRowAtIndexPath: *
NSMutableArray *contentsList;
NSMutableArray *searchResults;
NSString *savedSearchTerm;
---------------------
- (void)handleSearchForTerm:(NSString *)searchTerm
{
[self setSavedSearchTerm:searchTerm];
if ([self searchResults] == nil)
{
NSMutableArray *array = [[NSMutableArray alloc] init];
[self setSearchResults:array];
[array release], array = nil;
}
[[self searchResults] removeAllObjects];
if ([[self savedSearchTerm] length] != 0)
{
for (NSString *currentString in [[self contentsList] valueForKey:#"title"])
{
if ([currentString rangeOfString:searchTerm options:NSCaseInsensitiveSearch].location != NSNotFound)
{
[[self searchResults] addObject:currentString];
// NSDictionary *dic= [[NSDictionary alloc]allKeysForObject:searchResults];
}
}
}
}
The didSelectRowAtIndexPath is used to open the site in a webView
- (void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
NSString *arraySite = [[[self searchResults] objectAtIndex:indexPath.row] valueForKey:#"site"];
[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:arraySite]]];
[self performSelector:#selector(showSearch:) withObject:nil afterDelay:0];
}
ERROR I GET :
Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<NSCFString 0x6042730> valueForUndefinedKey:]: this class is not key value coding-compliant for the key site.'

The Basics
When you read the array from that plist, the array looks like the following:
(
{
"site" = "http://yahoo.com",
"title" = "Yahoo"
},
{
"site" = "http://google.com",
"title" = "Google"
},
…
)
It is an array whose elements are dictionaries, each dictionary containing two keys and their corresponding values.
When you use the KVC method -valueForKey: on the array passing the key title, it returns another array whose elements are the values corresponding to that key:
(
"Yahoo",
"Google",
…
)
The resulting array doesn’t hold a reference to the original array.
The Problem
In -handleSearchForTerm:, you get an array containing only the titles from the original array. For each title, you selectively add it to the searchResults array:
for (NSString *currentString in [[self contentsList] valueForKey:#"title"])
{
…
[[self searchResults] addObject:currentString];
}
This means that searchResults is an array containing a list of titles which are not automatically related to the corresponding dictionaries in the contentList array.
It seems like you want to keep the original dictionary because you’ve tried to create a dictionary:
// NSDictionary *dic= [[NSDictionary alloc]allKeysForObject:searchResults];
and, in another method, you’re trying to obtain the value corresponding to the site key:
NSString *arraySite = [[[self searchResults] objectAtIndex:indexPath.row]
valueForKey:#"site"];
As mentioned, your searchResults contains a list of strings representing titles. When you obtain an element from this array, it’s only a string — hence -valueForKey:#"site" doesn’t make sense, and Cocoa warns you that a string is not key-value compliant for the key site.
One Solution
From what I can tell, you should be storing, in your searchResults array, the original dictionary read from the plist file. In -handleSearchForTerm:, do the following:
for (NSDictionary *currentSite in [self contentsList])
{
NSString *title = [currentSite objectForKey:#"title"];
if ([title rangeOfString:searchTerm options:NSCaseInsensitiveSearch].location != NSNotFound)
{
[[self searchResults] addObject:currentSite];
}
}
Now every element in searchResults is a dictionary containing both site and title.
In -tableView:didSelectRowAtIndexPath:, use the dictionary to obtain the corresponding site:
- (void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
NSDictionary *selectedSite = [[self searchResults] objectAtIndex:indexPath.row];
NSString *siteStringURL = [selectedSite objectForKey:#"site"];
// or, if you prefer everything in a single line:
// NSString *siteStringURL = [[[self searchResults] objectAtIndex:indexPath.row] objectForKey:#"site"];
[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:siteStringURL]]];
[self performSelector:#selector(showSearch:) withObject:nil afterDelay:0];
}

Related

NSMutableArray Not Retaining Objects Outside Method [closed]

This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 9 years ago.
In my -viewDidLoad method, I initialize many NSMutableDictionaries, and add them to an initialized NSMutableArray declared via #property in the class header file. The relevant code is shown below. In short, I'm webscraping information from an HTML webpage.
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
_regionalDicts = [[NSMutableArray alloc] init];
for (int i = 0; i < [strings count]; i++) {
NSString *str = [strings objectAtIndex:i];
//Property parser:
if ([str rangeOfString:#"<td>"].location != NSNotFound) {
NSString *parsedTD1 = [str stringByReplacingOccurrencesOfString:#"<td>" withString:#""];
NSString *parsedTD2 = [parsedTD1 stringByReplacingOccurrencesOfString:#"</td>" withString:#""];
NSString *parsedTD3 = [parsedTD2 stringByReplacingOccurrencesOfString:#" " withString:#"\n"];
NSString *final = [parsedTD3 stringByReplacingOccurrencesOfString:#"\t" withString:#""];
//NSLog(#"Final string: %#", final);
if ([final isEqualToString:#""]) {
continue;
}
if (gotEventType == NO) {
gotEventType = YES;
[dict setObject:final forKey:#"type"];
continue;
}
if (gotRegional == YES && gotLocation == NO) {
gotLocation = YES;
[dict setObject:final forKey:#"location"];
continue;
}
if (gotLocation == YES && gotCity == NO) {
gotCity = YES;
NSString *cityToReturn = [final stringByReplacingOccurrencesOfString:#"\n" withString:#""];
[dict setObject:cityToReturn forKey:#"city"];
continue;
}
if (gotRegional == YES && gotEventType == YES && gotCity == YES && gotLocation == YES && gotURL == YES) {
gotRegional = NO;
gotEventType = NO;
gotCity = NO;
gotLocation = NO;
gotURL = NO;
NSLog(#"Regional: %#", [dict objectForKey:#"regional"]);
NSLog(#"Type: %#", [dict objectForKey:#"type"]);
NSLog(#"City: %#", [dict objectForKey:#"city"]);
//Testing to see if anything is nil
NSLog(#"Location: %#\n", [dict objectForKey:#"location"]);
if (!_regionalDicts) {
NSLog(#"Dict is nil");
}
[_regionalDicts addObject:dict];
NSLog(#"Objects in array: %u", [_regionalDicts count]);
NSMutableDictionary *tempDict = [_regionalDicts objectAtIndex:[_regionalDicts count]-1];
NSLog(#"Regional in array: %#", [tempDict objectForKey:#"regional"]);
[dict removeAllObjects];
continue;
}
It's clear that the generated dictionaries are generated and retained within the _regionalDicts mutable array, which is declared in the header file like this:
#property (strong, nonatomic) IBOutlet NSMutableArray *regionalDicts;
However, when I attempt to pass in information to table view cells in in the same class, the dictionaries' contents are null. There are as many objects within the array as dictionaries I am expecting, but they do not contain any content.
- (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];
}
if (_regionalDicts) {
NSMutableDictionary *dict = [_regionalDicts objectAtIndex:0];
NSLog(#"Setting label %#", [dict objectForKey:#"city"]);
[cell.textLabel setText:[dict objectForKey:#"regional"]];
}
return cell;
}
Returns:
2013-04-01 19:58:50.250 MatchScrape[53570:207] Setting label (null)
I can only imagine that a memory management issue is to blame. Why would the contents of a class array be nullified when accessed outside the scope of the method they are added in, but allow the array to retain the same count?
You seem to believe that adding the dictionary to the array doesn't actually add the dictionary to the array, but instead adds a copy of the dictionary. You're probably thinking of how it might work in a language like C++ — but that isn't how it works here. Remember, Objective-C objects are always accessed by reference: you never directly store the object itself in a variable or array — you're just shuffling around a pointer to the actual object, which usually lives on the heap.
So when you add _dict to the array, the one in the array is the very same object referenced by _dict. Anything you do to that dictionary — no matter what reference you use — will be reflected everywhere else that dictionary is referenced, because it's the same dictionary. You haven't made a copy of it. Thus, when you do [_dict removeAllObjects], that removes all the objects from the dictionary and you end up with an array that contains the same empty dictionary a bunch of times.

how pass array value on other class for display data in iphone

Hi friends thank for helping to me
I have doubt when i pass one array value for display on other controller class in tableView so I get nil value on that controller how to take array value on that class of other controller for display purpose
my lstAirports is a array which created on Airport.h and my Airport.h is simple class is not delegate the code of this class:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
loginStatus = [[NSString alloc] initWithBytes: [webData mutableBytes] length:[webData length] encoding:NSUTF8StringEncoding];
[connection release];
NSString *regexString = #"Stations\\[""(.*)""\\] = new Station\\((.*)new Array\\((.*)\\)\\);";
matchArray = [loginStatus arrayOfCaptureComponentsMatchedByRegex:regexString];
//NSLog(#"matchArray: %#", matchArray);
lstAirports = [[NSMutableArray alloc] initWithCapacity:[matchArray count]];
for (int i = 0; i < [matchArray count]; i++) {
airport *air=[[airport alloc]init];
//code
NSString *temp=[[matchArray objectAtIndex: i] objectAtIndex: 1];
NSString *newString=[temp stringByReplacingOccurrencesOfString:#"\"" withString:#""];
arrParts=[newString componentsSeparatedByString:#","];
air.Code =[arrParts objectAtIndex:0];
//air.Code = [[matchArray objectAtIndex: i] objectAtIndex: 1];
NSLog(#"air.Code: %#\n",air.Code);
//name
temp=[[matchArray objectAtIndex: i] objectAtIndex: 2];
newString=[temp stringByReplacingOccurrencesOfString:#"\"" withString:#""];
arrParts=[newString componentsSeparatedByString:#","];
air.Name=[arrParts objectAtIndex:2];
NSLog(#"air.Name: %#\n",air.Name);
//destination airports
temp=[[matchArray objectAtIndex: i] objectAtIndex: 3];
newString=[temp stringByReplacingOccurrencesOfString:#"\"" withString:#""];
arrParts=[newString componentsSeparatedByString:#","];
air.DestinationAirports =arrParts;
NSLog(#"air.DestinationAirports: %#\n",air.DestinationAirports);
[lstAirports addObject: air];
NSLog(#"lstAirports: %#\n",lstAirports);
//NSString *str=
//[air release];
}
}
- (void)dealloc {
[super dealloc];
[loginStatus release];
//[lstAirports release];
[webData release];
// [window release];
}
When I pass this array on my `Odselectioncontroller.m` then I get nil value of array where I am wrong friends please help me out this is my controller class code
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return obj.lstAirports.count;
}
- (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];
}
airport *a=(airport*)[obj.lstAirports objectAtIndex:indexPath.row];
NSLog(#"str:%#",a);
cell.textLabel.text =a.Name;
and `Airport` is other class where i am create name property for fetch value name from array in yhis class
You are always creating new instance of your Airports class obj which will return you nil value always.
If you want to pass a array from one to other then you can do it by two ways-
make your array global by using extern keyword.
you can define a array in second class as a property and then can set it.
for more you can refer this -
Passing data between classes using Objective-C
DO one thing declare the NSMutable Array in AppDelegate and synthesize the array and using this array to store objects in any class and use this array to display in any class directly by using appDelegate.ArrayName Hope it helps You :)
Here you can do like,
Airport.h class as Sigleton and use .
Otherwise
Declare a Array in APPDelegate and access it in you needed class by sharedObject mechanism
Either you can pass the array using an array object which is synthesized in the class. Another option is to have a method(which takes a NSArray object as parameter) in the class you want to populate and call the method on your first class passing the array as parameter.
Your code also requires a slight change in the dealloc method. Its advised not to call [super dealloc]; before releasing the objects of the class. Always call [super dealloc]; at the end after releasing all objects.

UITableView / UISearchBar Returns Incorrect Results

I am attempting to implement searching in a UITableView. When searching, it appears that the correct number of results are returned, but I am receiving entries from the original stories array in the results, rather than searchResults. I can see that the searchResults array should be the data source, but haven't been able to figure out after tons of searching quite how to pull it off with an array of NSDictionaries. Any help is appreciated.
- (void)handleSearchForTerm:(NSString *)searchTerm {
[self setSavedSearchTerm:searchTerm];
if ([self searchResults] == nil)
{
NSMutableArray *array = [[NSMutableArray alloc] init];
[self setSearchResults:array];
[array release], array = nil;
}
[[self searchResults] removeAllObjects];
if ([[self savedSearchTerm] length] != 0)
{
for (NSDictionary *currentItem in [self stories])
{
if ([[currentItem objectForKey:#"title"] rangeOfString:searchTerm options:NSCaseInsensitiveSearch].location != NSNotFound)
{
[[self searchResults] addObject:currentItem];
}
}
}
}
[tableView isEqual:self.searchDisplayController.searchResultsTableView] is also another alternative to making and managing your own BOOL isFiltering; variable
use NSPredicate for filtering
NSPredicate* predicate = [NSPredicate predicateWithFormat:#"self.title MATCHES %#",searchTerm];
Suppose that your original array is "originalArray" so to get the filtered array use this make two more global variables
NSArray* filteredArray;
BOOL isFiltering;
Now in search bar delegate method do following
-(void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
NSPredicate* predicate = [NSPredicate predicateWithFormat:#"self.title MATCHES %#",searchTerm];
filteredArray = [[originalArray filteredArrayUsingPredicate:predicate] retain];
}
Now you need to change l'll bit your table view delegate and data source, .... for all the places where you are using
NSDictionary *currentString = [originalArray objectAtIndex:indexPath.row];
use following
NSDictionary *currentString;
if(isFiltering)
currentString = [originalArray objectAtIndex:indexPath.row];
else
currentString = [filteredArray objectAtIndex:indexPath.row];

Counting allKeys from NSDictionary returns EXC_BAD_ACCESS

I am trying to send an NSDictionary to a TableViewController, the data originally comes from a .plist file. I simply want to send an object that exists further down the hierarchy to new TableViewController. But problems occur when I try to count the number of items in numberOfSectionsInTableView.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// Gets the dictionary for the currently selected row
NSDictionary *dict = [[data objectForKey:[[data allKeys] objectAtIndex:indexPath.section]] objectAtIndex:indexPath.row];
// Checks if the key "Room" exists
if([dict objectForKey:#"Room"]) {
SalesFairSessionTableViewController *sessionViewController = [[SalesFairSessionTableViewController alloc] init];
// Sets the data in the subview Controller
[sessionViewController setData:[dict objectForKey:#"Room"]];
// And the title
[sessionViewController setTitle:[dict objectForKey:#"Title"]];
// Problem is here... returns EXC_BAD_ACCESS
NSLog(#"%#", [[[dict objectForKey:#"Room"] allKeys] count]);
[self.navigationController pushViewController:sessionViewController animated:YES];
[sessionViewController release];
}
}
If I just use allKeys like this:
NSLog([[dict objectForKey:#"Room"] allKeys]);
It returns ("Item 1", "Item 2") in the console.
But when I add the ”count” method like this:
NSLog(#"%#", [[[dict objectForKey:#"Room"] allKeys] count]);
I just get: Program received signal: “EXC_BAD_ACCESS”.
What am I missing here?
NSLog(#"%#", [[[dict objectForKey:#"Room"] allKeys] count]);
count gives an int, but you print with %#, expecting a pointer to an object. Use %d instead.
As your code currently stands, you're telling the NSLog string to expect an object. count returns an NSInteger, hence the error. To fix, change this line:
NSLog(#"%#", [[[dict objectForKey:#"Room"] allKeys] count]);
to this:
NSLog(#"%i", [[[dict objectForKey:#"Room"] allKeys] count]);

Dealing with editable items in a filtered UITable with an NSMutableArray copy

I have a UITable with a datasource that is set to a 'copy' of the original data. I use this copy to displayed filtered results (either 'All' or only those that are 'checked' with a UISwitch in each row). My logic for doing the filtering is here:
-(void)filterItems {
[tableData removeAllObjects];
if (checkedOrNotSwitch.selectedSegmentIndex == 0) {
[tableData addObjectsFromArray:self.defaultChecklistItemsArray];
} else {
for (NSMutableDictionary *sectionDict in self.defaultChecklistItemsArray) {
NSMutableArray *newItemsArray = [[NSMutableArray alloc] init];
[newItemsArray removeAllObjects];
for (NSMutableDictionary *itemDict in [sectionDict objectForKey:#"categoryItems"]) {
if ([[itemDict objectForKey:#"isComplete"] boolValue]) {
[newItemsArray addObject:itemDict];
}
}
if ([newItemsArray count] > 0) {
NSMutableDictionary *newSectionDict = [[NSMutableDictionary alloc] initWithDictionary:sectionDict];
[newSectionDict setObject:newItemsArray forKey:#"categoryItems"];
[tableData addObject:newSectionDict];
[newSectionDict release];
}
}
}
[checklistTable reloadData];
}
The filtering itself now works correctly. In my custom cell, each row has a UISwitch. The switch runs this function when its changed:
-(IBAction) switchValueChanged{
NSIndexPath *indexPath = [(UITableView *)self.superview indexPathForCell: self];
[self.parentController updateCompletedStatusAtIndexPath:indexPath toStatus:isCompleted.on];
}
The function above is in the class for the tableviewcell itself. The function I call in the superview is this:
-(void)updateCompletedStatusAtIndexPath:(NSIndexPath *)indexPath toStatus:(BOOL)status{
NSUInteger section = [indexPath section];
NSUInteger row = [indexPath row];
NSMutableDictionary *currentsection = [[NSMutableDictionary alloc] initWithDictionary:[tableData objectAtIndex:section]];
NSMutableArray *itemsArray = [[NSMutableArray alloc] initWithArray:[currentsection objectForKey:#"categoryItems"] copyItems:YES];
NSMutableDictionary *tempDict = [[NSMutableDictionary alloc] initWithDictionary:[itemsArray objectAtIndex:row]];
NSLog(#"BOOL = %#\n", (status ? #"YES" : #"NO"));
[tempDict setValue:[NSNumber numberWithBool:status] forKey:#"isComplete"];
[itemsArray replaceObjectAtIndex:row withObject:tempDict];
[currentsection setValue:itemsArray forKey:#"categoryItems"];
[tableData replaceObjectAtIndex:section withObject:currentsection];
[tempDict release];
[itemsArray release];
[currentsection release];
[checklistTable reloadData];
}
Before I implemented the filtering and used the logic above on self.defaultChecklistItemsArray directly, it worked and saved the data when the switch was flipped.
Now when I first enter the app, it loads the array of data from nsuserdefaults. I navigate to the view with the table and it displays the data correctly there with the UISwitches all in the correct position given the data (some on, some off). When I tap one of the switches, then click the segmentedcontrol that does the filtering, the data reverts back to the state it was loaded in, implying that the flipping of the switch did not actually effect the data at all (even though I don't think I did a deep copy anywhere here so I figured it should be doing the right thing). Any suggestions?