NSArray losing values when I put self into it - iphone

So I have this piece of code:
- (void) connectSelector:(NSArray *)args {
NSError* error;
NSString* data = [NSString stringWithContentsOfURL:[NSURL URLWithString:[args objectAtIndex:0]] encoding:NSASCIIStringEncoding error:&error];
NSLog(#"%#", data);
NSDictionary* dictionary = [[NSDictionary alloc] initWithObjects:[NSArray arrayWithObjects:#"connector", #"data", #"error", nil] forKeys:[NSArray arrayWithObjects:self, data, error, nil]];
[[args objectAtIndex:1] performSelector:#selector(dataDownloaderDidDownloadData:) withObject:dictionary];
}
The values NSArray in dictionary is losing its 'self' value. Why is this so?
Thanks in advance!

Looks like you've got the keys and values element back-to-front. You probably meant:
NSDictionary* dictionary = [[NSDictionary alloc] initWithObjects:[NSArray arrayWithObjects:self, data, error, nil] forKeys:[NSArray arrayWithObjects:#"connector", #"data", #"error", nil] ];
Or to use modern syntax:
NSDictionary *dictionary = #{ #"connector" : self, #"data" : data, #"error" : error };
(Not every object can be used as a key in a dictionary).

Might be the case with arrayWithObjects(but not sure),because it returns an auto-released array.Use initWithObjects which returns an array you must then release to avoid a memory leak.
Check This : iPad large NSArray - initWithObjects vs. ArrayWithObjects

Related

Retrive values from NSDictionary

I am trying to retrieve the values from my NSDictionary however I am running into errors, I was wondering if someone might be able to help me with my solution.
I have 18 values, not all are shown here that I am checking, when the correct key is reached I would like to take the value in the NSDictionary and pass it into one of my NSString variables.
Below is an example of what I am trying to do however like I say I am having several issues
for (id key in searchData) {
if ([[searchData objectForKey:key] isEqualToString:#"Code"]) {
codeSeries = [searchData objectForKey:key];
}
else if ([[searchData objectForKey:key] isEqualToString:#"ID"]) {
IDSeries = [searchData objectForKey:key];
}
// ...
However, when I try to log out any of the values, they all return Null. I have checked the dictionary before hand and the values are all most definitely in there, so I am thinking there is something wrong with my code above.
Any help would be greatly appreciated.
Update
This is how I create the NSDictionary
//start of mymethod...
NSDictionary *sendSeriesDictionary = [[NSDictionary alloc] init];
// Keys for sendSeriesDictionary
NSArray *keys = [NSArray arrayWithObjects:#"Code", #"ID", nil];
// Objects for keys that are for sendSeriesDictionary
NSArray *objects = [NSArray arrayWithObjects: [NSNull null], IdString, nil];
// Add keys and objects to NSDictionary
sendSeriesDictionary = [NSDictionary dictionaryWithObjects:objects forKeys:keys];
[engineRequests SeriesSearch:sendSeriesDictionary]; // this is where I send the NSDictionary over to where I want to read it.
//end of mymethod
You've got several problems. First, you mix up keys and values (like Tom said). Second, you have a possible memory leak where you create the dictionary, or at least a unnecessary instantiation.
Try this for creating the dictionary:
// Keys for sendSeriesDictionary
NSArray *keys = [NSArray arrayWithObjects:#"Code", #"ID", nil];
// Objects for keys that are for sendSeriesDictionary
NSArray *objects = [NSArray arrayWithObjects: [NSNull null], IdString, nil];
// Add keys and objects to NSDictionary
NSDictionary *sendSeriesDictionary = [NSDictionary dictionaryWithObjects:objects forKeys:keys];
Retrieving the values can be done like this:
codeSeries = [searchData objectForKey:#"Code"];
IDSeries = [searchData objectForKey:#"ID"];
In your first loop you looped through all the keys, got their values and then compared those to the key again. Which makes no sense.
Are you mixing keys and values ?
Maybe you just want codeSeries = [searchData objectForKey:#"Code"]; ?

Create an NSDictionary of NSDictionaries

Believe it or not, I have searched the internet before asking this question. Unbelievably, I have yet to find a nice clear example of how to create an NSDictionary of NSDictionaries.
Here is my code so far, but it prints null. Any ideas?
// Here I am creating the dictionaries in the code until I start getting them from the server ;)
NSArray *keys = [NSArray arrayWithObjects:#"mission", #"target", #"distance",#"status", nil];
NSArray *objectsA = [NSArray arrayWithObjects:#"tiger", #"bill", #"5.4km", #"unknown", nil];
NSDictionary *tiger = [NSDictionary dictionaryWithObjects:objectsA
forKeys:keys];
NSArray *objectsB = [NSArray arrayWithObjects:#"bull", #"roger", #"10.1km", #"you are dead", nil];
NSDictionary *bull = [NSDictionary dictionaryWithObjects:objectsB
forKeys:keys];
NSArray *objectsC = [NSArray arrayWithObjects:#"peacock", #"geoff", #"1.4km", #"target liquidated", nil];
NSDictionary *peacock = [NSDictionary dictionaryWithObjects:objectsC
forKeys:keys];
// activeMissions = [NSArray arrayWithObjects:tiger, bull, peacock, nil];
[activeMissions setObject:tiger forKey:#"tiger"];
[activeMissions setObject:bull forKey:#"bull"];
[activeMissions setObject:peacock forKey:#"peacock"];
NSLog(#"active Missions %#", activeMissions);
You are not intializing activeMissions, that is why the NSLog statement is printing null (sending a message to a nil object in ObjC return nil).
Put this before assigning to activeMissions:
NSMutableDictionary *activeMissions = [NSMutableDictionary dictionaryWithCapacity:3];
Otherwise, if you prefer having a non mutable NSDictionary, you could do:
NSDictionary *activeMissions = [NSDictionary dictionaryWithObjects: [NSArray arrayWithObjects:tiger, bull, peacock, nil]
forKeys: [NSArray arrayWithObjects:#tiger, #"bull", #"peacock", nil]];
(Keep in mind that this is autoreleased, you'll have to retain somehow).

How to dissect and reorganize info in an NSDictionary

So I have an array of NSDictionaries, each NSDictionary has a bunch of key/value pairs pertaining to aspects of a photo (from Flickr).
I'm making an app that has a UITableViewController whose cells should be each of the different categories of the photos. So in pseudocode, I'm trying to construct a new NSDictionary (with keys being categories of photos, values being the NSDictionaries of the photos that contains that key). I'm iterating through each NSDictionary in the initial array, getting the category tags, and saying, if my new NSDict doesn't contain this key, make a new key to an empty array. Then add the current NSDict to that array. I'm getting consistent errors, not sure why.
Here's the diluted code.
photoList = [FlickrFetcher photosWithTags:[NSArray arrayWithObjects: #"CS193p_SPoT", nil]];
NSLog(#"%#", photoList);
categories = [[NSDictionary alloc] init];
NSArray *temp = [[NSArray alloc] init];
for (id obj in photoList) {
temp = [[obj objectForKey:#"tags"] componentsSeparatedByString:#" "];
for (id string in temp) {
if (![categories objectForKey:string]) {
NSMutableArray *arr = [[NSMutableArray alloc] init];
[categories setObject:arr forKey:string];
//[arr release];
}
NSMutableArray *photos = [categories objectForKey:string];
[photos addObject:obj];
[categories setObject:photos forKey:string];
}
}
Thanks!
NSDictionary doesn't have a method setObject:forKey:. You need an NSMutableDictionary.
self.categories = [NSMutableDictionary dictionary];
Other than that, please do use Joost's excellent rewrite of your code.
SIGABRT, just so you know, most likely means that an assertion somewhere failed. In this case, it may be an assertion all the way down in CoreFoundation*; CF checks for mutability when you try to access a dictionary like that and causes an interrupt if the object isn't mutable.
*I have just learned about the CF source's availability recently and have been looking through it, so this may be just "new thing" bias and incorrect.
I don't notice any errors (syntax-errors, that is) in your code, however here is an updated piece of code which has been implemented a bit cleaner (and without memory leaks)
self.photoList = [FlickrFetcher photosWithTags:[NSArray arrayWithObjects: #"CS193p_SPoT", nil]];
NSLog(#"%#", photoList);
self.categories = [NSDictionary dictionary];
for (NSDictionary *obj in photoList) {
NSArray *temp = [[obj objectForKey:#"tags"] componentsSeparatedByString:#" "];
for (NSString *string in temp) {
NSMutableArray *photos = [categories objectForKey:string];
if (!photos) {
photos = [NSMutableArray array];
[categories setObject:photos forKey:string];
}
[photos addObject:obj];
}
}
If it's not working please tell us the exact warning, and were it is caused.

Using objective-c objects with an NSDictionary

I want store a URL against a UILabel so that when a user touches the label it takes them to that URL in a UIWebView.
I have declared a NSDictionary like so:
NSMutableArray *linksArray = [[NSMutableArray alloc] init];
[linksArray addObject: [NSValue valueWithNonretainedObject: newsItem1ReadMoreLabel]];
[linksArray addObject: [NSValue valueWithNonretainedObject: newsItem2ReadMoreLabel]];
[linksArray addObject: [NSValue valueWithNonretainedObject: newsItem3ReadMoreLabel]];
[linksArray addObject: [NSValue valueWithNonretainedObject: newsItem4ReadMoreLabel]];
[linksArray addObject: [NSValue valueWithNonretainedObject: newsItem5ReadMoreLabel]];
//NSString *ageLink = #"http://www.theage.com.au";
NSArray *defaultLinks = [NSArray arrayWithObjects: #"1", #"2", #"3", #"4", #"5", nil];
self.urlToLinkDictionary = [[NSMutableDictionary alloc] init];
self.urlToLinkDictionary = [NSDictionary dictionaryWithObjects:defaultLinks forKeys:linksArray];
Considering I used a NSValue as the key, how do I get/set the URL associated with that key given that I only have references to the UILabels?
this is what I have but it doesn't work:
for(NSValue *key in [self.urlToLinkDictionary allKeys])
{
if ([key nonretainedObjectValue] == linkedLabel)
{
[self.urlToLinkDictionary setValue:[newsItem link] forKey: key];
}
}
but I get an error: "objc_exception_throw" resolved
These two lines are the cause of the issue:
self.urlToLinkDictionary = [[NSMutableDictionary alloc] init];
self.urlToLinkDictionary = [NSDictionary dictionaryWithObjects:defaultLinks forKeys:linksArray];
First you assign a mutable array to the property. Then you assign a different immutable array to the property. Your original mutable array leaks because you don't release it.
The exception is being caused by this line:
[self.urlToLinkDictionary setValue:[newsItem link] forKey: key];
By the time you get to it, self.urlToLinkDictionary is an immutable dictionary. You can't change it.
There are other problems:
linksArray leaks because you never release it.
newsItem1ReadMoreLabel etc. What type are they? Why are you wrapping them in values?
setValue:forKey: is part of key value coding. On a mutable dictionary it works, but the correct method for accessing objects by keys is setObject:forKey:
it seems a bit pointless to search a dictionary by doing a linear search through its keys.
Why not try a more simplified approach. Associate a tag with every label. The tag is a in integer value and you can use it to locate the url in an array.
So code for creating the array.
NSMutableArray* arrayURLs = [NSMutableArray arrayWithCapacity:2];
newsItem1ReadMoreLabel.tag = 0;
[arrayURLs insertObject:urlStringforLabel1 atIndex:newsItem1ReadMoreLabel.tag];
newsItem2ReadMoreLabel.tag = 1;
[arrayURLs insertObject:urlStringforLabel2 atIndex:newsItem2ReadMoreLabel.tag];
then you can access the urlstring for the appropriate label
NSString* url = [arrayURLs objectAtIndex:labelClicked.tag];

Can I access the keychain on the iPhone?

This question discusses encrypting data on the iPhone using the crypt() function. As an alternative, is there a keychain on the iPhone and if so, what code would I use to access it in order to store login details and then retrieve them for us in an application?
One other thing to note: the keychain APIs don't work in the simulator when using older versions (2.x, 3.x) of the iPhone SDK. This could save you a lot of frustration when testing!
There is a keychain you can use - for code, the best bet is to check out the GenericKeychain sample application from Apple:
GenericKeychain sample
I really like Buzz Anderson's Keychain abstraction layer and I eagerly await Jens Alfke's MYCrypto to reach a usable state. The latter does a competent job of allowing use on Mac OS X and the iPhone using the same code, though its features only mimic a small subset of the Keychain's.
Here is what i use to store Key/Value pairs in the keychain. Make sure to add Security.framework to your project
#import <Security/Security.h>
// -------------------------------------------------------------------------
-(NSString *)getSecureValueForKey:(NSString *)key {
/*
Return a value from the keychain
*/
// Retrieve a value from the keychain
NSDictionary *result;
NSArray *keys = [[[NSArray alloc] initWithObjects: (NSString *) kSecClass, kSecAttrAccount, kSecReturnAttributes, nil] autorelease];
NSArray *objects = [[[NSArray alloc] initWithObjects: (NSString *) kSecClassGenericPassword, key, kCFBooleanTrue, nil] autorelease];
NSDictionary *query = [[NSDictionary alloc] initWithObjects: objects forKeys: keys];
// Check if the value was found
OSStatus status = SecItemCopyMatching((CFDictionaryRef) query, (CFTypeRef *) &result);
[query release];
if (status != noErr) {
// Value not found
return nil;
} else {
// Value was found so return it
NSString *value = (NSString *) [result objectForKey: (NSString *) kSecAttrGeneric];
return value;
}
}
// -------------------------------------------------------------------------
-(bool)storeSecureValue:(NSString *)value forKey:(NSString *)key {
/*
Store a value in the keychain
*/
// Get the existing value for the key
NSString *existingValue = [self getSecureValueForKey:key];
// Check if a value already exists for this key
OSStatus status;
if (existingValue) {
// Value already exists, so update it
NSArray *keys = [[[NSArray alloc] initWithObjects: (NSString *) kSecClass, kSecAttrAccount, nil] autorelease];
NSArray *objects = [[[NSArray alloc] initWithObjects: (NSString *) kSecClassGenericPassword, key, nil] autorelease];
NSDictionary *query = [[[NSDictionary alloc] initWithObjects: objects forKeys: keys] autorelease];
status = SecItemUpdate((CFDictionaryRef) query, (CFDictionaryRef) [NSDictionary dictionaryWithObject:value forKey: (NSString *) kSecAttrGeneric]);
} else {
// Value does not exist, so add it
NSArray *keys = [[[NSArray alloc] initWithObjects: (NSString *) kSecClass, kSecAttrAccount, kSecAttrGeneric, nil] autorelease];
NSArray *objects = [[[NSArray alloc] initWithObjects: (NSString *) kSecClassGenericPassword, key, value, nil] autorelease];
NSDictionary *query = [[[NSDictionary alloc] initWithObjects: objects forKeys: keys] autorelease];
status = SecItemAdd((CFDictionaryRef) query, NULL);
}
// Check if the value was stored
if (status != noErr) {
// Value was not stored
return false;
} else {
// Value was stored
return true;
}
}
It is worth noting that these key/values will not get deleted if the user deletes your app. If a user deletes your app, then reinstalls it, the key/values will still be accessible.
Also remember that when generating an AppID, if you want more than one application to access the same Keychain information, you have to generate a wild card AppID (#####.com.prefix.*)...
With the latest version 1.2 of the GenericKeychain sample Apple provides a keychain wrapper that also runs on the iPhone Simulator. Check out at this article for details: http://dev-metal.blogspot.com/2010/08/howto-use-keychain-in-iphone-sdk-to.html
Here is one more good wrapper class from Mr.Granoff
https://github.com/granoff/Lockbox
Thanks