1

これはこの投稿の続きです。NSOperationのブロックに問題があり、アプリケーションがでクラッシュしますcompletionblock

問題は保持サイクルだと思います(警告があります:) 、この解決策Capturing self strongly in this block is likely to lead to a retain cycleを試します(上記のコードを参照)、警告は消えましたが、アプリはまだクラッシュします。

アプリケーションは単純なtableViewです。操作は、渡された画像にいくつかの変更を適用します。

自分でブロックを作成して使用するのはこれが初めてです。基本的な説明をお願いします。

コード:

TableViewController.m:

tableView内: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

ImageManager.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

ImageOperations.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

ImageOperations.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を使用したプログラミング」ガイドの「ブロックの操作」セクションでは、代わりに使用することをお勧めします。(ブロックが使用するオブジェクトではなく、ブロックをコピーしていることに注意してください。ただし、そのブロック内の強力な参照に注意する必要があります。)ブロック変数の範囲は驚くほど微妙です。(例については、「ブロックプログラミングトピックガイド」の「避けるべきパターン」を参照してください。)また、リリースされたaを呼び出すと、予期しない結果が生じる可能性があります。を使用します。copyblockweakcopy

  2. テーブルビューには微妙な問題があります。これは、完了ブロックが呼び出されるまでにセルがスクロールオフされた場合に発生します。セルは、テーブルの別の行で再利用された可能性があります。(結局のところ、コーナー丸めロジックはおそらくこの問題が顕在化する可能性が低いほど十分に高速ですが、画像のダウンロードなどの低速操作を行う場合や画像が巨大な場合は重要です。)とにかく、次の代わりに:

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

    UITableViewメソッドを呼び出したい場合があります(メソッドcellForRowAtIndexPathと混同しないでください)。UITableViewControllertableView: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プロセスをバックグラウンドキューにプッシュすることもできます。新しいデバイス(またはシミュレーター)では、問題は発生しませんが、古い低速のデバイスでテストすると、高速のテーブルビュースクロールで途切れが発生する場合があります(ただし、imageNamedキャッシュがあるため、これのみが表示されます)画像が最初に取得されたときのUIの途切れ...すべてに同じ画像名を使用している場合、これが表示されない可能性があります。)

  4. への呼び出しcompletBlockはバックグラウンドキューで実行されています。UIの更新を行っているので、これを必ずメインキューにディスパッチすることをお勧めします。

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