SKPhysicsBody bodyWithPolygonFromPath 内存泄漏

Posted

技术标签:

【中文标题】SKPhysicsBody bodyWithPolygonFromPath 内存泄漏【英文标题】:SKPhysicsBody bodyWithPolygonFromPath memory leaks 【发布时间】:2013-12-06 17:52:32 【问题描述】:

在使用自定义形状创建 Sprite Kit 物理实体时,我遇到了奇怪的内存泄漏。这是我的实现的样子:

CGFloat offsetX = self.frame.size.width * self.anchorPoint.x;
CGFloat offsetY = self.frame.size.height * self.anchorPoint.y;

CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, NULL, 4 - offsetX, 3 - offsetY);
CGPathAddLineToPoint(path, NULL, 66 - offsetX, 3 - offsetY);
CGPathAddLineToPoint(path, NULL, 35 - offsetX, 57 - offsetY);
CGPathCloseSubpath(path);

self.physicsBody = [SKPhysicsBody bodyWithPolygonFromPath:path];

CGPathRelease(path);

SKSpriteNode 方法中的一切都在进行。 Instruments 告诉我在创建此类主体后发生了几次内存泄漏:

Leaked object: 
  Malloc 32 Bytes
Size:
  32 Bytes
Responsible Library: 
  PhysicsKit
Responsible Frame:
  std::__1::__split_buffer<PKPoint, std::__1::allocator<PKPoint>&>::__split_buffer(unsigned long, unsigned long, std::__1::allocator<PKPoint>&)

CGPathRelease(path); 行是必要的 - 没有它,我会得到更多关于 CGPath 的内存泄漏,这是可以理解的。当我改用此实现时(出于测试目的):

CGFloat radius = MAX(self.frame.size.width, self.frame.size.height) * 0.5f;
self.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:radius];

...一切正常,没有内存泄漏。我想知道这是 Sprite Kit 错误还是我做错了什么。

【问题讨论】:

我看不出你能做些什么不同的事情......我认为这显然与对 bodyWithPolygonFromPath 的调用有关......也许你可以在 Apple Dev 论坛上提问?跨度> 调用树,如果有用的话:[SKPhysicsBody bodyWithPolygonFromPath:], [PKPhysicsBody bodyWithPolygonFromPath:], [PKPhysicsBody initWithPolygonFromPath:], [CGPathApply], CG::Path::Sequence::apply, CG::Path::Subpath::apply, CG::Chunk::apply, ::adaptor::callback, PKPathApplyCGPath, std::__1::vector::__push_back_slow_path, std::__1::__split_buffer::__split_buffer 我有同样的问题,除了 bodyWithEdgeChain - 它看起来像 Sprite Kits 处理 SKPhysicsBody 和 SKShapeNode 的 CGPath 中的一个错误 我无法重现该问题。这是否发生在设备和模拟器上?模拟器似乎有一些设备没有的泄漏。 【参考方案1】:

这是精灵套件中的错误。您将不得不等待修复。

【讨论】:

此错误是否已报告给 Apple?我遇到了同样的问题。 前段时间报告了,后来停止使用 sprite kit,所以不知道 7.1 有没有修复【参考方案2】:

如果您执行以下操作,有什么变化吗?

CGPathRef pathCopy = CGPathCreateCopy(path);
CGPathRelease(path);
self.physicsBody = [SKPhysicsBody bodyWithPolygonFromPath:pathCopy];
CGPathRelease(pathCopy);

【讨论】:

【参考方案3】:

path 参数的唯一限制是:

逆时针缠绕且没有自相交的凸多边形路径。这些点是相对于所属节点的原点指定的。

我不知道offsetXoffsetY的值所以我不知道路径是否正确,但假设它们都是0,在我看来这条路径是顺时针而不是逆时针.我会使用常量而不是变量来创建路径,只是为了确保它是正确的,如果它仍然泄漏,我会说这是PhysicsKit 中的一个错误。

【讨论】:

嗯...我会仔细检查路径,但我知道这些规则,我很确定我正确地创建了这条路径。具有凸多边形路径的主体正常运行,就像它应该的那样,内存泄漏只是我注意到的问题。 @Darrarski,如果您按照规范使用框架并且框架中出现泄漏,那么框架中存在错误。即使是 Apple 的开发人员也不是完美的。是泄漏很多还是可以忍受?【参考方案4】:

