等到NSURLConnection sendAsynchronousRequest完成

时间:2022-08-23 18:09:32

I have the following problem. I have a Model, called User. When the user now logins with Facebook, my app checks if the user exists already in the database. To not freeze the UI (since I'm coming from Android) I thought to use NSURLConnection sendAsynchronousRequest. What worked at first was the following:

我有以下问题。我有一个名为User的模型。当用户现在使用Facebook登录时,我的应用程序会检查用户是否已存在于数据库中。为了不冻结UI(因为我来自Android),我想使用NSURLConnection sendAsynchronousRequest。最初起作用的是:

My User Model had a method to do the whole task of the AsynchronousRequest and then when finished would set a variable to loading. Then other classes, could simply check with while ( !user.loading ) if the Request was finished or not. The problem that came here to me, was, that now, I had to put this method in every Model. So instead of this, I created a new Class HTTPPost. This class now has the method that gets an NSDictionary passed and returns one. This works ALMOST. The problem I was now encountering is, that I couldn't really determine if the process was finished or not. So I started to create a new class called Globals and use global Variable loading. But the global variable is ALWAYS NO. So, what would be the best way to do this?

我的用户模型有一个方法来完成AsynchronousRequest的整个任务,然后在完成时将变量设置为加载。然后其他类,如果请求完成,可以简单地检查while(!user.loading)。这里遇到的问题是,现在,我必须将此方法放在每个模型中。因此,我创建了一个新的类HTTPPost而不是这个。这个类现在有一个方法,它传递NSDictionary并返回一个。这几乎可以工作。我现在遇到的问题是,我无法确定流程是否已完成。所以我开始创建一个名为Globals的新类并使用全局变量加载。但全局变量总是没有。那么,最好的方法是什么?

Here is my code:

这是我的代码:

This is where I check for the user and load it. resultDictionary is the NSDictionary where everything gets loaded in, but is always nil

这是我检查用户并加载它的地方。 resultDictionary是NSDictionary,其中所有内容都被加载,但总是为零

 [user loadModelFrom:[NSString stringWithFormat:@"WHERE facebookId='%@'", graphUser.id]];
        NSLog(@"%@", user.resultDictionary);
        if ( user.resultDictionary == nil ) {
            NSLog(@"NIL");
        } else {
            NSLog(@"NOT NIL");
        }

The problem now, is, that, since I'm sending an AsynchronousRequest, the resultDictionary is always nil. What I did before and worked was the following.

现在的问题是,因为我发送的是AsynchronousRequest,所以resultDictionary总是为nil。我以前做过的工作如下。

In my Model I had the HTTP Request and a variable named loading. Now I set loading to false until the response has been made into a NSDictionary

在我的模型中,我有HTTP请求和一个名为loading的变量。现在我将loading设置为false,直到将响应设置为NSDictionary

returnDict = [NSJSONSerialization JSONObjectWithData: [responseBody dataUsingEncoding:NSUTF8StringEncoding]
                                                                   options: NSJSONReadingMutableContainers
                                                                     error: &error];

But, then I had another problem. I had to do this in all my Models again... So I created a new Class that subclasses NSObject, that has the asynchronousRequest. This is the whole request

但是,那时我又遇到了另一个问题。我必须再次在我的所有模型中执行此操作...所以我创建了一个新的Class,它是NSObject的子类,具有asynchronousRequest。这是整个请求

-(NSDictionary *)doHttpRequest:(NSDictionary *)postDict{
    loading = NO;
    __block NSDictionary *returnDict;
    NSError *error;
    NSString *jsonString;
    NSData *jsonData = [NSJSONSerialization dataWithJSONObject:postDict
                                                       options:NSJSONWritingPrettyPrinted // Pass 0 if you don't care about the readability of the generated string
                                                         error:&error];

    if (! jsonData) {
        NSLog(@"Got an error: %@", error);
    } else {
        jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
    }

    NSURL *aUrl = [NSURL URLWithString:@"http://xx.xx-xx.xx/xx.xx"];
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:aUrl
                                                           cachePolicy:NSURLRequestUseProtocolCachePolicy
                                                       timeoutInterval:60.0];
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    NSString *authStr = [NSString stringWithFormat:@"%@:%@", @"xx", @"xx"];
    NSData *authData = [authStr dataUsingEncoding:NSASCIIStringEncoding];
    NSString *authValue = [NSString stringWithFormat:@"Basic %@", [authData base64EncodedString]];
    [request setValue:authValue forHTTPHeaderField:@"Authorization"];
    [request setValue:@"application/json" forHTTPHeaderField:@"Content-type"];
    [request setHTTPMethod:@"POST"];
    [request setHTTPBody:[jsonString dataUsingEncoding:NSUTF8StringEncoding]];

    [NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
     {
         NSString *responseBody = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
         returnDict             = [NSJSONSerialization JSONObjectWithData: [responseBody dataUsingEncoding:NSUTF8StringEncoding]
                                                                   options: NSJSONReadingMutableContainers
                                                                     error: &error];

     }];
    [queue waitUntilAllOperationsAreFinished];
    loading = YES;
    return returnDict;
}

