【发布时间】:2012-07-12 13:38:44
【问题描述】:
最近我被放到了别人的代码库中,到目前为止,我已经能够处理它抛出的大多数事情,但这个有点超出我的想象。有一些保留周期我不知道如何解决。
有一个包装 FROAuthRequest 的自定义对象,FROAuthRequest 有一个完成块,其中还使用了 3 个块,一个解析块、完成块和失败块。完成、完成和失败块都导致了一个保留周期。
我知道原因是对块内 ivars 的引用,但我尝试过的方法没有奏效,请参阅帖子末尾了解我尝试过的内容。
以下代码是我开始尝试修复它之前的样子。代码路径如下:
1:创建请求:
//in the MainViewController.m
SHRequest *request = [api userInfo];
2:创建 SHRequest 的方法
//in API.m
-(SHRequest*)userInfo{
FROAuthRequest *request = [[FROAuthRequest alloc]initWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@%@",SH_URL_API,SH_URL_USER_INFO]]
consumer:consumer
token:token
realm:nil
signatureProvider:signatureProvider];
//wrap the FROAuthRequest in our custom object
//see below for the SHRequest
SHRequest *shRequest = [[SHRequest alloc]initWithRequest:request];
//set the parsing block
shRequest.parsingBlock = ^id(FROAuthRequest* finishedRequest){
NSDictionary *jsonResponse = [finishedRequest.responseData objectFromJSONData];
[user release];
user = [[SHUser alloc]initWithJSON:[jsonResponse objectForKey:@"user"]];
//more code
return [NSDictionary dictionaryWithObjectsAndKeys:user,kUserKey,nil];
};
[request release];
return [shRequest autorelease];
}
3:SHRequest
//in SHRequest.m
-(id)initWithRequest:(FROAuthRequest*)_underlyingRequest{
if(self = [super init]){
underlyingRequest = [_underlyingRequest retain];
//this is the majority of the post processing
underlyingRequest.completionBlock = ^{
//if the requests fails call the fail block
if(underlyingRequest.responseStatusCode != 200){
if(failBlock != nil)
failBlock();
return;
}
if([underlyingRequest.responseData length] > 0){
[object release];
object = parsingBlock(underlyingRequest);
[object retain];
if((underlyingRequest.error || !object) && failBlock != nil)
failBlock();
else if(finishBlock != nil)
finishBlock();
}else if(failBlock != nil)
failBlock();
};
underlyingRequest.failedBlock = ^{
if(failBlock)
failBlock();
};
}
return self;
}
4:一旦从 userInfo 方法 (1) 返回 SHRequest,就设置完成和失败块。 (对于这种情况,没有设置 failBlock。
//in MainViewController.m
request.finishBlock = ^{
NSDictionary *userInfo = request.object;
//User
SHUser *user = [userInfo objectForKey:kUserKey];
//more code
};
[request send];
这是我尝试过的方法
我将 completionBlock 代码移动到启动请求并使用 __block 类型的方法中,泄漏似乎已经消失,但是当完成块运行时,一些 __block vars 是僵尸。
//in SHRequest.m
-(void)send{
__block void(^fail)(void) = failBlock;
__block void(^finish)(id) = finishBlock;
__block id(^parsing)(FROAuthRequest*) = parsingBlock;
__block FROAuthRequest *req = underlyingRequest;
underlyingRequest.completionBlock = ^{
if(req.responseStatusCode != 200){
if(fail != nil)
fail();
return;
}
if([req.responseData length] > 0){
id obj = parsing(req);//<--- parsing is a zombie
if((req.error || !obj) && fail != nil)
fail();
else if(finish != nil)
finish(obj);//<--- finish is a zombie
}else if(fail != nil)
fail();
};
underlyingRequest.failedBlock = ^{
if(fail)
fail();
};
[underlyingRequest startAsynchronous];
}
关于我做错了什么有什么想法吗?
【问题讨论】:
-
您能否发布
failBlock、finishBlock和parsingBlock的声明方式?另外,这是 ARC 吗? -
不,它不是 ARC,并且问题中已经存在完成和解析块,请参阅第 2 点的解析和第 4 点的完成。
-
所以听起来这些块在被访问之前就被释放了。您是否在属性上使用
copy所有权修饰符?声明应类似于@property(copy)id(^parsingBlock)(FROAuthRequest* finishedRequest); -
是的,它被复制了,在块执行之前发生了一个自动释放,因为 SHRequest 对象作为一个方法从一个自动释放的对象返回,但我什至不明白这是怎么回事原因...
-
好的,还有一个问题——你支持 iOS 4,还是只支持 5+?
标签: ios memory-leaks objective-c-blocks