2013-03-07 8 views
5

ich ein CABasicAnimation für shadowPath Eigenschaft nur wurde die Konfiguration und das machte mich neugierig:Brücke Guss erforderlich, inkonsistente Warnung bei dem Versuch, CGPathRef verwenden

shadowAnimation.toValue = (id)newShadowPath.CGPath; 

[Anmerkung: shadowAnimation ist ein CABasicAnimation Objekt und newShadowPath ist ein UIBezierPath Objekt]

dies funktioniert und kein Fehler/Warnung ist in Xcode sichtbar. Allerdings, wenn ich, dass wie so schreiben:

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

Dies wird nicht kompilieren, werfen diese Warnmeldung:

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

So erfordert es mich, es zu geben mag:

shadowAnimation.toValue = (__bridge id)test; 

Jetzt Warum ist das so? Warum bekomme ich im ersten Beispiel nicht den gleichen Fehler, wenn ich nur (id) newShadowPath.CGPath; ? Wäre es richtig, auch dort __bridge zu platzieren, ohne dass Xcode Probleme erkennt? Oder fehlt mir, was ist der Unterschied hier?

Antwort

2

Der Grund für den Unterschied liegt in den neuen (und etwas komplizierten) Konvertierungsregeln, die in clang 3.1 eingeführt wurden.

Zuerst lässt sie einkochen:

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

Also, wenn das Ergebnis einer Nachricht Gießen wir die brdge Guss während ein normaler Funktionsaufruf braucht es weglassen. Das scheint seltsam.

Der Grund für den Unterschied liegt darin, wie ARC Funktionen und Methodennamen interpretiert und Annahmen über die Retain-Anzahl der so genannten C retainable pointer types (Core Foundation Objekte) ableitet.

In section 3.3.2 der ARC-Führung („Die Umstellung auf thermotrope Objektzeigertyp von Ausdrücken mit bekannten Semantik“) können Sie den Grund für die Diskrepanz finden:

nicht retardierte wenn es

Ein Ausdruck ist bekannt ist ein rvalue von C haltbaren Zeigertyp und es ist [...] eine Nachricht senden [...]]

und

Wenn der gegossene Operand bekannt nicht retardierte [...] die Umwandlung als __bridge

gegossen behandelt wird

Der gleiche Abschnitt beschreibt, warum dies nicht gilt für C-Funktionen und wie sie dekoriert werden können, um ähnliche Annahmen zu ermöglichen. So können wir das Beispiel von oben, um loszuwerden, die Brücke Besetzung für den C-Funktionsaufruf ändern, wie auch:

CGPathRef foo() __attribute__((cf_returns_not_retained)); 

Ihre letzte Frage zu beantworten: Es ist sicher die Brücke Besetzung in beiden Orten zu verwenden. Es stellt sogar sicher, ARC wählt die richtige __bridge Besetzung (es könnte die __bridge_transfer Besetzung je nach dem Namen der Methode auswählen. In diesem Fall würde es __bridge verwenden, obwohl).

+0

wow, danke, dass du dich so anstrengst, das zu analysieren. Ich akzeptierte dies als die richtige Antwort, weil es in diese große Länge geht, um genau zu erklären, was vor sich geht. –

2

Dies wird durch ARC verursacht, und die Magie, die es tut, wenn Zuordnungen auftreten.

Wenn Sie eine temporäre Variable CGPathRef test erstellen und ihr zuweisen, sieht ARC, dass Sie eine CoreBlank-Instanz verwenden, die nicht den Standard-Speicherparadigmen folgt und sie nicht behält oder etwas Besonderes macht.

Wenn Sie später versuchen, diese nicht verwaltete Instanz einer beibehaltenen id -typed-Eigenschaft zuzuweisen, flippt LLVM sofort aus, weil es dachte, dass es den Speicher für diese Instanz nicht verwalten musste, aber jetzt sollte es starten zu (was die Absicht des Schlüsselwortes __bridge ist).

Im ersten Code-Snippet ist der Grund dafür, dass ARC immer weiß, dass es diese Instanz als ARC-verwaltet behandeln sollte und dass es nie einen Verweis darauf gibt, der nicht ARC-verwaltet ist.

+0

Ich sehe nicht, wie diese Erklärung etwas klarer macht. Warum funktioniert 'id a = (id) [UIBezierPath neu];' funktioniert noch 'id b = (__bridge id) CGPathCreateCopy (NULL); 'benötigt die Bridge-Umwandlung? –

+2

@NikolaiRuhe 'UIBezierPath' ist eine ARC-verwaltete Klasse, da es kein C-Pointer ist, sondern ein Objective-C-Pointer. 'CGPathCreateCopy' gibt einen C-Struct-Zeiger zurück, den ARC nur verwaltet, wenn er überbrückt ist. –

+0

Sorry für die Verwirrung: Ich meinte 'id a = (id) [UIBezierPath neu] .CGPath;'. Der Unterschied zwischen den beiden Fällen besteht also nur darin, dass "a" das Ergebnis einer objc-Nachricht erhält, während "b" das Ergebnis einer C-Funktion erhält. Ich verstehe nicht, warum ARC diese Fälle anders behandeln sollte. –