[英]How to implement a class method with a block completion handler
我正在尝试实现类似于即弃类的方法
+ (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];
}
基本上,我将对象的所有权传递给自身,并将其释放到处理程序中。 有什么问题吗?
此代码有很多错误。 除了块属性需要copy
。 您不应该进行[self retain];
和[self release];
(ps在错误的情况下错过了[self release]
)。 这完全违反了内存管理规则。 如果您做正确的事,它们是完全不必要的。 Cocoa中的内存管理完全是本地的-函数或方法只需要关心它的作用,而不需要其他代码的作用。 init
没有理由进行[self retain]
,也不必“担心”任何其他代码的作用。 期。
然后_completionHandler = handler; _identifiers = identifiers;
_completionHandler = handler; _identifiers = identifiers;
错了。 如果将块存储在实例变量中,则需要复制该块; 并且该集需要保留或复制。 你需要做的要么 _completionHandler = [handler copy]; _identifiers = [identifiers retain];
_completionHandler = [handler copy]; _identifiers = [identifiers retain];
或者使用setter self.completionHandler = handler; self.identifiers = identifiers;
self.completionHandler = handler; self.identifiers = identifiers;
。
这样,就没有“保留周期”的问题。 保留循环需要一个循环-A保留B,B保留A。块保留self
,但是self
保留块吗? 我在任何地方都看不到。 您只是在此块上调用另一个类的类方法。 因此,您不应该做薄弱的参考。 无论如何,弱引用是不正确的,因为不能保证在块执行时当前对象将是有效的。
似乎是您(错误地)完成了整个[self retain]
事情,都是为了处理您(也是错误地)不允许该块保留self
的事实,它应该这样做。 只需摆脱这些弱参考资料,再摆脱[self retain]
资料,它就不仅会遵循内存管理规则,变得更加健壮,而且看起来更简洁,更容易理解。
@property (nonatomic, copy) StuffInfoDownloaderCompletionBlock
completionHandler;
然后初始化:
self.completionHandler = handler;
如果您以前没有复制过块,则永远不要保留它,这是没有道理的。
顺便说说
if ((self = [super init])) {
/* initialization stuff*/
}
return self;
似乎您的代码有很多retainCycle缺陷设计
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.