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

时间:2012-06-06 14:28:10

标签: iphone objective-c ios uiimagepickercontroller

我使用以下代码允许我的应用程序的用户拍摄/选择照片,然后将其保存到文档目录并设置为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];
}

基本上我的问题是这段代码似乎执行速度非常慢,例如当我从相机胶卷中选择一张图像时,它最终会保存并将我带回调用视图,但只是经过很长时间的延迟.. < / p>

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

2 个答案:

答案 0 :(得分:8)

保存大图像(如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];
    });
}

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

另外,作为一种风格说明,定义像

这样的Objective-C方法
- (void)saveImage:(UIImage*)image:(NSString*)imageName 
在允许的情况下,非常气馁。为每个参数指定一个名称,如下所示:

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

它会使您的代码更具描述性。

答案 1 :(得分:2)

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

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

您应该始终在异步队列中执行这些类型的操作。即使测试时性能对于您的应用程序看起来足够好,您仍然应该执行异步队列 - 您永远不会知道设备可能进行的其他进程,这可能会减慢一次应用程序掌握在用户手中。

使用Grand Central Dispatch(GCD),较新版本的iOS可以非常简单地异步保存。步骤是:

  1. 创建保存图片的NSBlockOperation
  2. 在块操作的完成块中,从磁盘&amp;读取映像。显示它。这里唯一需要注意的是,您必须使用主队列来显示图像:所有UI操作必须在主线程上进行
  3. 将块操作添加到操作队列并观察它!
  4. 就是这样。这是代码:

    // 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 stackoverflow
    // questions or something.
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    [queue addOperation:saveOp];
    

    一旦您对这种代码模式感到满意,您会发现自己经常使用它。它在生成大型数据集,加载时的长操作等方面非常有用。实际上,任何使UI偏差最小的操作都是此代码的良好候选者。请记住,当你不在主队列中时,你不能对UI做任何事情,其他一切都是蛋糕。