2013-05-16 71 views
3

我在NSURLConnection上使用sendSynchronousRequest方法从服务器加载图像。它在网络连接速度很快时工作正常,但在连接速度较慢时会崩溃我的应用程序。我究竟做错了什么?sendSynchronousRequest在缓慢的网络连接上崩溃?

[postBody appendData:[[NSString stringWithFormat:@"\r\n--%@\r\n",stringBoundary] dataUsingEncoding:NSUTF8StringEncoding]];    
[postBody appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"image\";filename=\"%@.jpg\"\r\n",uploadImageName] dataUsingEncoding:NSUTF8StringEncoding]];    
[postBody appendData:[[NSString stringWithString:@"Content-Type: application/octet-stream\r\n\r\n"] dataUsingEncoding:NSUTF8StringEncoding]]; 
[postBody appendData:[NSData dataWithData:UIImageJPEGRepresentation(Obj1.thumbImage, 0.5)]]; 
[postBody appendData:[[NSString stringWithFormat:@"\r\n--%@--\r\n",stringBoundary] dataUsingEncoding:NSUTF8StringEncoding]]; 
[postRequest setHTTPBody:postBody]; 

NSData *returndata=[NSURLConnection sendSynchronousRequest:postRequest returningResponse:nil error:nil]; 
NSString *string=[[NSString alloc]initWithData:returndata encoding:NSUTF8StringEncoding]; 

NSError *error; 
NSString *path; 
NSData *imageData; 

if (error != nil && [string isEqualToString:@"success"]) { 
    //doing other work 
} 

我使用后台线程来运行所有这些代码。当我在网络连接速度较慢时上传更多图片时,应用程序崩溃。

+1

当你崩溃时你会得到什么错误?并在哪条线崩溃? –

+0

我无法得到它,因为它发生在罕见的情况。这可能是什么原因呢? – Xcode

+0

你可以在放置断点时测试它吗?代码似乎没问题,因为它在这里显示。也许你可以在这里添加更多的代码可以解决问题。 –

回答

1

不幸的是,我没有看到任何明显的代码片断本身,这会导致崩溃。在这个答案的最后,我提供了一个我已经完成的代码示例,所以也许你可以交叉引用它来反对你的。

一对夫妇的想法:

  1. 你说这个工程上快速连接,但不慢的连接。这是从主线程运行同步查询的症状。如果您意外地从主队列中进行此同步调用,则在较慢的连接上,看门狗进程可能会终止您的应用程序。只要主线程没有响应,它就会这样做。

    因此,使用异步调用(如您在其他问题中追求的那样)或确保这发生在后台队列中。

    顺便说一句,你说你使用performSelectorInBackground提交到后台线程。现在大多数人会使用GCD或NSOperationQueue。请参阅Concurrency Programming Guide

  2. 一般来说,如果有例外,我会问你检查你的一些变量:

    • 你不向我们展示postDataalloc/init。如果这是nil或是NSData而不是NSMutableData,则可能会发生异常。

    • 您应该确保Obj1.thumbImage不是nil。这也可能导致异常。

    坦率地说,如果这个正在快速连接,而不是缓慢的,问题是不太可能在这些变量的基本设置,而不管你是他们在其他地方异步更新(不是同步线程编程指南Synchronization部分中的更新或通过Eliminating Lock-Based Code并发编程指南)中的队列更新。编写线程安全代码时,需要注意同步更改,并且在某些情况下,连接的速度可能会影响行为。

  3. 如果这两点不能解决您的问题,那么我们真的需要深入研究您的例外。不用说,只要你发布有关异常的任何问题,你必须:

    • 告诉我们的例外是

    • 确定引起异常的行什么;您可以通过

      • 看堆栈跟踪来缩小问题的根源做到这一点;

      • 尝试adding an exception breakpoint;有时候这会识别导致异常的确切线。

      • 如果这样不起作用,请尝试单步调试调试器中的代码。

      • 诊断问题的最不优雅的方式是添加一堆NSLog语句,因此您可以计算出代码在崩溃之前得到了多少程度,并且各种变量都是您所期望的值。

  4. 如果你原谅观察,而我很欣赏你试图从太多的代码饶了我们(有时人岗吨无关的代码,所以感谢节约我们),你一般不会共享不足的代码。十次中有九次,人们正在遭受的错误/异常是由于一些简单的变量没有被设置为您认为它所具有的(例如未初始化等)。因此,您需要包含稍微更完整的代码示例,或者您的代码片段应包含NSLogifassert语句,以证明这些值是有效的。

    例如,您可能希望在

    if ([NSThread isMainThread]) 
    { 
        NSLog(@"%s: should not be on main thread!!!", __FUNCTION__); 
    } 
    

    这扔可以保证自己这是在后台线程上运行。您可能还想在登录时登录Obj1.thumbImagepostBody。 (a)发布一些简单的代码,(b)告诉我们它正在崩溃在这个片段的某处(但不是确切的地方)和(c)向我们保证值传递给非常简单的代码片段都是有效的(尽管代码片段没有说明)。简单的现实,通常不是所有这三个条件在同一时间都是真实的。当人们发布崩溃时,问题就像在代码本身传递给违规代码的变量一样。

几个不相关的要点:

  1. 顺便说一句,在你sendSynchronousRequest,你为error指定nil。如果API为您提供诊断错误成功或失败的机会,那么您应该利用这一点。

  2. 使用UIImageJPEGRepresentation确实是数据丢失严重的最坏情况。如果可能的话,回到UIImage的来源(它是如何创建的?NSData?)。如果你不能这样做,可能会考虑UIImagePNGRepresentation,因为它仍然具有压缩功能,但数据丢失较少。有时候你必须做UIImageJPEGRepresentation,但认为它是最后的手段。

  3. 如果你的服务器只返回“成功”文本字符串,那么我想这就是你所能做的。如果可能的话,最好是如果你可以编程你的服务器返回一个JSON响应,你可以很容易地使用/解析不同类型的错误/响应的不同返回码。

