【发布时间】:2013-03-12 05:25:19
【问题描述】:
我为我的 iPhone 游戏开发了一个排行榜显示类。该类具有以下实例方法。
-(void)displayScoresWithRequest:(CBLeaderboard*)request completionHandler:(void(^)())completionHandler
{
if (request_ != nil)
return;
request_ = [[CBLeaderboard alloc] init];
[request_ setCategory:[request category]];
[request_ setPlayerScope:[request playerScope]];
[request_ setTimeScope:[request timeScope]];
[request_ setRange:[request range]];
__block CBLeaderboardDisplay* blockSelf = self;
[request_ loadScoresWithCompletionHandler:^(NSArray* scores, NSError* error)
{
blockSelf->request_ = nil;
NSUInteger scoresCount = [scores count];
if (scoresCount == 0 && error != nil)
return;
NSMutableArray* playerIDs = [NSMutableArray array];
for (GKScore* score in scores)
[playerIDs addObject:[score playerID]];
[GKPlayer loadPlayersForIdentifiers:playerIDs withCompletionHandler:^(NSArray* players, NSError* error)
{
if (scoresCount > [players count] && error != nil)
return;
[blockSelf displayScores:scores players:players];
completionHandler();
}];
}];
[request_ release];
}
如您所见,该方法复制排行榜请求,执行它,然后调用提供的完成处理程序。我游戏中的一个图层调用这个方法如下。
-(void)refreshDisplay
{
CBLeaderboard* request = [[CBLeaderboard alloc] init];
[request setCategory:[[sharedGameCenterManager_ classicLeaderboard] category]];
[request setPlayerScope:GKLeaderboardPlayerScopeFriendsOnly];
[request setTimeScope:GKLeaderboardTimeScopeAllTime];
static NSRange kRequestRange = NSMakeRange(1, 3);
[request setRange:kRequestRange];
__block GJGameOver* blockSelf = self;
[display_ displayScoresWithRequest:request completionHandler:^
{
CGSize displayContentSize = [blockSelf->display_ contentSize];
displayContentSize.width = width(blockSelf) - 2.0 * kGJLabelPadding;
[blockSelf->display_ setContentSize:displayContentSize];
CGFloat displayHeight =
bottomEdge(blockSelf->multiplierLabel_) - topEdge(blockSelf->menu_) - 2.0 * kGJLabelPadding;
CGFloat displayScoreDisplaysCount = [blockSelf->display_ scoreDisplaysCount];
CGFloat displayLabelPadding =
(displayHeight - [blockSelf->display_ minContentSize].height) / displayScoreDisplaysCount;
[blockSelf->display_ setLabelPadding:MIN(floor(displayLabelPadding), kGJLabelPadding)];
static CGFloat kFadeInDuration = 2.0;
if ([blockSelf->display_ opacity] == 0)
[blockSelf->display_ runAction:[CCFadeIn actionWithDuration:kFadeInDuration]];
}];
[request release];
}
当层和显示都被释放并且请求未完成时,我的游戏崩溃了。当请求完成时,它会尝试向已释放的实例发送消息,然后崩溃。是否可以取消排行榜请求?如果没有,有什么方法可以避免崩溃而不会导致内存泄漏?
【问题讨论】:
-
在 ARC 上下文中,除了 __block 之外,您还需要 __weak 来消除保留周期。此外,这些都不是明确的线程安全的,这是 GameKit 对回调的要求。
-
天哪。那么,这可能会有点毛茸茸。您的线程,因此该块需要一个 self 的副本来保留,这样就不会发生这种过早的释放事件。我会去掉块限定符,让块保留一个 self 的副本,这听起来很奇怪。
-
@CodaFi 谢谢。我自己也想过。希望dealloc仍然会发生。我必须调试和验证。
-
或者你可以广播一个通知。没有更多的保留周期,如果 self 走上了恐龙的道路,它将是一个 No-Op(只要您在 dealloc 中取消注册)
-
@CoadFi 你能详细说明一下吗?我可以想到一个解决方案,在 dealloc 上设置一些全局变量,然后块可以读取,但这样的解决方案至少可以说是丑陋的:D
标签: objective-c objective-c-blocks gamekit retain-cycle