2

我正在尝试在 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

4 回答 4

1

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

很高兴知道 NSURLConnection 实际上确实可以正常触发,但是没有收到委托调用。当您将 NSRunLoop 放在那里时,它可以确保您的连接不会超出该线程的范围。

于 2012-05-25T23:06:45.813 回答
1

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

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

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

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

使用 NSURLConnection

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

简而言之,这是模式:

  • 为您的响应数据保留一个NSMutableData
  • 清除 中的内容NSMutableData实例didReceiveResponse。(当您被重定向时,您可能会收到多个didReceiveResponse事件,但您应该只使用最后一个事件的数据。)
  • 将您收到的数据附加didReceiveDataNSMutableData. 不要试图立即处理它;您通常didReceiveData会在一次传输中收到多个事件。
  • connectionDidFinishLoading中,您的数据是完整的。在这里,你可以用它做点什么。

当您拥有所有数据时,您可以将其发送到不同的线程,或者(更简单)dispatch_async发送到不在主线程上运行的队列。请参见URL 加载系统编程指南的清单 5,“示例 connectionDidFinishLoading:实现” 。

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

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

使用NSURLConnection要求类成员在传输时累积数据。这意味着如果您要同时进行多个传输,则需要更复杂的东西。可能是一个包装类来驱动NSURLConnection,并保持每个响应分开。在您编写此包装类时,您可能已经编写了AFHTTPRequestOperationAFNetworking 的一部分的幼稚版本。

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

- (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在 a 中NSOperation(因此,可以添加到 a 中的东西NSOperationQueue)。AFNetworking 自动处理我谈到的这些事件。相反,您编写一个完成块。当你得到这个块时,传输要么成功,要么失败。如果失败,则错误在AFHTTPRequestOperation实例上可用。如果成功,您可以使用AFHTTPRequestOperation实例上的数据。

(注意:我相信AFHTTPRequestOperation实际上确实从另一个线程运行连接。但是,它是编写良好且经过良好测试的代码。这样做没有任何“错误”,只是更难实现并且通常是不必要的。但是如果您的库确实给你,为什么不呢?)

AFNetworking 提供了一些NSURLConnection没有的 HTTP 逻辑。使用 AFNetworking,您只需使用 aAFHTTPRequestOperation并设置一个完成块。以上所有内容都变成了这样:

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 现在可以发布任何版本)。至少值得考虑。

于 2012-05-29T20:08:29.383 回答
0

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

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

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

希望这可以帮助。

于 2012-05-24T21:31:10.617 回答
0

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

在您的- (void)signUpUsingEmail:...方法中,在[signUpConnection start];安排 URL 连接后在该线程上运行(由 GCD 创建和管理)运行循环,如下所示 -

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

[[NSRunLoop currentRunLoop] run];
于 2012-05-25T23:39:57.607 回答