iOS RESTful POST调用在UIWebView中有效,但在从线程调用时无效

时间:2012-05-24 21:24:56

标签: objective-c ios rest post webserver

我正在尝试在iOS应用中创建一个身份验证系统,允许用户登录并注册,如果他们还没有帐户。我昨天完全启动并运行了登录系统,但是当我为注册系统设置代码时,代码甚至不会ping服务器。然后我再次尝试测试登录系统,代码现在也不会ping服务器。

RegistrationTableViewController的相关代码(它是一个自定义TVC,其中包含某些单元格中的文本字段 - 例如,考虑创建新日历事件的视图):

- (IBAction)signUpButtonPressed { 
    // Get the values out of the text fields that the user has filled out.
    NSString *email = self.emailTextField.text;
    NSString *firstName = self.firstNameTextField.text;
    NSString *lastName = self.lastNameTextField.text;
    NSString *password = self.passwordTextField.text;
    // Assuming that sign-up could potentially take a noticeable amount of time, run the
    // process on a separate thread to avoid locking the UI.
    dispatch_queue_t signUpQueue = dispatch_queue_create("sign-up authenticator", NULL);
    dispatch_async(signUpQueue, ^{
        // self.brain refers to a SignUpBrain property. See the code for the class below.
        [self.brain signUpUsingEmail:email firstName:firstName lastName:lastName  
          andPassword:password];  
        dispatch_async(dispatch_get_main_queue(), ^{  
            [self performSegueWithIdentifier:@"ShowMainFromSignUp" sender:self];  
        });  
    });  
    dispatch_release(signUpQueue);  
}

SignUpBrain的相关代码:

- (void)signUpUsingEmail:(NSString *)email firstName:(NSString *)firstName
                lastName:(NSString *)lastName andPassword:(NSString *)password {
    self.email = email;
    self.firstName = firstName;
    self.lastName = lastName;
    self.password = password;

    // Handle sign-up web calls.
    NSMutableURLRequest *signUpRequest = [[NSMutableURLRequest alloc] initWithURL:[NSURL 
      URLWithString:@"URL GOES HERE"]]; // obviously there's an actual URL in real code
    NSString *postString = [NSString stringWithFormat:@"uname=%@&pw=%@&fname=%@&lname=%@", 
      email, password, firstName, lastName];
//NSLog(postString);
    [signUpRequest setHTTPMethod:@"POST"];
    [signUpRequest setHTTPBody:[postString dataUsingEncoding:NSUTF8StringEncoding]];
    NSURLConnection *signUpConnection = 
      [NSURLConnection connectionWithRequest:signUpRequest delegate:self];
    [signUpConnection start];

    // Store any user data.
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    self.signUpResponse = data;
    NSError *error;
    NSDictionary *jsonLoginResults = [NSJSONSerialization JSONObjectWithData:data 
      options:0 error:&error];
    if (error) {
        NSLog(error.description);
    }
    NSLog(jsonLoginResults.description);

    // Return whether the user has successfully been registered.
    // If success is 1, then registration has been completed successfully. 0 if not.
    if ([jsonLoginResults objectForKey:@"status"]) {
        NSLog(@"Success!");
    }
}

我还要注意,我创建了一个在UIWebView中使用这些相同Web调用的测试,并且它可以成功运行。

如果我需要澄清任何内容或包含更多代码,请告知我们!提前谢谢。

4 个答案:

答案 0 :(得分:1)

几年前我写了一篇关于这个确切问题的博客文章,所以它可能有点过时了:http://www.sortedbits.com/nsurlconnection-in-its-own-thread

很高兴知道NSURLConnection确实正常启动,但是没有收到代理调用。当您将NSRunLoop放在那里时,它确保您的连接不会超出该线程中的范围。

答案 1 :(得分:1)

在正常情况下,您不需要在其他线程上运行网络操作。

您需要避免的是主线程上的同步操作。这将导致iOS终止您的应用程序,因为它会在网络传输繁忙时停止响应事件。

Apple的建议是在主线程上使用异步操作,而不是在另一个线程上进行同步操作。

使用异步操作将导致网络本身在后台运行。您可以将此视为Apple的专用线程。您的委托方法将在主线程上以正确的顺序调用,以便在iOS确定合适的情况下赶上网络。

使用NSURLConnection

Apple在URL加载系统编程指南Using NSURLConnection中包含了如何使用NSURLConnection的示例。这是一篇简短的文章,充满了简单的代码示例。你应该花几分钟时间阅读这篇文章。

