简体   繁体   中英

NSDictionary to NSString not working

I'm completely new to native iOS programming and have some small and big questions about it. One of my questions is the following:

I want to retrieve a user_id from a server, so I use NSURLSession . My code looks like that:

NSString *phone_nr = @"00436604056720";
__block NSString *user_id;

NSURLSession *session_json = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session_json dataTaskWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://localhost/picshare/index.php?phone_nr=%@", phone_nr]] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
   NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
   NSLog(@"%@", json);

   user_id = @"%@",[json objectForKey:@"user_id"];
}];

[dataTask resume];

The code works fine (I retrieve the correct user_id and phone_nr ) but I can't alert the user_id ... but when I NSLog it, it works great. Problem is that I can't use the user_id variable anywhere outside of the function.

The UIAlertView looks like that:

UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Headline"
                                                    message:user_id
                                                   delegate:nil
                                          cancelButtonTitle:@"OK"
                                          otherButtonTitles:nil];
NSString *phone_nr = @"00436604056720";

NSURLSession *session_json = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session_json dataTaskWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://localhost/picshare/index.php?phone_nr=%@", phone_nr]] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
   NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
   NSLog(@"%@", json);

   NSString *user_id = [json objectForKey:@"user_id"];
   dispatch_async(dispatch_get_main_queue(), ^{
   UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Headline"
                                                message:user_id
                                               delegate:nil
                                      cancelButtonTitle:@"OK"
                                      otherButtonTitles:nil];
   [alert show];
  });
}];

[dataTask resume];

Do not declare the _block variable.

Items:

  • I'm not sure I, or the compiler, parse this line:

     user_id = @"%@",[json objectForKey:@"user_id"]; 
  • user_id will only be set after the block is called, which will not be "in line" to the code. How/when do you kick off your alert?

  • user_id also risks autorelease. Is the object valid when you hit your alert?


Addendum

Responding to @CouchDeveloper 's comment, given

NSDictionary *d = @{@"compiler": @"Apple LLVM 5.1"};
NSString *s = @"%@", [d objectForKey:@"compiler"];

My compiler yields

<path elided>: error: expected identifier or '('
NSString *s = @"%@", [d objectForKey:@"compiler"];
                     ^
<path elided>: error: missing '[' at start of message send expression
NSString *s = @"%@", [d objectForKey:@"compiler"];
                      ^
                      [
<path elided>: error: expected ']'
NSString *s = @"%@", [d objectForKey:@"compiler"];
                                                 ^
<path elided>: note: to match this '['
NSString *s = @"%@", [d objectForKey:@"compiler"];
                     ^
3 errors generated.

Your compiler may differ.

Use this. I think you are not using [alert show];

UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Headline"
                                                    message:user_id
                                                   delegate:nil
                                          cancelButtonTitle:@"OK"
                                          otherButtonTitles:nil];
[alert show];

I think I got Your solution->

First for-> "Problem is that I can't use the user_id variable anywhere outside of the function"

Solution-> use NSUserDefault to save the user id from block, or you can use a property variable to store user_id

[[NSUserDefaults standardUserDefaults]
    setObject:valueToSave forKey:@"USER_ID"];

Or

@property (retain, nonatomic) NSString *user_id;

Second for-> "but I can't alert the user_id..."

Solution-> Do this inside your block to call UIAlert View

[self performSelectorOnMainThread:@selector(showAlertWithTitle:)
                           withObject:user_id
                        waitUntilDone:YES];

- (void)showAlertWithTitle:(NSString *)t
{
    [[[[UIAlertView alloc] initWithTitle:t
                                 message:nil
                                delegate:nil
                       cancelButtonTitle:@"OK"
                       otherButtonTitles:nil
    ] autorelease] show];
}

As others have mentioned, you can use UIAlertView to display the user ID to the user. However, I don't think that's your main problem here. I don't believe you have a full grasp of your variable scope or c-blocks. Let's take a look at a simple example:

__block NSString *myID = nil;
[someObject getMyID:^(NSString *returnedID){
    myID = returnedID;
    NSLog(@"%@", myID); //myID has a value here
}];

NSLog(@"%@", myID); //myID is "NULL" here

So what is happening here is you're passing to someObject a block of code it can use to return returnedID once it has acquired it. This is normally done asynchronously, which means the method/function that called getMYID doesn't wait for the value. The rest of the statements in the method are processed before someObject can return returnedID and pass it to the block you gave it. This means that myID will be set until long after the method that created it has finished. Because myID was created inside the function, it's scope is done when that method has finished. However, myID will stick around because the block you passed to someObject is keeping a reference to it. Once that block is executed, myID is destroyed and the value is lost.

Likely, what you want is an instance variable that can stick around after the method has returned. So for example, you may have a property:

@property (strong) NSString *myID;

and now in your implementation you can do:

[someObject getMyID:^(NSString *returnedID){
    self.myID = returnedID;
}];

Now the value that someObject returns will stick around because it is assigned to an instance variable who's scope will last so long as the object that called getMyID lasts. Just be aware that it won't actually be set until a bit later (whenever someObject gets around to calling the block you passed it).

If you only need the value to stick around only for the method call, you'll probably just need to put that code in the block you pass to someObject getMyID :

[someObject getMyID:^(NSString *returnedID){
    //Do stuff with returnedID;
}];

Others have mentioned dispatch_async(dispatch_get_main_queue(), ^{ //do stuff }); . The reason for this is that likely the block of code you passed to someObject won't be called on the main thread, and any code that accesses the UI (like UIAlertView ) must be called on the main thread. dispatch_async will allow you to do this.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM