2013-03-06 15 views
14

jestem zaniepokojony dziwnym zachowaniem, ilustruje następujący przykład:W Objective C, dlaczego wolno mi przypisać NSArray do NSMutableArray bez błędu lub ostrzeżenia?

NSMutableArray *a1 = [[NSMutableArray alloc] init]; // fine 
NSMutableArray *a2 = [NSMutableArray array];  // fine, too 

// compiler reports incompatible pointer types; good: 
NSMutableArray *a3 = [[NSArray alloc] init]; 

// compiler says nothing and is happy to assign this! 
NSMutableArray *a4 = [NSArray array]; 

Zarówno init i array metoda obu klas NSArray i NSMutableArray powrócić id. Jednak zachowanie podczas wywoływania tych metod nie jest po prostu takie samo, a clang pozwala mi szczęśliwie przypisać pustą NSArray do zmiennej NSMutableArray!

Okazuje się, że clang will automatically change the return type of some methods, including the init family, to instancetype, a tym samym być w stanie określić w czasie kompilacji, które [[NSArray alloc] init] zwraca NSArray * a nie NSMutableArray *. Ale ta kontrola po prostu nie działa z metodą array.

Dlaczego? Czy linie, takie jak mój ostatni przykład, nie powinny generować co najmniej ostrzeżenia? Dlaczego wszystkie te metody nie są zadeklarowane jako zwracające instancetype? Czy zmieni się w przyszłości?


Aktualizacja

Dobra wiadomość: jak iOS 7 [NSArray array] zwrotów instancetype, więc przypisanie do a4 powyżej daje również ostrzeżenie. Inne metody, takie jak arrayWithContentsOfFile: lub arrayWithContentsOfURL jeszcze powrócić id, choć ...

+2

+1 Dobre pytanie. –

+1

Zgłaszałbym błąd na http://bugreport.apple.com i wymieniam tutaj numer błędu. – ericg

+0

@ericgorr jest problem, to nie jest błąd. – CodaFi

Odpowiedz

10

Ale to sprawdzenie po prostu nie działa z metodą tablicową. Czemu?

Ponieważ dokument został powiązany opisuje, to dlatego -array nie daje uznaną o wynikach Rodzaj. ObjC jest bardzo dynamiczny - kompilator nie może zagwarantować rezultatu typu +array. To czyni to za pomocą niektórych metod, ponieważ konwencje nazewnictwa są dobrze zdefiniowane (np. +alloc, -init, +new, -self itd.). Taka implementacja po prostu odwołuje się do konwencji nazewnictwa.

Kompilator sprawdza również pewne konwencje nazewnictwa w obszarach nie może oczekiwać:

@implementation NSArray (DEMO) 

- (id)initStr 
{ 
    return [NSString new]; // << warning. RE: init prefix 
} 

@end 

nie powinien linie jak mój ostatni przykład wygenerować przynajmniej ostrzeżenie? Dlaczego nie wszystkie te metody są zadeklarowane jako powracające instancetype? Czy zmieni się w przyszłości?

instancetype został wprowadzony około rok temu (z wyglądu). Niektóre z API zostały napisane dziesiątki lat temu. Podejrzewam, że to się stanie - w czasie - ponieważ (jeśli jest używany poprawnie) może wskazywać na wiele problemów w istniejącym kodzie. Oczywiście zmiany te zepsułyby istniejące kompilacje (ponownie, zazwyczaj dobre poprawki, jeśli zadeklarowano w odpowiednich miejscach).

Zgłaszaj błędy i przekaż narzędzia i biblioteki na kilka lat, aby je zaktualizować. Zakładając, że zmiany zostaną wprowadzone, prawdopodobnie nastąpi to przy dużej aktualizacji systemu operacyjnego.

Prawdopodobnie byłoby najlepiej, gdyby był włączony jako opcjonalne ostrzeżenie przez pewien czas (w przypadku nagłówków systemu). Oczywiście nadal mogliby go używać z kompatybilnością wsteczną dla starszych kompilatorów dla nowych interfejsów API.

Również ta zmiana może być dość łatwo zmodernizowana (nie że wcześniejsze kompilatory miałyby sens z semantycznej różnicy między id i instancetype) przez prosty typedef. Jednym z problemów z typedef jest to, że jest to deklaracja globalna - kompilator może ograniczyć słowo/modyfikator/atrybut do danego zakresu, bez powodowania wszelkiego bólu podczas symulacji słowa kluczowego przez dodanie globalnego typu. GCC firmy Apple może nigdy nie obsługiwać instancetype, więc logicznym sposobem wprowadzenia go do GCC firmy Apple może być globalne typedef z id, które może powodować problemy dla niektórych osób (bez korzyści semantycznych, jeśli ta trasa została podjęta). Zwróć uwagę, że podobne przełomowe zmiany zostały wprowadzone przez firmę Apple w przeszłości.

3

Jak się okazuje, nie jesteś po prostu wolno używać niewłaściwy typ tablicy, masz prawo do korzystania z niewłaściwego typu dowolnego obiektu z inicjator wygody, który zwraca id. Na przykład, to kompiluje bez ostrzeżenia w oczach:

NSMutableArray *a4 = [NSDictionary dictionary]; 

To jest efekt uboczny stosowania id zrezygnować z bezpieczeństwa typu, i jak można zauważyć należy przestarzałe zachowanie i zastąpione instancetype (co robi rzucać ostrzeżenie o niekompatybilnym typie, gdy jest używane w powyższy sposób).

Niestety, to nie jest błąd. instancetype jest dość nowym słowem kluczowym, jego przyjęcie nie jest jeszcze powszechne i byłoby śmiałym posunięciem, aby zacząć go używać w ramach Apple'a. Nigdy nie wiadomo, zawsze jest nadzieja na kolejne SDK!

+0

Dzięki. Zastanawiam się, dlaczego te metody powinny zwracać "id" w pierwszej kolejności zamiast prawdziwego typu, który zwracają. Po co wyrzucać takie bezpieczeństwo? –

+0

Więc nie łamią podklas. Jeśli zrezygnujesz z bezpieczeństwa w inicjalizatorze, wówczas podklasa S może przesłonić metodę init T i powrócić bez ostrzeżenia typu. Jeśli ObjC były podobne do java, gdzie inicjalizatory są unikalne i nie są domyślnie dziedziczone, nie stanowiłoby to problemu. – CodaFi

+0

Hmm, Zauważyłem, że ** nie ** zwracanie 'id' jest w rzeczywistości * nie * problemem, ponieważ clang pozwala na zwrócenie kowariantnego podtypu w nadpisanej metodzie. Więc podklasy też powinny być w porządku. –

Powiązane problemy