2013-03-07 11 views
5

właśnie Konfigurowanie CABasicAnimation za mienie shadowPath i to mnie ciekawi:Most odlew wymagane, niespójne ostrzeżenie podczas próby użycia CGPathRef

shadowAnimation.toValue = (id)newShadowPath.CGPath; 

[uwaga: shadowAnimation jest CABasicAnimation przedmiot i newShadowPath jest UIBezierPath obiekt]

to działa i nie występuje żaden błąd/ostrzeżenie w Xcode. Jednakże, jeśli napiszę, że tak:

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

Nie będzie to skompilować, rzucając ten komunikat ostrzegawczy:

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

Więc to wymaga ode mnie, aby wpisać to lubią:

shadowAnimation.toValue = (__bridge id)test; 

teraz dlaczego tak jest? Dlaczego nie otrzymam tego samego błędu w początkowym przykładzie, gdy używam tylko (id) newShadowPath.CGPath; ? Czy to byłoby właściwe, aby umieścić tam również __bridge, niezależnie od tego, czy Xcode nie wykryje żadnych problemów? Czy może brakuje mi tu jakiej różnicy?

Odpowiedz

2

Przyczyną różnicy są nowe (i nieco skomplikowane) zasady konwersji wprowadzone w klangu 3.1.

Po pierwsze, pozwala gotować w dół:

@@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(); 
} 

więc podczas odlewania wynik wiadomości możemy pominąć obsady brdge podczas normalnego wywołania funkcji potrzebuje. To wydaje się dziwne.

Powód różnicy polega na tym, że ARC interpretuje funkcje i nazwy metod oraz wyprowadza założenia dotyczące liczby zatrzymań tzw. C retainable pointer types (obiekty Core Foundation).

W section 3.3.2 prowadnicy ARC („konwersja do typu obiektu wygodny do przechowywania wskaźnik wyrażeń ze znanymi semantyki”) znajdziesz przyczynę rozbieżności:

Wyrażenie jest wiadomo czy to niezatrzymany jest wartością możliwego do zachowania wskaźnika typu C i jest [...] wysyłaniem wiadomości [...]]

i

Jeśli operand obsada znana jest niezatrzymany [...] konwersja jest traktowany jako __bridge cast

Ta sama sekcja opisuje, dlaczego nie odnosi się do funkcji C i jak można je udekorować, aby umożliwić podobnym założeniom brzęczenie. Więc możemy modyfikować za przykładem z góry pozbyć obsady mostka do wywołania funkcji C, a także:

CGPathRef foo() __attribute__((cf_returns_not_retained)); 

Aby odpowiedzieć na ostatnie pytanie: Jest bezpieczny w użyciu do obsady most w obu tych miejscach. Zapewnia to nawet, że ARC wybiera odpowiedni rzutowanie __bridge (może wybrać rzutowanie __bridge_transfer w zależności od nazwy metody.W tym przypadku użyłoby to jednak __bridge).

+0

Wow, dziękuję za podjęcie się takiego wysiłku, aby to przeanalizować. Ponownie uznałem to za poprawną odpowiedź, ponieważ jest tak wielka, aby dokładnie wyjaśnić, co się dzieje. –

2

Jest to spowodowane ARC i magią, która ma miejsce, gdy występują przypisania.

Po utworzeniu zmiennej tymczasowej CGPathRef test i przypisaniu jej, ARC widzi, że używasz instancji CoreBlank, która nie podąża za standardowymi paradygmatami pamięci i nie zachowuje jej ani nie robi nic nadzwyczajnego.

Później, przy próbie przypisania tej niezarządzanej instancji do zatrzymanej wartości id, LLVM freaks od razu, ponieważ uważał, że nie musi zarządzać pamięcią dla tej instancji, ale teraz powinien się uruchomić to (co jest celem słowa kluczowego __bridge).

W pierwszym fragmencie kodu, powodem jego działania jest to, że ARC zawsze wie, że powinno traktować tę instancję jako zarządzaną ARC i nigdy nie istnieje odwołanie do niej, które nie jest zarządzane ARC.

+0

Nie widzę, jak to wyjaśnienie czyni cokolwiek wyraźniejszego. Dlaczego 'id a = (id) [UIBezierPath new];' działa, ale 'id b = (__bridge id) CGPathCreateCopy (NULL);' potrzebuje rzutowania mostu? –

+2

@NikolaiRuhe 'UIBezierPath' jest klasą zarządzaną przez ARC, ponieważ nie jest wskaźnikiem C, jest wskaźnikiem Objective-C. 'CGPathCreateCopy' zwraca wskaźnik struktury C, którego ARC nie obsługuje, chyba że jest zmostkowany. –

+0

Przepraszamy za zamieszanie: Miałem na myśli 'id a = (id) [UIBezierPath new] .CGPath;'. Tak więc różnica między tymi dwoma przypadkami polega tylko na tym, że 'a' otrzymuje wynik komunikatu objc, podczas gdy' b' otrzymuje wynik funkcji C. Nie rozumiem, dlaczego ARC powinien obsługiwać te sprawy inaczej. –

Powiązane problemy