等待 NSURLConnection sendAsynchronousRequest 完成

2024-01-06

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

我的用户模型有一个方法来完成整个任务AsynchronousRequest然后完成后将设置一个变量来加载。然后其他类,可以简单地检查while ( !user.loading )请求是否完成。我遇到的问题是,现在我必须将此方法放入每个模型中。所以我创建了一个新的类来代替这个HTTPPost。这个类现在有一个方法可以获取NSDictionary通过并返回一个。这几乎有效。我现在遇到的问题是,我无法真正确定该过程是否完成。所以我开始创建一个名为Globals并使用全局变量loading。但全局变量始终为“否”。那么,最好的方法是什么?

这是我的代码:

这是我检查用户并加载它的地方。resultDictionary is the NSDictionary一切都被加载进去,但总是nil

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

现在的问题是,由于我正在发送 AsynchronousRequest,因此 resultDictionary 始终为零。我之前所做的和工作的内容如下。

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

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

但是,然后我遇到了另一个问题。我必须在所有模型中再次执行此操作...因此我创建了一个新类,该类是 NSObject 的子类,它具有异步请求。这是整个请求

-(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;
}

正如你所看到的,我现在有一个名为loading。它是一个全局变量。但不知何故,变量总是“否”。

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

UPDATE

我修改了代码,使其看起来像此处提供的用户,但仍然无法正常工作!

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
}];

看来您正在尝试采用异步过程(sendAsynchronousRequest) ,并使其表现得像同步进程(即您似乎想要等待它)。你不应该那样做。您应该拥抱异步模式而不是与之对抗。

The sendAsynchronousRequest方法有一个完成块,指定请求完成后要执行的操作。不要尝试输入代码after块并(尝试)等待块完成,而是放置依赖于网络请求完成的任何代码inside完成块,或者让完成块调用您的代码。

一种常见的方法是为您自己的方法提供自己的完成块,然后在completionHandler of sendAsynchronousRequest, 就像是:

- (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);
    }];
}

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

[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
    }
];

注意,调用这个方法performHttpRequest(假设你是从loadModelFrom ) 现在本身的行为是异步的。因此,您可能想再次使用这种完成块模式,例如添加您自己的completion块参数为loadModelFrom,然后在完成处理程序中调用该块loadModelFrom传递到performHttpRequest.

但希望你明白了:永远不要尝试等待完成块,而只是在该块中放入你希望它完成后执行的任何操作。无论您使用 AFNetworking(我建议),还是继续使用sendAsynchronousRequest,这是一个非常有用的模式,您应该熟悉它。


Update:

修改后的代码示例(很大程度上)对我来说非常有用。看到您修改后的问题,有一些观察结果:

  1. 我对此不熟悉base64EncodedString方法。在 iOS 7 中,有原生的base64EncodedStringWithOptions方法(或者对于早期的 iO​​S 版本,使用base64Encoding)。或者您是否使用第三方 base-64NSData类别?

  2. 创造没有意义jsonString,然后将其转换回NSData。只需使用jsonData在你的要求中。

    同样的道理responseBody:为什么转换为字符串只是为了转换回NSData?

  3. 拥有没有意义returnDict定义为__block之外的sendAsynchronousRequest堵塞。只需在该块内定义它即可__block那么就不再需要限定符了。

  4. 为什么要创建一个NSOperationQueue为了completionHandler of sendAsynchronousRequest?除非我正在做一些非常慢的事情,值得在后台队列上运行,否则我只是使用[NSOperationQueue mainQueue],因为您总是想要更新应用程序的模型或 UI(或两者),并且您想要在主队列上执行此类操作。

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

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

    同样,当你最初dataWithJSONObject中的参数postDict,你确实应该检查是否成功,如果没有,报告错误并退出。

  6. 我注意到你正在使用NSJSONReadingMutableContainers选项。如果您确实需要可变响应,我建议在您的块参数中明确说明(替换所有NSDictionary参考文献与NSMutableDictionary)。我认为您并不真正需要它是可变的,因此我建议删除NSJSONReadingMutableContainers option.

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

结合所有这些,得出:

-(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);
         }
     }];
}

如果这是从另一个需要处理异步发生的方法调用的,那么它也将采用完成块模式:

- (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);
    }];
}

然后视图控制器可能会通过以下方式访问该方法:

// 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
    }
}];
         
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

等待 NSURLConnection sendAsynchronousRequest 完成 的相关文章

随机推荐