可悲的是,我在代码片段本身中看不到任何会导致崩溃的明显内容。在这个答案的最后,我提供了一个我所做的代码示例,所以也许你可以将它与你的交叉引用。
几个想法:
您说这适用于快速连接,但不适用于慢速连接。这是从主线程运行同步查询的症状。如果您不小心从主队列进行了此同步调用,则在连接速度较慢的情况下,看门狗进程可能会杀死您的应用程序。只要主线程没有响应,它就会这样做。
因此,使用异步调用(正如您在其他问题中所追求的那样)或确保这发生在后台队列中。
顺便说一句,您说您使用performSelectorInBackground
. 现在大多数人会使用 GCD 或NSOperationQueue
. 请参阅并发编程指南。
一般来说,如果有例外,我会要求您检查一些变量:
坦率地说,如果这适用于快速连接而不是慢速连接,那么问题不太可能出在这些变量的基本设置中,而是您是否在其他地方异步更新它们(而不是按照同步部分同步更新)线程编程指南或队列通过消除并发编程指南中的基于锁的代码)。在编写线程安全代码时,您需要注意同步更改,并且在某些情况下,连接速度会影响行为。
如果这两点都不能解决你的问题,那么我们真的需要深入挖掘你的异常。不用说,每当您发布有关异常的任何问题时,您都必须:
告诉我们异常是什么
识别导致异常的行;你可以这样做
如果你原谅我的观察,虽然我很感激你试图让我们免于过多的代码(有时人们会发布大量不相关的代码;所以感谢你饶恕了我们),但你通常没有共享足够的代码。十分之九,人们遭受的错误/异常是由于某些简单变量未设置为您认为的值(例如未初始化等)的结果。因此,您要么需要包含更完整的代码示例,要么您的代码片段应包含NSLog
、if
或assert
表明这些值有效的语句。
例如,您可能想输入一个
if ([NSThread isMainThread])
{
NSLog(@"%s: should not be on main thread!!!", __FUNCTION__);
}
这可以确保您自己这是在后台线程上运行的。您可能还想在登录Obj1.thumbImage
时登录postBody
。
我已经看到您的一些问题,您 (a) 发布了一些简单的代码,(b) 告诉我们它在这个片段中的某个地方崩溃了(但不是确切的位置),并且 (c) 向我们保证传递给非常简单的代码片段都是有效的(即使片段没有说明这一点)。一个简单的现实,通常不是所有这三个条件都可以同时成立。当人们发布崩溃时,问题往往出在传递给违规代码的变量中,就像代码本身一样。
一些不相关的点:
顺便说一句,在您的sendSynchronousRequest
, 您nil
指定error
. 如果 API 让您有机会诊断错误的成功或失败,您应该利用它。
使用UIImageJPEGRepresentation
真的是最坏的情况,会大量丢失数据。如果人为可能,请回到源头UIImage
(它是如何创建的?a NSData
?)。如果你不能这样做,也许可以考虑,UIImagePNGRepresentation
因为它仍然享受压缩,但数据丢失更少。有时您必须这样做UIImageJPEGRepresentation
,但将其视为不得已的工具。
如果您的服务器只返回“成功”文本字符串,那么我想这就是您所能做的。如果可能,最好对服务器进行编程以返回 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);
}