1

这是这篇文章的延续。我遇到了 NSOperation 块的问题,我的应用程序在completionblock.

我认为问题是保留周期(我有一个警告:)Capturing self strongly in this block is likely to lead to a retain cycle,我尝试了这个解决方案(参见上面的代码),警告消失了,但应用程序仍然崩溃。

该应用程序是一个简单的 tableView。该操作在传递的图像中应用了一些修改。

这是我第一次自己创建和使用块,谢谢你给我一些基本的解释

代码:

表视图控制器.m:

在表视图中:cellForRowAtIndexPath:

   [[ImageManager sharedManager] modifyImage:[UIImage imageNamed:@"anImage.png"] completionBlock:^(UIImage *image, NSError *error) {        
    [cell.imageView setImage:image];
}];

ImageManager.h:

  #import <Foundation/Foundation.h>
 @interface ImageManager : NSObject
 @property(nonatomic, strong) NSOperationQueue *imageOperationQueu;

 -(void) modifyImage:(UIImage*)image completionBlock:(void(^)(UIImage *image,NSError *error)) completBlock;

 + (id) sharedManager;

 @end

图像管理器.m:

#import "ImageManager.h"
#import "ImageOperations.h"

static ImageManager *MySharedManager = nil;

@implementation ImageManager

@synthesize imageOperationQueu;

+ (id)sharedManager
{
  static dispatch_once_t onceToken;
  dispatch_once(&onceToken, ^{
    if (MySharedManager == nil) MySharedManager = [[self alloc] init];
  });
  return MySharedManager;
}

- (NSOperationQueue *)imageOperationQueu
{
   if (!imageOperationQueu)
 {
    imageOperationQueu = [[NSOperationQueue alloc] init];
    [imageOperationQueu setMaxConcurrentOperationCount:3];
    imageOperationQueu.name = @"imageOperationQueu";
 }
return imageOperationQueu;
}

-(void) modifyImage:(UIImage*)image completionBlock:(void(^)(UIImage *image,NSError *error)) completBlock
{
 ImageOperations *op = [[ImageOperations alloc]initWithImage:image WithCompletionBlock:completBlock];
[self.imageOperationQueu addOperation:op];
}
@end

图像操作.h:

 #import <Foundation/Foundation.h>

 typedef void (^CompletionBlock)(UIImage *image,NSError *error);

 @interface ImageOperations : NSOperation

 @property(nonatomic, weak) CompletionBlock completBlock;
 @property(nonatomic, strong) UIImage *imageToTransform;

 -(id)initWithImage:(UIImage *)image WithCompletionBlock:(CompletionBlock) block;
 @end

图像操作.m:

 #import "ImageOperations.h"

 @implementation ImageOperations
 @synthesize imageToTransform;
 @synthesize completBlock;

 -(id)initWithImage:(UIImage *)image WithCompletionBlock:(CompletionBlock) block
 {
  if (self = [super init])
  {
    NSLog(@"initWithImage");
    self.imageToTransform = image;
    [self setCompletBlock:block];
  }
 return self;
}

- (void)main
{
  @autoreleasepool
{        
    UIImage *img = [self setRoundedImage:self.imageToTransform];

    __weak ImageOperations *imgOp = self;

    [imgOp setCompletionBlock:^{
        NSLog(@"setCompletionBlock");
        imgOp.completBlock(img,nil);
    }];
}
}

-(UIImage*)setRoundedImage:(UIImage*)image
{
// ..
}

@end
4

1 回答 1

1

几个想法:

  1. 您将块定义为weak属性。在使用 Objective-C 指南编程的使用块部分中,他们建议您改用。(注意,我们复制的是块,而不是它使用的对象;不过,您必须小心该块中的强引用。)块变量的范围非常微妙。(有关示例,请参见Block Programming Topics指南中要避免的模式。)调用已发布的 a可能会产生一些不可预知的结果。使用.copyblockweakcopy

  2. 您的 tableview 有一个微妙的问题,如果在调用完成块时单元格已经滚动,将会发生这种情况。该单元格可能已被重新用于表格的另一行。(事实证明,圆角逻辑可能足够快,这个问题不太可能出现,但如果您执行较慢的操作(例如下载图像或图像很大),这一点至关重要。)无论如何,而不是:

    [[ImageManager sharedManager] modifyImage:[UIImage imageNamed:@"anImage.png"] completionBlock:^(UIImage *image, NSError *error) {        
        [cell.imageView setImage:image];
    }];
    

    您可能想要调用该UITableView方法cellForRowAtIndexPath(不要与该UITableViewController方法混淆tableView:cellForRowAtIndexPath:):

    [[ImageManager sharedManager] modifyImage:[UIImage imageNamed:@"anImage.png"] completionBlock:^(UIImage *image, NSError *error) {   
        if (error == nil)
        {     
            UITableViewCell *updateCell = [tableView cellForRowAtIndexPath:indexPath];
    
            // if cell has scrolled off screen, this will be `nil`, so let's check
    
            if (updateCell)
                [updateCell.imageView setImage:image];
        }
    }];
    
  3. 您可能希望对该imageNamed方法本身在首先检索图像时可能会很慢这一事实保持敏感。如果所有单元格都返回相同的imageNamed,这不是问题,但如果 (a) 使用唯一imageNamed文件;(b) 如果处理较旧、速度较慢的设备,您甚至可能还希望将imageNamed进程推入后台队列。在较新的设备(或模拟器)上,您永远不会看到问题,但如果您在旧的、速度较慢的设备上进行测试,您可能会在快速的 tableview 滚动中看到一些卡顿(但由于imageNamed缓存,您只会看到这个首次检索图像时 UI 口吃……如果您对所有图像使用相同的图像名称,您可能看不到这一点。)

  4. 您的呼叫completBlock正在后台队列中完成。由于您正在进行 UI 更新,您可能需要确保将其分派回主队列:

    [[NSOperationQueue mainQueue] addOperationWithBlock:^{
        // call the completBlock here
    }];
    
于 2013-03-27T16:06:00.533 回答