4

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

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

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

4

2 回答 2

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];
    });
}

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

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

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

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

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

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

于 2012-06-06T16:07:40.700 回答
2

我已经回答了一个类似的问题。为清楚起见,我将在此处复制它:

根据图像分辨率,UIImagePNGRepresentation确实可能会很慢,就像任何写入文件系统一样。

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

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

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

就是这样。这是代码:

// 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];

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

于 2012-11-07T23:03:00.933 回答