不管怎么说,这是一个工作的上传程序:

- (void)viewDidLoad 
{ 
    [super viewDidLoad]; 

    // [self performSelectorInBackground:@selector(upload) withObject:nil]; 

    NSURL *url = [NSURL URLWithString:@"http://my.url.com/upload.php"]; 
    NSString *path = [[NSBundle mainBundle] pathForResource:@"myimage" ofType:@"jpg"]; // note, I'm going back to bundle, not grabbing `UIImage` from imageview 

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 

     [self uploadFileAtPath:path 
         forField:@"file" 
          URL:url 
        parameters:nil]; 
    }); 
} 

- (NSString *)generateBoundaryString 
{ 
    // generate boundary string 
    // 
    // adapted from http://developer.apple.com/library/ios/#samplecode/SimpleURLConnections 
    // 
    // Note in iOS 6 and later, you can just: 
    // 
    // return [NSString stringWithFormat:@"Boundary-%@", [[NSUUID UUID] UUIDString]]; 

    CFUUIDRef uuid; 
    NSString *uuidStr; 

    uuid = CFUUIDCreate(NULL); 
    assert(uuid != NULL); 

    uuidStr = CFBridgingRelease(CFUUIDCreateString(NULL, uuid)); 
    assert(uuidStr != NULL); 

    CFRelease(uuid); 

    return [NSString stringWithFormat:@"Boundary-%@", uuidStr]; 
} 

- (NSString *)mimeTypeForPath:(NSString *)path 
{ 
    // Get a mime type for an extension using MobileCoreServices.framework. 
    // 
    // You could hard code this instead, but I like using MobileCoreServices as 
    // it increases my code reuse possibilities in the future. 

    CFStringRef extension = (__bridge CFStringRef)[path pathExtension]; 
    CFStringRef UTI = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, extension, NULL); 
    assert(UTI != NULL); 

    NSString *mimetype = CFBridgingRelease(UTTypeCopyPreferredTagWithClass(UTI, kUTTagClassMIMEType)); 
    assert(mimetype != NULL); 

    CFRelease(UTI); 

    return mimetype; 
} 

- (void)uploadFileAtPath:(NSString *)imagePath 
       forField:(NSString *)fieldName 
        URL:(NSURL*)url 
       parameters:(NSDictionary *)parameters 
{ 
    NSString *filename = [imagePath lastPathComponent]; 
    NSData *imageData = [NSData dataWithContentsOfFile:imagePath]; 

    NSMutableData *httpBody = [NSMutableData data]; 
    NSString *boundary = [self generateBoundaryString]; 
    NSString *mimetype = [self mimeTypeForPath:imagePath]; 

    // configure the request 

    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url]; 
    [request setCachePolicy:NSURLRequestReloadIgnoringLocalCacheData]; 
    [request setHTTPShouldHandleCookies:NO]; 
    [request setTimeoutInterval:30]; 
    [request setHTTPMethod:@"POST"]; 

    // set content type 

    NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary]; 
    [request setValue:contentType forHTTPHeaderField: @"Content-Type"]; 

    // add params (all params are strings) 

    [parameters enumerateKeysAndObjectsUsingBlock:^(NSString *parameterKey, NSString *parameterValue, BOOL *stop) { 
     [httpBody appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]]; 
     [httpBody appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n", parameterKey] dataUsingEncoding:NSUTF8StringEncoding]]; 
     [httpBody appendData:[[NSString stringWithFormat:@"%@\r\n", parameterValue] dataUsingEncoding:NSUTF8StringEncoding]]; 
    }]; 

    // add image data 

    if (imageData) { 
     [httpBody appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]]; 
     [httpBody appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\n", fieldName, filename] dataUsingEncoding:NSUTF8StringEncoding]]; 
     [httpBody appendData:[[NSString stringWithFormat:@"Content-Type: %@\r\n\r\n", mimetype] dataUsingEncoding:NSUTF8StringEncoding]]; 
     [httpBody appendData:imageData]; 
     [httpBody appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; 
    } 

    [httpBody appendData:[[NSString stringWithFormat:@"--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]]; 

    // setting the body of the post to the reqeust 

    [request setHTTPBody:httpBody]; 

    NSError *error; 
    NSData *returndata = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:&error]; 

    if (error) 
    { 
     NSLog(@"error=%@", error); 
     return; 
    } 

    // I generally return JSON response, so this is where I parse it; 
    // obviously, your validation process will differ. 

    NSDictionary *results = [NSJSONSerialization JSONObjectWithData:returndata options:0 error:&error]; 

    if (error) 
     NSLog(@"error=%@", error); 
    if (results) 
     NSLog(@"result=%@", results); 

} 
0

跟踪下来...

添加一个异常断点。这将停止正在发生异常的线上的调试器。

后台线程没有自己的异常处理程序,所以它们会生成一个信号并使您的进程崩溃。所以你可以添加自己的东西,特别是对于这样的情况,系统调用正在生成异常,而你没有一个好的方法来阻止它。

@try{ 

    NSData *returndata=[NSURLConnection sendSynchronousRequest:postRequest returningResponse:nil error:nil]; 
    NSString *string=[[NSString alloc]initWithData:returndata encoding:NSUTF8StringEncoding]; 

} 

@catch(NSException *e) 
{ 
    NSLog(@"caught exception: %@",e); 
    //you could also iterate over: - (NSArray *)callStackSymbols NS_AVAILABLE(10_6, 4_0); 
}