简而言之,这是模式:

  • 为您的回复数据保留NSMutableData
  • 清除NSMutableData中的内容didReceiveResponse实例。 (您在重定向时可能会收到多个didReceiveResponse个事件,但您应该只使用上一个事件中的数据。)
  • 将您从didReceiveData收到的数据附加到NSMutableData。不要试图立即处理它; 通常会收到多个didReceiveData个事件进行一次转移。
  • connectionDidFinishLoading中,您的数据已完成。在这里,你可以用它做点什么。

当您拥有所有数据时,您可以将其发送到不同的线程或(更简单)dispatch_async发送到未在主线程上运行的队列。请参阅清单5,URL Loading System Programming Guide的“示例connectionDidFinishLoading:implementation”。

这里的想法是,您开始(或重新启动)在didReceiveResponse:中累积数据,在didReceiveData:中附加数据,并实际对connectionDidFinishLoading:中的数据执行某些操作。

可以在另一个线程上运行NSURLConnection,当然。该线程需要使用运行循环来从网络接收委托事件。但除非有某种原因你需要一个单独的线程,否则使用异步网络是Apple的解决方案。

使用NSURLConnection要求类成员在传输数据时累积数据。这意味着如果你要同时进行多次转移,你需要更复杂的东西。可能是一个驱动NSURLConnection的包装类,并将每个响应分开。当你编写这个包装类时,你可能已经编写了一个简单的AFHTTPRequestOperation版本,它是AFNetworking的一部分。

假设您一次只进行一次转移,代码看起来有点像这样:

- (void)signUpUsingEmail:(NSString *)email firstName:(NSString *)firstName
                lastName:(NSString *)lastName andPassword:(NSString *)password {

    // set up your connection here, but don't start it yet.

    receivedData = [[NSMutableData alloc] init]; // receivedData should be a class member
    [signUpConnection start];
}

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
    [receivedData setLength:0];
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    [receivedData appendData:data];
}

- (void)connection:(NSURLConnection *)connection
  didFailWithError:(NSError *)error {
    // do something with error here
    // if you're not using ARC, release connection & receivedData here
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    dispatch_async(someQueue, ^{
        // do something that takes a long time with receivedData here
        dispatch_async( dispatch_get_main_queue(), ^{
            // go back to your main screen here
        });
    });
    // if you're not using ARC, release connection & receivedData here
}

使用AFNetworking

现在我已经解释了这一切,我将提出一个替代方案。您可能最好使用AFNetworking

AFNetworking是一个开源的第三方库。它最基本的类将NSURLConnection包裹在NSOperation中(因此可以添加到NSOperationQueue中)。 AFNetworking处理我自动谈到的这些事件。相反,你写一个完成块。当您获得此阻止时,传输已成功或失败。如果失败,则AFHTTPRequestOperation实例上会出现错误。如果成功,您可以使用AFHTTPRequestOperation实例上的数据。

(注意:我相信AFHTTPRequestOperation实际上是从另一个线程运行连接。但是,它是编写良好且经过良好测试的代码。执行此操作没有任何“错误”,它更难以启动通常是不必要的。但是如果你的图书馆为你做了,为什么不呢?)

AFNetworking提供了一些NSURLConnection没有的HTTP逻辑。使用AFNetworking,您只需使用AFHTTPRequestOperation并设置完成块。所有这些都变成了:

HTTPOperation = [[AFHTTPRequestOperation alloc] initWithRequest: URLRequest];
HTTPOperation.completionBlock = ^{
    dispatch_async(someQueue, ^{
        // get possible error or content from HTTPOperation
    }
};
[HTTPOperation start];

过去,我直接使用NSURLConnection编写。但最近,我更频繁地使用AFNetworking。与其他一些库不同,它与NSURLConnection的形状并不完全不同。事实上,它使用的是NSURLConnection,只是包含在NSOperation中(希望Apple现在能够获得任何版本)。值得考虑的是,至少。

答案 2 :(得分:0)

NSURLConnection需要一个默认情况下在辅助线程上不可用的runloop。如果需要在辅助线程上创建连接,请添加在NSDefaultRunLoopMode中运行的runloop。

在创建NSURLConnection对象之前使用以下代码。 “finished”是一个BOOL类型的iVar,当连接加载完成时应该更新为YES。

    do {
        [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
    } while (!finished);

希望这有帮助。

答案 3 :(得分:0)

每个线程都有一个关联的runloop。 NSURLConnection的委托方法被触发需要runloop。

- (void)signUpUsingEmail:...方法之后的[signUpConnection start];方法中,在该线程上运行URL连接(由GCD创建和管理),这样的runloop -

[signUpConnection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];

[[NSRunLoop currentRunLoop] run];