Сбой при использовании [UIBezierPath CGPath] с CAShapeLayer под ARC

Я получаю сообщение об ошибке BAD ACCESS при использовании [UIBezierPath CGPath] с CAShapeLayer в ARC. Я пробовал соединять различными способами, но я не уверен, что проблема в этом. Я изолировал сбой, используя результат метода makeToPath:

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

Но это не сбой:

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

Что-то не так с путем, созданным makeToPath? Я планирую использовать пути from и to с CABasicAnimation, как только решу этот сбой. Каково правильное соединение ARC для CGPathRefs от UIBezierPath?

-(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;

Я также попытался заставить мои методы возвращать экземпляр 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;
}

person spring    schedule 22.01.2013    source источник


Ответы (2)


Проблема заключается в возврате CGPath. Возвращаемое значение представляет собой CGPathRef, который не охватывается ARC. Созданный вами UIBezierPath освобождается после завершения метода. Таким образом также освобождается CGPathRef. Вы можете указать исходную аннотацию, чтобы сообщить ARC о ваших намерениях:

В файле .h:

-(CGPathRef)makeToPath CF_RETURNS_RETAINED;
-(CGPathRef)makeFromPath CF_RETURNS_RETAINED;
person diederikh    schedule 22.01.2013
comment
спасибо - у меня не возникает сбой при использовании метода с использованием функции bezierPathWithRect. Это сбивает с толку. Знаете, почему этот подход не дает сбоев? - person spring; 22.01.2013
comment
Хм. Я еще не знал, что эти аннотации можно использовать. Это потрясающе, спасибо! (Документы здесь, для любопытных: clang.llvm.org/docs/) - person Jesse Rusak; 22.01.2013
comment
@skinnyTOD: поведение при доступе к выпущенному объекту не определено. - person diederikh; 23.01.2013
comment
ARC не управляет объектами CF. Как добавление квалификатора к методу повлияет на управление памятью? - person Duncan C; 26.01.2013
comment
@DuncanC - это не так. добавление этих квалификаторов не повлияло на сбои, которые я испытываю. - person Alex Gray; 28.03.2013

Как указал другой плакат, вы возвращаете ссылку CGPath, взятую из объекта UIBezierPath, который выходит за рамки в конце метода. Как говорится в документации по свойству 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);
}
person Duncan C    schedule 25.01.2013
comment
Аннотация CF_RETURNS_RETAINED на самом деле необходима только в том случае, если ваш метод не соответствует правила именования CoreFoundation. Вместо этого рекомендуется переименовать ваши методы. - person Jan Gorman; 14.08.2013