Bil*_*Kan 3 objective-c ios objective-c-blocks automatic-ref-counting sprite-kit
我在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];
});
});
Run Code Online (Sandbox Code Playgroud)
它正常工作正常.但是,如果Facebook个人资料图片的加载速度很慢,则用户可能已经在加载图片时切换到另一个屏幕.在这种情况下,self实际上将从场景层次结构中删除,并且不会对其进行引用.
当我读取块文档时,我认为异步块将保留self,因此我认为它在调用主线程块时仍然有效.事实证明,如果pic加载非常慢,并且当从层次结构中删除self时调用第二个dispatch_async,则该行将发生错误的访问错误[self addChild:fbFrame].
我是否理解块内存管理不正确?有没有办法解决这类问题?
您对内存管理的理解是正确的,self此块中的存在将保留,self直到调度块完成.解决方案是self在块运行时不保留,通过使用块内部的weak引用self:
__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];
});
});
Run Code Online (Sandbox Code Playgroud)
您应该查看该块以获取任何引用self(通过直接引用ivar显式或隐式),并将其替换为weakSelf.
或者,(在这种情况下不太可能)有时你会看到weakSelf/ strongSelfpattern必须确保self在执行嵌套块期间不释放:
__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
}
});
});
Run Code Online (Sandbox Code Playgroud)
顺便说一下,另一种方法是在取消视图控制器时取消网络请求.这需要更大的改变(使用NSOperation基于GCD的方法;或者使用NSURLSessionTask基于允许您取消请求的基于方法),但这是解决此问题的另一种方法.