【问题标题】:Crash using [UIBezierPath CGPath] with CAShapeLayer under ARC在 ARC 下使用带有 CAShapeLayer 的 [UIBezierPath CGPath] 崩溃
【发布时间】:2013-01-22 19:19:00
【问题描述】:

在 ARC 下使用 [UIBezierPath CGPath]CAShapeLayer 时遇到 BAD ACCESS 错误。我尝试过以各种方式进行桥接,但我不清楚这是否是问题所在。我已将崩溃隔离为使用 makeToPath 方法的结果:

 maskLayer = [CAShapeLayer layer];
 maskLayer.path = [self makeToPath];

但这不会崩溃:

 maskLayer = [CAShapeLayer layer];
 maskLayer.path = [self makeFromPath];

makeToPath 创建的路径是否有问题?一旦我解决了这个崩溃,我计划使用带有CABasicAnimationfromto 路径。来自UIBezierPathCGPathRefs 的正确ARC 桥接是什么?

-(CGPathRef)makeToPath
{
    UIBezierPath* triangle = [UIBezierPath bezierPath];
    [triangle moveToPoint:CGPointZero];
    [triangle addLineToPoint:CGPointMake(self.view.frame.size.width,0)];
    [triangle addLineToPoint:CGPointMake(0, self.view.frame.size.height)];
    [triangle closePath];
    return [triangle CGPath];
}

-(CGPathRef)makeFromPath
{
    UIBezierPath*rect = [UIBezierPath bezierPathWithRect:self.view.frame];
    return [rect CGPath];
}

更新所以我根据下面的答案更改了我的 .h 文件,但我仍然遇到崩溃

-(CGPathRef)makeToPath CF_RETURNS_RETAINED;
-(CGPathRef)makeFromPath CF_RETURNS_RETAINED;

我还尝试让我的方法根据答案 here(如下所示)返回一个 UIBezierPath 实例。仍然没有成功。有人想给我详细解释一下如何解决这个问题吗?

maskLayer.path = [[self makeToPath] CGPath];// CRASHES
morph.toValue =  CFBridgingRelease([[self makeToPath] CGPath]);// CRASHES

-(UIBezierPath*)makeToPath
{
    UIBezierPath* triangle = [UIBezierPath bezierPath];
    [triangle moveToPoint:CGPointZero];
    [triangle addLineToPoint:CGPointMake(self.view.frame.size.width,0)];
    [triangle addLineToPoint:CGPointMake(0, self.view.frame.size.height)];
    [triangle closePath];
    return triangle;
}

【问题讨论】:

  • 我一直使用最后的语法,让makeToPath 返回UIBezierPath *,然后使用maskLayer.path = [[self makeToPath] CGPath];。但我也立即使用maskLayer,例如[self.view.layer addSublayer:maskLayer]。设置path 后,你在用maskLayer 做什么?你能告诉我们吗?
  • @Rob - 我清理了一些代码,运行了nap debugger,现在它可以工作了。

标签: ios core-animation uibezierpath cashapelayer


【解决方案1】:

问题在于返回 CGPath。返回的值是 ARC 未涵盖的 CGPathRef。您创建的UIBezierPath 在方法结束后被释放。因此也释放了 CGPathRef。 您可以指定源注释以让 ARC 知道您的意图:

在.h文件中:

-(CGPathRef)makeToPath CF_RETURNS_RETAINED;
-(CGPathRef)makeFromPath CF_RETURNS_RETAINED;

【讨论】:

  • 谢谢 - 我没有得到使用 bezierPathWithRect 函数的方法的崩溃。这很令人困惑。你知道为什么这种方法不会崩溃吗?
  • 嗯。我还不知道这些注释是否可用。太棒了,谢谢! (文档在这里,对于好奇:clang.llvm.org/docs/…
  • @skinnyTOD:访问已释放对象的行为未定义。
  • ARC 不管理 CF 对象。向方法添加限定符对内存管理有何影响?
  • @DuncanC - 它没有。添加这些限定符对我遇到的崩溃没有影响。
【解决方案2】:

正如另一张海报所指出的,您正在返回从 UIBezierPath 对象获取的 CGPath 引用,该对象在方法结束时超出范围。正如 UIBezierPath CGPath 属性上的文档所说:

路径对象本身归 UIBezierPath 对象所有,并且是 仅在您对路径进行进一步修改之前有效。

您需要创建 CGPath 的副本并将其返回:

-(CGPathRef)makeToPath
{
    UIBezierPath* triangle = [UIBezierPath bezierPath];
    [triangle moveToPoint:CGPointZero];
    [triangle addLineToPoint:CGPointMake(self.view.frame.size.width,0)];
    [triangle addLineToPoint:CGPointMake(0, self.view.frame.size.height)];
    [triangle closePath];
    CGPathRef theCGPath = [triangle CGPath];
    return CGPathCreateCopy(theCGPath);
}

我阅读 llvm 项目链接的方式,我认为 cf_returns_retained 限定符是为了告诉调用者返回值的内存管理策略,而不是为你做保留。

因此,我认为您都需要创建路径的副本并添加 cf_returns_retained 限定符。但是,我不清楚该限定符的语法。 (以前没用过。)

假设另一张海报的语法正确,它看起来像这样:

-(CGPathRef)makeToPath CF_RETURNS_RETAINED;
{
    UIBezierPath* triangle = [UIBezierPath bezierPath];
    [triangle moveToPoint:CGPointZero];
    [triangle addLineToPoint:CGPointMake(self.view.frame.size.width,0)];
    [triangle addLineToPoint:CGPointMake(0, self.view.frame.size.height)];
    [triangle closePath];
    CGPathRef theCGPath = [triangle CGPath];
    return CGPathCreateCopy(theCGPath);
}

【讨论】:

  • CF_RETURNS_RETAINED 注解实际上仅在您的方法不符合CoreFoundation naming rules 时才需要。建议改为重命名您的方法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多