2013-03-07 2 views
5

Я просто Настройкой CABasicAnimation для shadowPath собственности, и это сделало меня любопытным:моста литой требуется, противоречивое предупреждение при попытке использовать CGPathRef

shadowAnimation.toValue = (id)newShadowPath.CGPath; 

[Примечание: shadowAnimation приведен CABasicAnimation объекта и newShadowPath является UIBezierPath объект]

это работает, и никаких ошибок/предупреждений не видно в Xcode. Однако, если я пишу, что как так:

CGPathRef test = newShadowPath.CGPath; 
shadowAnimation.toValue = (id)test; 

Это не будет компилироваться, выбрасывая это предупреждение:

Cast of C pointer type 'CGPathRef' (aka 'const struct CGPath *') to Objective-C pointer type 'id' requires a bridged cast 

Так что требует от меня, чтобы напечатать это нравится:

shadowAnimation.toValue = (__bridge id)test; 

сейчас почему это так? Почему бы мне не получить ту же ошибку в первом примере, когда я использую только (id) newShadowPath.CGPath; ? Правильно ли было бы размещать __bridge, вне зависимости от того, Xcode не обнаружил никаких проблем? Или я не понимаю, в чем тут разница?

ответ

2

Причина разницы заключается в новых (и немного сложных) правилах преобразования, введенных в clang 3.1.

Во-первых, позволяет варить его вниз:

@@interface Foo : NSObject 
+ (CGPathRef)bar; 
@end 

CGPathRef foo(); 

void test() 
{ 
    // works without bridged cast: 
    id a = (id)[Foo bar]; 

    // needs bridged cast 
    id b = (__bridge id)foo(); 
} 

Итак, когда отливку результат сообщения можно опустить brdge бросок в то время как вызов нормальной функции нуждается. Это кажется странным.

Причина разницы заключается в том, как ARC интерпретирует функции и имена методов и выводит предположения о количестве удержаний так называемого C retainable pointer types (Объекты Core Foundation).

В section 3.3.2 направляющей ARC («Преобразование в объект типа могущий быть удержанным указатель выражений с известной семантикой») вы найдете причину несоответствия:

Приведено выражение известно unretained если он является rvalue C сохраняемым типом указателя, и это [...] сообщение отправить [...]

и

Если литая операнд известно unretained [...] преобразование трактуется как __bridge отливать

В этом же разделе описывается, почему это не относится к функциям C и как их можно украсить, чтобы позволить clang делать подобные предположения. Таким образом, мы можем изменить приведенный выше пример, чтобы избавиться от моста гипсе вызова функции C, а также:

CGPathRef foo() __attribute__((cf_returns_not_retained)); 

Чтобы ответить на последний вопрос: Это безопасно использовать мост бросание в обоих местах. Он даже делает уверенным, что ARC выбирает нужный лимит __bridge (он может выбрать листинг __bridge_transfer в зависимости от названия метода. В этом случае он будет использовать __bridge, хотя).

+0

Вау, спасибо за то, что вы пытаетесь проанализировать это. Я повторил это как правильный ответ, потому что он идет в эту большую длину, чтобы точно объяснить, что происходит. –

2

Это вызвано ARC и магией, которую она выполняет при выполнении заданий.

Когда вы создаете временную переменную CGPathRef test и назначаете ей, ARC видит, что вы используете экземпляр CoreBlank, который не соответствует парадигме стандартной памяти и не сохраняет его и не делает ничего интересного.

Позже, когда вы попытаетесь присвоить этот неконтролируемый экземпляр сохраненному объекту id, LLVM уродливый, так как считал, что ему не нужно управлять памятью для этого экземпляра, но теперь он должен начинаться (что является целью ключевого слова __bridge).

В первом фрагменте кода причина, по которой он работает, заключается в том, что ARC всегда знает, что он должен рассматривать этот экземпляр как управляемый ARC, и никогда не существует ссылки на него, которая не управляется ARC.

+0

Я не вижу, как это объяснение делает что-то более ясное. Почему 'id a = (id) [UIBezierPath new];' работать, но 'id b = (__bridge id) CGPathCreateCopy (NULL);' нужен ли мост? –

+2

@NikolaiRuhe 'UIBezierPath' - это класс, управляемый ARC, поскольку он не является C-указателем, это указатель Objective-C. 'CGPathCreateCopy' возвращает указатель C-структуры, который ARC не управляет, если не сконфигурирован. –

+0

Извините за путаницу: я имел в виду 'id a = (id) [UIBezierPath new] .CGPath;'. Таким образом, разница между двумя случаями заключается только в том, что 'a' получает результат сообщения objc, а' b' получает результат функции C. Я не понимаю, почему ARC должна обрабатывать эти случаи по-разному. –

Смежные вопросы