As you can see I have now a variable called loading. It is a global variable. But somehow, the variable is always NO.

如你所见,我现在有一个名为loading的变量。这是一个全局变量。但不知何故,变量始终为NO。

What would be the best way to do this? I hope I'm understandable, I'm new to Objective-C, and English isn't my native language.

最好的方法是什么?我希望我能理解,我是Objective-C的新手,英语不是我的母语。

UPDATE

UPDATE

I modified the code to look like a user provided here, but still not working!

我将代码修改为看起来像这里提供的用户,但仍然无法正常工作!

HTTPPost.h
-(void)doHttpRequest:(NSDictionary *)postDict completion:(void(^)(NSDictionary *dict, NSError *error))completion {
    __block NSDictionary *returnDict;
    NSError *error;
    NSString *jsonString;
    NSString *authValue;
    NSString *authStr;

    NSData *jsonData;
    NSData *authData;
    NSURL *aUrl;
    NSMutableURLRequest *request;
    NSOperationQueue *queue;


    jsonData = [NSJSONSerialization dataWithJSONObject:postDict
                                                       options:NSJSONWritingPrettyPrinted
                                                         error:&error];

    if (! jsonData) {
        NSLog(@"Got an error: %@", error);
    } else {
        jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
    }

    aUrl        = [NSURL URLWithString:@"http://xx.xx-xx.com/xx.php"];
    request     = [NSMutableURLRequest  requestWithURL:aUrl
                                           cachePolicy:NSURLRequestUseProtocolCachePolicy
                                           timeoutInterval:60.0];

    queue       = [[NSOperationQueue alloc] init];
    authStr     = [NSString stringWithFormat:@"%@:%@", @"xx", @"xx"];
    authData    = [authStr dataUsingEncoding:NSASCIIStringEncoding];
    authValue   = [NSString stringWithFormat:@"Basic %@", [authData base64EncodedString]];
    [request setValue:authValue forHTTPHeaderField:@"Authorization"];
    [request setValue:@"application/json" forHTTPHeaderField:@"Content-type"];
    [request setHTTPMethod:@"POST"];
    [request setHTTPBody:[jsonString dataUsingEncoding:NSUTF8StringEncoding]];

    [NSURLConnection sendAsynchronousRequest:request queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
     {
         NSString *responseBody = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
         returnDict             = [NSJSONSerialization JSONObjectWithData: [responseBody dataUsingEncoding:NSUTF8StringEncoding]
                                                                  options: NSJSONReadingMutableContainers
                                                                    error: &error];
         if ( completion ) {
             completion(returnDict, error);
         }
     }];
}

//User.h
[_httpPost doHttpRequest:_dbDictionary completion:^(NSDictionary *dict, NSError *error) {
    NSLog(@"completed") // NEVER GETS FIRED
}];

3 个解决方案

#1


9  

It seems that you're trying to take an asynchronous process (sendAsynchronousRequest) , and make it behave like a synchronous process (i.e. you appear to want to wait for it). You should not do that. You should to embrace the asynchronous patterns rather than fighting them.

您似乎正在尝试采用异步进程(sendAsynchronousRequest),并使其行为类似于同步进程(即您似乎希望等待它)。你不应该这样做。你应该接受异步模式而不是对抗它们。

The sendAsynchronousRequest method has a completion block that specifies what you want to do when the request is done. Do not try to put the code after the block and (try to) wait for the block to complete, but rather put any of your code that is dependent upon the completion of the network request inside the completion block, or have the completion block call your code.

