1

我写了一个从相机获取图像的类。它的标题如下:

typedef void(^ImageTakenCallback)(UIImage *image);

@interface ImageGetter : NSObject <UIImagePickerControllerDelegate, UIPopoverControllerDelegate>
{
    UIImagePickerController *picker;
    ImageTakenCallback completionBlock
}

-(void) requestImageInView:(UIView*)view withCompletionBlock:(void(^)(UIImage*))completion;

@end

如您所见,我正在尝试在客户端代码中进行类似的操作:

[[[ImageGetter alloc] init] requestImageInView:_viewController.view withCompletionBlock:^(UIImage *image) {
    // do stuff with taken image
}];

这是我实现 ImageGetter 的方式:

-(void) requestImageInView:(UIView*)view withCompletionBlock:(ImageTakenCallback)completion
{
    completionBlock = [completion copy];

    picker = [[UIImagePickerController alloc] init];
    picker.sourceType = UIImagePickerControllerSourceTypeCamera;
    picker.delegate = self;
    [view addSubview:picker.view];
}

- (void)imagePickerController:(UIImagePickerController *)picker_
        didFinishPickingImage:(UIImage *)image
                  editingInfo:(NSDictionary *)editingInfo
{
    [picker.view removeFromSuperview];
    picker = nil;

    completionBlock(image);
}

问题是因为我使用的是 ARC,ImageGetter 的实例在调用后立即被释放-requestImage...,所以弱的委托picker就变成了nil

解决此类问题的常用方法有哪些?

我可以看到一些方法,但是,它们似乎都不是很正确:

  1. 从客户端代码中保留 ImageGetter,例如,将其分配给强属性。这里的问题是:我将无法通过在获取图像后立即将此属性设置为 nil 来释放它,因为这意味着在执行此对象的方法时将对象的保留计数设置为 0。另外,我不想要不必要的属性(嗯,这不是一个大问题,但尽管如此)。
  2. 为 ImageGetter 禁用 ARC 并在启动时手动保留并在将图像发送到回调后释放。
  3. 制作静态管理器 ImageGetterManager,它将具有方法requestImage...,它将创建 ImageGetter 实例,保留它们,重定向requestImage...调用,从它们获取回调并释放。这似乎是最一致的方式,但是对于这么小的代码来说是不是有点复杂?

那么我怎样才能建立这样一个类呢?

4

3 回答 3

1

ImageGetter您可以通过创建和释放“自引用”在类中处理它。在实现文件的类扩展中,声明一个属性

@interface ImageGetter ()
@property (strong, nonatomic) id selfRef;
@end

requestImageInView:中,设置self.selfRef = self为防止释放。

在完成方法中,设置self.selfRef = nil.


备注:实际上,即使使用 ARC,您也可以管理保留计数:

CFRetain((__bridge CFTypeRef)(self));   // Increases the retain count to prevent deallocation.
CFRelease((__bridge CFTypeRef)(self));  // Decreases the retain count.

但我不确定这是否被认为是 ARC 的“良好编程”。欢迎任何反馈!

于 2013-11-13T12:30:58.053 回答
0

如果在切换到 ARC 时引入了这个问题,我应该选择选项 1,并将其定义为强属性。

但是,该行为与您为选项 1 描述的行为有点不同:将属性设置为 nil,并不意味着对象会立即释放,它只会导致保留计数减少。ARC 会处理得很好,一旦所有引用的对象都“释放”了它,该对象就会被释放。

于 2013-11-13T12:40:24.793 回答
0

您可以使用以下策略:

ImageGetter* imgGetter = [[ImageGetter alloc] init];
[imgGetter requestImageInView:_viewController.view withCompletionBlock:^(UIImage *image) {
    // do stuff with taken image
    [imgGetter releaseCompletionBlock]; // With this line, the completion block will retain automatically imgGetter, which will be released after the release of the completionBlock.
}];

在 ImageGetter 实现类中,创建一个可以像这样在块内调用的方法。

-(void) releaseCompletionBlock
{
  completionBlock = nil;
}
于 2013-11-13T13:07:51.283 回答