9

在创建具有自定义形状的 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 错误还是我做错了什么。

4

5 回答 5

4

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

于 2013-12-05T08:38:25.150 回答
2

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

CGPathRef pathCopy = CGPathCreateCopy(path);
CGPathRelease(path);
self.physicsBody = [SKPhysicsBody bodyWithPolygonFromPath:pathCopy];
CGPathRelease(pathCopy);
于 2013-11-29T22:07:19.880 回答
2

对参数的唯一限制path是:

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

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

于 2013-11-29T22:10:33.453 回答
2

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

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

但是,您可以通过以下方式解决此问题

(1) 子类化 SKShapeNode,应该持有 SKPhysicsBody,

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

(3)当shape节点被释放时,检查路径的retain count。如果它 > 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 实际发布路径(正如他们应该做的那样)后,它也应该适用于其他版本。

于 2014-07-26T16:49:30.083 回答
2

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

于 2016-01-28T22:59:18.253 回答