如何使用块完成处理程序实现类方法

时间:2012-10-11 10:02:39

标签: objective-c ios asynchronous objective-c-blocks

我正在尝试实现类似于

的即发即弃类方法
+ (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];
}

基本上,我将对象的所有权传递给自己,并在处理程序中释放它。有什么问题吗?

2 个答案:

答案 0 :(得分:1)

这段代码有很多问题。除了需要copy的块属性。您不应该执行[self retain];[self release];(在错误情况下,您错过了[self release]。这完全违背了内存管理规则。如果你做对了就完全没必要了。 Cocoa中的内存管理完全是本地的 - 一个函数或方法只需要关心所做的事情,而不是任何其他代码所做的事情。 init没有理由[self retain],并且不必担心其他代码的作用。周期。

然后_completionHandler = handler; _identifiers = identifiers;错了。如果要将块存储在实例变量中,则需要复制该块;并且需要保留或复制该集。您需要 _completionHandler = [handler copy]; _identifiers = [identifiers retain];或使用setter self.completionHandler = handler; self.identifiers = identifiers;

然后,没有“保留周期”的问题。保留周期需要一个周期 - A保留B,B保留A.块保留self,但self保留块?我什么都没看到。您只是在此块上调用另一个类的类方法。所以你不应该做弱参考。弱引用无论如何都是不正确的,因为无法保证当前对象在块执行时有效。

似乎你(错误地)做了整个[self retain]事情,所有这些都是为了处理你(也是错误地)不允许块保留self的事实,因为它应该。只是摆脱这些弱参考的东西,摆脱[self retain]的东西,然后它不仅会遵循内存管理规则,更健壮,而且看起来更干净,更简单,更容易理解。

答案 1 :(得分:0)

@property (nonatomic, copy) StuffInfoDownloaderCompletionBlock
completionHandler;

然后在init:

self.completionHandler = handler;

如果您之前没有复制过,那么你永远不应该保留阻止,这是没有意义的。

顺便说一下

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


 return self;

似乎你的代码有很多retainCycle缺陷设计