Objective-C wywodzi składnię obiektu z Smalltalka. Wszystkie składnie operacji nie obiektowych (w tym prymitywne zmienne, wstępne przetwarzanie, wyrażenia, deklaracje funkcji i wywołania funkcji) są identyczne jak w C, podczas gdy składnia funkcji obiektowych jest implementacją wiadomości w stylu Smalltalk.
Wiadomościedytuj
model Objective-C programowania obiektowego opiera się na przekazywaniu komunikatów do instancji obiektowych. W Objective-C nie wywołuje się metody; wysyła się wiadomość., Jest to w przeciwieństwie do modelu programowania w stylu Simula używanego przez C++. Różnica między tymi dwoma pojęciami polega na tym, jak kod, do którego odwołuje się nazwa metody lub wiadomości, jest wykonywany. W języku Simula nazwa metody jest w większości przypadków związana z sekcją kodu w klasie docelowej przez kompilator. W Smalltalk i Objective-C, cel wiadomości jest rozwiązywany w czasie wykonywania, a sam obiekt odbierający interpretuje wiadomość., Metoda jest identyfikowana przez selektor lub Sel-unikalny identyfikator dla każdej nazwy wiadomości, często tylko zakończony znakiem NUL łańcuch reprezentujący jej nazwę — i rozwiązywany do wskaźnika metody C implementującego ją: IMP. Konsekwencją tego jest to, że system przekazywania wiadomości nie ma sprawdzania typu. Obiekt, do którego wiadomość jest kierowana — odbiorca-nie ma gwarancji, że odpowie na wiadomość, a jeśli nie, spowoduje to wyjątek.,
wysłanie metody message do obiektu wskazywanego przez wskaźnik obj wymagałoby następującego kodu w C++:
obj->method(argument);
w Objective-C jest to zapisane w następujący sposób:
;
wywołanie „method” jest przetłumaczone przez kompilator do Objc_msgsend(ID SELF, Sel op,…) rodzina funkcji runtime. Różne implementacje obsługują nowoczesne dodatki jak super. W rodzinach GNU ta funkcja nosi nazwę objc_msg_sendv, ale została wycofana na rzecz nowoczesnego systemu wyszukiwania pod objc_msg_lookup.,
oba style programowania mają swoje mocne i słabe strony. Programowanie obiektowe w stylu Simula (C++) umożliwia wielokrotne dziedziczenie i szybsze wykonywanie przy użyciu wiązania w czasie kompilacji, o ile to możliwe, ale domyślnie nie obsługuje wiązania dynamicznego. Wymusza to również, aby wszystkie metody miały odpowiednią implementację, chyba że są abstrakcyjne. Programowanie w stylu Smalltalk używane w Objective-C pozwala na niezrealizowanie komunikatów, przy czym metoda została rozwiązana do jej implementacji w czasie wykonywania., Na przykład, wiadomość może zostać wysłana do kolekcji obiektów, na które tylko niektóre z nich będą odpowiadać, bez obawy o błędy uruchomieniowe. Przekazywanie wiadomości nie wymaga również, aby obiekt był zdefiniowany podczas kompilacji. Implementacja jest nadal wymagana do wywołania metody w obiekcie pochodnym. (Więcej zalet dynamicznego (późnego) wiązania można znaleźć w sekcji dynamiczne typowanie poniżej.)
Interfejsy i implementacjeedit
Objective-C wymaga, aby interfejs i Implementacja klasy były w oddzielnie deklarowanych blokach kodu., Zgodnie z konwencją Programiści umieszczają interfejs w pliku nagłówkowym, a implementację w pliku kodu. Pliki nagłówkowe, zwykle zakończone przyrostkiem .h, są podobne do plików nagłówkowych C, podczas gdy pliki implementacji (metody), zwykle zakończone .m, może być bardzo podobny do plików kodu C.
InterfaceEdit
jest to analogiczne do deklaracji klas używanych w innych językach obiektowych, takich jak C++ lub Python.
interfejs klasy jest zwykle zdefiniowany w pliku nagłówkowym. Powszechną konwencją jest nazwanie pliku nagłówkowego po nazwie klasy, np. Ball.,h zawiera interfejs dla piłki klasowej.
deklaracja interfejsu ma postać:
w powyższym przykładzie, znaki plus oznaczają metody klasy lub metody, które mogą być wywołane na samej klasie (nie na instancji), a znaki minus oznaczają metody instancji, które mogą być wywołane tylko na konkretnej instancji klasy. Metody klas również nie mają dostępu do zmiennych instancji.,
powyższy kod jest w przybliżeniu odpowiednikiem następującego interfejsu C++:
zauważ, że instanceMethod2With2Parameters:param2_callName: pokazuje przeplatanie segmentów selektora z wyrażeniami argumentów, dla których nie ma bezpośredniego odpowiednika w C / C++.
typy zwracane mogą być dowolnym standardowym typem C, wskaźnikiem do ogólnego obiektu Objective-C, wskaźnikiem do określonego typu obiektu, takiego jak NSArray*, NSImage * lub NSString *, lub wskaźnikiem do klasy, do której należy metoda (instancetype). Domyślnym typem zwracanym jest generic Objective-C type id.,
argumenty metody rozpoczynają się nazwą określającą argument będący częścią nazwy metody, a następnie dwukropkiem, po którym następuje oczekiwany typ argumentu w nawiasach i nazwa argumentu. Etykietę można pominąć.
pochodną definicji interfejsu jest kategoria, która umożliwia dodawanie metod do istniejących klas.
Implementacjaedit
interfejs deklaruje tylko interfejs klasy, a nie same metody: rzeczywisty kod jest zapisany w pliku implementacji., Pliki implementacji (metody) zwykle mają rozszerzenie .m
, które pierwotnie oznaczało „wiadomości”.
@implementation classname+ (return_type)classMethod { // implementation}- (return_type)instanceMethod { // implementation}@end
metody są pisane przy użyciu ich deklaracji interfejsu.Porównywanie Objective-C i C:
- (int)method:(int)i { return ;}
int function(int i) { return square_root(i);}
składnia pozwala na pseudo-nazywanie argumentów.
wewnętrzne reprezentacje metody różnią się w zależności od implementacji Objective-C., Jeśli myColor należy do klasy Color, metoda instancji-changeColorToRed: green: blue: może być wewnętrznie oznaczona _i_Color_changeColorToRed_green_blue. I ma odnosić się do metody instancji, z dołączonymi nazwami klas, a następnie metod i dwukropkami zmienionymi na podkreślenia. Ponieważ kolejność parametrów jest częścią nazwy metody, nie można jej zmienić tak, aby odpowiadała stylowi kodowania lub wyrażeniu, jak w przypadku true named parameters.
jednak wewnętrzne nazwy funkcji są rzadko używane bezpośrednio. Ogólnie wiadomości są konwertowane na wywołania funkcji zdefiniowane w bibliotece runtime Objective-C., W czasie łącza nie zawsze wiadomo, która metoda zostanie wywołana, ponieważ Klasa odbiornika (obiektu, do którego wysłana jest wiadomość)nie musi być znana do czasu uruchomienia.
InstantiationEdit
Po napisaniu klasy Objective-C można ją utworzyć. Odbywa się to najpierw przydzielając niezainicjalizowaną instancję klasy (obiekt), a następnie inicjując ją. Obiekt nie jest w pełni funkcjonalny, dopóki oba etapy nie zostaną zakończone., Te kroki powinny być wykonane z jednej linii kodu tak, że nigdy nie ma przydzielonego obiektu, który nie został zainicjalizowany (i ponieważ jest nierozsądne, aby zachować wynik pośredni, ponieważ -init
może zwrócić inny obiekt niż ten, na którym jest wywołany).,
Tworzenie Instancji z domyślnym inicjalizatorem bez parametru:
MyObject *foo = init];
Tworzenie Instancji z niestandardowym inicjalizatorem:
MyObject *foo = initWithString:myString];
w przypadku, gdy nie jest wykonywana niestandardowa inicjalizacja, „nowa” metoda może być często używane zamiast wiadomości Alloc-INIT:
MyObject *foo = ;
niektóre klasy implementują inicjalizatory metod klasowych., Podobnie jak +new
, łączą +alloc
I -init
, ale w przeciwieństwie do +new
zwracają instancję z automatycznym odczytem. Niektóre inicjalizatory metod klasowych przyjmują parametry:
MyObject *foo = ;MyObject *bar = ;
komunikat alloc przydziela wystarczającą ilość pamięci do przechowywania wszystkich zmiennych instancji obiektu, ustawia wszystkie zmienne instancji na wartości zerowe i zamienia pamięć w instancję klasy; w żadnym momencie podczas inicjalizacji pamięć nie jest instancją klasy nadrzędnej.,
komunikat init wykonuje konfigurację instancji podczas jej utworzenia. Metoda init jest często zapisywana w następujący sposób:
- (id)init { self = ; if (self) { // perform initialization of object here } return self;}
w powyższym przykładzie zwróć Typ id
. Ten typ oznacza „wskaźnik do dowolnego obiektu” w Objective-C(patrz sekcja dynamiczne pisanie).
wzorzec inicjalizacji służy do zapewnienia, że obiekt jest prawidłowo zainicjowany przez jego klasę nadrzędną, zanim metoda init wykona inicjalizację., Wykonuje następujące czynności:
- self = wysyła instancję klasy nadrzędnej wiadomość init i przypisuje wynik do self (wskaźnik do bieżącego obiektu).
- if (self)sprawdza, czy zwracany wskaźnik obiektu jest prawidłowy przed wykonaniem jakiejkolwiek inicjalizacji.
- return self zwraca wartość self wywołującemu.
nieprawidłowy wskaźnik obiektu ma wartość nil; polecenia warunkowe takie jak ” if ” traktują nil jak wskaźnik null, więc kod inicjalizacji nie zostanie wykonany, jeśli zwróci nil., Jeśli wystąpi błąd podczas inicjalizacji, metoda init powinna wykonać wszelkie niezbędne czyszczenie, w tym wysłanie komunikatu „release” do self, i zwrócić nil, aby wskazać, że inicjalizacja nie powiodła się. Sprawdzanie takich błędów musi być wykonywane tylko po wywołaniu inicjalizacji superclass, aby upewnić się, że zniszczenie obiektu zostanie wykonane poprawnie.
Jeśli klasa ma więcej niż jedną metodę inicjalizacji, tylko jedna z nich („wyznaczony inicjalizator”) musi postępować zgodnie z tym wzorcem; inne powinny wywoływać wyznaczony inicjalizator zamiast superclass inicjalizator.,
Protokołyedytuj
w innych językach programowania nazywane są one „interfejsami”.
Objective-C został rozszerzony o wprowadzenie koncepcji wielokrotnego dziedziczenia specyfikacji, ale nie implementacji, poprzez wprowadzenie protokołów. Jest to wzorzec osiągalny albo jako abstrakcyjna dziedziczona klasa bazowa w C++, albo jako „interfejs” (jak w Javie i C#). Objective-C korzysta z protokołów ad hoc zwanych protokołami nieformalnymi oraz protokołów wymuszonych przez kompilator zwanych protokołami formalnymi.,
nieformalny protokół jest listą metod, które klasa może zaimplementować. Jest określony w dokumentacji, ponieważ nie występuje w języku. Nieformalne protokoły są zaimplementowane jako kategoria (patrz poniżej) na NSObject i często zawierają Opcjonalne metody, które, jeśli zostaną zaimplementowane, mogą zmienić zachowanie klasy. Na przykład klasa pól tekstowych może mieć delegata, który implementuje nieformalny protokół z opcjonalną metodą automatycznego uzupełniania tekstu wpisanego przez użytkownika., Pole tekstowe wykrywa, czy delegat implementuje tę metodę (poprzez odbicie), a jeśli tak, wywołuje metodę delegata w celu obsługi funkcji automatycznego uzupełniania.
formalny protokół jest podobny do interfejsu w Javie, C# i Ada 2005. Jest to lista metod, które każda klasa może zadeklarować do implementacji. Wersje Objective-C przed 2.0 wymagały, że klasa musi zaimplementować wszystkie metody w protokole, który deklaruje jako przyjęty; kompilator wyda błąd, jeśli klasa nie zaimplementuje każdej metody ze swoich zadeklarowanych protokołów. Cel-C 2.,0 dodano obsługę oznaczania pewnych metod w protokole opcjonalnym, a kompilator nie wymusi implementacji opcjonalnych metod.
klasa musi być zadeklarowana, aby zaimplementować ten protokół, aby była zgodna z nim. Jest to wykrywalne w czasie wykonywania. Formalne protokoły nie mogą dostarczyć żadnych implementacji; po prostu zapewniają wywołującym, że klasy zgodne z protokołem dostarczą implementacji. W bibliotece NeXT/Apple protokoły są często używane przez rozproszony system obiektów do reprezentowania możliwości obiektu wykonywanego na zdalnym systemie.,
składnia
@protocol NSLocking- (void)lock;- (void)unlock;@end
oznacza, że istnieje abstrakcyjna idea blokowania. Poprzez podanie w definicji klasy, że protokół jest zaimplementowany,
@interface NSLock : NSObject <NSLocking>// ...@end
instancje NSLock twierdzą, że zapewnią implementację dla dwóch metod instancji.
typowanie Dynamiczneedytuj
Objective-C, podobnie jak Smalltalk, może używać typowania dynamicznego: obiektowi można wysłać wiadomość, która nie jest określona w jego interfejsie., Może to pozwolić na większą elastyczność, ponieważ pozwala obiektowi „przechwycić” wiadomość i wysłać wiadomość do innego obiektu, który może odpowiednio odpowiedzieć na wiadomość, lub podobnie wysłać wiadomość do innego obiektu. To zachowanie jest znane jako przekazywanie lub delegowanie wiadomości (patrz poniżej). Alternatywnie można użyć funkcji obsługi błędów w przypadku, gdy wiadomość nie może zostać przekazana. Jeśli obiekt nie przekaże wiadomości, nie odpowie na nią ani nie obsłuży błędu, system wygeneruje wyjątek runtime., Jeśli wiadomości są wysyłane do nil (wskaźnik obiektu null), będą one po cichu ignorowane lub wywołują ogólny wyjątek, w zależności od opcji kompilatora.
statyczne informacje typowania mogą być również opcjonalnie dodawane do zmiennych. Informacje te są następnie sprawdzane podczas kompilacji. W następujących czterech wypowiedziach podaje się coraz bardziej szczegółowe informacje o typie. Instrukcje są równoważne w czasie wykonywania, ale dodatkowe informacje pozwalają kompilatorowi ostrzec programistę, jeśli przekazany argument nie pasuje do określonego typu.,
- (void)setMyValue:(id)foo;
w powyższej instrukcji foo może należeć do dowolnej klasy.
- (void)setMyValue:(id<NSCopying>)foo;
w powyższej instrukcji foo może być instancją dowolnej klasy zgodnej z protokołemNSCopying
.
- (void)setMyValue:(NSNumber *)foo;
w powyższej instrukcji foo musi być instancją klasy NSNumber.
- (void)setMyValue:(NSNumber<NSCopying> *)foo;
w powyższej instrukcji foo musi być instancją klasy NSNumber i musi być zgodne z protokołemNSCopying
.,
w Objective-C wszystkie obiekty są reprezentowane jako wskaźniki, a statyczna inicjalizacja nie jest dozwolona. Najprostszym obiektem jest typ, na który wskazuje id (objc_obj*), który ma tylko wskaźnik isa opisujący jego klasę. Inne typy z C, takie jak wartości i struktury, pozostają niezmienione, ponieważ nie są częścią systemu obiektowego. Ta decyzja różni się od modelu obiektowego C++, w którym struktury i klasy są Zjednoczone.
ForwardingEdit
Objective-C umożliwia wysłanie wiadomości do obiektu, który może nie odpowiedzieć., Zamiast odpowiadać lub po prostu upuszczać wiadomość, obiekt może przesłać wiadomość do obiektu, który może odpowiedzieć. Forwarding może być używany do uproszczenia implementacji pewnych wzorców projektowych, takich jak wzór obserwatora lub wzór proxy.
runtime Objective-C określa parę metod w obiekcie
obiekt, który chce zaimplementować przekazywanie, musi tylko nadpisać metodę przekazywania nową metodą, aby zdefiniować zachowanie przekazywania. Metoda akcji performv:: nie musi być nadpisywana, ponieważ metoda ta wykonuje jedynie akcję opartą na selektorze i argumentach., Zwróć uwagę na typSEL
, który jest typem wiadomości w Objective-C.
Uwaga: w OPENSTEP, Cocoa i GNUstep, powszechnie używanych frameworkach Objective-C, nie używa się klasy Object. Do przekazywania danych służy metoda – (void)forwardInvocation:(NSInvocation *)do przekazywania danych służy metoda anInvocation klasy NSObject.
Przykładedytuj
oto przykład programu, który demonstruje podstawy przekazywania.
spedytor.h spedytor.m odbiorca.h
#import <objc/Object.h>// A simple Recipient object.@interface Recipient : Object- (id)hello;@end
m
#import "Recipient.h"@implementation Recipient- (id)hello { printf("Recipient says hello!\n"); return self;}@end
main.,m
NotesEdit
podczas kompilacji przy użyciu gcc, kompilator zgłasza:
kompilator zgłasza punkt podany wcześniej, że spedytor nie odpowiada na wiadomości hello. W tej sytuacji bezpiecznie jest zignorować ostrzeżenie, ponieważ przekierowanie zostało wdrożone. Uruchomienie programu daje takie wyjście:
$ ./a.outRecipient says hello!
CategoriesEdit
podczas projektowania Objective-C jednym z głównych problemów była konserwacja dużych baz kodu., Doświadczenia ze świata programowania strukturalnego pokazały, że jednym z głównych sposobów ulepszania kodu jest rozbicie go na mniejsze części. Objective-C zapożyczył i rozszerzył koncepcję kategorii z implementacji Smalltalk, aby pomóc w tym procesie.
ponadto metody w ramach kategorii są dodawane do klasy w czasie wykonywania. Tak więc kategorie pozwalają programiście na dodawanie metod do istniejącej klasy-otwartej klasy-bez konieczności rekompilowania tej klasy lub nawet dostępu do jej kodu źródłowego., Na przykład, jeśli system nie zawiera sprawdzania pisowni w swojej implementacji ciągów znaków, można go dodać bez modyfikowania kodu źródłowego ciągów znaków.
metody wewnątrz kategorii stają się nie do odróżnienia od metod w klasie, gdy program jest uruchomiony. Kategoria ma pełny dostęp do wszystkich zmiennych instancji klasy, w tym zmiennych prywatnych.
Jeśli Kategoria deklaruje metodę z tym samym podpisem co istniejąca metoda w klasie, metoda kategorii jest przyjmowana. W ten sposób kategorie mogą nie tylko dodawać metody do klasy, ale także zastępować istniejące metody., Ta funkcja może być używana do naprawiania błędów w innych klasach przez przepisywanie ich metod lub do powodowania globalnej zmiany zachowania klasy w programie. Jeśli dwie kategorie mają metody o tej samej nazwie, ale różne podpisy metod, nie jest zdefiniowane, która metoda kategorii zostanie przyjęta.
inne języki próbowały dodać tę funkcję na wiele sposobów. TOM poszedł o krok dalej w systemie Objective-C i pozwolił na dodanie zmiennych. Inne języki używały rozwiązań opartych na prototypach, z których najbardziej godnym uwagi jest Self.
C# i Visual Basic.,Języki NET implementują powierzchownie podobną funkcjonalność w postaci metod rozszerzeń, ale te nie mają dostępu do prywatnych zmiennych klasy. Ruby i kilka innych dynamicznych języków programowania określa tę technikę jako „Monkey patching”.
Logtalk implementuje koncepcję kategorii (jako jednostek pierwszej klasy), która zastępuje funkcjonalność kategorii Objective-C (kategorie Logtalk mogą być również używane jako drobnoziarniste jednostki kompozycji podczas definiowania np. nowych klas lub prototypów; w szczególności Kategoria Logtalk może być praktycznie importowana przez dowolną liczbę klas i prototypów).,
przykład użycie categoriesEdit
Ten przykład buduje klasę Integer, definiując najpierw klasę basic z zaimplementowanymi tylko metodami accessora i dodając dwie kategorie, arytmetykę i wyświetlanie, które rozszerzają klasę basic. Chociaż kategorie mogą uzyskać dostęp do prywatnych członków danych klasy bazowej, często dobrą praktyką jest uzyskiwanie dostępu do tych prywatnych członków danych za pomocą metod accessora, co pomaga zachować niezależność kategorii od klasy bazowej. Wdrożenie takich accesorów jest jednym z typowych zastosowań kategorii. Innym jest użycie kategorii do dodawania metod do klasy bazowej., Nie uważa się jednak za dobrą praktykę stosowania kategorii do nadpisywania podklas, znanych również jako łatanie małp. Protokoły nieformalne są implementowane jako kategoria na podstawowej klasie NSObject. Zgodnie z konwencją, pliki zawierające kategorie rozszerzające klasy bazowe przyjmą nazwę BaseClass+ExtensionClass.h.
Integer.h
#import <objc/Object.h>@interface Integer : Object { int integer;}- (int)integer;- (id)integer:(int)_integer;@end
liczba całkowita.M liczba całkowita + arytmetyka.h
#import "Integer.h"@interface Integer (Arithmetic)- (id) add: (Integer *) addend;- (id) sub: (Integer *) subtrahend;@end
liczba całkowita+arytmetyka.m liczba całkowita + Wyświetlacz.h
#import "Integer.h"@interface Integer (Display)- (id) showstars;- (id) showint;@end
liczba całkowita+Wyświetlacz.m main.,m
NotesEdit
Kompilacja jest wykonywana na przykład przez:
gcc -x objective-c main.m Integer.m Integer+Arithmetic.m Integer+Display.m -lobjc
można eksperymentować, pomijając #import „liczba całkowita+arytmetyka.h ” i linie oraz pomijanie liczby całkowitej + arytmetyka.m w kompilacji. Program nadal będzie działał. Oznacza to, że możliwe jest mieszanie i dopasowywanie dodanych kategorii w razie potrzeby; jeśli kategoria nie musi mieć jakiejś zdolności, po prostu nie może być kompilowana.
PosingEdit
Objective-C pozwala klasie całkowicie zastąpić inną klasę w programie. Mówi się, że Klasa zastępująca „pozuje jako” klasa docelowa.,
Klasa posing została uznana za przestarzałą w systemie Mac OS X v10.5 i jest niedostępna w 64-bitowym środowisku wykonawczym. Podobną funkcjonalność można uzyskać za pomocą metody swizzling in categories, która zamienia implementację jednej metody na inną, która ma ten sam podpis.
dla wersji nadal obsługujących pozowanie, wszystkie wiadomości wysyłane do klasy docelowej są odbierane przez klasę pozowania. Istnieje kilka ograniczeń:
- klasa może być tylko jedną ze swoich bezpośrednich lub pośrednich superklas.,
- Klasa posing nie może definiować żadnych nowych zmiennych instancji, których nie ma w klasie docelowej (choć może definiować lub nadpisywać metody).
- Klasa docelowa może nie otrzymywać żadnych wiadomości przed pozowaniem.
pozowanie, podobnie jak w przypadku kategorii, umożliwia globalną augmentację istniejących klas. Pozowanie pozwala na dwie funkcje nieobecne w kategoriach:
- Klasa posing może wywoływać nadpisane metody poprzez super, włączając w to implementację klasy docelowej.
- Klasa pozująca może nadpisywać metody zdefiniowane w kategoriach.,
na przykład
to przechwytuje każde wywołanie setMainMenu do Nsapply.
#importEdit
w języku C, dyrektywa#include
pre-compile zawsze powoduje, że zawartość pliku jest wstawiana do źródła w tym momencie. Objective-C posiada dyrektywę #import
, równoważną z tym, że każdy plik jest dołączany tylko raz na jednostkę kompilacji, co eliminuje potrzebę dołączania strażników.