0

私は次のようなファイアアンドフォーゲットクラスメソッドを実装しようとしています

+ (void)sendAsynchronousRequest:(NSURLRequest *)request queue:(NSOperationQueue *)queue completionHandler:(void (^)(NSURLResponse*, NSData*, NSError*))handler

NSURLConnection、しかし私はメモリ管理について少し混乱しています(私は現在ARCを使用していません)。

私の現在のコードは次のようになります。

@interface StuffInfoDownloader() <UIAlertViewDelegate>

typedef void (^StuffInfoDownloaderCompletionBlock)(NSArray *stuffs);

- (id)initStuffsWithIdentifiers:(NSSet *)identifiers
         completionHandler:(void (^)(NSArray *stuffs))handler;

@property (retain, nonatomic) StuffInfoDownloaderCompletionBlock completionHandler;
@property (retain, nonatomic) NSSet *identifiers;

@end

@implementation StuffInfoDownloader

@synthesize completionHandler = _completionHandler;
@synthesize identifiers = _identifiers;

+ (void)loadAsynchronouslyWithIdentifiers:(NSSet *)identifiers
                    completionHandler:(void (^)(NSArray *stuffs))handler
{
    StuffInfoDownloader *downloader = [[StuffInfoDownloader alloc] initStuffsWithIdentifiers:identifiers completionHandler:handler];

    [downloader downloadStuffs];
    [downloader release]; // will retain itself
}

- (id)initStuffsWithIdentifiers:(NSSet *)identifiers
          completionHandler:(void (^)(NSArray *stuffs))handler
{

    if (!(self = [super init])) {
        return nil;
    }

    [self retain];

    _completionHandler = handler;
    _identifiers = identifiers;

    return self;
}

- (void)downloadStuffs
{
    __block StuffInfoDownloader *me = self; // avoid reference cycle between self and the block
    [StuffsConnection loadAsynchronouslyWithIdentifiers:self.identifiers completionHandler:
    ^(NSArray *stuffs, NSError *error) {
         if(error) {
             UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Connection Failed."
                                                         message:@"TODO do localised string"
                                                        delegate:self cancelButtonTitle:@"OK"
                                               otherButtonTitles:nil, nil];
             [alert show];
             [alert release];
         } else {
             me.completionHandler(stuffs);
             [self release];
         }
    }];
}

#pragma mark UIAlertViewDelegate

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
#pragma unused(alertView, buttonIndex)
    // try again
    [self downloadStuffs];
}

- (void)dealloc
{
    [_completionHandler release];
    [_identifiers release];
    [super dealloc];
}

基本的に、私はオブジェクトの所有権をそれ自体に渡し、ハンドラーでそれを解放します。それで何か問題はありますか?

4

2 に答える 2

1

このコードには多くの問題があります。ブロックプロパティに加えて、である必要がありますcopy[self retain];and (エラーの場合に[self release];見逃したps )を実行しないでください。[self release]これは、メモリ管理ルールに完全に反します。あなたが正しいことをすれば、それらは完全に不要です。Cocoaのメモリ管理は完全にローカルです。関数またはメソッドは、他のコードが行うことではなく、それが行うことだけを気にする必要があります。init行う理由はなく[self retain]、他のコードが何をするかについて「心配」する必要はありません。限目。

その後、_completionHandler = handler; _identifiers = identifiers;間違っています。ブロックをインスタンス変数に格納する場合は、ブロックをコピーする必要があります。セットは保持またはコピーする必要があります。どちら かを行うか_completionHandler = [handler copy]; _identifiers = [identifiers retain];、セッターを使用する必要がありますself.completionHandler = handler; self.identifiers = identifiers;

そうすれば、「保持サイクル」の問題はありません。保持サイクルにはサイクルが必要です-AはBを保持し、BはAを保持します。ブロックは保持しますselfself、ブロックを保持しますか?どこにも見当たりません。このブロックで別のクラスのクラスメソッドを呼び出しているだけです。したがって、弱参照を行うべきではありません。ブロックが実行されるまでに現在のオブジェクトが有効であるという保証がないため、弱参照はとにかく正しくありません。

あなたは(間違って)すべてをやったようですが[self retain]、それはすべて、あなたが(また間違って)ブロックを保持することを許可しなかったという事実に対処するためselfです。この弱参照のものを取り除く[self retain]だけで、それを取り除くと、メモリ管理ルールに従うだけでなく、より堅牢になるだけでなく、よりクリーンで、よりシンプルで、より理解しやすくなります。

于 2012-10-12T08:54:24.747 に答える
0
@property (nonatomic, copy) StuffInfoDownloaderCompletionBlock
completionHandler;

その後、initで:

self.completionHandler = handler;

ブロックを以前にコピーしたことがない場合は、ブロックを保持しないでください。意味がありません。

ところで

 if ((self = [super init])) {
    /* initialization stuff*/
    }


 return self;

あなたのコードにはretainCycleの欠陥のデザインがたくさんあるようです

于 2012-10-11T11:20:24.573 に答える