正如在多个地方所评论的,这看起来像是 SKPhysicsBody 实现中的一个错误,它至少会持续到 ios 7.1。这样做的原因是:

SKPhysicsBody 包含一个实例变量“_path”,它包含调用“bodyWithEdgeChainFromPath”或类似构造函数时传递的初始 CGPathRef 的副本。此实例变量永远不会被释放,因此所有路径都将保留在内存中。

但是,您可以通过

来解决此问题

(1) 继承 SKShapeNode,应该包含 SKPhysicsBody,

(2)在为该节点创建并分配SKPhysicsBody后,检索引用SKPhysicsBody的CGPathRef的实例变量,

(3) 当形状节点被释放时,检查路径的保留计数。如果大于 0,释放它,内存泄漏就消失了。

几乎没有代码开销(除了对所有形状节点进行子类化,使用依赖于 CGPath 的物理体)。只需这样做:

给你的子类添加一个实例变量:


    CGPathRef myPath;

实现在任何子类 SKShapeNode 实现中检索此 CGPath 值的方法。您也可以考虑将其添加为 SKNode 上的一般类别:

- (CGPathRef) getPhysicsBodyPath

    CGPathRef path = nil;
    if ( self.physicsBody )
        object_getInstanceVariable(self.physicsBody, "_path", (void**) &path);
    
    return(path);

此代码将返回节点物理体使用的 CGPathRef 实例。但是请注意,这必须在将物理实体分配给节点后立即完成。稍后(即在 dealloc() 中,这可能会返回一个空值。因此,在创建主体后,将此值存储在实例变量“myPath”中。为了使该代码即使在 Apple 可能修复错误之后也能正常工作,我们'将添加一个额外的保留,这将确保我们可以在我们的 SKNode 被释放后访问该对象(见下文):

    /*
     *  we need to keep a copy of the final path reference, that has been created for the physics body.
     *  retrieving this value during deallocation won't work any more...
     */
    myPath = CFRetain([self getPhysicsBodyPath]);

最后,在你的 SKNode 被释放后,覆盖 'dealloc' 方法并释放路径:

- (void) dealloc

    self.physicsBody = nil;
    [super dealloc];
    if ( myPath != nil ) 
        /* this will work, because we've retained the path for this instance */
        CFIndex rc = CFGetRetainCount (myPath);
        /* this is our own release ... */
        CGPathRelease(myPath);
        /* in case Apple has fixed the release, this is OK, otherwise we'll need
         * to release the path twice to avoid memory leaks
         */
        if ( rc > 1 ) 
            CGPathRelease(myPath);
        
    

这将最终释放此路径并修复内存泄漏。此代码适用于最高 7.1 的所有 iOS 版本,并且在 Apple 最终修复此错误并且 SKPhysicsBoy 实际发布路径(正如他们应该做的那样)后,它也应该适用于其他版本。

【讨论】:

这是迄今为止我见过的最好的解决方案。经过测试、验证和实施:github.com/Tantas/SKShapeNodeLeakFree. 更新:对于 iOS 版本 > 7.1,它看起来已经修复了。如果您有更高版本,请避免调用 CFRetain([self getPhysicsBodyPath])。通过检查 tagret 设备的 iOS 版本。然后,其余部分仍将按说明工作。【参考方案5】:

我也遇到过类似的内存泄漏,但在我从 viewDidLoad 函数中删除了一行后,它得到了修复skView.showsPhysics = YES;

【讨论】:

以上是关于SKPhysicsBody bodyWithPolygonFromPath 内存泄漏的主要内容,如果未能解决你的问题,请参考以下文章

SpriteKit:SKPhysicsBody 溜走

将精灵附加到 SKPhysicsBody

SKSpriteNode 卡在 init(edgeLoopF​​rom:) - SKPhysicsBody

SKPhysicsBody 未与 SKShapeNode 对齐

为啥 SKPhysicsBody -bodyWithEdgeLoopF​​romRect 不对碰撞做出反应?

SKPhysicsBody 不如预期