2012-05-17 6 views
5

Ist das erlaubt und warum?Darf man die Signatur eines Blocks in Ziel C wie folgt ändern/missbrauchen/überschreiben?

void (^bar)(NSNumber *) = ^(NSNumber *number) { 
    NSLog(@"Value is %@, class is %@.", number, [number class]); 
}; 
bar([NSNumber numberWithInt:10]); 

void (^foo)(id) = bar; 
foo([NSDate date]); 

Ausgang ist:

Value is 10, class is __NSCFNumber. 
Value is 2012-05-17 18:54:14 +0000, class is __NSDate. 

ich nichts finden konnte im Zusammenhang, die dies erklärt. Können Sie einen Link zu der objektiven c-Block-Spezifikation angeben, die dies abdeckt?

Derzeit arbeite ich an einer blockbasierten UITableView-Unterklasse und es würde die Dinge viel einfacher machen, wenn ich sicher bin, dies zu verwenden.

+0

Ausgabe _ist_, oder Ausgabe _wäre be_? Denn wenn Sie getestet haben und das ist die tatsächliche Ausgabe, dann ist es erlaubt. – zneak

+1

FYI das heißt * [Kovarianz und Kontravarianz] (http://en.wikipedia.org/wiki/Covariance_and_contravariance_%28computer_science%29) *. –

+0

Ich würde stattdessen void (^ bar) (id) verwenden, wenn Sie es so verwenden würden. – Cthutu

Antwort

6

Die zugrunde liegenden Gründe dies funktioniert, ist eine Kombination von:

[1] Objective-C (und C) wird bei der Kompilierung nicht stark typisiert. Während Warnungen vom Compiler erzeugt werden können, können sie normalerweise durch (manchmal unsichere) Casts zum Schweigen gebracht werden. Ihre Zuweisung ist in diesem Fall ungültig, da Sie eine Blockreferenz zuweisen, die angibt, dass einen mit NSNumber * kompatiblen Argumentwert für eine andere Blockreferenz erfordert, die nur einen mit id kompatiblen Argumentwert erfordert. Dieser Typ ist unsicher und führt manchmal zu Laufzeit Fehlern, siehe unten.

[2] Die Objective-C-Laufzeitnachrichtenübergabe ist dynamisch, dh der Zielcode für eine Nachricht wird ermittelt, während der Code ausgeführt wird. Dies bedeutet, dass alle Ihre Verwendungen von number im Block nicht spezifisch für NSNumber sind, wenn Sie eine NSDate zur Laufzeit übergeben geeignete Methoden sind immer noch dynamisch lokalisiert. ändern jedoch Ihre bar zu:

void (^bar)(NSNumber *) = ^(NSNumber *number) 
{ 
    NSLog(@"Value is %@, class is %@, int value is %d.", number, [number class], [number intValue]); 
}; 

und Sie werden Laufzeit Fehler sehen.

[3] Beide [NSNumber numberWithInt:10] und [NSDate date] deklariert Werte vom Typ zurückzukehren id, nicht NSNumber * & NSDate * wie man erwarten könnte. Dies bedeutet, dass Sie nicht foo brauchen, können Sie einfach eingeben:

bar([NSDate date]); 

und das gleiche Ergebnis ohne Warnungen bekommen ... Als weiteres Beispiel betrachten dies:

NSNumber *num = [NSNumber numberWithInt:3]; 
NSDate *date = num;   // produces a warning 
id erase = num;    // erase type info and do... 
date = erase;    // effectively the same assignment, no warning 

Nehmen Weg : Objective-C ist nicht eine Art sichere Sprache, der Compiler wird Sie in vielen Fällen vor möglichen Problemen warnen, aber es wird in allen Fällen nicht tun.

+0

Danke für Ihre großartige Erklärung.Das bedeutet, dass selbst bei einer ungültigen Zuweisung [1] keine Laufzeitfehler auftreten werden, solange [2] sichergestellt ist, dass alle meine Verwendungen von 'number' innerhalb des Blocks auch für den infiltrierte Klasse (in diesem Fall NSDate). – Klaas