sendAsynchronousRequest方法有一个完成块,用于指定请求完成后要执行的操作。不要尝试将代码放在块之后并(尝试)等待块完成,而是将任何依赖于完成块内网络请求完成的代码放入或完成块调用你的代码。

A common way would be to give your own methods their own completion blocks and then call those blocks in the completionHandler of sendAsynchronousRequest, something like:

一种常见的方法是为自己的方法提供自己的完成块,然后在sendAsynchronousRequest的completionHandler中调用这些块,如:

- (void)performHttpRequest:(NSDictionary *)postDict completion:(void (^)(NSDictionary *dictionary, NSError *error))completion
{
    // prepare the request

    // now issue the request

    [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
        if (error) {
            if (completion)
                completion(data, error);
        } else {
            NSString *responseBody = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
            returnDict             = [NSJSONSerialization JSONObjectWithData:data
                                                                     options: NSJSONReadingMutableContainers
                                                                      error: &error];
            if (completion)
                completion(returnDict, error);
    }];
}

Now, when you want to perform your request, you simply do:

现在,当您想要执行请求时,您只需执行以下操作:

[self performHttpRequest:someDictionary completion:^(NSDictionary *dictionary, NSError *error) {
    if (error) {
        // ok, handle the error here
    } else {
        // ok, use the `dictionary` results as you see fit here
    }
];

Note, the method that calls this performHttpRequest (let's imagine you called it from loadModelFrom) now behaves asynchronously, itself. So you might want to employ this completion-block pattern again, e.g. adding your own completion block parameter to loadModelFrom, and then invoke that block in the completion handler loadModelFrom passes to performHttpRequest.

请注意,调用此performHttpRequest的方法(让我们假设您从loadModelFrom调用它)现在本身就是异步行为。因此,您可能希望再次使用此完成块模式,例如将您自己的完成块参数添加到loadModelFrom,然后在完成处理程序loadModelFrom中调用该块传递给performHttpRequest。

But hopefully you get the idea: Never try to wait for a completion block, but rather just put inside that block anything you want it to do when its done. Whether you use AFNetworking (which I'd advise), or continue to use sendAsynchronousRequest, this is a very useful pattern with which you should become familiar.

但希望你能得到这样的想法:永远不要试图等待一个完成块,而只是把它完成后放在那个块里面你想要它做什么。无论您使用AFNetworking(我建议使用),还是继续使用sendAsynchronousRequest,这都是您应该熟悉的非常有用的模式。


Update:

更新:

The revised code sample (largely) works great for me. Seeing your revised question, a couple of observations:

修改后的代码示例(很大程度上)对我很有用。看到你修改过的问题,有几点意见:

  1. I am not familiar with this base64EncodedString method. In iOS 7, there is the native base64EncodedStringWithOptions method (or for earlier iOS versions use base64Encoding). Or are you using a third party base-64 NSData category?

    我不熟悉这个base64EncodedString方法。在iOS 7中,存在本机base64EncodedStringWithOptions方法(或者对于早期iOS版本使用base64Encoding)。或者您使用的是第三方base-64 NSData类别?

  2. There's no point in creating jsonString, only to then convert it back to a NSData. Just use jsonData in your request.

    创建jsonString没有意义,只能将其转换回NSData。只需在请求中使用jsonData即可。

    The same is true with responseBody: Why convert to string only to convert back to NSData?

    responseBody也是如此:为什么只转换为字符串才能转换回NSData?

  3. There's no point in having returnDict to be defined as __block outside the sendAsynchronousRequest block. Just define it inside that block and the __block qualifier is then no longer necessary.

    将returnDict定义为sendAsynchronousRequest块之外的__block没有意义。只需在该块内定义它,然后就不再需要__block限定符。

  4. Why create a NSOperationQueue for the completionHandler of sendAsynchronousRequest? Unless I'm doing something really slow that merits running on a background queue, I just use [NSOperationQueue mainQueue], because you invariably want to update the app's model or UI (or both), and you want to do that sort of stuff on the main queue.

    为什么要为sendAsynchronousRequest的completionHandler创建一个NSOperationQueue?除非我做的事情非常慢,值得在后台队列上运行,我只是使用[NSOperationQueue mainQueue],因为你总是希望更新应用程序的模型或UI(或两者),并且你想要做那种东西主队列。

    The request still runs asynchronously but the queue parameter just specifies which queue the completion block will run on.

    请求仍然以异步方式运行,但queue参数只指定完成块将在哪个队列上运行。

  5. By the way, in sendAsynchronousRequest, you aren't checking to see if the request succeeded before proceeding with JSONObjectWithData. If the request failed, you could theoretically be losing the NSError object that it returned. You really should check to make sure the request succeeded before you try to parse it.

    顺便说一下,在sendAsynchronousRequest中,在继续使用JSONObjectWithData之前,您不会检查请求是否成功。如果请求失败,理论上可能会丢失它返回的NSError对象。在尝试解析请求之前,您确实应该检查以确保请求成功。

    Likewise, when you originally dataWithJSONObject the parameters in postDict, you really should check for success, and if not, report the error and quit.

    同样,当你最初使用postWict中的dataWithJSONObject参数时,你真的应该检查是否成功,如果没有,则报告错误并退出。

  6. I notice that you're using the NSJSONReadingMutableContainers option. If you really need a mutable response, I'd suggest making that explicit in your block parameters (replacing all the NSDictionary references with NSMutableDictionary). I assume you don't really need it to be mutable, so I therefore recommend removing the NSJSONReadingMutableContainers option.

    我注意到你正在使用NSJSONReadingMutableContainers选项。如果你真的需要一个可变的响应,我建议在你的块参数中明确表示(用NSMutableDictionary替换所有的NSDictionary引用)。我假设你并不真的需要它是可变的,因此我建议删除NSJSONReadingMutableContainers选项。

    Likewise, when creating the JSON, you don't need to use the NSJSONWritingPrettyPrinted option. It only makes the request unnecessary larger.

    同样,在创建JSON时,您不需要使用NSJSONWritingPrettyPrinted选项。它只会使请求变得更大。

Combining all of this, that yields:

结合所有这些,产生:

-(void)performHttpRequest:(NSDictionary *)postDict completion:(void(^)(NSDictionary *dict, NSError *error))completion {
    NSError *error;
    NSString *authValue;
    NSString *authStr;

    NSData *jsonData;
    NSData *authData;
    NSURL *aUrl;
    NSMutableURLRequest *request;

    jsonData = [NSJSONSerialization dataWithJSONObject:postDict options:0 error:&error];

    if (!jsonData) {
        if (completion)
            completion(nil, error);
        return;
    }

    aUrl        = [NSURL URLWithString:@"...."];
    request     = [NSMutableURLRequest requestWithURL:aUrl
                                          cachePolicy:NSURLRequestUseProtocolCachePolicy
                                      timeoutInterval:60.0];

    authStr     = [NSString stringWithFormat:@"%@:%@", @"xx", @"xx"];
    authData    = [authStr dataUsingEncoding:NSASCIIStringEncoding];
    if ([authData respondsToSelector:@selector(base64EncodedStringWithOptions:)])
        authValue   = [NSString stringWithFormat:@"Basic %@", [authData base64EncodedStringWithOptions:0]];
    else
        authValue   = [NSString stringWithFormat:@"Basic %@", [authData base64Encoding]]; // if only supporting iOS7+, you don't need this if-else logic and you can just use base64EncodedStringWithOptions
    [request setValue:authValue forHTTPHeaderField:@"Authorization"];
    [request setValue:@"application/json" forHTTPHeaderField:@"Content-type"];
    [request setHTTPMethod:@"POST"];
    [request setHTTPBody:jsonData];

    [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
     {
         if (!data) {
             if (completion)
                 completion(nil, error);
             return;
         }

         NSError *parseError = nil;
         NSDictionary *returnDict = [NSJSONSerialization JSONObjectWithData:data options:0 error:&parseError];

         if (completion) {
             completion(returnDict, parseError);
         }
     }];
}

And if this is being called from another method that needs to handle the fact that this is happening asynchronously, then it would employ a completion block pattern, too:

如果从另一个需要处理异步发生这种事实的方法调用它,那么它也会使用完成块模式:

- (void)authenticateUser:(NSString *)userid password:(NSString *)password completion:(void (^)(BOOL success))completion
{
    NSDictionary *dictionary = @{ ... };

    [self performHttpRequest:dictionary completion:^(NSDictionary *dict, NSError *error) {
        if (error) {
            completion(NO);
            return;
        }

        // now validate login by examining resulting dictionary

        BOOL success = ...;

        // and call this level's completion block

        completion(success);
    }];
}

Then the view controller might access that method with something like:

然后视图控制器可以使用以下内容访问该方法:

// maybe add UIActivityIndicatorView here

[self.userModel authenticateUser:self.userTextField.text password:self.passwordTextField.text completion:^(BOOL success) {
    // remove UIActivityIndicatorView here
    if (success) {
        // do whatever you want if everything was successful, maybe segue to another view controller
    } else {
        // show the user an alert view, letting them know that authentication failed and let them try again
    }
}];

#2


0  

After seeing you adding specific code to handle request and its responses, I would point out that you should try using AFNetworking. It abstracts out lots of boiler plate code.

在看到您添加特定代码以处理请求及其响应后,我会指出您应该尝试使用AFNetworking。它抽象出了大量的锅炉板代码。

As you mentioned, you are new to obj-c, it may take some time to understand AFNetworking but in long run, it will save you lots of headache. Plus it is one of the widely used open source for network related stuff.

正如你所提到的,你是obj-c的新手,可能需要一些时间来了解AFNetworking,但从长远来看,它将为你节省很多麻烦。此外,它是广泛使用的网络相关的开源之一。

I hope this would be helpful.

我希望这会有所帮助。

#3


0  

If you want to wait for a request, then you should not use sendAsynchronousRequest. Use sendSynchonousRequest instead. That's where it's made for:

如果要等待请求,则不应使用sendAsynchronousRequest。请改用sendSynchonousRequest。这就是它的用途:

NSURLResponse *response;
NSError * error;    
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];

But, the UI is blocked when the synchronous call is made. I doubt if that is what you want.

但是,在进行同步调用时会阻止UI。我怀疑这是不是你想要的。

#1


9  

It seems that you're trying to take an asynchronous process (sendAsynchronousRequest) , and make it behave like a synchronous process (i.e. you appear to want to wait for it). You should not do that. You should to embrace the asynchronous patterns rather than fighting them.

您似乎正在尝试采用异步进程(sendAsynchronousRequest),并使其行为类似于同步进程(即您似乎希望等待它)。你不应该这样做。你应该接受异步模式而不是对抗它们。

The sendAsynchronousRequest method has a completion block that specifies what you want to do when the request is done. Do not try to put the code after the block and (try to) wait for the block to complete, but rather put any of your code that is dependent upon the completion of the network request inside the completion block, or have the completion block call your code.

sendAsynchronousRequest方法有一个完成块,用于指定请求完成后要执行的操作。不要尝试将代码放在块之后并(尝试)等待块完成,而是将任何依赖于完成块内网络请求完成的代码放入或完成块调用你的代码。

A common way would be to give your own methods their own completion blocks and then call those blocks in the completionHandler of sendAsynchronousRequest, something like:

一种常见的方法是为自己的方法提供自己的完成块,然后在sendAsynchronousRequest的completionHandler中调用这些块,如:

- (void)performHttpRequest:(NSDictionary *)postDict completion:(void (^)(NSDictionary *dictionary, NSError *error))completion
{
    // prepare the request

    // now issue the request

    [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
        if (error) {
            if (completion)
                completion(data, error);
        } else {
            NSString *responseBody = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
            returnDict             = [NSJSONSerialization JSONObjectWithData:data
                                                                     options: NSJSONReadingMutableContainers
                                                                      error: &error];
            if (completion)
                completion(returnDict, error);
    }];
}

Now, when you want to perform your request, you simply do:

现在,当您想要执行请求时,您只需执行以下操作:

[self performHttpRequest:someDictionary completion:^(NSDictionary *dictionary, NSError *error) {
    if (error) {
        // ok, handle the error here
    } else {
        // ok, use the `dictionary` results as you see fit here
    }
];

Note, the method that calls this performHttpRequest (let's imagine you called it from loadModelFrom) now behaves asynchronously, itself. So you might want to employ this completion-block pattern again, e.g. adding your own completion block parameter to loadModelFrom, and then invoke that block in the completion handler loadModelFrom passes to performHttpRequest.

请注意,调用此performHttpRequest的方法(让我们假设您从loadModelFrom调用它)现在本身就是异步行为。因此,您可能希望再次使用此完成块模式,例如将您自己的完成块参数添加到loadModelFrom,然后在完成处理程序loadModelFrom中调用该块传递给performHttpRequest。

But hopefully you get the idea: Never try to wait for a completion block, but rather just put inside that block anything you want it to do when its done. Whether you use AFNetworking (which I'd advise), or continue to use sendAsynchronousRequest, this is a very useful pattern with which you should become familiar.

但希望你能得到这样的想法:永远不要试图等待一个完成块,而只是把它完成后放在那个块里面你想要它做什么。无论您使用AFNetworking(我建议使用),还是继续使用sendAsynchronousRequest,这都是您应该熟悉的非常有用的模式。


Update:

更新:

The revised code sample (largely) works great for me. Seeing your revised question, a couple of observations:

修改后的代码示例(很大程度上)对我很有用。看到你修改过的问题,有几点意见:

  1. I am not familiar with this base64EncodedString method. In iOS 7, there is the native base64EncodedStringWithOptions method (or for earlier iOS versions use base64Encoding). Or are you using a third party base-64 NSData category?

    我不熟悉这个base64EncodedString方法。在iOS 7中,存在本机base64EncodedStringWithOptions方法(或者对于早期iOS版本使用base64Encoding)。或者您使用的是第三方base-64 NSData类别?

  2. There's no point in creating jsonString, only to then convert it back to a NSData. Just use jsonData in your request.

    创建jsonString没有意义,只能将其转换回NSData。只需在请求中使用jsonData即可。

    The same is true with responseBody: Why convert to string only to convert back to NSData?

    responseBody也是如此:为什么只转换为字符串才能转换回NSData?

  3. There's no point in having returnDict to be defined as __block outside the sendAsynchronousRequest block. Just define it inside that block and the __block qualifier is then no longer necessary.

    将returnDict定义为sendAsynchronousRequest块之外的__block没有意义。只需在该块内定义它,然后就不再需要__block限定符。

  4. Why create a NSOperationQueue for the completionHandler of sendAsynchronousRequest? Unless I'm doing something really slow that merits running on a background queue, I just use [NSOperationQueue mainQueue], because you invariably want to update the app's model or UI (or both), and you want to do that sort of stuff on the main queue.

    为什么要为sendAsynchronousRequest的completionHandler创建一个NSOperationQueue?除非我做的事情非常慢,值得在后台队列上运行,我只是使用[NSOperationQueue mainQueue],因为你总是希望更新应用程序的模型或UI(或两者),并且你想要做那种东西主队列。

    The request still runs asynchronously but the queue parameter just specifies which queue the completion block will run on.

    请求仍然以异步方式运行,但queue参数只指定完成块将在哪个队列上运行。

  5. By the way, in sendAsynchronousRequest, you aren't checking to see if the request succeeded before proceeding with JSONObjectWithData. If the request failed, you could theoretically be losing the NSError object that it returned. You really should check to make sure the request succeeded before you try to parse it.

    顺便说一下,在sendAsynchronousRequest中,在继续使用JSONObjectWithData之前,您不会检查请求是否成功。如果请求失败,理论上可能会丢失它返回的NSError对象。在尝试解析请求之前,您确实应该检查以确保请求成功。

    Likewise, when you originally dataWithJSONObject the parameters in postDict, you really should check for success, and if not, report the error and quit.

    同样,当你最初使用postWict中的dataWithJSONObject参数时,你真的应该检查是否成功,如果没有,则报告错误并退出。

  6. I notice that you're using the NSJSONReadingMutableContainers option. If you really need a mutable response, I'd suggest making that explicit in your block parameters (replacing all the NSDictionary references with NSMutableDictionary). I assume you don't really need it to be mutable, so I therefore recommend removing the NSJSONReadingMutableContainers option.

    我注意到你正在使用NSJSONReadingMutableContainers选项。如果你真的需要一个可变的响应,我建议在你的块参数中明确表示(用NSMutableDictionary替换所有的NSDictionary引用)。我假设你并不真的需要它是可变的,因此我建议删除NSJSONReadingMutableContainers选项。

    Likewise, when creating the JSON, you don't need to use the NSJSONWritingPrettyPrinted option. It only makes the request unnecessary larger.

    同样,在创建JSON时,您不需要使用NSJSONWritingPrettyPrinted选项。它只会使请求变得更大。

Combining all of this, that yields:

结合所有这些,产生:

-(void)performHttpRequest:(NSDictionary *)postDict completion:(void(^)(NSDictionary *dict, NSError *error))completion {
    NSError *error;
    NSString *authValue;
    NSString *authStr;

    NSData *jsonData;
    NSData *authData;
    NSURL *aUrl;
    NSMutableURLRequest *request;

    jsonData = [NSJSONSerialization dataWithJSONObject:postDict options:0 error:&error];

    if (!jsonData) {
        if (completion)
            completion(nil, error);
        return;
    }

    aUrl        = [NSURL URLWithString:@"...."];
    request     = [NSMutableURLRequest requestWithURL:aUrl
                                          cachePolicy:NSURLRequestUseProtocolCachePolicy
                                      timeoutInterval:60.0];

    authStr     = [NSString stringWithFormat:@"%@:%@", @"xx", @"xx"];
    authData    = [authStr dataUsingEncoding:NSASCIIStringEncoding];
    if ([authData respondsToSelector:@selector(base64EncodedStringWithOptions:)])
        authValue   = [NSString stringWithFormat:@"Basic %@", [authData base64EncodedStringWithOptions:0]];
    else
        authValue   = [NSString stringWithFormat:@"Basic %@", [authData base64Encoding]]; // if only supporting iOS7+, you don't need this if-else logic and you can just use base64EncodedStringWithOptions
    [request setValue:authValue forHTTPHeaderField:@"Authorization"];
    [request setValue:@"application/json" forHTTPHeaderField:@"Content-type"];
    [request setHTTPMethod:@"POST"];
    [request setHTTPBody:jsonData];

    [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
     {
         if (!data) {
             if (completion)
                 completion(nil, error);
             return;
         }

         NSError *parseError = nil;
         NSDictionary *returnDict = [NSJSONSerialization JSONObjectWithData:data options:0 error:&parseError];

         if (completion) {
             completion(returnDict, parseError);
         }
     }];
}

And if this is being called from another method that needs to handle the fact that this is happening asynchronously, then it would employ a completion block pattern, too:

如果从另一个需要处理异步发生这种事实的方法调用它,那么它也会使用完成块模式:

- (void)authenticateUser:(NSString *)userid password:(NSString *)password completion:(void (^)(BOOL success))completion
{
    NSDictionary *dictionary = @{ ... };

    [self performHttpRequest:dictionary completion:^(NSDictionary *dict, NSError *error) {
        if (error) {
            completion(NO);
            return;
        }

        // now validate login by examining resulting dictionary

        BOOL success = ...;

        // and call this level's completion block

        completion(success);
    }];
}

Then the view controller might access that method with something like:

然后视图控制器可以使用以下内容访问该方法:

// maybe add UIActivityIndicatorView here

[self.userModel authenticateUser:self.userTextField.text password:self.passwordTextField.text completion:^(BOOL success) {
    // remove UIActivityIndicatorView here
    if (success) {
        // do whatever you want if everything was successful, maybe segue to another view controller
    } else {
        // show the user an alert view, letting them know that authentication failed and let them try again
    }
}];

#2


0  

After seeing you adding specific code to handle request and its responses, I would point out that you should try using AFNetworking. It abstracts out lots of boiler plate code.

在看到您添加特定代码以处理请求及其响应后,我会指出您应该尝试使用AFNetworking。它抽象出了大量的锅炉板代码。

As you mentioned, you are new to obj-c, it may take some time to understand AFNetworking but in long run, it will save you lots of headache. Plus it is one of the widely used open source for network related stuff.

正如你所提到的,你是obj-c的新手,可能需要一些时间来了解AFNetworking,但从长远来看,它将为你节省很多麻烦。此外,它是广泛使用的网络相关的开源之一。

I hope this would be helpful.

我希望这会有所帮助。

#3


0  

If you want to wait for a request, then you should not use sendAsynchronousRequest. Use sendSynchonousRequest instead. That's where it's made for:

如果要等待请求,则不应使用sendAsynchronousRequest。请改用sendSynchonousRequest。这就是它的用途:

NSURLResponse *response;
NSError * error;    
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];

But, the UI is blocked when the synchronous call is made. I doubt if that is what you want.

但是,在进行同步调用时会阻止UI。我怀疑这是不是你想要的。