Getting valueForKey returns (null) - objective-c

I am trying to put a value in a NSArray and receive it again later. Here is my code:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[_system setValue:[_objects objectAtIndex:indexPath.row] forKey:#"selected"];
NSLog(#"%#", [_objects objectAtIndex:indexPath.row]);
[tableView deselectRowAtIndexPath:indexPath animated:YES];
NSLog(#"%#", [_system valueForKey:#"selected"]);
}
Here is the log result:
2013-11-01 23:38:04.210 ClassPoints[187:60b] test
2013-11-01 23:38:04.211 ClassPoints[187:60b] (null)
What I find odd is even creating the array in the void doesn't properly load the value.
NSArray *testArray;
[testArray setValue:#"test" forKey:#"test"];
NSLog(#"%#", [testArray valueForKey:#"test"]);
Could anyone shed some light on this? I am completely lost. Thanks!

Make your NSArray an NSMutableArray so you can edit objects in it and in your viewDidLoad do not forget to initialize it.
-(void)viewDidLoad {
myMutableArray = [[NSMutableArray alloc] init];
}

Once an NSArray has been initialized, you won't be able to add/remove/edit objects on it. That is what an NSMutableArray is for.

First you need to use the mutable versions of the collection type you want to use, then you need to alloc and init them. Then unless your _system (NSArray?) contains objects that are key value coding compliant for "selected". setValue:forKey calls setValue:forKey on each of your arrays elements. The same applies for valueForKey:. Thats why your isolated example wont work either. I think you want a NSMutableDictionary then you can get/set objects through keys while arrays only works with indexes.
NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] init];
[dictionary setObject:yourObject forKey:yourKey];
id obj = [dictionary objectForKey:yourKey];

Try like this use NSMutableArray and for setting use setObject api inspite of setting setValue.
NSMutableArray *testArray = [NSMutableArray array];
[testArray setObject:#"test" forKey:#"test"];
NSLog(#"%#", [testArray objectForKey:#"test"]);

Related

Working with NSMutableArray and NSUserDefaults

i`m trying to make an NSMutableArray From NSUserDefaults so i can add/delete and edit it later
my code is
- (void)viewDidLoad {
[super viewDidLoad];
NSUserDefaults *ArrayTable = [NSUserDefaults standardUserDefaults];
[ArrayTable setObject:#"One" forKey:#"myArray"];
[ArrayTable setObject:#"Two" forKey:#"myArray"];
[ArrayTable setObject:#"Three" forKey:#"myArray"];
[ArrayTable setObject:#"Four" forKey:#"myArray"];
[ArrayTable setObject:#"Five" forKey:#"myArray"];
[ArrayTable synchronize];
NSMutableArray *array = [[NSMutableArray alloc] init];
array = [ArrayTable objectForKey:#"myArray"];
}
- (void)viewDidUnload
{
[super viewDidUnload];
}
#pragma mark - Table View
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [array 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];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
cell.textLabel.text = [array objectAtIndex:indexPath.row];
return cell;
}
when i build and run nothing shows up
i googled it but with no help, i`m sure i didn't understand how to do it
what i need is to build an app that contain a tableview with empty data, the use will fill in the data Add/Delete/Edit
can someone please explain it for me
thank you in advance
First, you assigned your array from the user defaults to a local variable named array. Assuming you have a property for this class also named array, this local assignment masks that. If it had not, you would have crashed when you tried to call -count on a string.
The NSUserDefaults object is a dictionary. Each time you call -setObject:forKey: on it, you are actually replacing the object previously set for that key. So at the end of your series of calls to -setObject:forKey:, the resulting value is the NSString Five.
You can't really store a mutable object in the NSUserDefaults, instead you would take a mutable copy of the object when you assign it to your local variable or ivar. To get the behavior you are probably expecting, you should do something like the following:
- (void)viewDidLoad
{
// Create the array template and store it in NSUserDefaults
NSArray* arrayTemplate = [NSArray arrayWithObjects:#"One", #"Two", #"Three", #"Four", #"Five", nil];
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:arrayTemplate forKey:#"myArray"];
[defaults synchronize];
// Retrieve a mutable copy of the array from user defaults and assign it to the
// the property 'array' <- note, this should be an NSMutableArray
self.array = [[defaults objectForKey:#"myArray"] mutableCopy]; // if not using arc, autorelease this here
}
With that example code, it should behave the way you expected it to behave, and you can continue on. Obviously it makes no sense to set the array in -viewDidLoad and then immediately read a mutable copy. The key thing to keep in mind is that -setObject:forKey: will always replace any object already set for that key. It doesn't add elements or anything like that.
Your problem is that you are setting a bunch of strings all to the same key:
[ArrayTable setObject:#"yourvalue" forKey:#"myArray"];
just keeps overriding your last value. In order to save an array to your defaults you will have to go about it another way. This question may help you: array to defaults
Since you don't actually want anything to do with the contents of NSUserDefaults, you're just asking how to initialize a mutable array. Here's how to do what you were trying to do:
NSMutableArray *array = [[NSMutableArray alloc] init];
[array addObject:#"One"];
[array addObject:#"Two"];
[array addObject:#"Three"];
[array addObject:#"Four"];
[array addObject:#"Five"];
Or, more simply:
NSMutableArray *array = [NSMutableArray arrayWithObjects:#"One", #"Two", #"Three", #"Four", #"Five", nil];
(These aren't quite identical. If you're using MRC, the first creates an array that you own and will have to release manually, while the second creates an array that's autoreleased. If you don't understand the difference, the second is better until you learn, but soon you should go learn.)
If you want to know why what you were doing didn't work, here goes:
NSUserDefaults *ArrayTable = [NSUserDefaults standardUserDefaults];
Here you're copying an NSUserDefaults object, which is not an array, or a table; it's a wrapper around the user and system preferences that acts like a dictionary with extra functionality.
[ArrayTable setObject:#"One" forKey:#"myArray"];
This line adds a preference named "myArray" to the user-domain preferences for your application, with the value "One".
[ArrayTable setObject:#"Two" forKey:#"myArray"];
[ArrayTable setObject:#"Three" forKey:#"myArray"];
[ArrayTable setObject:#"Four" forKey:#"myArray"];
[ArrayTable setObject:#"Five" forKey:#"myArray"];
These lines change the value of your "myArray" preference repeatedly, ending with "Five".
[ArrayTable synchronize];
This makes sure that your preference is saved to disk.
NSMutableArray *array = [[NSMutableArray alloc] init];
This creates a new mutable array and stores it in the variable "array".
array = [ArrayTable objectForKey:#"myArray"];
This gets the "five" string out of your preferences and stores it into the variable "array".
This means you've now lost the only reference you had to the actual array.
This also means the static type of the variable no longer matches the dynamic type. When you later call [array count], or [array objectAtIndex:n], you're sending those messages to a string, not an array, so you're probably going to get an exception or other unexpected behavior. (Well, it's pretty much guaranteed that whatever you get is going to be unexpected, since you thought you were talking to an array of 5 objects, not a string.)
Create the array and add that to user defaults.
- (void)viewDidLoad {
//...
NSArray * tmpArray = [NSArray arrayWithObjects:#"One",#"Two",#"Three",#"Four", #"Five", nil];
NSUserDefaults *ArrayTable = [NSUserDefaults standardUserDefaults];
[ArrayTable setObject:tmpArray forKey:#"myArray"];
[ArrayTable synchronize];
array = [[ArrayTable objectForKey:#"myArray"] mutableCopy];
// array has to be an ivar of your viewcontroller to access it outside of viewDidLoad
// define in your header like #property (nonatomic, strong) NSMutableArray * array;
// with #synthesize array; in the implementation
}

NSDictionary to TableView

because i'm a newby at Stackoverflow i cannot comment someones anwser yet. (my reputation is 16..). I got a question about this anwser: How do I put this JSON data into my table view? Please help me, I'm living in a nightmare :)
Fulvio sais you have to use [eventNameList addObject:event]; and [eventNameList objectAtIndex:indexPath.row]; to store and get the event data but. addObject is an NSMutableSet method and objectAtIndex:indexPath.row is not. So i cannot use this method to get the data from the NSMutableSet.
Besides that, i can use the count methods neither.
Any Idea's ?
Assuming you have an NSDictionary, you could use the [dictionary allKeys] method to retrieve an array with all keys (lets call it keyArray for now). For the rowCount you could return the count of objects in this keyArray. To get the item that needs to be displayed in the cell you could use [dictionary objectForKey:[keyArray objectAtIndex:indexPath.row]]] to get the appropriate dictionary for the displayed cell.
In code:
// use the keyArray as a datasource ...
NSArray *keyArray = [jsonDictionary allKeys];
// ------------------------- //
// somewhere else in your code ...
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [keyArray count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = (UITableViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
// set some cell defaults here (mainly design) ...
}
NSString *key = [keyArray objectAtIndex:indexPath.row];
NSDictionary *dictionary = [jsonDictionary objectForKey:key];
// get values from the dictionary and set the values for the displayed cell ...
return cell;
}
#Tieme: apparantly the URL you use already returns an array, you don't really need to process a dictionary (you could just use the array as the dataSource), check out the following:
SBJSON *json = [[[SBJSON alloc] init] autorelease];
NSURL *url = [NSURL URLWithString:#"http://www.my-bjoeks.nl/competitions/fetchRoutes/25.json"];
NSString *string = [[[NSString alloc] initWithContentsOfURL:url] autorelease];
NSError *jsonError = nil;
id object = [json objectWithString:string error:&jsonError];
if (!jsonError) {
NSLog(#"%#", object);
NSLog(#"%#", [object class]); // seems an array is returned, NOT a dictionary ...
}
// if you need a mutableArray for the tableView, you can convert it.
NSMutableArray *dataArray = [NSMutableArray arrayWithArray:object]
eventNameList should be defined as an NSMutableArray, not an NSMutableSet. NSMutableArray responds to both -addObject (it puts the new object at the end of the array) and -objectAtIndex: and when you think about it, a table view is essentially an ordered list and so is an array whereas a set is not.
LUCKY:)
Assuming that you might be having nsmutablearray of nsdictionary.
In such case you can get data using:
[dictionary objectforkey:#"key"] objectAtIndex:indexpath.row]

Does NSString componentsSeparatedByString: return autoreleased array?

In the following method, I'm unsure of why releasing one of the arrays leads to an exception. The only reason that I could see, would be if componentsSeparatedByString returns an autoreleased array, but I can't see that the documentation mentions that it do.
-(void)addRow:(NSString *)stringWithNumbers;
{
NSArray *numbers = [stringWithNumbers componentsSeparatedByString:#" "];
NSMutableArray *row = [[NSMutableArray alloc] initWithCapacity:[numbers count]];
for (NSString *number in numbers) {
Number *n = [[Number alloc] initWithNumber:number];
[row addObject:n];
[n release];
}
[rows addObject:row];
[row release];
// [numbers release]; <-- leads to exception
}
Can anyone confirm if the array is autoreleased? If so, how can I know/why should I have known?
Is it possible to check if any one instance of an object is autoreleased or not by code?
Yes, because the name of the method:
does not start with new
does not start with alloc
is not retain
does not contain copy
This is commonly known as the "NARC" rule, and is fully explained here: http://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmObjectOwnership.html#//apple_ref/doc/uid/20000043-SW1
unless you specifically allocate memory, a system method will give you back an autoreleased method.
By convention all methods with init or copy in their names return non-autoreleased objects.

Objective C - UITableView after calling reloadData my object properties are null/nil

I have a ViewController defined as follows:
#interface SectionController : UITableViewController {
NSMutableArray *sections;
}
- (void) LoadSections;
When LoadSection is call it makes a call to NSURLConnection to load a url which in turn calls
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
[connection release];
[responseData release];
NSDictionary *results = [responseString JSONValue];
NSMutableArray *jSections = [results objectForKey:#"Items"];
sections = [NSMutableArray array];
for (NSArray* jSection in jSections)
{
Section* section = [Section alloc];
section.Id = [jSection objectForKey:#"Id"];
section.Description = [jSection objectForKey:#"Description"];
section.Image = [jSection objectForKey:#"Image"];
section.Parent = [jSection objectForKey:#"Parent"];
section.ProductCount = [jSection objectForKey:#"ProductCount"];
[sections addObject:section];
[section release];
}
[jSections release];
[results release];
[delegate sectionsLoaded];
[self.view reloadData];
}
The data parses correctly and I now have sections filled with many items.
Calling [self.view reloadData] forces a callback to the delegate method cellForRowAtIndexPath which should then present the data into the cell however its at this point that sections is now nil again.
Can someone please point out my mistake? I must admit I am a newbie to objective c and it probably a pointer issue. What is need to do is retain the value of sections after calling reloadData.
Many thanks.
Seeing the new code the problem is obvious:
sections = [NSMutableArray array];
should become
[sections release];
sections = [[NSMutableArray alloc] init];
note that the array does not become again "nil", is instead deallocated and you get an invalid reference, which might (should) generate a crash on dereferencing.
I suggest you to read some articles on reference counted memory management as it might be not obvious if you are new to Objective-C, and often leads to mistake (i.e: autorelease is not magic at all)
best way to avoid all memory leaks here is just simply use #property (nonatomic, retain) NSMutableArray *sections; by using property you can be sure that all men management works will be correctly managed by system. Just don't forget that property retains value when you doing setSections:, so that you need to pass autoreleased object here.
self.sections = [NSMutableArray array];
...
[self.sections addObject:section];
Also to avoid all problem try to make all objects which should live only in this method autorelease. Like this:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSString *responseString = [[[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding] autorelease];
NSDictionary *results = [responseString JSONValue];
NSMutableArray *jSections = [results objectForKey:#"Items"];
self.sections = [NSMutableArray array];
for (NSArray* jSection in jSections) {
Section* section = [[[Section alloc] init] autorelease];
section.Id = [jSection objectForKey:#"Id"];
section.Description = [jSection objectForKey:#"Description"];
section.Image = [jSection objectForKey:#"Image"];
section.Parent = [jSection objectForKey:#"Parent"];
section.ProductCount = [jSection objectForKey:#"ProductCount"];
[self.sections addObject:section];
}
[delegate sectionsLoaded];
[self.view reloadData];
}
And also most of object you trying to release already autoreleased:
all params passed into your method shouldn't be released manually, check I think JSONValue also should returns autoreleased object and anything you getting by enumerating or by call objectForKey:

Adding an object to an array read from a file

There is nothing in the file, but I add row1 to my array right after. NSLog tells me that the array is empty. Why isn't row1 added to the array? All of my other code is fine as far as I can tell. My app worked when I put hard values into the array. Now that I'm loading from a file, it doesn't work.
- (void)viewDidLoad {
//array value
//NSMutableArray *array;
NSString *filePath = [self dataFilePath];
if ([[NSFileManager defaultManager] fileExistsAtPath:filePath])
{
NSMutableArray *array/*array*/ = [[NSMutableArray alloc] initWithContentsOfFile:filePath];
NSDictionary *row1 = [[NSDictionary alloc] initWithObjectsAndKeys:#"Study", #"Task", #"2 hours", #"Length", #"4", #"Hours", #"0", #"Minutes", #"15", #"Tiredness", nil];
[array addObject: row1];
self.tasks = array;
NSLog(#"The contents of the array (file exists) is %#", array);
[array release];
[myTableView reloadData];
}
UIApplication *app = [UIApplication sharedApplication];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(applicationWillTerminate:)
name:UIApplicationWillTerminateNotification
object:app];
[super viewDidLoad];
}
Please help!
Thanks in advance,
Matt
Two possibilities I see:
The object containing this code is not correctly set up as the table's datasource.
A file exists at the path you're looking at, but its contents cannot be parsed as an array. In this case, you would hit the first branch of the if-clause, but initWithContentsOfFile: would return nil. You could easily diagnose this by checking for nil after calling that method.
make sure the tableview delegate and datasource is set properly
It is difficult to answer your question. Here are two suggestions:
When you post code here, clean it up. Remove comments and format it nicely.
Show all your code. In your code there could be many things wrong. Maybe the self.tasks property is not correct. Maybe you did not correctly setup the dataview. Maybe you did not implement the correct table view delegate methods. It is hard to tell.
Also, try ruling out the most basic things. For example if you are in doubt whether that array is setup correctly, then simply print it:
NSLog(#"The contents of the array is %#", array);