保存用相机拍摄或从相机胶卷中选择的图像时出现长时间延迟 - iPhone

Posted

技术标签:

【中文标题】保存用相机拍摄或从相机胶卷中选择的图像时出现长时间延迟 - iPhone【英文标题】:Long delay when saving image taken with camera or chosen from camera roll - iPhone 【发布时间】:2012-06-06 14:28:09 【问题描述】:

我正在使用以下代码来允许我的应用程序的用户拍摄/选择一张照片,然后将其保存到文档目录并设置为 UIImageView 的图像:

    -(void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex 

        if (actionSheet.tag == 0)
            if (buttonIndex == 0) 
                NSLog(@"Take Picture Button Clicked");
                // Create image picker controller
                UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];

                // Set source to the camera
                imagePicker.sourceType =  UIImagePickerControllerSourceTypeCamera;

                // Delegate is self
                imagePicker.delegate = self;

                // Show image picker
                [self presentModalViewController:imagePicker animated:YES];
             
            else if (buttonIndex == 1) 
                NSLog(@"Choose From Library Button Clicked");
                // Create image picker controller
                UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];

                // Set source to the camera
                imagePicker.sourceType =  UIImagePickerControllerSourceTypePhotoLibrary;

                // Delegate is self
                imagePicker.delegate = self;

                // Show image picker
                [self presentModalViewController:imagePicker animated:YES];
             
            else if (buttonIndex == 2) 
                NSLog(@"Cancel Button Clicked");
             
        
......

- (void)saveImage:(UIImage*)image:(NSString*)imageName 
 
    NSData *imageData = UIImagePNGRepresentation(image); //convert image into .png format.

    NSFileManager *fileManager = [NSFileManager defaultManager];//create instance of NSFileManager

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); //create an array and store result of our search for the documents directory in it

    NSString *documentsDirectory = [paths objectAtIndex:0]; //create NSString object, that holds our exact path to the documents directory

    NSString *fullPath = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.png", imageName]]; //add our image to the path 

    [fileManager createFileAtPath:fullPath contents:imageData attributes:nil]; //finally save the path (image)

    receiptImageView1.image = [UIImage imageWithContentsOfFile:fullPath];
    self.receiptImage1 = fullPath;

    NSLog(@"image saved");


//Receive the image the user picks from the image picker controller
-(void)imagePickerController:(UIImagePickerController*)picker
didFinishPickingMediaWithInfo:(NSDictionary*)info 
    UIImage* image = [info objectForKey: UIImagePickerControllerOriginalImage];
    NSString* imageName = @"Receipt1Image1";
    [self saveImage:image :imageName];

基本上我的问题是这段代码似乎执行得很慢,例如,当我从相机胶卷中选择一张图像时,它最终会保存并将我带回调用视图,但只有在很长的延迟之后..

任何人都可以对此有所了解吗?

【问题讨论】:

使用 Time Profiler 工具找出最慢的部分并尝试优化这些部分。 【参考方案1】:

保存大图像(如 iPhone 4/4S 中的相机拍摄的图像)需要很长时间。如果你分析这个过程,你会发现UIImagePNGRepresentation() 需要一段时间来生成你的 PNG 图像,但根据我的经验,主要瓶颈是写入 1 MB 以上的图像到磁盘。

除了使用 JPEG 压缩(我发现在我的基准测试中速度稍快一些)或使用更快的第三方图像压缩例程之外,您几乎无法加快此过程。因此,如果您不想在发生这种情况时阻止您的用户界面,请在后台线程或队列上调度此保存过程。您可以执行以下操作:

-(void)imagePickerController:(UIImagePickerController*)picker
didFinishPickingMediaWithInfo:(NSDictionary*)info 

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^
        UIImage* image = [info objectForKey: UIImagePickerControllerOriginalImage];
        NSString* imageName = @"Receipt1Image1";
        [self saveImage:image :imageName];
    );

但是,请注意这些 UIImage 中的每一个都会占用相当多的内存来保存,因此您可能需要使用调度信号量来防止多个此类图像保存操作同时发生.

另外,作为一个风格说明,定义一个 Objective-C 方法,如

- (void)saveImage:(UIImage*)image:(NSString*)imageName 

虽然允许,但非常不鼓励。为每个参数命名,如下所示:

- (void)saveImage:(UIImage*)image fileName:(NSString*)imageName 

这将使您的代码更具描述性。

【讨论】:

这不适用于 ios7 版本的 iPhone 5c,但适用于相同 iOS 版本的 iPhone 4s。为什么会这样? 像魅力一样工作。谢谢@Brad Larson【参考方案2】:

我已回复similar question。为清楚起见,我将在此处复制:

根据图像分辨率,UIImagePNGRepresentation 确实可能非常慢,任何写入文件系统的操作都可能如此。

您应该始终在异步队列中执行这些类型的操作。即使在测试时性能看起来对您的应用程序来说已经足够好,您仍然应该使用异步队列——您永远不知道设备可能正在进行哪些其他进程,这可能会减慢保存速度。应用在用户手中。

较新版本的 iOS 使用 Grand Central Dispatch (GCD) 可以非常轻松地进行异步保存。步骤是:

    创建一个NSBlockOperation 来保存图像 在块操作的完成块中,从磁盘读取图像并显示它。这里唯一需要注意的是,您必须使用主队列来显示图像:所有 UI 操作都必须发生在主线程上。 将块操作添加到操作队列并观察它!

就是这样。这是代码:

// Create a block operation with our saves
NSBlockOperation* saveOp = [NSBlockOperation blockOperationWithBlock: ^

   [UIImagePNGRepresentation(image) writeToFile:file atomically:YES];
   [UIImagePNGRepresentation(thumbImage) writeToFile:thumbfile atomically:YES];

];

// Use the completion block to update our UI from the main queue
[saveOp setCompletionBlock:^

   [[NSOperationQueue mainQueue] addOperationWithBlock:^ 

      UIImage *image = [UIImage imageWithContentsOfFile:thumbfile];
      // TODO: Assign image to imageview

   ];
];

// Kick off the operation, sit back, and relax. Go answer some ***
// questions or something.
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:saveOp];

一旦您熟悉了此 Code Pattern,您就会发现自己经常使用它。在生成大型数据集、长时间的加载操作等时,它非常有用。本质上,任何让你的 UI 至少滞后的操作都是此代码的良好候选者。请记住,当您不在主队列中时,您无法对 UI 进行任何操作,其他一切都是小菜一碟。

【讨论】:

以上是关于保存用相机拍摄或从相机胶卷中选择的图像时出现长时间延迟 - iPhone的主要内容,如果未能解决你的问题,请参考以下文章

拍摄快照并保存到相机胶卷时的 iOS 图像大小

尝试在 Windows Phone 上显示相机胶卷中的图像时出现跨线程异常

将图像保存在相机胶卷中并获取资产 url

如何让用户选择在保存之前裁剪从相机拍摄的照片?

文件上传选项以从相机拍摄图像或从图库中选择不适用于 Mozilla Firefox 中的 Web 应用程序

将应用内视频保存到相机胶卷?