我有以下问题。我有一个模型,叫做用户。当用户现在登录Facebook时,我的应用程序会检查用户是否已经存在于数据库中。为了不冻结UI (因为我来自安卓),我想使用NSURLConnection sendAsynchronousRequest。最初起作用的是以下几点:
我的用户模型有一个方法来完成AsynchronousRequest的整个任务,然后在完成后将一个变量设置为would。然后,其他类可以简单地使用while ( !user.loading )检查请求是否完成。我遇到的问题是,现在,我必须把这个方法应用到每一个模型中。因此,我创建了一个新的类HTTPPost,而不是这个。这个类现在拥有获得一个NSDictionary并返回一个的方法。这几乎成功了。我现在遇到的问题是,我无法真正确定流程是否完成。因此,我开始创建一个名为Globals的新类,并使用全局变量loading。但是全局变量总是不存在。那么,怎样才是最好的方法呢?
这是我的代码:
这是我检查用户并加载它的地方。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总是为零。我以前做过的工作如下。
在我的模型中,我有HTTP请求和一个名为loading的变量。现在,我将加载设置为false,直到将响应设置为NSDictionary
returnDict = [NSJSONSerialization JSONObjectWithData: [responseBody dataUsingEncoding:NSUTF8StringEncoding]
options: NSJSONReadingMutableContainers
error: &error];但是,然后我又遇到了另一个问题。我不得不在我所有的模特身上再这么做.所以我创建了一个子类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;
}如您所见,我现在有一个名为loading的变量。它是一个全局变量。但不知何故,变量总是不存在。
做这件事最好的方法是什么?我希望我能理解,我对目标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
}];发布于 2014-02-12 18:32:49
似乎您正在尝试接受一个异步进程(sendAsynchronousRequest),并使它的行为像一个同步进程(也就是说,您似乎想要等待它)。你不应该那样做。您应该接受异步模式,而不是对抗它们。
sendAsynchronousRequest方法有一个完成块,该块指定执行请求时要执行的操作。不要尝试将代码放在块之后,并且(尝试)等待块完成,而是将依赖于完成块内的网络请求的任何代码放在完成块中,或者让完成块调用您的代码。
一种常见的方法是将自己的方法赋予自己的完成块,然后在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中调用该块,loadModelFrom传递给performHttpRequest。
但是希望你有了这样的想法:永远不要等待一个完成块,而是在它完成后,将任何你想要它做的事情都放进这个块中。无论您是使用AFNetworking (我建议您使用)还是继续使用sendAsynchronousRequest,这都是您应该熟悉的非常有用的模式。
更新:
修改后的代码示例(很大程度上)对我非常有用。看到你修改过的问题,有几点意见:
base64EncodedString方法。在iOS 7中,有本机base64EncodedStringWithOptions方法(或者对于早期的iOS版本使用base64Encoding)。还是使用第三方基础-64 NSData类别?jsonString没有意义,只需要将其转换回NSData。只需在请求中使用jsonData即可。
responseBody也是如此:为什么只将字符串转换为NSDatareturnDict块之外将__block定义为__block是没有意义的。只需在该块中定义它,__block限定符就不再必要了。NSOperationQueue completionHandler of sendAsynchronousRequest创建一个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);
}];
}然后,视图控制器可以使用如下内容访问该方法:
// 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
}
}];发布于 2014-02-12 17:40:29
在看到您添加了处理请求及其响应的特定代码之后,我要指出,您应该尝试使用AFNetworking。它提取了大量的锅炉板规范。
正如您所提到的,对于obj-c来说,您是新手,理解AFNetworking可能需要一些时间,但从长远来看,它将为您节省大量的头痛。此外,它也是广泛使用的网络相关内容的开放源码之一。
我希望这会有所帮助。
发布于 2014-12-16 11:56:53
如果要等待请求,则不应使用sendAsynchronousRequest。使用sendSynchonousRequest代替。它是用来做的:
NSURLResponse *response;
NSError * error;
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];但是,在进行同步调用时,UI会被阻塞。我怀疑这是不是你想要的。
https://stackoverflow.com/questions/21735112
复制相似问题