gpt4 book ai didi

ios - 等待直到NSURLConnection sendAsynchronousRequest完成

转载 作者:可可西里 更新时间:2023-11-01 04:23:31 26 4
gpt4 key购买 nike

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

我的用户模型有一种方法可以完成AsynchronousRequest的全部任务,然后在完成后将设置一个变量以进行加载。然后其他类(class),可以简单地检查while ( !user.loading )(如果请求已完成)。我遇到的问题是,现在,我不得不在每个模型中都使用此方法。因此,代替此,我创建了一个新的Class HTTPPost。此类现在具有一种方法,该方法获取传递的NSDictionary并返回一个。这工作几乎。我现在遇到的问题是,我无法真正确定该过程是否完成。因此,我开始创建一个名为Globals的新类,并使用全局变量loading。但是全局变量始终为NO。那么,什么是最好的方法呢?

这是我的代码:

这是我检查用户并加载它的地方。 resultDictionary是加载所有内容的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始终为nil。我之前做过的工作是以下内容。

在我的模型中,我有HTTP请求和一个名为 loading的变量。现在,我将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的变量。它是一个全局变量。但是以某种方式,该变量始终为NO。

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

更新

我修改了代码,使其看起来像此处提供的用户,但仍然无法正常工作!
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),并使它的行为类似于同步过程(即,您似乎想等待它)。你不应该那样做。您应该接受异步模式,而不是与之抗争。sendAsynchronousRequest方法具有一个完成块,该块指定完成请求后要执行的操作。不要尝试将代码放在块之后,并(等待)等待该块完成,而应将依赖于网络请求完成的任何代码放入完成块中,或者进行完成块调用您的代码。
一种常见的方法是为自己的方法提供自己的完成块,然后在completionHandlersendAsynchronousRequest中调用这些块,例如:

- (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,这都是您应该熟悉的非常有用的模式。

更新:
修改后的代码示例(大部分)对我很有用。看到修改后的问题,有以下几点观察:
  • 我对base64EncodedString方法不熟悉。在iOS 7中,存在 native 的base64EncodedStringWithOptions方法(或对于较早的iOS版本,请使用base64Encoding)。还是使用第三方base-64 NSData类别?
  • 创建jsonString毫无意义,仅是将其转换回NSData即可。只需在您的请求中使用jsonDataresponseBody也是如此:为什么只转换为字符串才能转换回NSData
  • 没有必要将returnDict定义为__block块之外的sendAsynchronousRequest。只需在该块中定义它,就不再需要__block限定符。
  • 为什么要为NSOperationQueuecompletionHandler创建sendAsynchronousRequest?除非我在后台队列上执行的操作真的很慢,否则我只使用[NSOperationQueue mainQueue],因为您总是要更新应用程序的模型或UI(或两者),并且想要在主队列上执行这种操作。
    该请求仍然异步运行,但是queue参数仅指定完成块将在哪个队列上运行。
  • 顺便说一句,在sendAsynchronousRequest中,在继续进行JSONObjectWithData之前,您无需检查请求是否成功。如果请求失败,则理论上您可能会丢失它返回的NSError对象。在尝试解析请求之前,您确实应该检查以确保请求成功。
    同样,当您最初使用dataWithJSONObject设置postDict中的参数时,您确实应该检查是否成功,如果不成功,请报告错误并退出。
  • 我注意到您正在使用NSJSONReadingMutableContainers选项。如果您确实需要可变的响应,建议您在您的块参数中明确指出(用NSDictionary替换所有NSMutableDictionary引用)。我认为您实际上并不需要可变,因此建议删除NSJSONReadingMutableContainers选项。
    同样,在创建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);
    }];
    }
    然后, View Controller 可能会通过以下方式访问该方法:
    // 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
    }
    }];

    关于ios - 等待直到NSURLConnection sendAsynchronousRequest完成,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21735112/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com