我在iOS7中使用新的SpriteKit编写了一个游戏。我有一个定制的SKSpriteNode
,可以获取并显示Facebook个人资料图片。但是,因为加载图片可能需要一些时间。我初始化节点时尝试在后台加载图片,并仅在加载图片时显示。这是我写的代码片段:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Code to load Facebook Profile picture
// ...
SKSpriteNode *fbFrame = [SKSpriteNode spriteNodeWithTexture:facebookPicTexture];
dispatch_async(dispatch_get_main_queue(), ^{
// self is my customised SKSpriteNode - so here I just add a sprite node of
// a facebook picture to myself as a child
[self addChild:fbFrame];
});
});
通常工作正常。但是,如果Facebook个人资料图片的加载速度很慢,则用户可能已经在加载图片时切换到另一个屏幕。在这种情况下,self
实际上将从场景层次结构中删除,并且不会对其进行引用。
当我读取块文档时,我认为异步块将保留self
,因此我认为在调用主线程块时它仍然有效。事实证明,如果pic加载非常慢,并且当从层次结构中删除self时调用第二个dispatch_async,则行[self addChild:fbFrame]
将发生错误的访问错误。
我是否理解块内存管理不正确?有没有办法解决这类问题?
答案 0 :(得分:7)
您对内存管理的理解是正确的,此块中self
的存在将保留self
,直到调度块完成。解决方案是在块运行时不保留self
,方法是使用块内weak
的{{1}}引用:
self
您应该查看该块以查看对{{1}}的任何引用(通过直接引用ivar显式或隐式),并将其替换为__weak typeof(self) weakSelf = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// do some stuff in background here; when done, then:
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf addChild:fbFrame];
});
});
。
或者,(在这种情况下不太可能)有时您会看到self
/ weakSelf
模式,您必须确保在执行该嵌套块期间未释放weakSelf
:
strongSelf
顺便说一下,另一种方法是在取消视图控制器时取消网络请求。这需要更大的改变(使用基于self
的方法而不是GCD;或者使用基于__weak typeof(self) weakSelf = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// do some stuff in background here; when done, then:
dispatch_async(dispatch_get_main_queue(), ^{
typeof(self) strongSelf = weakSelf;
if (strongSelf) {
// stuff that requires that if `self` existed at the start of this block,
// that it won't be released during this block
}
});
});
的方法来允许您取消请求),但这是解决此问题的另一种方法。
答案 1 :(得分:-1)
我认为你需要这样写:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0ul);
objc_setAssociatedObject(self, @"yourTag", @"Alive", OBJC_ASSOCIATION_RETAIN);
dispatch_async(queue, ^{
// Code to load Facebook Profile picture
// ...
SKSpriteNode *fbFrame = [SKSpriteNode spriteNodeWithTexture:facebookPicTexture];
dispatch_async(dispatch_get_main_queue(), ^{
NSString *strAlive = (NSString *)objc_getAssociatedObject(self, @"yourTag");
if (strAlive)
{
// self is my customised SKSpriteNode - so here I just add a sprite node of
// a facebook picture to myself as a child
[self addChild:fbFrame];
}
});
});
dispatch_release(queue);
当你不希望调度继续执行过程时,自我(ViewController)不再可见,所以写下:
objc_setAssociatedObject(self, @"yourTag", nil, OBJC_ASSOCIATION_RETAIN);
在:
- viewWillDisapear
或- viewDidDisapear
当你回到屏幕时更新调用发送。