Jako programista Java, który czyta dokumentację Apple Objective-C 2.0: zastanawiam się, co oznacza „ wysłanie wiadomości do zera ” - nie mówiąc już o tym, jak jest to w rzeczywistości przydatne. Wyciąg z dokumentacji:
Jest kilka wzorów w kakao, które wykorzystują ten fakt. Wartość zwrócona z wiadomości do nil może być również poprawna:
- Jeśli metoda zwraca obiekt, dowolny typ wskaźnika, dowolny skalar całkowity o rozmiarze mniejszym lub równym sizeof (void *), float, a double, a long double lub long long, to wiadomość wysłana do zera zwraca 0 .
- Jeśli metoda zwraca strukturę, zgodnie z definicją zawartą w przewodniku wywołań funkcji ABI systemu Mac OS X, która ma zostać zwrócona w rejestrach, wówczas komunikat wysłany do zera zwraca wartość 0,0 dla każdego pola w strukturze danych. Inne typy danych struktur nie będą wypełnione zerami.
- Jeśli metoda zwraca cokolwiek innego niż wymienione powyżej typy wartości, wartość zwracana wiadomości wysłanej do nil jest niezdefiniowana.
Czy Java sprawiła, że mój mózg nie był w stanie zrozumieć powyższego wyjaśnienia? A może jest coś, czego mi brakuje, co sprawiłoby, że byłby tak przejrzysty jak szkło?
Rozumiem wiadomości / odbiorniki w Objective-C, jestem po prostu zdezorientowany co do odbiornika, który tak się składa nil
.
objective-c
Ryan Delucchi
źródło
źródło
Odpowiedzi:
Cóż, myślę, że można to opisać na bardzo wymyślnym przykładzie. Załóżmy, że masz w Javie metodę, która wypisuje wszystkie elementy z ArrayList:
Teraz, jeśli wywołasz tę metodę w ten sposób: someObject.foo (NULL); prawdopodobnie otrzymasz wyjątek NullPointerException podczas próby uzyskania dostępu do listy, w tym przypadku w wywołaniu list.size (); Teraz prawdopodobnie nigdy nie wywołałbyś obiektu someObject.foo (NULL) z taką wartością NULL. Jednak możesz pobrać swoją ArrayList z metody, która zwraca NULL, jeśli napotka jakiś błąd podczas generowania ArrayList, jak someObject.foo (otherObject.getArrayList ());
Oczywiście będziesz mieć również problemy, jeśli zrobisz coś takiego:
Teraz w Objective-C mamy równoważną metodę:
Teraz, jeśli mamy następujący kod:
mamy taką samą sytuację, w której Java wygeneruje wyjątek NullPointerException. Obiekt zerowy będzie dostępny jako pierwszy pod adresem [anArray count] Jednak zamiast rzucać wyjątek NullPointerException, Objective-C zwróci po prostu 0 zgodnie z powyższymi regułami, więc pętla nie zostanie uruchomiona. Jeśli jednak ustawimy pętlę tak, aby uruchamiała się określoną liczbę razy, najpierw wysyłamy wiadomość do tablicy anArray pod adresem [anArray objectAtIndex: i]; Zwróci to również 0, ale ponieważ objectAtIndex: zwraca wskaźnik, a wskaźnik do 0 ma wartość nil / NULL, NSLog zostanie przekazany do zera za każdym razem przez pętlę. (Chociaż NSLog jest funkcją, a nie metodą, wypisuje (null), jeśli przekazano nil NSString.
W niektórych przypadkach przyjemniej jest mieć wyjątek NullPointerException, ponieważ można od razu stwierdzić, że coś jest nie tak z programem, ale jeśli nie złapiesz wyjątku, program się zawiesi. (W C, próba wyłuskiwania NULL w ten sposób powoduje awarię programu.) W Objective-C, zamiast tego powoduje to prawdopodobnie nieprawidłowe zachowanie w czasie wykonywania. Jeśli jednak masz metodę, która nie psuje się, jeśli zwraca 0 / nil / NULL / zerowaną strukturę, to oszczędza ci to konieczności sprawdzania, czy obiekt lub parametry są zerowe.
źródło
myObject->iVar
, ulegnie awarii, niezależnie od tego, czy jest to C z obiektami, czy bez . (przepraszam za gravedig.)->
nie jest to już operacja celu-C, ale w dużym stopniu ogólny C-izm.Komunikat na
nil
nic nie robi i powracanil
,Nil
,NULL
,0
, lub0.0
.źródło
Wszystkie inne posty są poprawne, ale może to koncepcja jest tutaj ważna.
W wywołaniach metod Objective-C każde odwołanie do obiektu, które może zaakceptować selektor, jest prawidłowym celem dla tego selektora.
Oszczędza to DUŻO „czy obiekt docelowy jest typu X?” kod - o ile obiekt odbierający implementuje selektor, nie ma absolutnie żadnego znaczenia, jaka to klasa!
nil
jest obiektem NSO, który akceptuje dowolny selektor - po prostu nic nie robi . Eliminuje to również wiele kodów typu „sprawdź zero, nie wysyłaj wiadomości, jeśli prawda”. (Koncepcja „jeśli to akceptuje, to implementuje” pozwala również na tworzenie protokołów , które są trochę podobne do interfejsów Java: deklaracja, że jeśli klasa implementuje podane metody, to jest zgodna z protokołem.)Powodem tego jest wyeliminowanie kodu małpy, który nie robi nic poza utrzymywaniem kompilatora w dobrym stanie. Tak, otrzymujesz dodatkowe wywołanie metody, ale oszczędzasz czas programisty , który jest znacznie droższym zasobem niż czas procesora. Ponadto eliminujesz z aplikacji więcej kodu i większą złożoność warunkową.
Wyjaśnienie dla zwolenników osłabienia: możesz pomyśleć, że to nie jest dobra droga, ale tak właśnie jest zaimplementowany język i jest to zalecany idiom programowania w Objective-C (zobacz wykłady z programowania w Stanford na iPhone'a).
źródło
Oznacza to, że środowisko wykonawcze nie generuje błędu, gdy wywoływana jest objc_msgSend na wskaźniku nil; zamiast tego zwraca pewną (często użyteczną) wartość. Wiadomości, które mogą mieć efekt uboczny, nic nie robią.
Jest to przydatne, ponieważ większość wartości domyślnych jest bardziej odpowiednia niż błąd. Na przykład:
To znaczy, nil wydaje się być pustą tablicą. Ukrywanie zerowego odwołania NSView nic nie robi. Handy, co?
źródło
W cytacie z dokumentacji są dwie odrębne koncepcje - być może byłoby lepiej, gdyby dokumentacja to wyjaśniała:
To pierwsze jest prawdopodobnie bardziej istotne w tym przypadku: zazwyczaj możliwość wysyłania wiadomości w celu
nil
uczynienia kodu prostszym - nie trzeba wszędzie sprawdzać wartości null. Przykładem kanonicznym jest prawdopodobnie metoda akcesora:Gdyby wysyłanie wiadomości do
nil
nie było prawidłowe, ta metoda byłaby bardziej złożona - musiałbyś mieć dwie dodatkowe kontrole, aby się upewnić,value
anewValue
nienil
przed wysłaniem im wiadomości.Drugi punkt (że wartości zwracane z wiadomości
nil
są również zwykle prawidłowe), jednak dodaje efekt mnożnikowy do pierwszego. Na przykład:Ten kod ponownie nie wymaga sprawdzania
nil
wartości i płynie naturalnie ...Wszystko to powiedziawszy, dodatkowa elastyczność, do której można wysyłać wiadomości,
nil
wiąże się z pewnym kosztem. Istnieje możliwość, że na pewnym etapie napiszesz kod, który zawiedzie w szczególny sposób, ponieważ nie wziąłeś pod uwagę możliwości, że może to być wartośćnil
.źródło
Od Greg Parker „s stronie :
Jeśli jest uruchomiony LLVM Compiler 3.0 (Xcode 4.2) lub nowszy
źródło
Oznacza to często brak konieczności sprawdzania wszędzie pod kątem bezpieczeństwa żadnych obiektów - w szczególności:
lub, jak już wspomniano, wszystkie metody liczenia i długości zwracają 0, gdy masz wartość zerową, więc nie musisz dodawać dodatkowych sprawdzeń na zero:
albo to:
źródło
Nie myśl o tym, że „odbiorca jest zerowy”; Zgadzam się, to dość dziwne. Jeśli wysyłasz wiadomość do zera, nie ma odbiorcy. Po prostu wysyłasz wiadomość do niczego.
Jak sobie z tym poradzić, to filozoficzna różnica między Javą a Objective-C: w Javie to błąd; w Objective-C nie jest to operacja.
źródło
Komunikaty ObjC, które są wysyłane do nil i których wartości zwracane mają rozmiar większy niż sizeof (void *), generują niezdefiniowane wartości na procesorach PowerPC. Ponadto komunikaty te powodują zwracanie niezdefiniowanych wartości w polach struktur, których rozmiar jest większy niż 8 bajtów również w procesorach Intel. Vincent Gable ładnie to opisał na swoim blogu
źródło
Nie sądzę, aby w żadnej z pozostałych odpowiedzi wyraźnie o tym wspomniało: jeśli jesteś przyzwyczajony do języka Java, należy pamiętać, że chociaż Objective-C w systemie Mac OS X obsługuje obsługę wyjątków, jest to opcjonalna funkcja języka, którą można włączony / wyłączony z flagą kompilatora. Domyślam się, że ten projekt „wysyłania wiadomości
nil
jest bezpieczny” poprzedza włączenie obsługi wyjątków w języku i został wykonany z podobnym celem: metody mogą powrócić,nil
aby wskazać błędy, a ponieważ wysyłanie wiadomościnil
zwykle zwracanil
to z kolei umożliwia propagację wskazania błędu w kodzie, dzięki czemu nie trzeba go sprawdzać w każdej wiadomości. Musisz to sprawdzić tylko w punktach, w których ma to znaczenie. Osobiście uważam, że propagowanie i obsługa wyjątków jest lepszym sposobem osiągnięcia tego celu, ale nie każdy może się z tym zgodzić. (Z drugiej strony, na przykład nie podoba mi się wymaganie Java, abyś musiał deklarować, jakie wyjątki może rzucać metoda, co często zmusza cię do syntaktycznego propagowania deklaracji wyjątków w całym kodzie; ale to inna dyskusja.)Opublikowałem podobną, ale dłuższą odpowiedź na powiązane pytanie „Czy twierdzenie, że każde utworzenie obiektu zakończyło się sukcesem w celu C, jest konieczne?” jeśli chcesz więcej szczegółów.
źródło
nil
powrót często był używany do zwarcia łańcucha w NO-op.C nie reprezentuje niczego jako 0 dla wartości pierwotnych i NULL dla wskaźników (co jest równoważne 0 w kontekście wskaźnika).
Cel-C opiera się na reprezentacji niczego w języku C, dodając zero. nil jest wskaźnikiem obiektu do niczego. Chociaż semantycznie różnią się od NULL, są one technicznie równoważne sobie.
Nowo przydzielone obiekty NSO zaczynają życie z zawartością ustawioną na 0. Oznacza to, że wszystkie wskaźniki, które ten obiekt ma do innych obiektów, zaczynają się od zera, więc nie jest konieczne, na przykład, ustawianie self. (Asocjacja) = nil w metodach init.
Najbardziej godne uwagi zachowanie nil polega jednak na tym, że może mieć wysyłane do niego wiadomości.
W innych językach, takich jak C ++ (lub Java), spowodowałoby to awarię programu, ale w Objective-C wywołanie metody na nil zwraca wartość zero. To znacznie upraszcza wyrażenia, ponieważ eliminuje potrzebę sprawdzania nil przed zrobieniem czegokolwiek:
Świadomość, jak nil działa w Objective-C, sprawia, że ta wygoda jest funkcją, a nie czającym się błędem w aplikacji. Upewnij się, że chronisz się przed przypadkami, w których wartości nil są niepożądane, sprawdzając i zwracając wcześnie, aby po cichu zakończyć się niepowodzeniem, lub dodając NSParameterAssert, aby zgłosić wyjątek.
Źródło: http://nshipster.com/nil/ https://developer.apple.com/library/ios/#documentation/cocoa/conceptual/objectivec/Chapters/ocObjectsClasses.html (wysyłanie wiadomości do nil).
źródło