Krótki przewodnik dotyczący celu-C
Objective-C jest językiem ogólnego przeznaczenia, który został opracowany na podstawie języka programowania C poprzez dodanie funkcji języka programowania Small Talk, co czyni go językiem zorientowanym obiektowo. Jest używany głównie w tworzeniu systemów operacyjnych iOS i Mac OS X, a także w aplikacjach.
Początkowo Objective-C został opracowany przez NeXT dla systemu operacyjnego NeXTSTEP, od którego został przejęty przez Apple dla systemów iOS i Mac OS X.
Programowanie obiektowe
W pełni obsługuje programowanie zorientowane obiektowo, w tym cztery filary programowania obiektowego -
- Encapsulation
- Ukrywanie danych
- Inheritance
- Polymorphism
Przykładowy kod
#import <Foundation/Foundation.h>
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSLog (@"hello world");
[pool drain];
return 0;
}
Ramy fundamentowe
Foundation Framework zapewnia duży zestaw funkcji, które są wymienione poniżej.
Zawiera listę rozszerzonych typów danych, takich jak NSArray, NSDictionary, NSSet i tak dalej.
Składa się z bogatego zestawu funkcji obsługujących pliki, ciągi znaków itp.
Zapewnia funkcje do obsługi adresów URL, narzędzia takie jak formatowanie daty, obsługa danych, obsługa błędów itp.
Cel nauczania-C
Najważniejszą rzeczą do zrobienia podczas nauki Objective-C jest skupienie się na pojęciach i nie zagubienie się w technicznych szczegółach języka.
Celem nauki języka programowania jest stać się lepszym programistą; to znaczy bardziej efektywnie projektować i wdrażać nowe systemy oraz konserwować stare.
Korzystanie z Objective-C
Objective-C, jak wspomniano wcześniej, jest używany w systemach iOS i Mac OS X. Ma dużą bazę użytkowników iOS i znacznie zwiększa liczbę użytkowników Mac OS X. A ponieważ Apple stawia przede wszystkim na jakość i jest to wspaniałe dla tych, którzy zaczęli uczyć się Objective-C.
Konfiguracja środowiska lokalnego
Jeśli nadal chcesz skonfigurować swoje środowisko dla języka programowania Objective-C, potrzebujesz następujących dwóch programów dostępnych na komputerze: (a) Edytor tekstu i (b) Kompilator GCC.
Edytor tekstu
Będzie to użyte do wpisania twojego programu. Przykłady kilku edytorów obejmują Notatnik Windows, polecenie edycji systemu operacyjnego, Brief, Epsilon, EMACS i vim lub vi.
Nazwa i wersja edytora tekstu mogą się różnić w różnych systemach operacyjnych. Na przykład Notatnik będzie używany w systemie Windows, a vim lub vi może być używany w systemie Windows, a także w systemie Linux lub UNIX.
Pliki utworzone za pomocą edytora nazywane są plikami źródłowymi i zawierają kod źródłowy programu. Pliki źródłowe programów Objective-C są zwykle nazywane z rozszerzeniem ".m”.
Przed rozpoczęciem programowania upewnij się, że masz jeden edytor tekstu i masz wystarczające doświadczenie, aby napisać program komputerowy, zapisać go w pliku, skompilować i ostatecznie wykonać.
Kompilator GCC
Kod źródłowy zapisany w pliku źródłowym jest czytelnym dla człowieka źródłem programu. Musi zostać „skompilowany”, aby przekształcić się w język maszynowy, aby procesor mógł faktycznie wykonać program zgodnie z podanymi instrukcjami.
Ten kompilator GCC zostanie użyty do skompilowania twojego kodu źródłowego do końcowego programu wykonywalnego. Zakładam, że masz podstawową wiedzę na temat kompilatora języka programowania.
Kompilator GCC jest dostępny bezpłatnie na różnych platformach, a procedurę konfigurowania na różnych platformach wyjaśniono poniżej.
Instalacja w systemie UNIX / Linux
Pierwszym krokiem jest zainstalowanie gcc wraz z pakietem gcc Objective-C. Odbywa się to przez -
$ su - $ yum install gcc
$ yum install gcc-objc
Następnym krokiem jest skonfigurowanie zależności pakietów za pomocą następującego polecenia -
$ yum install make libpng libpng-devel libtiff libtiff-devel libobjc
libxml2 libxml2-devel libX11-devel libXt-devel libjpeg libjpeg-devel
Aby uzyskać pełne funkcje Objective-C, pobierz i zainstaluj GNUStep. Można to zrobić, pobierając pakiet z witrynyhttp://main.gnustep.org/resources/downloads.php.
Teraz musimy przejść do pobranego folderu i rozpakować plik przez -
$ tar xvfz gnustep-startup-
.tar.gz
Teraz musimy przejść do folderu gnustep-startup, który jest tworzony za pomocą -
$ cd gnustep-startup-<version>
Następnie musimy skonfigurować proces kompilacji -
$ ./configure
Następnie możemy budować przez -
$ make
Musimy w końcu skonfigurować środowisko poprzez -
$ . /usr/GNUstep/System/Library/Makefiles/GNUstep.sh
Mamy helloWorld. M Objective-C w następujący sposób -
#import <Foundation/Foundation.h>
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSLog (@"hello world");
[pool drain];
return 0;
}
Teraz możemy skompilować i uruchomić plik Objective-C, powiedz helloWorld.m, przełączając się do folderu zawierającego plik za pomocą cd, a następnie wykonując następujące kroki -
$ gcc `gnustep-config --objc-flags`
-L/usr/GNUstep/Local/Library/Libraries
-lgnustep-base helloWorld.m -o helloWorld
$ ./helloWorld
Możemy zobaczyć następujące dane wyjściowe -
2013-09-07 10:48:39.772 tutorialsPoint[12906] hello world
Instalacja w systemie Mac OS
Jeśli używasz Mac OS X, najłatwiejszym sposobem uzyskania GCC jest pobranie środowiska programistycznego Xcode ze strony internetowej Apple i wykonanie prostych instrukcji instalacji. Po skonfigurowaniu Xcode będziesz mógł używać kompilatora GNU dla C / C ++.
Xcode jest obecnie dostępny pod adresem developer.apple.com/technologies/tools/ .
Instalacja w systemie Windows
Aby uruchomić program Objective-C w systemie Windows, musimy zainstalować MinGW i GNUStep Core. Oba są dostępne pod adresemhttps://www.gnu.org/software/gnustep/windows/installer.html.
Najpierw musimy zainstalować pakiet MSYS / MinGW System. Następnie musimy zainstalować pakiet GNUstep Core. Oba zapewniają instalator systemu Windows, co jest oczywiste.
Następnie użyj Objective-C i GNUstep, wybierając Start -> Wszystkie programy -> GNUstep -> Shell
Przejdź do folderu zawierającego helloWorld.m
Możemy skompilować program za pomocą -
$ gcc `gnustep-config --objc-flags`
-L /GNUstep/System/Library/Libraries hello.m -o hello -lgnustep-base -lobjc
Możemy uruchomić program za pomocą -
./hello.exe
Otrzymujemy następujący wynik -
2013-09-07 10:48:39.772 tutorialsPoint[1200] hello world
Zanim przestudiujemy podstawowe elementy składowe języka programowania Objective-C, przyjrzyjmy się minimalnej strukturze programu Objective-C, abyśmy mogli przyjąć ją jako odniesienie w kolejnych rozdziałach.
Przykład Hello World Objective-C
Program Objective-C składa się zasadniczo z następujących części -
- Polecenia preprocesora
- Interface
- Implementation
- Method
- Variables
- Instrukcje i wyrażenia
- Comments
Spójrzmy na prosty kod, który wypisuje słowa „Hello World” -
#import <Foundation/Foundation.h>
@interface SampleClass:NSObject
- (void)sampleMethod;
@end
@implementation SampleClass
- (void)sampleMethod {
NSLog(@"Hello, World! \n");
}
@end
int main() {
/* my first program in Objective-C */
SampleClass *sampleClass = [[SampleClass alloc]init];
[sampleClass sampleMethod];
return 0;
}
Spójrzmy na różne części powyższego programu -
Pierwsza linia programu #import <Foundation / Foundation.h> to polecenie preprocesora, które nakazuje kompilatorowi Objective-C dołączenie pliku Foundation.h przed przejściem do właściwej kompilacji.
Następny wiersz @interface SampleClass: NSObject pokazuje, jak utworzyć interfejs. Dziedziczy NSObject, który jest klasą bazową wszystkich obiektów.
Następna linia - (void) sampleMethod; pokazuje, jak zadeklarować metodę.
Następna linia @end oznacza koniec interfejsu.
W następnym wierszu @implementation SampleClass pokazano, jak zaimplementować interfejs SampleClass.
Następny wiersz - (void) sampleMethod {} pokazuje implementację metody sampleMethod.
Następna linia @end oznacza koniec implementacji.
Następna linia int main () to główna funkcja, od której rozpoczyna się wykonywanie programu.
Następna linia /*...*/ zostanie zignorowana przez kompilator i została umieszczona w celu dodania dodatkowych komentarzy w programie. Dlatego takie linie nazywane są w programie komentarzami.
Następna linia NSLog (...) to kolejna funkcja dostępna w Objective-C, która powoduje wyświetlenie komunikatu „Hello, World!” do wyświetlenia na ekranie.
Następna linia return 0; kończy funkcję main () i zwraca wartość 0.
Skompiluj i wykonaj program Objective-C
Teraz, kiedy kompilujemy i uruchamiamy program, otrzymamy następujący wynik.
2017-10-06 07:48:32.020 demo[65832] Hello, World!
Widzieliście już podstawową strukturę programu Objective-C, więc będzie łatwo zrozumieć inne podstawowe elementy składowe języka programowania Objective-C.
Tokeny w Objective-C
Program Objective-C składa się z różnych tokenów, a token jest słowem kluczowym, identyfikatorem, stałą, literałem ciągu lub symbolem. Na przykład następująca instrukcja celu-C składa się z sześciu tokenów -
NSLog(@"Hello, World! \n");
Poszczególne żetony to -
NSLog
@
(
"Hello, World! \n"
)
;
Średniki;
W programie Objective-C średnik jest zakończeniem instrukcji. Oznacza to, że każda instrukcja musi być zakończona średnikiem. Wskazuje koniec jednej logicznej jednostki.
Na przykład poniżej znajdują się dwie różne instrukcje -
NSLog(@"Hello, World! \n");
return 0;
Komentarze
Komentarze są jak pomocny tekst w programie Objective-C i są ignorowane przez kompilator. Zaczynają się od / * i kończą znakami * /, jak pokazano poniżej -
/* my first program in Objective-C */
Nie możesz mieć komentarzy z w komentarzach i nie pojawiają się one w ciągu znaków lub literałów znakowych.
Identyfikatory
Identyfikator celu-C to nazwa używana do identyfikacji zmiennej, funkcji lub dowolnego innego elementu zdefiniowanego przez użytkownika. Identyfikator zaczyna się od litery od A do Z lub od a do z lub podkreślenia _, po którym następuje zero lub więcej liter, znaków podkreślenia i cyfr (od 0 do 9).
Cel-C nie zezwala na znaki interpunkcyjne, takie jak @, $ i% w identyfikatorach. Cel-C tocase-sensitivejęzyk programowania. Zatem siła robocza i siła robocza to dwa różne identyfikatory w celu C. Oto kilka przykładów akceptowanych identyfikatorów -
mohd zara abc move_name a_123
myname50 _temp j a23b9 retVal
Słowa kluczowe
Poniższa lista przedstawia kilka zastrzeżonych słów w Objective-C. Te zarezerwowane słowa nie mogą być używane jako stałe, zmienne ani żadne inne nazwy identyfikatorów.
automatyczny | jeszcze | długo | przełącznik |
przerwa | enum | zarejestrować | typedef |
walizka | zewnętrzny | powrót | unia |
zwęglać | pływak | krótki | niepodpisany |
konst | dla | podpisany | unieważnić |
kontyntynuj | iść do | rozmiar | lotny |
domyślna | gdyby | statyczny | podczas |
zrobić | int | struct | _Zapakowane |
podwójnie | protokół | berło | realizacja |
NSObject | NSInteger | NSNumber | CGFloat |
własność | nieatomowy; | zachować | silny |
słaby | unsafe_unretained; | readwrite | tylko czytać |
Białe spacje w Objective-C
Linia zawierająca tylko białe znaki, prawdopodobnie z komentarzem, jest nazywana pustą linią, a kompilator Objective-C całkowicie ją ignoruje.
Białe znaki to termin używany w Objective-C do opisywania spacji, tabulatorów, znaków nowej linii i komentarzy. Biała spacja oddziela jedną część instrukcji od drugiej i umożliwia kompilatorowi określenie, gdzie kończy się jeden element instrukcji, taki jak int, a zaczyna następny element. Dlatego w poniższym oświadczeniu -
int age;
Aby kompilator mógł je rozróżnić, między int a age musi znajdować się co najmniej jeden biały znak (zwykle spacja). Z drugiej strony w poniższym oświadczeniu
fruit = apples + oranges; // get the total fruit
Pomiędzy owocami a = lub między = a jabłkami nie są potrzebne żadne spacje, chociaż możesz je dodać, jeśli chcesz, aby były czytelne.
W języku programowania Objective-C typy danych odnoszą się do rozbudowanego systemu używanego do deklarowania zmiennych lub funkcji różnych typów. Typ zmiennej określa, ile miejsca zajmuje ona w pamięci i jak jest interpretowany przechowywany wzór bitowy.
Typy w Celu C można sklasyfikować w następujący sposób -
Sr.No. | Rodzaje i opis |
---|---|
1 | Basic Types − Są to typy arytmetyczne i składają się z dwóch typów: (a) typy całkowite i (b) typy zmiennoprzecinkowe. |
2 | Enumerated types − Ponownie są to typy arytmetyczne i są używane do definiowania zmiennych, którym można przypisać tylko określone dyskretne wartości całkowite w całym programie. |
3 | The type void − Specyfikator typu void wskazuje, że żadna wartość nie jest dostępna. |
4 | Derived types − Obejmują one (a) typy wskaźników, (b) typy tablic, (c) typy struktur, (d) typy unii oraz (e) typy funkcji. |
Typy tablic i typy struktur są nazywane zbiorczo typami zagregowanymi. Typ funkcji określa typ wartości zwracanej przez funkcję. W następnej sekcji zobaczymy podstawowe typy, podczas gdy inne typy zostaną omówione w kolejnych rozdziałach.
Typy całkowite
Poniższa tabela zawiera szczegółowe informacje na temat standardowych typów liczb całkowitych wraz z rozmiarami pamięci i zakresami wartości -
Rodzaj | Rozmiar pamięci | Zakres wartości |
---|---|---|
zwęglać | 1 bajt | -128 do 127 lub 0 do 255 |
unsigned char | 1 bajt | Od 0 do 255 |
podpisany char | 1 bajt | -128 do 127 |
int | 2 lub 4 bajty | Od -32 768 do 32767 lub od -2 147 483 648 do 2 147 483 647 |
unsigned int | 2 lub 4 bajty | 0 do 65 535 lub 0 do 4 294 967 295 |
krótki | 2 bajty | -32 768 do 32767 |
unsigned short | 2 bajty | 0 do 65 535 |
długo | 4 bajty | -2 147 483 648 do 2 147 483 647 |
unsigned long | 4 bajty | Od 0 do 4 294 967 295 |
Aby uzyskać dokładny rozmiar typu lub zmiennej na określonej platformie, możesz użyć rozszerzenia sizeofoperator. Wyrażenie sizeof (type) zwraca rozmiar magazynu obiektu lub typ w bajtach. Poniżej znajduje się przykład, aby uzyskać rozmiar typu int na dowolnym komputerze -
#import <Foundation/Foundation.h>
int main() {
NSLog(@"Storage size for int : %d \n", sizeof(int));
return 0;
}
Kiedy kompilujesz i uruchamiasz powyższy program, daje on następujący wynik w systemie Linux -
2013-09-07 22:21:39.155 demo[1340] Storage size for int : 4
Typy zmiennoprzecinkowe
Poniższa tabela zawiera szczegółowe informacje na temat standardowych typów zmiennoprzecinkowych z rozmiarami pamięci i zakresami wartości oraz ich dokładnością -
Rodzaj | Rozmiar pamięci | Zakres wartości | Precyzja |
---|---|---|---|
pływak | 4 bajty | 1,2E-38 do 3,4E + 38 | 6 miejsc po przecinku |
podwójnie | 8 bajtów | 2,3E-308 do 1,7E + 308 | 15 miejsc po przecinku |
długie podwójne | 10 bajtów | 3.4E-4932 do 1.1E + 4932 | 19 miejsc po przecinku |
Plik nagłówkowy float.h definiuje makra, które pozwalają na użycie tych wartości i innych szczegółów dotyczących binarnej reprezentacji liczb rzeczywistych w programach. Poniższy przykład wydrukuje miejsce zajmowane przez typ zmiennoprzecinkowy i jego wartości zakresu -
#import <Foundation/Foundation.h>
int main() {
NSLog(@"Storage size for float : %d \n", sizeof(float));
return 0;
}
Kiedy kompilujesz i uruchamiasz powyższy program, daje on następujący wynik w systemie Linux -
2013-09-07 22:22:21.729 demo[3927] Storage size for float : 4
The void Type
Typ void określa, że żadna wartość nie jest dostępna. Jest używany w trzech rodzajach sytuacji -
Sr.No. | Rodzaje i opis |
---|---|
1 | Function returns as void Istnieją różne funkcje w Objective-C, które nie zwracają wartości lub można powiedzieć, że zwracają void. Funkcja bez zwracanej wartości ma zwracany typ void. Na przykład,void exit (int status); |
2 | Function arguments as void Istnieją różne funkcje w Objective-C, które nie akceptują żadnego parametru. Funkcja bez parametru może zostać uznana za nieważną. Na przykład,int rand(void); |
Typ pustki może nie być dla Ciebie w tym momencie zrozumiały, więc przejdźmy dalej i omówimy te koncepcje w kolejnych rozdziałach.
Zmienna to nic innego jak nazwa nadana obszarowi pamięci, którym nasze programy mogą manipulować. Każda zmienna w Objective-C ma określony typ, który określa rozmiar i układ pamięci zmiennej; zakres wartości, które mogą być przechowywane w tej pamięci; oraz zestaw operacji, które można zastosować do zmiennej.
Nazwa zmiennej może składać się z liter, cyfr i znaku podkreślenia. Musi zaczynać się od litery lub podkreślenia. Wielkie i małe litery są różne, ponieważ Objective-C rozróżnia wielkość liter. W oparciu o podstawowe typy wyjaśnione w poprzednim rozdziale, będą następujące podstawowe typy zmiennych -
Sr.No. | Typ i opis |
---|---|
1 | char Zwykle pojedynczy oktet (jeden bajt). To jest typ całkowity. |
2 | int Najbardziej naturalny rozmiar liczby całkowitej dla maszyny. |
3 | float Wartość zmiennoprzecinkowa o pojedynczej precyzji. |
4 | double Wartość zmiennoprzecinkowa podwójnej precyzji. |
5 | void Reprezentuje brak typu. |
Język programowania Objective-C pozwala również na zdefiniowanie różnych innych typów zmiennych, które omówimy w kolejnych rozdziałach, takich jak Enumeration, Pointer, Array, Structure, Union, itp. W tym rozdziale przyjrzymy się tylko podstawowym typom zmiennych.
Definicja zmiennej w celu C
Definicja zmiennej oznacza poinformowanie kompilatora, gdzie i ile ma utworzyć magazyn dla zmiennej. Definicja zmiennej określa typ danych i zawiera listę co najmniej jednej zmiennej tego typu w następujący sposób -
type variable_list;
Tutaj, type musi być prawidłowym typem danych Objective-C, w tym char, w_char, int, float, double, bool lub dowolny obiekt zdefiniowany przez użytkownika itp. oraz variable_listmoże składać się z jednej lub więcej nazw identyfikatorów oddzielonych przecinkami. Tutaj pokazano kilka ważnych deklaracji -
int i, j, k;
char c, ch;
float f, salary;
double d;
Linia int i, j, k;zarówno deklaruje, jak i definiuje zmienne i, j oraz k; co instruuje kompilator, aby utworzył zmienne o nazwach i, j oraz k typu int.
Zmienne można zainicjować (przypisać wartość początkową) w ich deklaracji. Inicjator składa się ze znaku równości, po którym następuje stałe wyrażenie w następujący sposób -
type variable_name = value;
Oto kilka przykładów:
extern int d = 3, f = 5; // declaration of d and f.
int d = 3, f = 5; // definition and initializing d and f.
byte z = 22; // definition and initializes z.
char x = 'x'; // the variable x has the value 'x'.
Definicja bez inicjalizatora: zmienne ze statycznym czasem trwania są niejawnie inicjowane wartością NULL (wszystkie bajty mają wartość 0); początkowa wartość wszystkich innych zmiennych jest niezdefiniowana.
Deklaracja zmiennej w celu C
Deklaracja zmiennej zapewnia kompilatorowi, że istnieje jedna zmienna o podanym typie i nazwie, dzięki czemu kompilator przechodzi do dalszej kompilacji bez konieczności posiadania pełnych szczegółów na temat zmiennej. Deklaracja zmiennej ma swoje znaczenie tylko w momencie kompilacji, kompilator potrzebuje rzeczywistej deklaracji zmiennej w momencie linkowania programu.
Deklaracja zmiennej jest przydatna, gdy korzystasz z wielu plików i definiujesz swoją zmienną w jednym z plików, który będzie dostępny w momencie linkowania programu. Będziesz używaćexternsłowo kluczowe, aby zadeklarować zmienną w dowolnym miejscu. Chociaż możesz deklarować zmienną wiele razy w swoim programie Objective-C, ale można ją zdefiniować tylko raz w pliku, funkcji lub bloku kodu.
Przykład
Wypróbuj poniższy przykład, w którym zmienne zostały zadeklarowane u góry, ale zostały zdefiniowane i zainicjowane wewnątrz funkcji głównej -
#import <Foundation/Foundation.h>
// Variable declaration:
extern int a, b;
extern int c;
extern float f;
int main () {
/* variable definition: */
int a, b;
int c;
float f;
/* actual initialization */
a = 10;
b = 20;
c = a + b;
NSLog(@"value of c : %d \n", c);
f = 70.0/3.0;
NSLog(@"value of f : %f \n", f);
return 0;
}
Kiedy powyższy kod jest kompilowany i wykonywany, daje następujący wynik -
2013-09-07 22:43:31.695 demo[14019] value of c : 30
2013-09-07 22:43:31.695 demo[14019] value of f : 23.333334
Ta sama koncepcja dotyczy deklaracji funkcji, gdzie podajesz nazwę funkcji w momencie jej deklaracji, a jej rzeczywistą definicję można podać gdziekolwiek indziej. W poniższym przykładzie wyjaśniono to za pomocą funkcji C i jak wiesz, Objective-C obsługuje również funkcje w stylu C -
// function declaration
int func();
int main() {
// function call
int i = func();
}
// function definition
int func() {
return 0;
}
Lvalues i Rvalues in Objective-C
Istnieją dwa rodzaje wyrażeń w Objective-C -
lvalue- Wyrażenia odnoszące się do miejsca w pamięci nazywane są wyrażeniem „l-wartość”. Wartość l może pojawić się jako lewa lub prawa strona przypisania.
rvalue- Termin rvalue odnosi się do wartości danych przechowywanych pod jakimś adresem w pamięci. Wartość r to wyrażenie, któremu nie można przypisać wartości, co oznacza, że wartość r może pojawić się po prawej, ale nie po lewej stronie przypisania.
Zmienne to lvalues, więc mogą pojawiać się po lewej stronie przydziału. Literały numeryczne są wartościami r, więc nie można ich przypisywać i nie mogą pojawiać się po lewej stronie. Poniżej znajduje się prawidłowe oświadczenie -
int g = 20;
Ale poniższe nie jest prawidłową instrukcją i spowodowałoby błąd w czasie kompilacji -
10 = 20;
Stałe odnoszą się do stałych wartości, których program nie może zmieniać podczas wykonywania. Te stałe wartości są również nazywaneliterals.
Stałe mogą mieć dowolny z podstawowych typów danych, takich jak stała całkowita, stała zmiennoprzecinkowa, stała znakowa lub literał ciągu . Istnieją również stałe wyliczania.
Plik constants są traktowane jak zwykłe zmienne, z tym wyjątkiem, że ich wartości nie mogą być modyfikowane po ich definicji.
Literały całkowite
Literał liczby całkowitej może być stałą dziesiętną, ósemkową lub szesnastkową. Prefiks określa podstawę lub podstawę: 0x lub 0X dla szesnastkowej, 0 dla ósemkowej i nic dla dziesiętnej.
Literał liczby całkowitej może również mieć sufiks będący kombinacją U i L, odpowiednio dla unsigned i long. Sufiks może być pisany wielką lub małą literą i może mieć dowolną kolejność.
Oto kilka przykładów literałów całkowitych -
212 /* Legal */
215u /* Legal */
0xFeeL /* Legal */
078 /* Illegal: 8 is not an octal digit */
032UU /* Illegal: cannot repeat a suffix */
Poniżej znajdują się inne przykłady różnych typów literałów całkowitych -
85 /* decimal */
0213 /* octal */
0x4b /* hexadecimal */
30 /* int */
30u /* unsigned int */
30l /* long */
30ul /* unsigned long */
Literały zmiennoprzecinkowe
Literał zmiennoprzecinkowy ma część całkowitą, przecinek dziesiętny, część ułamkową i część wykładniczą. Literały zmiennoprzecinkowe można przedstawiać w postaci dziesiętnej lub wykładniczej.
Przedstawiając przy użyciu postaci dziesiętnej, należy uwzględnić kropkę dziesiętną, wykładnik lub jedno i drugie, a podczas przedstawiania w postaci wykładniczej należy uwzględnić część całkowitą, część ułamkową lub oba te elementy. Podpisany wykładnik jest wprowadzany przez e lub E.
Oto kilka przykładów literałów zmiennoprzecinkowych -
3.14159 /* Legal */
314159E-5L /* Legal */
510E /* Illegal: incomplete exponent */
210f /* Illegal: no decimal or exponent */
.e55 /* Illegal: missing integer or fraction */
Stałe znakowe
Literały znakowe są ujęte w pojedyncze cudzysłowy, np. „X” i mogą być przechowywane w prostej zmiennej typu char rodzaj.
Literał znakowy może być zwykłym znakiem (np. „X”), sekwencją ucieczki (np. „\ T”) lub znakiem uniwersalnym (np. „\ U02C0”).
Istnieją pewne znaki w C, które są poprzedzone odwrotnym ukośnikiem, będą miały specjalne znaczenie i są używane do reprezentowania jak nowa linia (\ n) lub tabulacja (\ t). Tutaj masz listę niektórych takich kodów sekwencji ucieczki -
Sekwencja ewakuacyjna | Znaczenie |
---|---|
\\ | \ postać |
\ ' | ' postać |
\ " | " postać |
\? | ? postać |
\za | Alert lub dzwonek |
\b | Backspace |
\fa | Form feed |
\ n | Nowa linia |
\ r | Powrót karetki |
\ t | Zakładka pozioma |
\ v | Zakładka pionowa |
\ ooo | Liczba ósemkowa składająca się z jednej do trzech cyfr |
\ xhh. . . | Liczba szesnastkowa składająca się z jednej lub więcej cyfr |
Poniżej znajduje się przykład pokazujący kilka znaków sekwencji sterującej -
#import <Foundation/Foundation.h>
int main() {
NSLog(@"Hello\tWorld\n\n");
return 0;
}
Kiedy powyższy kod jest kompilowany i wykonywany, daje następujący wynik -
2013-09-07 22:17:17.923 demo[17871] Hello World
Literały ciągów
Literały łańcuchowe lub stałe są ujęte w podwójne cudzysłowy „”. Ciąg zawiera znaki podobne do literałów znakowych: zwykłe znaki, sekwencje ucieczki i znaki uniwersalne.
Możesz podzielić długi wiersz na wiele wierszy, używając literałów łańcuchowych i oddzielając je białymi znakami.
Oto kilka przykładów literałów ciągów. Wszystkie trzy formy są identycznymi ciągami.
"hello, dear"
"hello, \
dear"
"hello, " "d" "ear"
Definiowanie stałych
Istnieją dwa proste sposoby definiowania stałych w C -
Za pomocą #define preprocesor.
Za pomocą const słowo kluczowe.
#Define Preprocessor
Poniżej znajduje się formularz użycia #define preprocesora do zdefiniowania stałej -
#define identifier value
Poniższy przykład wyjaśnia to szczegółowo -
#import <Foundation/Foundation.h>
#define LENGTH 10
#define WIDTH 5
#define NEWLINE '\n'
int main() {
int area;
area = LENGTH * WIDTH;
NSLog(@"value of area : %d", area);
NSLog(@"%c", NEWLINE);
return 0;
}
Kiedy powyższy kod jest kompilowany i wykonywany, daje następujący wynik -
2013-09-07 22:18:16.637 demo[21460] value of area : 50
2013-09-07 22:18:16.638 demo[21460]
Słowo kluczowe const
Możesz użyć const prefiks do deklarowania stałych o określonym typie w następujący sposób -
const type variable = value;
Poniższy przykład wyjaśnia to szczegółowo -
#import <Foundation/Foundation.h>
int main() {
const int LENGTH = 10;
const int WIDTH = 5;
const char NEWLINE = '\n';
int area;
area = LENGTH * WIDTH;
NSLog(@"value of area : %d", area);
NSLog(@"%c", NEWLINE);
return 0;
}
Kiedy powyższy kod jest kompilowany i wykonywany, daje następujący wynik -
2013-09-07 22:19:24.780 demo[25621] value of area : 50
2013-09-07 22:19:24.781 demo[25621]
Zauważ, że dobrą praktyką programistyczną jest definiowanie stałych WIELKIMI LITERAMI.
Operator to symbol, który mówi kompilatorowi, aby wykonał określone operacje matematyczne lub logiczne. Język Objective-C jest bogaty we wbudowane operatory i zapewnia następujące typy operatorów -
- Operatory arytmetyczne
- Operatorzy relacyjni
- Operatory logiczne
- Operatory bitowe
- Operatory przypisania
- Różne operatory
Ten samouczek wyjaśni po kolei operatory arytmetyczne, relacyjne, logiczne, bitowe, przypisania i inne.
Operatory arytmetyczne
Poniższa tabela przedstawia wszystkie operatory arytmetyczne obsługiwane przez język Objective-C. Przyjmij zmiennąA posiada 10 i zmienną B mieści 20, a następnie -
Pokaż przykłady
Operator | Opis | Przykład |
---|---|---|
+ | Dodaje dwa operandy | A + B da 30 |
- | Odejmuje drugi operand od pierwszego | A - B da -10 |
* | Mnoży oba operandy | A * B da 200 |
/ | Dzieli licznik przez mianownik | B / A da 2 |
% | Operator modułu i reszta po dzieleniu całkowitoliczbowym | B% A da 0 |
++ | Operator inkrementacji zwiększa wartość całkowitą o jeden | A ++ da 11 |
- | Operator zmniejszania zmniejsza wartość całkowitą o jeden | A-- da 9 |
Operatorzy relacyjni
Poniższa tabela przedstawia wszystkie operatory relacyjne obsługiwane przez język Objective-C. Przyjmij zmiennąA posiada 10 i zmienną B mieści 20, a następnie -
Pokaż przykłady
Operator | Opis | Przykład |
---|---|---|
== | Sprawdza, czy wartości dwóch operandów są równe, czy nie; jeśli tak, to warunek staje się prawdziwy. | (A == B) nie jest prawdą. |
! = | Sprawdza, czy wartości dwóch operandów są równe, czy nie; jeśli wartości nie są równe, warunek staje się prawdziwy. | (A! = B) jest prawdą. |
> | Sprawdza, czy wartość lewego operandu jest większa niż wartość prawego operandu; jeśli tak, to warunek staje się prawdziwy. | (A> B) nie jest prawdą. |
< | Sprawdza, czy wartość lewego operandu jest mniejsza niż wartość prawego operandu; jeśli tak, to warunek staje się prawdziwy. | (A <B) jest prawdą. |
> = | Sprawdza, czy wartość lewego operandu jest większa lub równa wartości prawego operandu; jeśli tak, to warunek staje się prawdziwy. | (A> = B) nie jest prawdą. |
<= | Sprawdza, czy wartość lewego operandu jest mniejsza lub równa wartości prawego operandu; jeśli tak, to warunek staje się prawdziwy. | (A <= B) jest prawdą. |
Operatory logiczne
Poniższa tabela przedstawia wszystkie operatory logiczne obsługiwane przez język Objective-C. Przyjmij zmiennąA zawiera 1 i zmienną B posiada 0, a następnie -
Pokaż przykłady
Operator | Opis | Przykład |
---|---|---|
&& | Nazywany operatorem logicznym AND. Jeśli oba operandy są niezerowe, warunek staje się prawdziwy. | (A && B) jest fałszem. |
|| | Nazywany operatorem logicznym OR. Jeśli którykolwiek z dwóch operandów jest różny od zera, warunek staje się prawdziwy. | (A || B) jest prawdą. |
! | Nazywany operatorem logicznym NOT. Służy do odwracania stanu logicznego operandu. Jeśli warunek jest spełniony, operator logiczny NIE spowoduje fałsz. | ! (A && B) jest prawdą. |
Operatory bitowe
Operator bitowy działa na bitach i wykonuje operacje bit po bicie. Tabele prawdy dla &, | i ^ są następujące -
p | q | p & q | p | q | p ^ q |
---|---|---|---|---|
0 | 0 | 0 | 0 | 0 |
0 | 1 | 0 | 1 | 1 |
1 | 1 | 1 | 1 | 0 |
1 | 0 | 0 | 1 | 1 |
Załóżmy, że A = 60; i B = 13; teraz w formacie binarnym będą wyglądać następująco -
A = 0011 1100
B = 0000 1101
-----------------
A&B = 0000 1100
A | B = 0011 1101
A ^ B = 0011 0001
~ A = 1100 0011
W poniższej tabeli wymieniono operatory bitowe obsługiwane przez język Objective-C. Załóżmy, że zmienna A zawiera 60, a zmienna B 13, a następnie -
Pokaż przykłady
Operator | Opis | Przykład |
---|---|---|
& | Operator binarny AND kopiuje trochę do wyniku, jeśli istnieje w obu operandach. | (A i B) da 12, czyli 0000 1100 |
| | Operator binarny OR kopiuje bit, jeśli istnieje w którymkolwiek operandzie. | (A | B) da 61, czyli 0011 1101 |
^ | Binarny operator XOR kopiuje bit, jeśli jest ustawiony w jednym operandzie, ale nie w obu. | (A ^ B) da 49, czyli 0011 0001 |
~ | Operator dopełniacza binarnego jest jednoargumentowy i powoduje „odwracanie” bitów. | (~ A) da -61, czyli 1100 0011 w postaci dopełnienia 2. |
<< | Binarny operator przesunięcia w lewo. Wartość lewych operandów jest przesuwana w lewo o liczbę bitów określoną przez prawy operand. | A << 2 da 240, czyli 1111 0000 |
>> | Binarny operator przesunięcia w prawo. Wartość lewego operandu jest przesuwana w prawo o liczbę bitów określoną przez prawy operand. | >> 2 da 15, czyli 0000 1111 |
Operatory przypisania
Istnieją następujące operatory przypisania obsługiwane przez język Objective-C -
Pokaż przykłady
Operator | Opis | Przykład |
---|---|---|
= | Prosty operator przypisania, przypisuje wartości z operandów po prawej stronie do operandów po lewej stronie | C = A + B przypisze wartość A + B do C. |
+ = | Dodaj operator przypisania AND, dodaje prawy operand do lewego operandu i przypisuje wynik do lewego operandu | C + = A jest równoważne C = C + A |
- = | Operator odejmowania AND przypisania, odejmuje prawy operand od lewego operandu i przypisuje wynik do lewego operandu | C - = A jest równoważne C = C - A |
* = | Operator mnożenia AND przypisania, mnoży prawy operand z lewym operandem i przypisuje wynik do lewego operandu | C * = A jest równoważne C = C * A |
/ = | Operator dzielenia AND przypisania, dzieli lewy operand z prawym operandem i przypisuje wynik do lewego operandu | C / = A jest równoważne C = C / A |
% = | Operator przypisania modułu AND, pobiera moduł przy użyciu dwóch operandów i przypisuje wynik lewemu operandowi | C% = A jest równoważne C = C% A |
<< = | Operator przesunięcia w lewo AND przypisania | C << = 2 to to samo, co C = C << 2 |
>> = | Operator prawego przesunięcia AND przypisania | C >> = 2 to to samo, co C = C >> 2 |
& = | Operator przypisania bitowego AND | C & = 2 to to samo, co C = C & 2 |
^ = | bitowe wykluczające OR i operator przypisania | C ^ = 2 to to samo, co C = C ^ 2 |
| = | bitowy operator OR i przypisanie | C | = 2 to to samo, co C = C | 2 |
Różne operatory ↦ sizeof & ternary
Istnieje kilka innych ważnych operatorów, w tym sizeof i ? : obsługiwane przez język Objective-C.
Pokaż przykłady
Operator | Opis | Przykład |
---|---|---|
rozmiar() | Zwraca rozmiar zmiennej. | sizeof (a), gdzie a jest liczbą całkowitą, zwróci 4. |
& | Zwraca adres zmiennej. | &za; poda rzeczywisty adres zmiennej. |
* | Wskaźnik do zmiennej. | *za; wskaże zmienną. |
? : | Wyrażenie warunkowe | Jeśli warunek jest prawdziwy? Wtedy wartość X: W przeciwnym razie wartość Y |
Pierwszeństwo operatorów w celu C
Pierwszeństwo operatorów określa grupowanie terminów w wyrażeniu. Ma to wpływ na sposób oceny wyrażenia. Niektórzy operatorzy mają wyższy priorytet niż inni; na przykład operator mnożenia ma wyższy priorytet niż operator dodawania -
Na przykład x = 7 + 3 * 2; tutaj x ma przypisane 13, a nie 20, ponieważ operator * ma wyższy priorytet niż +, więc najpierw jest mnożony przez 3 * 2, a następnie sumowany do 7.
Tutaj operatory o najwyższym priorytecie pojawiają się na górze tabeli, a operatory o najniższym priorytecie - na dole. W wyrażeniu najpierw zostaną ocenione operatory o wyższym priorytecie.
Kategoria | Operator | Łączność |
---|---|---|
Przyrostek | () [] ->. ++ - - | Z lewej na prawą |
Jednoargumentowe | + -! ~ ++ - - (typ) * & sizeof | Od prawej do lewej |
Mnożny | * /% | Z lewej na prawą |
Przyłączeniowy | + - | Z lewej na prawą |
Zmiana | << >> | Z lewej na prawą |
Relacyjny | <<=>> = | Z lewej na prawą |
Równość | ==! = | Z lewej na prawą |
Bitowe XOR | ^ | Z lewej na prawą |
Bitowe OR | | | Z lewej na prawą |
Logiczne AND | && | Z lewej na prawą |
Logiczne LUB | || | Z lewej na prawą |
Warunkowy | ?: | Od prawej do lewej |
Zadanie | = + = - = * = / =% = >> = << = & = ^ = | = | Od prawej do lewej |
Przecinek | , | Z lewej na prawą |
Może zaistnieć sytuacja, gdy trzeba będzie kilkakrotnie wykonać blok kodu. Ogólnie instrukcje są wykonywane sekwencyjnie: pierwsza instrukcja funkcji jest wykonywana jako pierwsza, po niej następuje druga i tak dalej.
Języki programowania zapewniają różne struktury kontrolne, które pozwalają na bardziej skomplikowane ścieżki wykonywania.
Instrukcja pętli pozwala nam wielokrotnie wykonać instrukcję lub grupę instrukcji, a następująca po niej jest ogólną postacią instrukcji pętli w większości języków programowania -
Język programowania Objective-C zapewnia następujące typy pętli do obsługi wymagań dotyczących pętli. Kliknij poniższe łącza, aby sprawdzić ich szczegóły.
Sr.No. | Typ i opis pętli |
---|---|
1 | pętla while Powtarza instrukcję lub grupę instrukcji, gdy dany warunek jest prawdziwy. Testuje warunek przed wykonaniem treści pętli. |
2 | dla pętli Wykonuje sekwencję instrukcji wiele razy i skraca kod zarządzający zmienną pętli. |
3 | zrobić ... pętla while Podobnie jak instrukcja while, z tą różnicą, że testuje warunek na końcu treści pętli. |
4 | pętle zagnieżdżone Możesz użyć jednej lub więcej pętli w dowolnej innej pętli while, for lub do..while. |
Instrukcje sterowania pętlą
Instrukcje sterujące pętlą zmieniają wykonanie z jego normalnej sekwencji. Gdy wykonanie opuszcza zakres, wszystkie automatyczne obiekty utworzone w tym zakresie są niszczone.
Objective-C obsługuje następujące instrukcje sterujące. Kliknij poniższe łącza, aby sprawdzić ich szczegóły.
Sr.No. | Oświadczenie i opis kontroli |
---|---|
1 | instrukcja break Kończy loop lub switch instrukcja i przekazuje wykonanie do instrukcji bezpośrednio po pętli lub przełączniku. |
2 | kontynuuj oświadczenie Powoduje, że pętla pomija pozostałą część swojego ciała i natychmiast ponownie testuje swój stan przed ponownym powtórzeniem. |
Nieskończona pętla
Pętla staje się nieskończoną pętlą, jeśli warunek nigdy nie staje się fałszywy. Plikforpętla jest tradycyjnie używana do tego celu. Ponieważ żadne z trzech wyrażeń tworzących pętlę for nie jest wymagane, można utworzyć nieskończoną pętlę, pozostawiając puste wyrażenie warunkowe.
#import <Foundation/Foundation.h>
int main () {
for( ; ; ) {
NSLog(@"This loop will run forever.\n");
}
return 0;
}
W przypadku braku wyrażenia warunkowego przyjmuje się, że jest ono prawdziwe. Możesz mieć wyrażenie inicjujące i inkrementujące, ale programiści Objective-C częściej używają konstrukcji for (;;) do oznaczenia nieskończonej pętli.
Struktury decyzyjne wymagają, aby programista określił jeden lub więcej warunków, które mają być ocenione lub przetestowane przez program, wraz z instrukcją lub instrukcjami, które mają zostać wykonane, jeśli warunek zostanie określony jako prawdziwy, i opcjonalnie inne instrukcje do wykonania, jeśli warunek jest zdeterminowany, aby być fałszywy.
Poniżej przedstawiono ogólną formę typowej struktury podejmowania decyzji występującej w większości języków programowania -
Język programowania Objective-C zakłada dowolny non-zero i non-null wartości jako truei jeśli tak jest zero lub null, to przyjmuje się, że false wartość.
Język programowania Objective-C zapewnia następujące typy instrukcji decyzyjnych. Kliknij poniższe linki, aby sprawdzić ich szczegóły -
Sr.No. | Oświadczenie i opis |
---|---|
1 | jeśli oświadczenie Na if statement składa się z wyrażenia logicznego, po którym następuje co najmniej jedna instrukcja. |
2 | if ... else oświadczenie Na if statement może następować opcjonalnie else statement, która jest wykonywana, gdy wyrażenie logiczne ma wartość false. |
3 | zagnieżdżone instrukcje if Możesz użyć jednego if lub else if oświadczenie wewnątrz innego if lub else if sprawozdania). |
4 | instrukcja przełączania ZA switch Instrukcja umożliwia testowanie zmiennej pod kątem równości względem listy wartości. |
5 | zagnieżdżone instrukcje przełączające Możesz użyć jednego switch oświadczenie wewnątrz innego switch sprawozdania). |
The? : Operator
Omówiliśmy conditional operator ? : w poprzednim rozdziale, które można zastąpić if...elsesprawozdania. Ma następującą ogólną postać -
Exp1 ? Exp2 : Exp3;
Gdzie Exp1, Exp2 i Exp3 to wyrażenia. Zwróć uwagę na użycie i położenie okrężnicy.
Wartość? wyrażenie jest określane w następujący sposób: Exp1 jest oceniane. Jeśli to prawda, to Exp2 jest obliczane i staje się wartością całości? wyrażenie. Jeśli Exp1 ma wartość false, to Exp3 jest oceniane, a jego wartość staje się wartością wyrażenia.
Funkcja to grupa instrukcji, które razem wykonują zadanie. Każdy program Objective-C ma jedną funkcję w C.main(), a wszystkie najbardziej trywialne programy mogą definiować dodatkowe funkcje.
Możesz podzielić swój kod na osobne funkcje. To, w jaki sposób podzielisz swój kod na różne funkcje, zależy od Ciebie, ale logicznie jest to zazwyczaj taki podział, że każda funkcja wykonuje określone zadanie.
Funkcja declarationinformuje kompilator o nazwie funkcji, typie zwracanym i parametrach. Funkcjadefinition dostarcza rzeczywistą treść funkcji.
Zasadniczo w Objective-C nazywamy funkcję jako metodę.
Struktura podstawowa Objective-C zapewnia wiele wbudowanych metod, które program może wywołać. Na przykład metodaappendString() aby dołączyć ciąg do innego ciągu.
Metoda jest znana pod różnymi nazwami, takimi jak funkcja, podprogram lub procedura itp.
Definiowanie metody
Ogólna forma definicji metody w języku programowania Objective-C jest następująca -
- (return_type) method_name:( argumentType1 )argumentName1
joiningArgument2:( argumentType2 )argumentName2 ...
joiningArgumentn:( argumentTypen )argumentNamen {
body of the function
}
Definicja metody w języku programowania Objective-C składa się z nagłówka metody i treści metody . Oto wszystkie części metody -
Return Type − A method may return a value. The return_type is the data type of the value the function returns. Some methods perform the desired operations without returning a value. In this case, the return_type is the keyword void.
Method Name − This is the actual name of the method. The method name and the parameter list together constitute the method signature.
Arguments − A argument is like a placeholder. When a function is invoked, you pass a value to the argument. This value is referred to as actual parameter or argument. The parameter list refers to the type, order, and number of the arguments of a method. Arguments are optional; that is, a method may contain no argument.
Joining Argument − A joining argument is to make it easier to read and to make it clear while calling it.
Method Body − The method body contains a collection of statements that define what the method does.
Przykład
Poniżej znajduje się kod źródłowy metody o nazwie max(). Ta metoda przyjmuje dwa parametry num1 i num2 i zwraca maksimum między dwoma -
/* function returning the max between two numbers */
- (int) max:(int) num1 secondNumber:(int) num2 {
/* local variable declaration */
int result;
if (num1 > num2) {
result = num1;
} else {
result = num2;
}
return result;
}
Deklaracje metod
Metoda declarationinformuje kompilator o nazwie funkcji i sposobie wywołania metody. Rzeczywistą treść funkcji można zdefiniować oddzielnie.
Deklaracja metody składa się z następujących części -
- (return_type) function_name:( argumentType1 )argumentName1
joiningArgument2:( argumentType2 )argumentName2 ...
joiningArgumentn:( argumentTypen )argumentNamen;
Dla wyżej zdefiniowanej funkcji max (), poniżej znajduje się deklaracja metody -
-(int) max:(int)num1 andNum2:(int)num2;
Deklaracja metody jest wymagana, gdy definiujesz metodę w jednym pliku źródłowym i wywołujesz tę metodę w innym pliku. W takim przypadku należy zadeklarować funkcję na początku pliku wywołującego funkcję.
Wywołanie metody
Tworząc metodę celu-C, podajesz definicję tego, co funkcja ma robić. Aby użyć metody, będziesz musiał wywołać tę funkcję, aby wykonać określone zadanie.
Gdy program wywołuje funkcję, sterowanie programem jest przekazywane do wywoływanej metody. Wywołana metoda wykonuje zdefiniowane zadanie, a po wykonaniu jej instrukcji return lub po osiągnięciu zamykającego nawiasu zamykającego funkcję, zwraca sterowanie programem z powrotem do programu głównego.
Aby wywołać metodę, wystarczy przekazać wymagane parametry wraz z nazwą metody, a jeśli metoda zwraca wartość, można ją zapisać. Na przykład -
#import <Foundation/Foundation.h>
@interface SampleClass:NSObject
/* method declaration */
- (int)max:(int)num1 andNum2:(int)num2;
@end
@implementation SampleClass
/* method returning the max between two numbers */
- (int)max:(int)num1 andNum2:(int)num2 {
/* local variable declaration */
int result;
if (num1 > num2) {
result = num1;
} else {
result = num2;
}
return result;
}
@end
int main () {
/* local variable definition */
int a = 100;
int b = 200;
int ret;
SampleClass *sampleClass = [[SampleClass alloc]init];
/* calling a method to get max value */
ret = [sampleClass max:a andNum2:b];
NSLog(@"Max value is : %d\n", ret );
return 0;
}
Zachowałem funkcję max () wraz z funkcją main () i przestrzegałem kodu źródłowego. Podczas uruchamiania końcowego pliku wykonywalnego dałoby to następujący wynik -
2013-09-07 22:28:45.912 demo[26080] Max value is : 200
Argumenty funkcji
Jeśli funkcja ma używać argumentów, musi zadeklarować zmienne, które akceptują wartości argumentów. Te zmienne nazywane sąformal parameters funkcji.
Parametry formalne zachowują się jak inne zmienne lokalne wewnątrz funkcji i są tworzone po wejściu do funkcji i niszczone po zakończeniu.
Podczas wywoływania funkcji istnieją dwa sposoby przekazywania argumentów do funkcji -
Sr.No. | Typ i opis połączenia |
---|---|
1 | Zadzwoń według wartości Ta metoda kopiuje rzeczywistą wartość argumentu do parametru formalnego funkcji. W tym przypadku zmiany wprowadzone w parametrze wewnątrz funkcji nie mają wpływu na argument. |
2 | Zadzwoń przez odniesienie Ta metoda kopiuje adres argumentu do parametru formalnego. Wewnątrz funkcji adres służy do uzyskania dostępu do rzeczywistego argumentu użytego w wywołaniu. Oznacza to, że zmiany wprowadzone w parametrze wpływają na argument. |
Domyślnie Objective-C używa call by valueprzekazywać argumenty. Generalnie oznacza to, że kod wewnątrz funkcji nie może zmieniać argumentów użytych do wywołania funkcji, a powyższy przykład podczas wywoływania funkcji max () używał tej samej metody.
Klasa Objective-C definiuje obiekt, który łączy dane z powiązanym zachowaniem. Czasami sensowne jest przedstawienie pojedynczego zadania lub jednostki zachowania, a nie zbioru metod.
Bloki to funkcja na poziomie języka dodana do C, Objective-C i C ++, która umożliwia tworzenie odrębnych segmentów kodu, które można przekazywać do metod lub funkcji tak, jakby były wartościami. Bloki to obiekty Objective-C, co oznacza, że można je dodawać do kolekcji, takich jak NSArray lub NSDictionary. Mają również możliwość przechwytywania wartości z otaczającego zakresu, upodabniając je do domknięć lub lambd w innych językach programowania
Prosta składnia deklaracji bloku
returntype (^blockName)(argumentType);
Prosta implementacja blokowa
returntype (^blockName)(argumentType)= ^{
};
Oto prosty przykład
void (^simpleBlock)(void) = ^{
NSLog(@"This is a block");
};
Możemy wywołać blok za pomocą
simpleBlock();
Bloki przyjmują argumenty i zwracają wartości
Bloki mogą również przyjmować argumenty i zwracać wartości, podobnie jak metody i funkcje.
Oto prosty przykład implementacji i wywołania bloku z argumentami i zwracanymi wartościami.
double (^multiplyTwoValues)(double, double) =
^(double firstValue, double secondValue) {
return firstValue * secondValue;
};
double result = multiplyTwoValues(2,4);
NSLog(@"The result is %f", result);
Bloki używające definicji typów
Oto prosty przykład wykorzystujący typedef w bloku. Zwróć uwagę na tę próbkędoesn't work na online compilerNa razie. Posługiwać sięXCode uruchomić to samo.
#import <Foundation/Foundation.h>
typedef void (^CompletionBlock)();
@interface SampleClass:NSObject
- (void)performActionWithCompletion:(CompletionBlock)completionBlock;
@end
@implementation SampleClass
- (void)performActionWithCompletion:(CompletionBlock)completionBlock {
NSLog(@"Action Performed");
completionBlock();
}
@end
int main() {
/* my first program in Objective-C */
SampleClass *sampleClass = [[SampleClass alloc]init];
[sampleClass performActionWithCompletion:^{
NSLog(@"Completion is called to intimate action is performed.");
}];
return 0;
}
Skompilujmy go i wykonajmy, da następujący wynik -
2013-09-10 08:13:57.155 demo[284:303] Action Performed
2013-09-10 08:13:57.157 demo[284:303] Completion is called to intimate action is performed.
Bloki są częściej używane w aplikacjach iOS i Mac OS X. Dlatego ważniejsze jest zrozumienie użycia bloków.
W języku programowania Objective-C, aby zapisać podstawowe typy danych, takie jak int, float, bool w postaci obiektowej,
Objective-C zapewnia szereg metod pracy z NSNumber, a najważniejsze z nich są wymienione w poniższej tabeli.
Sr.No. | Metoda i opis |
---|---|
1 | + (NSNumber *)numberWithBool:(BOOL)value Tworzy i zwraca obiekt NSNumber zawierający daną wartość, traktując go jako BOOL. |
2 | + (NSNumber *)numberWithChar:(char)value Tworzy i zwraca obiekt NSNumber zawierający daną wartość, traktując go jako podpisany znak. |
3 | + (NSNumber *)numberWithDouble:(double)value Tworzy i zwraca obiekt NSNumber zawierający daną wartość, traktując ją jako podwójną. |
4 | + (NSNumber *)numberWithFloat:(float)value Tworzy i zwraca obiekt NSNumber zawierający daną wartość, traktując go jako zmiennoprzecinkową. |
5 | + (NSNumber *)numberWithInt:(int)value Tworzy i zwraca obiekt NSNumber zawierający daną wartość, traktując go jako int podpisaną. |
6 | + (NSNumber *)numberWithInteger:(NSInteger)value Tworzy i zwraca obiekt NSNumber zawierający daną wartość, traktując go jako NSInteger. |
7 | - (BOOL)boolValue Zwraca wartość odbiorcy jako BOOL. |
8 | - (char)charValue Zwraca wartość odbiorcy jako znak. |
9 | - (double)doubleValue Zwraca wartość odbiorcy jako podwójną. |
10 | - (float)floatValue Zwraca wartość odbiorcy jako liczbę zmiennoprzecinkową. |
11 | - (NSInteger)integerValue Zwraca wartość odbiorcy jako NSInteger. |
12 | - (int)intValue Zwraca wartość odbiorcy jako liczbę całkowitą. |
13 | - (NSString *)stringValue Zwraca wartość odbiorcy jako ciąg czytelny dla człowieka. |
Oto prosty przykład użycia NSNumber, który mnoży dwie liczby i zwraca iloczyn.
#import <Foundation/Foundation.h>
@interface SampleClass:NSObject
- (NSNumber *)multiplyA:(NSNumber *)a withB:(NSNumber *)b;
@end
@implementation SampleClass
- (NSNumber *)multiplyA:(NSNumber *)a withB:(NSNumber *)b {
float number1 = [a floatValue];
float number2 = [b floatValue];
float product = number1 * number2;
NSNumber *result = [NSNumber numberWithFloat:product];
return result;
}
@end
int main() {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
SampleClass *sampleClass = [[SampleClass alloc]init];
NSNumber *a = [NSNumber numberWithFloat:10.5];
NSNumber *b = [NSNumber numberWithFloat:10.0];
NSNumber *result = [sampleClass multiplyA:a withB:b];
NSString *resultString = [result stringValue];
NSLog(@"The product is %@",resultString);
[pool drain];
return 0;
}
Teraz, kiedy kompilujemy i uruchamiamy program, otrzymamy następujący wynik.
2013-09-14 18:53:40.575 demo[16787] The product is 105
Język programowania Objective-C zapewnia strukturę danych o nazwie the array, który może przechowywać sekwencyjną kolekcję elementów tego samego typu o stałym rozmiarze. Tablica jest używana do przechowywania kolekcji danych, ale często bardziej przydatne jest myślenie o tablicy jako o zbiorze zmiennych tego samego typu.
Zamiast deklarować pojedyncze zmienne, takie jak liczba0, liczba1, ... i liczba99, deklarujesz jedną zmienną tablicową, taką jak liczby, i używasz liczb [0], liczb [1] i ..., liczb [99] do reprezentowania indywidualne zmienne. Dostęp do określonego elementu w tablicy uzyskuje się za pomocą indeksu.
Wszystkie tablice składają się z ciągłych lokalizacji pamięci. Najniższy adres odpowiada pierwszemu elementowi, a najwyższy adres ostatniemu elementowi.
Deklarowanie tablic
Aby zadeklarować tablicę w Objective-C, programista określa typ elementów i liczbę elementów wymaganych przez tablicę w następujący sposób -
type arrayName [ arraySize ];
Nazywa się to tablicą jednowymiarową . PlikarraySize musi być stałą liczbą całkowitą większą od zera i typemoże być dowolnym prawidłowym typem danych celu-C. Na przykład, aby zadeklarować 10-elementową tablicę o nazwiebalance typu double, użyj tej instrukcji -
double balance[10];
Teraz saldo jest tablicą zmiennych, która wystarcza do przechowywania do 10 podwójnych liczb.
Inicjowanie tablic
Możesz zainicjować tablicę w Objective-C albo pojedynczo, albo używając pojedynczej instrukcji w następujący sposób -
double balance[5] = {1000.0, 2.0, 3.4, 17.0, 50.0};
Liczba wartości w nawiasach klamrowych {} nie może być większa niż liczba elementów, które zadeklarujemy dla tablicy w nawiasach kwadratowych []. Poniżej znajduje się przykład przypisywania pojedynczego elementu tablicy -
Jeśli pominiesz rozmiar tablicy, zostanie utworzona tablica wystarczająco duża, aby pomieścić inicjalizację. Dlatego jeśli napiszesz -
double balance[] = {1000.0, 2.0, 3.4, 17.0, 50.0};
Utworzysz dokładnie taką samą tablicę, jak w poprzednim przykładzie.
balance[4] = 50.0;
Powyższa instrukcja przypisuje elementowi numerowi 5. w tablicy wartość 50,0. Tablica z czwartym indeksem będzie piątym, czyli ostatnim elementem, ponieważ wszystkie tablice mają 0 jako indeks pierwszego elementu, który jest również nazywany indeksem bazowym. Poniżej znajduje się obrazkowa reprezentacja tej samej tablicy, którą omówiliśmy powyżej -
Dostęp do elementów tablicy
Dostęp do elementu uzyskuje się poprzez indeksowanie nazwy tablicy. Odbywa się to poprzez umieszczenie indeksu elementu w nawiasach kwadratowych po nazwie tablicy. Na przykład -
double salary = balance[9];
Powyższa instrukcja pobierze dziesiąty element z tablicy i przypisze wartość do zmiennej wynagrodzenia. Poniżej znajduje się przykład, w którym zostaną wykorzystane wszystkie wyżej wymienione trzy koncepcje, a mianowicie. deklaracja, przypisanie i dostęp do tablic -
#import <Foundation/Foundation.h>
int main () {
int n[ 10 ]; /* n is an array of 10 integers */
int i,j;
/* initialize elements of array n to 0 */
for ( i = 0; i < 10; i++ ) {
n[ i ] = i + 100; /* set element at location i to i + 100 */
}
/* output each array element's value */
for (j = 0; j < 10; j++ ) {
NSLog(@"Element[%d] = %d\n", j, n[j] );
}
return 0;
}
Kiedy powyższy kod jest kompilowany i wykonywany, daje następujący wynik -
2013-09-14 01:24:06.669 demo[16508] Element[0] = 100
2013-09-14 01:24:06.669 demo[16508] Element[1] = 101
2013-09-14 01:24:06.669 demo[16508] Element[2] = 102
2013-09-14 01:24:06.669 demo[16508] Element[3] = 103
2013-09-14 01:24:06.669 demo[16508] Element[4] = 104
2013-09-14 01:24:06.669 demo[16508] Element[5] = 105
2013-09-14 01:24:06.669 demo[16508] Element[6] = 106
2013-09-14 01:24:06.669 demo[16508] Element[7] = 107
2013-09-14 01:24:06.669 demo[16508] Element[8] = 108
2013-09-14 01:24:06.669 demo[16508] Element[9] = 109
Tablice Objective-C w szczegółach
Tablice są ważne dla Objective-C i wymagają wielu dodatkowych szczegółów. Istnieje kilka ważnych pojęć związanych z tablicą, które powinny być jasne dla programisty Objective-C -
Sr.No. | Koncepcja i opis |
---|---|
1 | Tablice wielowymiarowe Objective-C obsługuje tablice wielowymiarowe. Najprostszą formą tablicy wielowymiarowej jest tablica dwuwymiarowa. |
2 | Przekazywanie tablic do funkcji Możesz przekazać do funkcji wskaźnik do tablicy, podając nazwę tablicy bez indeksu. |
3 | Zwróć tablicę z funkcji Objective-C umożliwia funkcji zwrócenie tablicy. |
4 | Wskaźnik do tablicy Możesz wygenerować wskaźnik do pierwszego elementu tablicy, po prostu określając nazwę tablicy, bez żadnego indeksu. |
Wskaźniki w Objective-C są łatwe i przyjemne do nauczenia. Niektóre zadania programowania Objective-C są łatwiejsze do wykonania za pomocą wskaźników, a innych zadań, takich jak dynamiczna alokacja pamięci, nie można wykonać bez użycia wskaźników. Dlatego konieczne jest nauczenie się wskaźników, aby stać się doskonałym programistą Objective-C. Zacznijmy uczyć się ich w prostych i łatwych krokach.
Jak wiadomo, każda zmienna jest miejscem w pamięci i każda lokalizacja pamięci ma zdefiniowany adres, do którego można uzyskać dostęp za pomocą operatora ampersand (&), który oznacza adres w pamięci. Rozważ poniższy przykład, w którym zostanie wyświetlony adres zdefiniowanych zmiennych -
#import <Foundation/Foundation.h>
int main () {
int var1;
char var2[10];
NSLog(@"Address of var1 variable: %x\n", &var1 );
NSLog(@"Address of var2 variable: %x\n", &var2 );
return 0;
}
Kiedy powyższy kod jest kompilowany i wykonywany, daje następujący wynik -
2013-09-13 03:18:45.727 demo[17552] Address of var1 variable: 1c0843fc
2013-09-13 03:18:45.728 demo[17552] Address of var2 variable: 1c0843f0
Więc zrozumiałeś, czym jest adres pamięci i jak uzyskać do niego dostęp, więc podstawa koncepcji dobiegła końca. Zobaczmy teraz, czym jest wskaźnik.
Co to są wskaźniki?
ZA pointerjest zmienną, której wartością jest adres innej zmiennej, tj. bezpośredni adres miejsca w pamięci. Jak w przypadku każdej zmiennej lub stałej, należy zadeklarować wskaźnik, zanim będzie można go użyć do przechowywania adresu zmiennej. Ogólną postacią deklaracji zmiennej wskaźnikowej jest -
type *var-name;
Tutaj, typejest typem bazowym wskaźnika; musi to być prawidłowy typ danych Objective-C ivar-namejest nazwą zmiennej wskaźnika. Gwiazdka *, której użyłeś do zadeklarowania wskaźnika, to ta sama gwiazdka, której używasz do mnożenia. Jednak w tej instrukcji gwiazdka jest używana do oznaczania zmiennej jako wskaźnika. Poniżej znajduje się prawidłowa deklaracja wskaźnika -
int *ip; /* pointer to an integer */
double *dp; /* pointer to a double */
float *fp; /* pointer to a float */
char *ch /* pointer to a character */
Rzeczywisty typ danych wartości wszystkich wskaźników, niezależnie od tego, czy są to liczby całkowite, zmiennoprzecinkowe, znakowe, czy inne, jest taki sam, długa liczba szesnastkowa, która reprezentuje adres pamięci. Jedyną różnicą między wskaźnikami różnych typów danych jest typ danych zmiennej lub stałej, na którą wskazuje wskaźnik.
Jak używać wskaźników?
Jest kilka ważnych operacji, które będziemy wykonywać bardzo często za pomocą wskaźników. (a) definiujemy zmienną wskaźnikową, (b) przypisać adres zmiennej do wskaźnika, a (c)na koniec uzyskaj dostęp do wartości pod adresem dostępnym w zmiennej wskaźnika. Odbywa się to za pomocą jednoargumentowego operatora*która zwraca wartość zmiennej znajdującej się pod adresem określonym przez jej operand. Poniższy przykład wykorzystuje te operacje -
#import <Foundation/Foundation.h>
int main () {
int var = 20; /* actual variable declaration */
int *ip; /* pointer variable declaration */
ip = &var; /* store address of var in pointer variable*/
NSLog(@"Address of var variable: %x\n", &var );
/* address stored in pointer variable */
NSLog(@"Address stored in ip variable: %x\n", ip );
/* access the value using the pointer */
NSLog(@"Value of *ip variable: %d\n", *ip );
return 0;
}
Kiedy powyższy kod jest kompilowany i wykonywany, daje następujący wynik -
2013-09-13 03:20:21.873 demo[24179] Address of var variable: 337ed41c
2013-09-13 03:20:21.873 demo[24179] Address stored in ip variable: 337ed41c
2013-09-13 03:20:21.874 demo[24179] Value of *ip variable: 20
NULL wskaźniki w celu C
Zawsze dobrze jest przypisać wartość NULL zmiennej wskaźnikowej, na wypadek gdybyś nie posiadał dokładnego adresu do przypisania. Odbywa się to w momencie deklaracji zmiennej. Wskaźnik, któremu przypisano wartość NULL, nazywany jest anull wskaźnik.
Wskaźnik NULL jest stałą o wartości zero zdefiniowaną w kilku standardowych bibliotekach. Rozważ następujący program -
#import <Foundation/Foundation.h>
int main () {
int *ptr = NULL;
NSLog(@"The value of ptr is : %x\n", ptr );
return 0;
}
Kiedy powyższy kod jest kompilowany i wykonywany, daje następujący wynik -
2013-09-13 03:21:19.447 demo[28027] The value of ptr is : 0
W większości systemów operacyjnych programy nie mają dostępu do pamięci pod adresem 0, ponieważ pamięć ta jest zarezerwowana przez system operacyjny. Jednak adres pamięci 0 ma szczególne znaczenie; sygnalizuje, że wskaźnik nie ma wskazywać dostępnego miejsca w pamięci. Ale zgodnie z konwencją, jeśli wskaźnik zawiera wartość null (zero), zakłada się, że nie wskazuje na nic.
Aby sprawdzić pusty wskaźnik, możesz użyć instrukcji if w następujący sposób -
if(ptr) /* succeeds if p is not null */
if(!ptr) /* succeeds if p is null */
Szczegółowe wskazówki dotyczące celu-C
Wskaźniki mają wiele, ale łatwych koncepcji i są bardzo ważne dla programowania Objective-C. Istnieje kilka ważnych pojęć dotyczących wskaźników, które powinny być jasne dla programisty Objective-C:
Sr.No. | Koncepcja i opis |
---|---|
1 | Objective-C - Arytmetyka wskaźnika Istnieją cztery operatory arytmetyczne, których można używać na wskaźnikach: ++, -, +, - |
2 | Cel-C - Tablica wskaźników Możesz zdefiniować tablice do przechowywania wielu wskaźników. |
3 | Cel-C - wskaźnik do wskaźnika Objective-C pozwala na umieszczenie wskaźnika na wskaźniku i tak dalej. |
4 | Przekazywanie wskaźników do funkcji w Objective-C Przekazywanie argumentu przez odwołanie lub przez adres umożliwia zmianę przekazanego argumentu w funkcji wywołującej przez wywoływaną funkcję. |
5 | Zwróć wskaźnik z funkcji w Objective-C Objective-C umożliwia funkcji zwrócenie wskaźnika do zmiennej lokalnej, zmiennej statycznej i dynamicznie przydzielonej pamięci. |
Łańcuch w języku programowania Objective-C jest reprezentowany za pomocą NSString, a jego podklasa NSMutableString zapewnia kilka sposobów tworzenia obiektów ciągu. Najprostszym sposobem utworzenia obiektu ciągu znaków jest użycie konstrukcji Objective-C @ „...” -
NSString *greeting = @"Hello";
Poniżej przedstawiono prosty przykład tworzenia i drukowania łańcucha.
#import <Foundation/Foundation.h>
int main () {
NSString *greeting = @"Hello";
NSLog(@"Greeting message: %@\n", greeting );
return 0;
}
Kiedy powyższy kod jest kompilowany i wykonywany, daje wynik w następujący sposób -
2013-09-11 01:21:39.922 demo[23926] Greeting message: Hello
Objective-C obsługuje szeroką gamę metod manipulowania napisami -
Sr.No. | Metoda i cel |
---|---|
1 | - (NSString *)capitalizedString; Zwraca reprezentację odbiorcy zapisaną wielką literą. |
2 | - (unichar)characterAtIndex:(NSUInteger)index; Zwraca znak z podanej pozycji w tablicy. |
3 | - (double)doubleValue; Zwraca wartość zmiennoprzecinkową tekstu odbiorcy jako podwójną. |
4 | - (float)floatValue; Zwraca wartość zmiennoprzecinkową tekstu odbiorcy jako liczbę zmiennoprzecinkową. |
5 | - (BOOL)hasPrefix:(NSString *)aString; Zwraca wartość logiczną, która wskazuje, czy podany ciąg pasuje do początkowych znaków odbiornika. |
6 | - (BOOL)hasSuffix:(NSString *)aString; Zwraca wartość logiczną, która wskazuje, czy podany ciąg pasuje do końcowych znaków odbiornika. |
7 | - (id)initWithFormat:(NSString *)format ...; Zwraca obiekt NSString zainicjowany przy użyciu danego ciągu formatu jako szablonu, w którym są podstawiane pozostałe wartości argumentów. |
8 | - (NSInteger)integerValue; Zwraca wartość NSInteger tekstu odbiorcy. |
9 | - (BOOL)isEqualToString:(NSString *)aString; Zwraca wartość logiczną wskazującą, czy dany ciąg jest równy adresatowi przy użyciu dosłownego porównania opartego na standardzie Unicode. |
10 | - (NSUInteger)length; Zwraca liczbę znaków Unicode w odbiorniku. |
11 | - (NSString *)lowercaseString; Zwraca odwzorowanie adresata zapisane małymi literami. |
12 | - (NSRange)rangeOfString:(NSString *)aString; Znajduje i zwraca zakres pierwszego wystąpienia danego ciągu w odbiorniku. |
13 | - (NSString *)stringByAppendingFormat:(NSString *)format ...; Zwraca ciąg utworzony przez dołączenie do odbiornika ciągu utworzonego z danego ciągu formatu i następujących argumentów. |
14 | - (NSString *)stringByTrimmingCharactersInSet:(NSCharacterSet *)set; Zwraca nowy ciąg utworzony przez usunięcie z obu końców znaków odbiorcy zawartych w danym zestawie znaków. |
15 | - (NSString *)substringFromIndex:(NSUInteger)anIndex; Zwraca nowy ciąg zawierający znaki odbiorcy od tego pod danym indeksem do końca. |
Poniższy przykład wykorzystuje kilka z wyżej wymienionych funkcji -
#import <Foundation/Foundation.h>
int main () {
NSString *str1 = @"Hello";
NSString *str2 = @"World";
NSString *str3;
int len ;
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
/* uppercase string */
str3 = [str2 uppercaseString];
NSLog(@"Uppercase String : %@\n", str3 );
/* concatenates str1 and str2 */
str3 = [str1 stringByAppendingFormat:@"World"];
NSLog(@"Concatenated string: %@\n", str3 );
/* total length of str3 after concatenation */
len = [str3 length];
NSLog(@"Length of Str3 : %d\n", len );
/* InitWithFormat */
str3 = [[NSString alloc] initWithFormat:@"%@ %@",str1,str2];
NSLog(@"Using initWithFormat: %@\n", str3 );
[pool drain];
return 0;
}
Kiedy powyższy kod jest kompilowany i wykonywany, daje wynik w następujący sposób -
2013-09-11 01:15:45.069 demo[30378] Uppercase String : WORLD
2013-09-11 01:15:45.070 demo[30378] Concatenated string: HelloWorld
2013-09-11 01:15:45.070 demo[30378] Length of Str3 : 10
2013-09-11 01:15:45.070 demo[30378] Using initWithFormat: Hello World
Pełną listę metod powiązanych z Objective-C NSString można znaleźć w dokumencie NSString Class Reference.
Tablice Objective-C pozwalają zdefiniować typ zmiennych, które mogą zawierać kilka elementów danych tego samego rodzaju, ale structure jest kolejnym typem danych zdefiniowanym przez użytkownika dostępnym w programowaniu Objective-C, który umożliwia łączenie elementów danych różnego rodzaju.
Struktury są używane do reprezentowania rekordu. Przypuśćmy, że chcesz śledzić swoje książki w bibliotece. Możesz chcieć śledzić następujące atrybuty dotyczące każdej książki -
- Title
- Author
- Subject
- Identyfikator książki
Definiowanie struktury
Aby zdefiniować strukturę, musisz użyć structkomunikat. Instrukcja struct definiuje nowy typ danych z więcej niż jednym składnikiem programu. Format instrukcji struct jest następujący -
struct [structure tag] {
member definition;
member definition;
...
member definition;
} [one or more structure variables];
Plik structure tagjest opcjonalna, a każda definicja elementu jest zwykłą definicją zmiennej, na przykład int i; lub float f; lub jakąkolwiek inną prawidłową definicję zmiennej. Na końcu definicji struktury, przed ostatnim średnikiem, można określić jedną lub więcej zmiennych strukturalnych, ale jest to opcjonalne. Oto sposób, w jaki można zadeklarować strukturę książki -
struct Books {
NSString *title;
NSString *author;
NSString *subject;
int book_id;
} book;
Dostęp do członków struktury
Aby uzyskać dostęp do dowolnego elementu członkowskiego struktury, używamy rozszerzenia member access operator (.). Operator dostępu do elementu jest zakodowany jako okres między nazwą zmiennej strukturalnej a elementem struktury, do którego chcemy uzyskać dostęp. Użyłbyśstructsłowo kluczowe do definiowania zmiennych typu konstrukcji. Poniżej znajduje się przykład wyjaśniający użycie struktury -
#import <Foundation/Foundation.h>
struct Books {
NSString *title;
NSString *author;
NSString *subject;
int book_id;
};
int main() {
struct Books Book1; /* Declare Book1 of type Book */
struct Books Book2; /* Declare Book2 of type Book */
/* book 1 specification */
Book1.title = @"Objective-C Programming";
Book1.author = @"Nuha Ali";
Book1.subject = @"Objective-C Programming Tutorial";
Book1.book_id = 6495407;
/* book 2 specification */
Book2.title = @"Telecom Billing";
Book2.author = @"Zara Ali";
Book2.subject = @"Telecom Billing Tutorial";
Book2.book_id = 6495700;
/* print Book1 info */
NSLog(@"Book 1 title : %@\n", Book1.title);
NSLog(@"Book 1 author : %@\n", Book1.author);
NSLog(@"Book 1 subject : %@\n", Book1.subject);
NSLog(@"Book 1 book_id : %d\n", Book1.book_id);
/* print Book2 info */
NSLog(@"Book 2 title : %@\n", Book2.title);
NSLog(@"Book 2 author : %@\n", Book2.author);
NSLog(@"Book 2 subject : %@\n", Book2.subject);
NSLog(@"Book 2 book_id : %d\n", Book2.book_id);
return 0;
}
Kiedy powyższy kod jest kompilowany i wykonywany, daje następujący wynik -
2013-09-14 04:20:07.947 demo[20591] Book 1 title : Objective-C Programming
2013-09-14 04:20:07.947 demo[20591] Book 1 author : Nuha Ali
2013-09-14 04:20:07.947 demo[20591] Book 1 subject : Objective-C Programming Tutorial
2013-09-14 04:20:07.947 demo[20591] Book 1 book_id : 6495407
2013-09-14 04:20:07.947 demo[20591] Book 2 title : Telecom Billing
2013-09-14 04:20:07.947 demo[20591] Book 2 author : Zara Ali
2013-09-14 04:20:07.947 demo[20591] Book 2 subject : Telecom Billing Tutorial
2013-09-14 04:20:07.947 demo[20591] Book 2 book_id : 6495700
Struktury jako argumenty funkcji
Możesz przekazać strukturę jako argument funkcji w bardzo podobny sposób, jak przekazujesz dowolną inną zmienną lub wskaźnik. Dostęp do zmiennych strukturalnych uzyskasz w podobny sposób, jak w powyższym przykładzie -
#import <Foundation/Foundation.h>
struct Books {
NSString *title;
NSString *author;
NSString *subject;
int book_id;
};
@interface SampleClass:NSObject
/* function declaration */
- (void) printBook:( struct Books) book ;
@end
@implementation SampleClass
- (void) printBook:( struct Books) book {
NSLog(@"Book title : %@\n", book.title);
NSLog(@"Book author : %@\n", book.author);
NSLog(@"Book subject : %@\n", book.subject);
NSLog(@"Book book_id : %d\n", book.book_id);
}
@end
int main() {
struct Books Book1; /* Declare Book1 of type Book */
struct Books Book2; /* Declare Book2 of type Book */
/* book 1 specification */
Book1.title = @"Objective-C Programming";
Book1.author = @"Nuha Ali";
Book1.subject = @"Objective-C Programming Tutorial";
Book1.book_id = 6495407;
/* book 2 specification */
Book2.title = @"Telecom Billing";
Book2.author = @"Zara Ali";
Book2.subject = @"Telecom Billing Tutorial";
Book2.book_id = 6495700;
SampleClass *sampleClass = [[SampleClass alloc]init];
/* print Book1 info */
[sampleClass printBook: Book1];
/* Print Book2 info */
[sampleClass printBook: Book2];
return 0;
}
Kiedy powyższy kod jest kompilowany i wykonywany, daje następujący wynik -
2013-09-14 04:34:45.725 demo[8060] Book title : Objective-C Programming
2013-09-14 04:34:45.725 demo[8060] Book author : Nuha Ali
2013-09-14 04:34:45.725 demo[8060] Book subject : Objective-C Programming Tutorial
2013-09-14 04:34:45.725 demo[8060] Book book_id : 6495407
2013-09-14 04:34:45.725 demo[8060] Book title : Telecom Billing
2013-09-14 04:34:45.725 demo[8060] Book author : Zara Ali
2013-09-14 04:34:45.725 demo[8060] Book subject : Telecom Billing Tutorial
2013-09-14 04:34:45.725 demo[8060] Book book_id : 6495700
Wskaźniki do struktur
Możesz zdefiniować wskaźniki do struktur w bardzo podobny sposób, jak definiujesz wskaźnik do dowolnej innej zmiennej w następujący sposób -
struct Books *struct_pointer;
Teraz możesz zapisać adres zmiennej strukturalnej w zdefiniowanej powyżej zmiennej wskaźnikowej. Aby znaleźć adres zmiennej strukturalnej, umieść operator & przed nazwą struktury w następujący sposób -
struct_pointer = &Book1;
Aby uzyskać dostęp do elementów struktury za pomocą wskaźnika do tej struktury, należy użyć operatora -> w następujący sposób -
struct_pointer->title;
Napiszmy ponownie powyższy przykład za pomocą wskaźnika struktury, mam nadzieję, że będzie to łatwe do zrozumienia koncepcji -
#import <Foundation/Foundation.h>
struct Books {
NSString *title;
NSString *author;
NSString *subject;
int book_id;
};
@interface SampleClass:NSObject
/* function declaration */
- (void) printBook:( struct Books *) book ;
@end
@implementation SampleClass
- (void) printBook:( struct Books *) book {
NSLog(@"Book title : %@\n", book->title);
NSLog(@"Book author : %@\n", book->author);
NSLog(@"Book subject : %@\n", book->subject);
NSLog(@"Book book_id : %d\n", book->book_id);
}
@end
int main() {
struct Books Book1; /* Declare Book1 of type Book */
struct Books Book2; /* Declare Book2 of type Book */
/* book 1 specification */
Book1.title = @"Objective-C Programming";
Book1.author = @"Nuha Ali";
Book1.subject = @"Objective-C Programming Tutorial";
Book1.book_id = 6495407;
/* book 2 specification */
Book2.title = @"Telecom Billing";
Book2.author = @"Zara Ali";
Book2.subject = @"Telecom Billing Tutorial";
Book2.book_id = 6495700;
SampleClass *sampleClass = [[SampleClass alloc]init];
/* print Book1 info by passing address of Book1 */
[sampleClass printBook:&Book1];
/* print Book2 info by passing address of Book2 */
[sampleClass printBook:&Book2];
return 0;
}
Kiedy powyższy kod jest kompilowany i wykonywany, daje następujący wynik -
2013-09-14 04:38:13.942 demo[20745] Book title : Objective-C Programming
2013-09-14 04:38:13.942 demo[20745] Book author : Nuha Ali
2013-09-14 04:38:13.942 demo[20745] Book subject : Objective-C Programming Tutorial
2013-09-14 04:38:13.942 demo[20745] Book book_id : 6495407
2013-09-14 04:38:13.942 demo[20745] Book title : Telecom Billing
2013-09-14 04:38:13.942 demo[20745] Book author : Zara Ali
2013-09-14 04:38:13.942 demo[20745] Book subject : Telecom Billing Tutorial
2013-09-14 04:38:13.942 demo[20745] Book book_id : 6495700
Pola bitowe
Pola bitowe umożliwiają upakowanie danych w strukturę. Jest to szczególnie przydatne, gdy cenna jest pamięć lub przechowywanie danych. Typowe przykłady -
Pakowanie kilku obiektów w słowo maszynowe. np. flagi 1-bitowe mogą być kompaktowane.
Odczytywanie zewnętrznych formatów plików - można wczytać niestandardowe formaty plików. Np. 9-bitowe liczby całkowite.
Objective-C pozwala nam to zrobić w definicji struktury poprzez umieszczenie: długości bitu po zmiennej. Na przykład -
struct packed_struct {
unsigned int f1:1;
unsigned int f2:1;
unsigned int f3:1;
unsigned int f4:1;
unsigned int type:4;
unsigned int my_int:9;
} pack;
Tutaj spakowany_strukt zawiera 6 elementów: Cztery 1-bitowe flagi f1..f3, 4-bitowy typ i 9-bitowy my_int.
Objective-C automatycznie pakuje powyższe pola bitowe tak kompaktowo, jak to tylko możliwe, pod warunkiem, że maksymalna długość pola jest mniejsza lub równa całkowitej długości słowa komputera. Jeśli tak nie jest, niektóre kompilatory mogą pozwolić na nakładanie się pamięci dla pól, podczas gdy inne będą przechowywać następne pole w następnym słowie.
Plik Objective-C Preprocessornie jest częścią kompilatora, ale stanowi oddzielny krok w procesie kompilacji. Mówiąc prościej, preprocesor Objective-C jest tylko narzędziem do podstawiania tekstu i instruuje kompilator, aby wykonał wymagane wstępne przetwarzanie przed właściwą kompilacją. Będziemy nazywać Preprocesor Objective-C jako OCPP.
Wszystkie polecenia preprocesora zaczynają się od symbolu krzyżyka (#). Musi to być pierwszy niepusty znak, a dla czytelności dyrektywa preprocesora powinna zaczynać się w pierwszej kolumnie. Poniższa sekcja zawiera listę wszystkich ważnych dyrektyw preprocesora -
Sr.No. | Dyrektywa i opis |
---|---|
1 | #define Zastępuje makro preprocesora |
2 | #include Wstawia określony nagłówek z innego pliku |
3 | #undef Odznacza makro preprocesora |
4 | #ifdef Zwraca wartość true, jeśli to makro jest zdefiniowane |
5 | #ifndef Zwraca wartość true, jeśli to makro nie jest zdefiniowane |
6 | #if Testuje, czy warunek czasu kompilacji jest prawdziwy |
7 | #else Alternatywa dla #if |
8 | #elif #else an #if w jednej instrukcji |
9 | #endif Warunkowe zakończenie preprocesora |
10 | #error Wyświetla komunikat o błędzie na stderr |
11 | #pragma Wydaje specjalne polecenia kompilatorowi przy użyciu standardowej metody |
Przykłady preprocesorów
Przeanalizuj poniższe przykłady, aby zrozumieć różne dyrektywy.
#define MAX_ARRAY_LENGTH 20
Ta dyrektywa nakazuje OCPP zamianę wystąpień MAX_ARRAY_LENGTH na 20. Użyj #define dla stałych, aby zwiększyć czytelność.
#import <Foundation/Foundation.h>
#include "myheader.h"
Te dyrektywy mówią OCPP, aby pobrać plik Foundation.h z Foundation Frameworki dodaj tekst do bieżącego pliku źródłowego. Następna linia mówi OCPP, aby uzyskaćmyheader.h z katalogu lokalnego i dodaj zawartość do bieżącego pliku źródłowego.
#undef FILE_SIZE
#define FILE_SIZE 42
To mówi OCPP, aby cofnął definicję istniejącego FILE_SIZE i zdefiniował go jako 42.
#ifndef MESSAGE
#define MESSAGE "You wish!"
#endif
To mówi OCPP, aby zdefiniował MESSAGE tylko wtedy, gdy MESSAGE nie jest jeszcze zdefiniowany.
#ifdef DEBUG
/* Your debugging statements here */
#endif
Informuje to OCPP, aby wykonał proces dołączonych instrukcji, jeśli zdefiniowano DEBUG. Jest to przydatne, jeśli przekazujesz flagę -DDEBUG do kompilatora gcc w czasie kompilacji. Spowoduje to zdefiniowanie funkcji DEBUG, dzięki czemu można włączać i wyłączać debugowanie w locie podczas kompilacji.
Predefiniowane makra
ANSI C definiuje szereg makr. Chociaż każdy z nich jest dostępny do użytku w programowaniu, predefiniowanych makr nie należy bezpośrednio modyfikować.
Sr.No. | Makro i opis |
---|---|
1 | __DATE__ Bieżąca data jako literał znakowy w formacie „MMM DD RRRR” |
2 | __TIME__ Bieżący czas jako literał znaku w formacie „HH: MM: SS” |
3 | __FILE__ Zawiera bieżącą nazwę pliku jako literał ciągu. |
4 | __LINE__ Zawiera aktualny numer wiersza jako stałą dziesiętną. |
5 | __STDC__ Zdefiniowany jako 1, gdy kompilator jest zgodny ze standardem ANSI. |
Wypróbujmy następujący przykład -
#import <Foundation/Foundation.h>
int main() {
NSLog(@"File :%s\n", __FILE__ );
NSLog(@"Date :%s\n", __DATE__ );
NSLog(@"Time :%s\n", __TIME__ );
NSLog(@"Line :%d\n", __LINE__ );
NSLog(@"ANSI :%d\n", __STDC__ );
return 0;
}
Gdy powyższy kod w pliku main.m jest kompilowany i wykonywany, daje następujący wynik -
2013-09-14 04:46:14.859 demo[20683] File :main.m
2013-09-14 04:46:14.859 demo[20683] Date :Sep 14 2013
2013-09-14 04:46:14.859 demo[20683] Time :04:46:14
2013-09-14 04:46:14.859 demo[20683] Line :8
2013-09-14 04:46:14.859 demo[20683] ANSI :1
Operatory preprocesora
Preprocesor Objective-C oferuje następujące operatory, które pomogą Ci w tworzeniu makr -
Kontynuacja makra (\)
Makro zwykle musi znajdować się w jednym wierszu. Operator kontynuacji makra służy do kontynuowania makra, które jest zbyt długie dla pojedynczego wiersza. Na przykład -
#define message_for(a, b) \
NSLog(@#a " and " #b ": We love you!\n")
Stringize (#)
Operator stringize lub number-sign („#”) użyty w definicji makra konwertuje parametr makra na stałą łańcuchową. Ten operator może być używany tylko w makrze, które ma określony argument lub listę parametrów. Na przykład -
#import <Foundation/Foundation.h>
#define message_for(a, b) \
NSLog(@#a " and " #b ": We love you!\n")
int main(void) {
message_for(Carole, Debra);
return 0;
}
Kiedy powyższy kod jest kompilowany i wykonywany, daje następujący wynik -
2013-09-14 05:46:14.859 demo[20683] Carole and Debra: We love you!
Wklejanie tokenu (##)
Operator wklejania tokenu (##) w definicji makra łączy dwa argumenty. Umożliwia połączenie dwóch oddzielnych tokenów w definicji makra w jeden token. Na przykład -
#import <Foundation/Foundation.h>
#define tokenpaster(n) NSLog (@"token" #n " = %d", token##n)
int main(void) {
int token34 = 40;
tokenpaster(34);
return 0;
}
Kiedy powyższy kod jest kompilowany i wykonywany, daje następujący wynik -
2013-09-14 05:48:14.859 demo[20683] token34 = 40
Jak to się stało, ponieważ ten przykład skutkuje następującym faktycznym wyjściem z preprocesora -
NSLog (@"token34 = %d", token34);
Ten przykład pokazuje konkatenację tokenu ## n w token34 i tutaj użyliśmy obu stringize i token-pasting.
Operator zdefiniowany ()
Preprocesor definedoperator jest używany w wyrażeniach stałych do określenia, czy identyfikator jest zdefiniowany za pomocą #define. Jeśli określony identyfikator jest zdefiniowany, wartością jest prawda (różna od zera). Jeśli symbol nie jest zdefiniowany, wartość jest fałszywa (zero). Zdefiniowany operator jest określony w następujący sposób -
#import <Foundation/Foundation.h>
#if !defined (MESSAGE)
#define MESSAGE "You wish!"
#endif
int main(void) {
NSLog(@"Here is the message: %s\n", MESSAGE);
return 0;
}
Kiedy powyższy kod jest kompilowany i wykonywany, daje następujący wynik -
2013-09-14 05:48:19.859 demo[20683] Here is the message: You wish!
Sparametryzowane makra
Jedną z potężnych funkcji OCPP jest możliwość symulacji funkcji przy użyciu sparametryzowanych makr. Na przykład możemy mieć kod do kwadratu liczby w następujący sposób -
int square(int x) {
return x * x;
}
Możemy przepisać powyższy kod za pomocą makra w następujący sposób -
#define square(x) ((x) * (x))
Makra z argumentami należy definiować przy użyciu rozszerzenia #definedyrektywy, zanim będzie można ich użyć. Lista argumentów jest ujęta w nawiasy i musi następować bezpośrednio po nazwie makra. Pomiędzy nazwą makra a otwartym nawiasem nie są dozwolone spacje. Na przykład -
#import <Foundation/Foundation.h>
#define MAX(x,y) ((x) > (y) ? (x) : (y))
int main(void) {
NSLog(@"Max between 20 and 10 is %d\n", MAX(10, 20));
return 0;
}
Kiedy powyższy kod jest kompilowany i wykonywany, daje następujący wynik -
2013-09-14 05:52:15.859 demo[20683] Max between 20 and 10 is 20
Język programowania Objective-C udostępnia słowo kluczowe o nazwie typedef, którego możesz użyć, aby nadać typowi nową nazwę. Poniżej znajduje się przykład definiowania terminuBYTE dla liczb jednobajtowych -
typedef unsigned char BYTE;
Po tej definicji typu identyfikator BYTE może być używany jako skrót dla typu unsigned char, for example:.
BYTE b1, b2;
Zgodnie z konwencją w tych definicjach używa się wielkich liter, aby przypomnieć użytkownikowi, że nazwa typu jest w rzeczywistości symbolicznym skrótem, ale można użyć małych liter w następujący sposób -
typedef unsigned char byte;
Możesz użyć typedefaby nadać również nazwę typowi danych zdefiniowanemu przez użytkownika. Na przykład możesz użyć typedef with structure, aby zdefiniować nowy typ danych, a następnie użyć tego typu danych do zdefiniowania zmiennych strukturalnych bezpośrednio w następujący sposób -
#import <Foundation/Foundation.h>
typedef struct Books {
NSString *title;
NSString *author;
NSString *subject;
int book_id;
} Book;
int main() {
Book book;
book.title = @"Objective-C Programming";
book.author = @"TutorialsPoint";
book.subject = @"Programming tutorial";
book.book_id = 100;
NSLog( @"Book title : %@\n", book.title);
NSLog( @"Book author : %@\n", book.author);
NSLog( @"Book subject : %@\n", book.subject);
NSLog( @"Book Id : %d\n", book.book_id);
return 0;
}
Kiedy powyższy kod jest kompilowany i wykonywany, daje następujący wynik -
2013-09-12 12:21:53.745 demo[31183] Book title : Objective-C Programming
2013-09-12 12:21:53.745 demo[31183] Book author : TutorialsPoint
2013-09-12 12:21:53.745 demo[31183] Book subject : Programming tutorial
2013-09-12 12:21:53.745 demo[31183] Book Id : 100
typedef vs #define
Plik #define to dyrektywa Objective-C, która jest również używana do definiowania aliasów dla różnych typów danych, podobnych do typedef ale z następującymi różnicami -
Plik typedef ogranicza się do nadawania nazw symbolicznych tylko typom, podczas gdy #define można również użyć do zdefiniowania aliasu dla wartości, na przykład można zdefiniować 1 jako JEDEN itp.
Plik typedef interpretacja jest wykonywana przez kompilator, gdzie as #define instrukcje są przetwarzane przez preprocesor.
Oto najprostsze użycie #define -
#import <Foundation/Foundation.h>
#define TRUE 1
#define FALSE 0
int main( ) {
NSLog( @"Value of TRUE : %d\n", TRUE);
NSLog( @"Value of FALSE : %d\n", FALSE);
return 0;
}
Kiedy powyższy kod jest kompilowany i wykonywany, daje następujący wynik -
2013-09-12 12:23:37.993 demo[5160] Value of TRUE : 1
2013-09-12 12:23:37.994 demo[5160] Value of FALSE : 0
Rzutowanie typu to sposób na przekonwertowanie zmiennej z jednego typu danych na inny typ danych. Na przykład, jeśli chcesz zapisać długą wartość w prostej liczbie całkowitej, możesz wpisać rzut long na int. Można jawnie konwertować wartości z jednego typu na inny przy użyciu rozszerzeniacast operator w następujący sposób -
(type_name) expression
W Objective-C generalnie używamy CGFloat do wykonywania operacji zmiennoprzecinkowych, które pochodzą od podstawowego typu float w przypadku 32-bitów i double w przypadku 64-bitów. Rozważmy następujący przykład, w którym operator rzutowania powoduje, że dzielenie jednej zmiennej całkowitej przez inną jest wykonywane jako operacja zmiennoprzecinkowa -
#import <Foundation/Foundation.h>
int main() {
int sum = 17, count = 5;
CGFloat mean;
mean = (CGFloat) sum / count;
NSLog(@"Value of mean : %f\n", mean );
return 0;
}
Kiedy powyższy kod jest kompilowany i wykonywany, daje następujący wynik -
2013-09-11 01:35:40.047 demo[20634] Value of mean : 3.400000
Należy tutaj zauważyć, że operator rzutowania ma pierwszeństwo przed dzieleniem, więc wartość sum jest najpierw konwertowany na typ double i na koniec zostaje podzielona przez liczbę, co daje podwójną wartość.
Konwersje typów mogą być niejawne, które są wykonywane przez kompilator automatycznie lub można je określić jawnie za pomocą cast operator. Uważa się, że dobrą praktyką programistyczną jest używanie operatora rzutowania, gdy konieczne są konwersje typów.
Promocja Integer
Promocja liczb całkowitych to proces, w którym wartości typu całkowitego są „mniejsze” niż int lub unsigned int są konwertowane na int lub unsigned int. Rozważmy przykład dodawania znaku w int -
#import <Foundation/Foundation.h>
int main() {
int i = 17;
char c = 'c'; /* ascii value is 99 */
int sum;
sum = i + c;
NSLog(@"Value of sum : %d\n", sum );
return 0;
}
Kiedy powyższy kod jest kompilowany i wykonywany, daje następujący wynik -
2013-09-11 01:38:28.492 demo[980] Value of sum : 116
Tutaj wartość sumy wynosi 116, ponieważ kompilator wykonuje promocję liczb całkowitych i konwertuje wartość „c” na ascii przed wykonaniem operacji dodawania.
Zwykłe konwersje arytmetyczne
Plik usual arithmetic conversionssą niejawnie wykonywane w celu rzutowania ich wartości na wspólny typ. Kompilator najpierw przeprowadza promocję liczb całkowitych , jeśli operandy nadal mają różne typy, są konwertowane na typ, który pojawia się najwyżej w następującej hierarchii -
Zwykłe konwersje arytmetyczne nie są wykonywane dla operatorów przypisania ani dla operatorów logicznych && i ||. Weźmy następujący przykład, aby zrozumieć koncepcję -
#import <Foundation/Foundation.h>
int main() {
int i = 17;
char c = 'c'; /* ascii value is 99 */
CGFloat sum;
sum = i + c;
NSLog(@"Value of sum : %f\n", sum );
return 0;
}
Kiedy powyższy kod jest kompilowany i wykonywany, daje następujący wynik -
2013-09-11 01:41:39.192 demo[15351] Value of sum : 116.000000
Tutaj łatwo jest zrozumieć, że pierwsze c jest konwertowane na liczbę całkowitą, ale ponieważ wartość końcowa jest zmiennoprzecinkowa, więc ma zastosowanie zwykła konwersja arytmetyczna, a kompilator konwertuje i i c na zmiennoprzecinkowe i dodaje je, otrzymując wynik typu float.
Metoda NSLog
Aby wydrukować logi, używamy metody NSLog w języku programowania Objective-C, którego użyliśmy bezpośrednio z przykładu Hello World.
Spójrzmy na prosty kod, który wypisuje słowa „Hello World” -
#import <Foundation/Foundation.h>
int main() {
NSLog(@"Hello, World! \n");
return 0;
}
Teraz, kiedy kompilujemy i uruchamiamy program, otrzymamy następujący wynik.
2013-09-16 00:32:50.888 demo[16669] Hello, World!
Wyłączanie dzienników w aplikacjach na żywo
Ponieważ dzienniki NSLog, których używamy w naszej aplikacji, będą drukowane w dziennikach urządzenia i nie jest dobrze drukować dzienniki w kompilacji na żywo. Dlatego używamy definicji typu do drukowania dzienników i możemy ich używać, jak pokazano poniżej.
#import <Foundation/Foundation.h>
#if DEBUG == 0
#define DebugLog(...)
#elif DEBUG == 1
#define DebugLog(...) NSLog(__VA_ARGS__)
#endif
int main() {
DebugLog(@"Debug log, our custom addition gets \
printed during debug only" );
NSLog(@"NSLog gets printed always" );
return 0;
}
Teraz, kiedy kompilujemy i uruchamiamy program w trybie debugowania, otrzymamy następujący wynik.
2013-09-11 02:47:07.723 demo[618] Debug log, our custom addition gets printed during debug only
2013-09-11 02:47:07.723 demo[618] NSLog gets printed always
Teraz, kiedy kompilujemy i uruchamiamy program w trybie wydania, otrzymamy następujący wynik.
2013-09-11 02:47:45.248 demo[3158] NSLog gets printed always
W programowaniu Objective-C obsługa błędów jest zapewniana przez klasę NSError dostępną w Foundation framework.
Obiekt NSError hermetyzuje bogatsze i bardziej rozszerzalne informacje o błędach, niż jest to możliwe przy użyciu tylko kodu błędu lub ciągu błędu. Podstawowe atrybuty obiektu NSError to domena błędów (reprezentowana przez ciąg znaków), kod błędu właściwy dla domeny oraz słownik informacji o użytkowniku zawierający informacje specyficzne dla aplikacji.
NSError
Programy Objective-C używają obiektów NSError do przekazywania informacji o błędach w czasie wykonywania, o których użytkownicy muszą być informowani. W większości przypadków program wyświetla te informacje o błędzie w oknie dialogowym lub arkuszu. Ale może również zinterpretować informacje i poprosić użytkownika o próbę naprawy błędu lub próbę samodzielnego naprawienia błędu
Obiekt NSError składa się z -
Domain - Domena błędów może być jedną z predefiniowanych domen NSError lub dowolnym ciągiem opisującym domenę niestandardową, przy czym domena nie może być zerowa.
Code - Kod błędu dotyczący błędu.
User Info - Słownik informacji o użytkowniku dla błędu i informacje o użytkowniku może być zerowy.
Poniższy przykład pokazuje, jak utworzyć niestandardowy błąd
NSString *domain = @"com.MyCompany.MyApplication.ErrorDomain";
NSString *desc = NSLocalizedString(@"Unable to complete the process", @"");
NSDictionary *userInfo = @{ NSLocalizedDescriptionKey : desc };
NSError *error = [NSError errorWithDomain:domain code:-101 userInfo:userInfo];
Oto pełny kod powyższego przykładu błędu przekazanego jako odniesienie do wskaźnika -
#import <Foundation/Foundation.h>
@interface SampleClass:NSObject
-(NSString *) getEmployeeNameForID:(int) id withError:(NSError **)errorPtr;
@end
@implementation SampleClass
-(NSString *) getEmployeeNameForID:(int) id withError:(NSError **)errorPtr {
if(id == 1) {
return @"Employee Test Name";
} else {
NSString *domain = @"com.MyCompany.MyApplication.ErrorDomain";
NSString *desc =@"Unable to complete the process";
NSDictionary *userInfo = [[NSDictionary alloc]
initWithObjectsAndKeys:desc,
@"NSLocalizedDescriptionKey",NULL];
*errorPtr = [NSError errorWithDomain:domain code:-101
userInfo:userInfo];
return @"";
}
}
@end
int main() {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
SampleClass *sampleClass = [[SampleClass alloc]init];
NSError *error = nil;
NSString *name1 = [sampleClass getEmployeeNameForID:1 withError:&error];
if(error) {
NSLog(@"Error finding Name1: %@",error);
} else {
NSLog(@"Name1: %@",name1);
}
error = nil;
NSString *name2 = [sampleClass getEmployeeNameForID:2 withError:&error];
if(error) {
NSLog(@"Error finding Name2: %@",error);
} else {
NSLog(@"Name2: %@",name2);
}
[pool drain];
return 0;
}
W powyższym przykładzie zwracamy nazwę, jeśli id wynosi 1, w przeciwnym razie ustawiamy obiekt błędu zdefiniowanego przez użytkownika.
Kiedy powyższy kod jest kompilowany i wykonywany, daje następujący wynik -
2013-09-14 18:01:00.809 demo[27632] Name1: Employee Test Name
2013-09-14 18:01:00.809 demo[27632] Error finding Name2: Unable to complete the process
Możliwe jest przekazanie niektórych wartości z wiersza poleceń do programów Objective-C, gdy są one wykonywane. Te wartości są nazywanecommand line arguments i wiele razy są one ważne dla twojego programu, zwłaszcza gdy chcesz kontrolować swój program z zewnątrz zamiast na sztywno zakodować te wartości wewnątrz kodu.
Argumenty wiersza poleceń są obsługiwane za pomocą argumentów funkcji main () gdzie argc odnosi się do liczby przekazanych argumentów, a argv[]jest tablicą wskaźników, która wskazuje na każdy argument przekazany do programu. Poniżej znajduje się prosty przykład, który sprawdza, czy jest jakiś argument dostarczony z wiersza poleceń i podejmuje odpowiednie działania -
#import <Foundation/Foundation.h>
int main( int argc, char *argv[] ) {
if( argc == 2 ) {
NSLog(@"The argument supplied is %s\n", argv[1]);
} else if( argc > 2 ) {
NSLog(@"Too many arguments supplied.\n");
} else {
NSLog(@"One argument expected.\n");
}
}
Kiedy powyższy kod jest kompilowany i wykonywany z jednym argumentem, powiedzmy „testowanie”, daje następujący wynik.
2013-09-13 03:01:17.333 demo[7640] The argument supplied is testing
Gdy powyższy kod jest kompilowany i wykonywany z dwoma argumentami, powiedzmy testing1 i testing2, daje następujący wynik.
2013-09-13 03:01:18.333 demo[7640] Too many arguments supplied.
Gdy powyższy kod jest kompilowany i wykonywany bez przekazywania żadnego argumentu, daje następujący wynik.
2013-09-13 03:01:18.333 demo[7640] One argument expected
Należy zauważyć że argv[0] zawiera nazwę samego programu i argv[1]jest wskaźnikiem do pierwszego dostarczonego argumentu wiersza poleceń, a * argv [n] jest ostatnim argumentem. Jeśli nie podano żadnych argumentów, argc będzie równe jeden, w przeciwnym razie, jeśli przekażesz jeden argument, toargc jest ustawiony na 2.
Przekazujesz wszystkie argumenty wiersza poleceń oddzielone spacją, ale jeśli sam argument ma spację, możesz przekazać takie argumenty, umieszczając je w podwójnych cudzysłowach "" lub pojedynczych cudzysłowach ''. Przepiszmy jeszcze raz powyższy przykład, w którym wypisujemy nazwę programu, a także przekażemy argument wiersza poleceń, umieszczając wewnątrz podwójne cudzysłowy -
#import <Foundation/Foundation.h>
int main( int argc, char *argv[] ) {
NSLog(@"Program name %s\n", argv[0]);
if( argc == 2 ) {
NSLog(@"The argument supplied is %s\n", argv[1]);
} else if( argc > 2 ) {
NSLog(@"Too many arguments supplied.\n");
} else {
NSLog(@"One argument expected.\n");
}
return 0;
}
Kiedy powyższy kod jest kompilowany i wykonywany z pojedynczym argumentem oddzielonym spacją, ale wewnątrz podwójnych cudzysłowów z napisem „Testing1 Testing2”, daje to następujący wynik.
2017-11-30 06:36:59.081 main[71010] Program name main
2017-11-30 06:36:59.082 main[71010] One argument expected.
Głównym celem języka programowania Objective-C jest dodanie orientacji obiektowej do języka programowania C, a klasy są centralną cechą Objective-C, które obsługują programowanie obiektowe i są często nazywane typami zdefiniowanymi przez użytkownika.
Klasa służy do określania formy obiektu i łączy reprezentację danych i metody manipulowania tymi danymi w jeden zgrabny pakiet. Dane i metody w klasie nazywane są członkami klasy.
Charakterystyka celu-C
Klasa jest zdefiniowana w dwóch różnych sekcjach, a mianowicie @interface i @implementation.
Prawie wszystko ma postać przedmiotów.
Obiekty odbierają wiadomości, a obiekty są często nazywane odbiornikami.
Obiekty zawierają zmienne instancji.
Obiekty i zmienne instancji mają zasięg.
Klasy ukrywają implementację obiektu.
Właściwości służą do zapewnienia dostępu do zmiennych instancji klas w innych klasach.
Definicje klasy celu-C
Definiując klasę, definiujesz plan dla typu danych. W rzeczywistości nie definiuje to żadnych danych, ale definiuje, co oznacza nazwa klasy, czyli z czego będzie się składał obiekt klasy i jakie operacje można wykonać na takim obiekcie.
Definicja klasy zaczyna się od słowa kluczowego @interfacepo którym następuje nazwa interfejsu (klasy); i treść klasy, ujęta w nawiasy klamrowe. W Objective-C wszystkie klasy pochodzą z klasy bazowej o nazwieNSObject. Jest to nadklasa wszystkich klas Objective-C. Zapewnia podstawowe metody, takie jak alokacja pamięci i inicjalizacja. Na przykład zdefiniowaliśmy typ danych Box za pomocą słowa kluczowegoclass w następujący sposób -
@interface Box:NSObject {
//Instance variables
double length; // Length of a box
double breadth; // Breadth of a box
}
@property(nonatomic, readwrite) double height; // Property
@end
Zmienne instancji są prywatne i są dostępne tylko wewnątrz implementacji klasy.
Przydzielanie i inicjowanie obiektów Objective-C
Klasa zapewnia plany obiektów, więc zasadniczo obiekt jest tworzony z klasy. Deklarujemy obiekty klasy z dokładnie takim samym rodzajem deklaracji, jak deklarujemy zmienne typu podstawowego. Następujące instrukcje deklarują dwa obiekty klasy Box -
Box box1 = [[Box alloc]init]; // Create box1 object of type Box
Box box2 = [[Box alloc]init]; // Create box2 object of type Box
Oba obiekty box1 i box2 będą miały własną kopię członków danych.
Dostęp do członków danych
Dostęp do właściwości obiektów klasy można uzyskać za pomocą operatora bezpośredniego dostępu do elementu członkowskiego (.). Wypróbujmy następujący przykład, aby wyjaśnić sprawę -
#import <Foundation/Foundation.h>
@interface Box:NSObject {
double length; // Length of a box
double breadth; // Breadth of a box
double height; // Height of a box
}
@property(nonatomic, readwrite) double height; // Property
-(double) volume;
@end
@implementation Box
@synthesize height;
-(id)init {
self = [super init];
length = 1.0;
breadth = 1.0;
return self;
}
-(double) volume {
return length*breadth*height;
}
@end
int main() {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
Box *box1 = [[Box alloc]init]; // Create box1 object of type Box
Box *box2 = [[Box alloc]init]; // Create box2 object of type Box
double volume = 0.0; // Store the volume of a box here
// box 1 specification
box1.height = 5.0;
// box 2 specification
box2.height = 10.0;
// volume of box 1
volume = [box1 volume];
NSLog(@"Volume of Box1 : %f", volume);
// volume of box 2
volume = [box2 volume];
NSLog(@"Volume of Box2 : %f", volume);
[pool drain];
return 0;
}
Kiedy powyższy kod jest kompilowany i wykonywany, daje następujący wynik -
2013-09-22 21:25:33.314 ClassAndObjects[387:303] Volume of Box1 : 5.000000
2013-09-22 21:25:33.316 ClassAndObjects[387:303] Volume of Box2 : 10.000000
Nieruchomości
Właściwości są wprowadzane w Objective-C, aby zapewnić, że do zmiennej wystąpienia klasy można uzyskać dostęp poza klasą.
Właściwości zaczynają się od @property, który jest słowem kluczowym
Po nim znajdują się specyfikatory dostępu, które są nieatomowe lub atomowe, tylko do odczytu lub do odczytu i silne, unsafe_unretained lub słabe. Zależy to od typu zmiennej. Dla dowolnego typu wskaźnika możemy użyć silnego, unsafe_unretained lub słabego. Podobnie w przypadku innych typów możemy użyć readwrite lub readonly.
Po tym następuje typ danych zmiennej.
Wreszcie mamy nazwę właściwości zakończoną średnikiem.
W klasie implementacji możemy dodać instrukcję synthesize. Ale w najnowszym XCode, część syntezy jest obsługiwana przez XCode i nie ma potrzeby dołączania instrukcji synthesize.
Jest to możliwe tylko dzięki właściwościom, do których mamy dostęp do zmiennych instancji klasy. W rzeczywistości dla właściwości tworzone są wewnętrznie metody pobierające i ustawiające.
Na przykład załóżmy, że mamy właściwość @property (nonatomic ,readonly ) BOOL isDone. Pod maską znajdują się setery i gettery utworzone w sposób pokazany poniżej.
-(void)setIsDone(BOOL)isDone;
-(BOOL)isDone;
Jedną z najważniejszych koncepcji programowania obiektowego jest dziedziczenie. Dziedziczenie pozwala nam zdefiniować klasę w kategoriach innej klasy, co ułatwia tworzenie i utrzymywanie aplikacji. Daje to również możliwość ponownego wykorzystania funkcjonalności kodu i szybkiego czasu implementacji.
Tworząc klasę, zamiast pisać zupełnie nowe składowe danych i funkcje składowe, programista może wyznaczyć, że nowa klasa powinna dziedziczyć składowe istniejącej klasy. Ta istniejąca klasa nosi nazwębase class, a nowa klasa jest nazywana derived klasa.
Idea dziedziczenia implementuje is azwiązek. Na przykład ssak IS-A, pies IS-A ssak, stąd pies również IS-A i tak dalej.
Klasy bazowe i pochodne
Objective-C zezwala tylko na dziedziczenie wielopoziomowe, tj. Może mieć tylko jedną klasę bazową, ale umożliwia dziedziczenie wielopoziomowe. Wszystkie klasy w Objective-C wywodzą się z nadklasyNSObject.
@interface derived-class: base-class
Rozważ klasę bazową Person i jej klasa pochodna Employee w następujący sposób -
#import <Foundation/Foundation.h>
@interface Person : NSObject {
NSString *personName;
NSInteger personAge;
}
- (id)initWithName:(NSString *)name andAge:(NSInteger)age;
- (void)print;
@end
@implementation Person
- (id)initWithName:(NSString *)name andAge:(NSInteger)age {
personName = name;
personAge = age;
return self;
}
- (void)print {
NSLog(@"Name: %@", personName);
NSLog(@"Age: %ld", personAge);
}
@end
@interface Employee : Person {
NSString *employeeEducation;
}
- (id)initWithName:(NSString *)name andAge:(NSInteger)age
andEducation:(NSString *)education;
- (void)print;
@end
@implementation Employee
- (id)initWithName:(NSString *)name andAge:(NSInteger)age
andEducation: (NSString *)education {
personName = name;
personAge = age;
employeeEducation = education;
return self;
}
- (void)print {
NSLog(@"Name: %@", personName);
NSLog(@"Age: %ld", personAge);
NSLog(@"Education: %@", employeeEducation);
}
@end
int main(int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSLog(@"Base class Person Object");
Person *person = [[Person alloc]initWithName:@"Raj" andAge:5];
[person print];
NSLog(@"Inherited Class Employee Object");
Employee *employee = [[Employee alloc]initWithName:@"Raj"
andAge:5 andEducation:@"MBA"];
[employee print];
[pool drain];
return 0;
}
Kiedy powyższy kod jest kompilowany i wykonywany, daje następujący wynik -
2013-09-22 21:20:09.842 Inheritance[349:303] Base class Person Object
2013-09-22 21:20:09.844 Inheritance[349:303] Name: Raj
2013-09-22 21:20:09.844 Inheritance[349:303] Age: 5
2013-09-22 21:20:09.845 Inheritance[349:303] Inherited Class Employee Object
2013-09-22 21:20:09.845 Inheritance[349:303] Name: Raj
2013-09-22 21:20:09.846 Inheritance[349:303] Age: 5
2013-09-22 21:20:09.846 Inheritance[349:303] Education: MBA
Kontrola dostępu i dziedziczenie
Klasa pochodna może uzyskać dostęp do wszystkich prywatnych elementów członkowskich swojej klasy bazowej, jeśli jest zdefiniowana w klasie interfejsu, ale nie może uzyskać dostępu do prywatnych elementów członkowskich zdefiniowanych w pliku implementacji.
Możemy podsumować różne typy dostępu według tego, kto może uzyskać do nich dostęp w następujący sposób -
Klasa pochodna dziedziczy wszystkie metody i zmienne klasy bazowej z następującymi wyjątkami -
Zmienne zadeklarowane w pliku implementacji za pomocą rozszerzeń nie są dostępne.
Metody zadeklarowane w pliku implementacji za pomocą rozszerzeń nie są dostępne.
W przypadku, gdy dziedziczona klasa implementuje metodę w klasie bazowej, wykonywana jest metoda z klasy pochodnej.
Słowo polymorphismoznacza posiadanie wielu form. Zwykle polimorfizm występuje, gdy istnieje hierarchia klas i są one powiązane dziedziczeniem.
Polimorfizm celu-C oznacza, że wywołanie funkcji składowej spowoduje wykonanie innej funkcji w zależności od typu obiektu, który wywołuje funkcję.
Rozważmy przykład, mamy klasę Shape, która zapewnia podstawowy interfejs dla wszystkich kształtów. Square i Rectangle są wyprowadzane z klasy bazowej Shape.
Mamy metodę printArea, która pokaże o funkcji OOP polymorphism.
#import <Foundation/Foundation.h>
@interface Shape : NSObject {
CGFloat area;
}
- (void)printArea;
- (void)calculateArea;
@end
@implementation Shape
- (void)printArea {
NSLog(@"The area is %f", area);
}
- (void)calculateArea {
}
@end
@interface Square : Shape {
CGFloat length;
}
- (id)initWithSide:(CGFloat)side;
- (void)calculateArea;
@end
@implementation Square
- (id)initWithSide:(CGFloat)side {
length = side;
return self;
}
- (void)calculateArea {
area = length * length;
}
- (void)printArea {
NSLog(@"The area of square is %f", area);
}
@end
@interface Rectangle : Shape {
CGFloat length;
CGFloat breadth;
}
- (id)initWithLength:(CGFloat)rLength andBreadth:(CGFloat)rBreadth;
@end
@implementation Rectangle
- (id)initWithLength:(CGFloat)rLength andBreadth:(CGFloat)rBreadth {
length = rLength;
breadth = rBreadth;
return self;
}
- (void)calculateArea {
area = length * breadth;
}
@end
int main(int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
Shape *square = [[Square alloc]initWithSide:10.0];
[square calculateArea];
[square printArea];
Shape *rect = [[Rectangle alloc]
initWithLength:10.0 andBreadth:5.0];
[rect calculateArea];
[rect printArea];
[pool drain];
return 0;
}
Kiedy powyższy kod jest kompilowany i wykonywany, daje następujący wynik -
2013-09-22 21:21:50.785 Polymorphism[358:303] The area of square is 100.000000
2013-09-22 21:21:50.786 Polymorphism[358:303] The area is 50.000000
W powyższym przykładzie opartym na dostępności metod obliczArea i printArea, albo metoda z klasy bazowej, albo wykonana klasa pochodna.
Polimorfizm obsługuje przełączanie metod między klasą bazową i pochodną w oparciu o implementację metody dwóch klas.
Wszystkie programy w ramach celu C składają się z następujących dwóch podstawowych elementów -
Program statements (code) - To jest część programu, która wykonuje akcje i nazywa się je metodami.
Program data - Dane to informacje o programie, na które mają wpływ funkcje programu.
Hermetyzacja to koncepcja programowania zorientowanego obiektowo, która wiąże ze sobą dane i funkcje, które manipulują danymi i która chroni zarówno przed zewnętrznymi zakłóceniami, jak i nadużyciami. Hermetyzacja danych doprowadziła do powstania ważnej koncepcji OOPdata hiding.
Data encapsulation jest mechanizmem łączenia danych i funkcji, które z nich korzystają, oraz data abstraction to mechanizm ujawniania tylko interfejsów i ukrywania szczegółów implementacji przed użytkownikiem.
Objective-C obsługuje właściwości hermetyzacji i ukrywania danych poprzez tworzenie typów zdefiniowanych przez użytkownika, tzw classes. Na przykład -
@interface Adder : NSObject {
NSInteger total;
}
- (id)initWithInitialNumber:(NSInteger)initialNumber;
- (void)addNumber:(NSInteger)newNumber;
- (NSInteger)getTotal;
@end
Zmienna total jest prywatna i nie możemy uzyskać do niej dostępu spoza klasy. Oznacza to, że mogą być dostępne tylko dla innych członków klasy Adder, a nie przez żadną inną część programu. Jest to jeden ze sposobów osiągnięcia hermetyzacji.
Metody wewnątrz pliku interfejsu są dostępne i mają zasięg publiczny.
Istnieją metody prywatne, które są napisane przy pomocy extensions, o czym dowiemy się w kolejnych rozdziałach.
Przykład enkapsulacji danych
Każdy program Objective-C, w którym zaimplementowano klasę ze zmiennymi publicznymi i prywatnymi składowymi, jest przykładem hermetyzacji i abstrakcji danych. Rozważmy następujący przykład -
#import <Foundation/Foundation.h>
@interface Adder : NSObject {
NSInteger total;
}
- (id)initWithInitialNumber:(NSInteger)initialNumber;
- (void)addNumber:(NSInteger)newNumber;
- (NSInteger)getTotal;
@end
@implementation Adder
-(id)initWithInitialNumber:(NSInteger)initialNumber {
total = initialNumber;
return self;
}
- (void)addNumber:(NSInteger)newNumber {
total = total + newNumber;
}
- (NSInteger)getTotal {
return total;
}
@end
int main(int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
Adder *adder = [[Adder alloc]initWithInitialNumber:10];
[adder addNumber:5];
[adder addNumber:4];
NSLog(@"The total is %ld",[adder getTotal]);
[pool drain];
return 0;
}
Kiedy powyższy kod jest kompilowany i wykonywany, daje następujący wynik -
2013-09-22 21:17:30.485 DataEncapsulation[317:303] The total is 19
Powyższa klasa dodaje liczby do siebie i zwraca sumę. Członkowie publiczniaddNum i getTotal to interfejsy do świata zewnętrznego i użytkownik musi je znać, aby używać tej klasy. Członek prywatnytotal jest czymś, co jest ukryte przed światem zewnętrznym, ale jest potrzebne klasie do prawidłowego działania.
Projektowanie strategii
Większość z nas nauczyła się poprzez gorzkie doświadczenie, aby domyślnie uczynić członków klasy prywatnymi, chyba że naprawdę musimy ich ujawniać. Po prostu dobrzeencapsulation.
Ważne jest, aby zrozumieć hermetyzację danych, ponieważ jest to jedna z podstawowych funkcji wszystkich języków programowania obiektowego (OOP), w tym Objective-C.
Czasami może się okazać, że chcesz rozszerzyć istniejącą klasę, dodając zachowanie, które jest przydatne tylko w określonych sytuacjach. Aby dodać takie rozszerzenie do istniejących klas, Objective-C zapewniacategories i extensions.
Jeśli potrzebujesz dodać metodę do istniejącej klasy, być może w celu dodania funkcjonalności ułatwiającej wykonanie jakiejś czynności we własnej aplikacji, najłatwiejszym sposobem jest użycie kategorii.
Składnia deklarowania kategorii używa słowa kluczowego @interface, podobnie jak standardowy opis klasy Objective-C, ale nie wskazuje na żadne dziedziczenie z podklasy. Zamiast tego określa nazwę kategorii w nawiasach, na przykład:
@interface ClassName (CategoryName)
@end
Charakterystyka kategorii
Kategorię można zadeklarować dla dowolnej klasy, nawet jeśli nie masz oryginalnego kodu źródłowego implementacji.
Wszelkie metody zadeklarowane w kategorii będą dostępne dla wszystkich wystąpień oryginalnej klasy, a także dla wszystkich podklas oryginalnej klasy.
W czasie wykonywania nie ma różnicy między metodą dodaną przez kategorię a metodą zaimplementowaną przez klasę oryginalną.
Przyjrzyjmy się teraz przykładowej implementacji kategorii. Dodajmy kategorię do klasy Cocoa NSString. Ta kategoria umożliwi nam dodanie nowej metody getCopyRightString, która pomoże nam zwrócić ciąg znaków autorskich. Jest to pokazane poniżej.
#import <Foundation/Foundation.h>
@interface NSString(MyAdditions)
+(NSString *)getCopyRightString;
@end
@implementation NSString(MyAdditions)
+(NSString *)getCopyRightString {
return @"Copyright TutorialsPoint.com 2013";
}
@end
int main(int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSString *copyrightString = [NSString getCopyRightString];
NSLog(@"Accessing Category: %@",copyrightString);
[pool drain];
return 0;
}
Teraz, kiedy kompilujemy i uruchamiamy program, otrzymamy następujący wynik.
2013-09-22 21:19:12.125 Categories[340:303] Accessing Category: Copyright TutorialsPoint.com 2013
Nawet jeśli metody dodane przez kategorię są dostępne dla wszystkich instancji klasy i jej podklas, musisz zaimportować plik nagłówkowy kategorii do dowolnego pliku kodu źródłowego, w którym chcesz użyć dodatkowych metod, w przeciwnym razie napotkasz ostrzeżenia i błędy kompilatora.
W naszym przykładzie, ponieważ mamy tylko jedną klasę, nie włączyliśmy żadnych plików nagłówkowych, w takim przypadku powinniśmy dołączyć pliki nagłówkowe, jak wspomniano powyżej.
Zanim zacznę o Posing w Objective-C, chciałbym zwrócić uwagę, że w systemie Mac OS X 10.5 funkcja Posing została uznana za przestarzałą i nie będzie można z niej później korzystać. Więc ci, którzy nie są zaniepokojeni tymi przestarzałymi metodami, mogą pominąć ten rozdział.
Cel-C pozwala klasie na całkowite zastąpienie innej klasy w programie. O klasie zastępującej mówi się, że „udaje” klasę docelową.
W przypadku wersji obsługujących pozowanie wszystkie wiadomości wysyłane do klasy docelowej są zamiast tego odbierane przez klasę pozującą.
NSObject zawiera metodę poseAsClass:, która umożliwia nam zastąpienie istniejącej klasy, jak wspomniano powyżej.
Ograniczenia w pozowaniu
Klasa może udawać tylko jedną ze swoich bezpośrednich lub pośrednich nadklas.
Klasa pozująca nie może definiować żadnych nowych zmiennych instancji, których nie ma w klasie docelowej (chociaż może definiować lub przesłonić metody).
Klasa docelowa mogła nie otrzymać żadnych wiadomości przed pozowaniem.
Klasa pozująca może wywoływać metody przesłonięte przez super, włączając w to implementację klasy docelowej.
Klasa pozująca może przesłaniać metody zdefiniowane w kategoriach.
#import <Foundation/Foundation.h>
@interface MyString : NSString
@end
@implementation MyString
- (NSString *)stringByReplacingOccurrencesOfString:(NSString *)target
withString:(NSString *)replacement {
NSLog(@"The Target string is %@",target);
NSLog(@"The Replacement string is %@",replacement);
}
@end
int main() {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
[MyString poseAsClass:[NSString class]];
NSString *string = @"Test";
[string stringByReplacingOccurrencesOfString:@"a" withString:@"c"];
[pool drain];
return 0;
}
Teraz, gdy kompilujemy i uruchamiamy program w starszym systemie Mac OS X (V_10.5 lub starszym), otrzymamy następujący wynik.
2013-09-22 21:23:46.829 Posing[372:303] The Target string is a
2013-09-22 21:23:46.830 Posing[372:303] The Replacement string is c
W powyższym przykładzie po prostu zaśmieciliśmy oryginalną metodę naszą implementacją i wpłynie to na wszystkie operacje NSString z powyższą metodą.
Rozszerzenie klasy ma pewne podobieństwo do kategorii, ale można je dodać tylko do klasy, dla której masz kod źródłowy w czasie kompilacji (klasa jest kompilowana w tym samym czasie co rozszerzenie klasy).
Metody zadeklarowane przez rozszerzenie klasy są zaimplementowane w bloku implementacji dla oryginalnej klasy, więc nie można na przykład zadeklarować rozszerzenia klasy w klasie struktury, takiej jak klasa Cocoa lub Cocoa Touch, taka jak NSString.
Rozszerzenia to w rzeczywistości kategorie bez nazwy kategorii. Jest często określany jakoanonymous categories.
Składnia deklarowania rozszerzenia używa słowa kluczowego @interface, podobnie jak standardowy opis klasy Objective-C, ale nie wskazuje na żadne dziedziczenie z podklasy. Zamiast tego po prostu dodaje nawiasy, jak pokazano poniżej -
@interface ClassName ()
@end
Charakterystyka rozszerzeń
Rozszerzenia nie można zadeklarować dla żadnej klasy, tylko dla klas, dla których mamy oryginalną implementację kodu źródłowego.
Rozszerzenie dodaje metody prywatne i zmienne prywatne, które są specyficzne tylko dla klasy.
Żadna metoda lub zmienna zadeklarowana wewnątrz rozszerzeń nie jest dostępna nawet dla dziedziczonych klas.
Przykład rozszerzeń
Utwórzmy klasę SampleClass, która ma rozszerzenie. W rozszerzeniu miejmy prywatną zmienną internalID.
Następnie przygotujmy metodę getExternalID, która zwraca zewnętrzny identyfikator po przetworzeniu wewnętrznego identyfikatora.
Przykład jest pokazany poniżej i to nie zadziała na kompilatorze online.
#import <Foundation/Foundation.h>
@interface SampleClass : NSObject {
NSString *name;
}
- (void)setInternalID;
- (NSString *)getExternalID;
@end
@interface SampleClass() {
NSString *internalID;
}
@end
@implementation SampleClass
- (void)setInternalID {
internalID = [NSString stringWithFormat:
@"UNIQUEINTERNALKEY%dUNIQUEINTERNALKEY",arc4random()%100];
}
- (NSString *)getExternalID {
return [internalID stringByReplacingOccurrencesOfString:
@"UNIQUEINTERNALKEY" withString:@""];
}
@end
int main(int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
SampleClass *sampleClass = [[SampleClass alloc]init];
[sampleClass setInternalID];
NSLog(@"ExternalID: %@",[sampleClass getExternalID]);
[pool drain];
return 0;
}
Teraz, kiedy kompilujemy i uruchamiamy program, otrzymamy następujący wynik.
2013-09-22 21:18:31.754 Extensions[331:303] ExternalID: 51
W powyższym przykładzie widzimy, że InternalID nie jest zwracany bezpośrednio. W tym miejscu usuwamy UNIQUEINTERNALKEY i udostępniamy tylko pozostałą wartość metodzie getExternalID.
Powyższy przykład używa tylko operacji na łańcuchach, ale może mieć wiele funkcji, takich jak szyfrowanie / deszyfrowanie i tak dalej.
Objective-C umożliwia zdefiniowanie protokołów, które deklarują metody, które mają być użyte w określonej sytuacji. Protokoły są realizowane w klasach zgodnych z protokołem.
Prostym przykładem może być klasa obsługująca sieciowe adresy URL, która będzie miała protokół z metodami takimi jak processCompleted delegate, który zastrzega klasę wywołującą po zakończeniu operacji pobierania adresu URL sieci.
Poniżej przedstawiono składnię protokołu.
@protocol ProtocolName
@required
// list of required methods
@optional
// list of optional methods
@end
Metody pod słowem kluczowym @required muszą być zaimplementowane w klasach, które są zgodne z protokołem i metodami w ramach @optional słowa kluczowe są opcjonalne do zaimplementowania.
Oto składnia klasy zgodnej z protokołem
@interface MyClass : NSObject <MyProtocol>
...
@end
Oznacza to, że każde wystąpienie MyClass będzie odpowiadać nie tylko na metody zadeklarowane konkretnie w interfejsie, ale także że MyClass zapewnia również implementacje dla wymaganych metod w MyProtocol. Nie ma potrzeby ponownego deklarowania metod protokołu w interfejsie klasy - wystarczy adopcja protokołu.
Jeśli potrzebujesz, aby klasa przyjęła wiele protokołów, możesz określić je jako listę oddzieloną przecinkami. Mamy obiekt delegata, który przechowuje odwołanie do obiektu wywołującego, który implementuje protokół.
Przykład jest pokazany poniżej.
#import <Foundation/Foundation.h>
@protocol PrintProtocolDelegate
- (void)processCompleted;
@end
@interface PrintClass :NSObject {
id delegate;
}
- (void) printDetails;
- (void) setDelegate:(id)newDelegate;
@end
@implementation PrintClass
- (void)printDetails {
NSLog(@"Printing Details");
[delegate processCompleted];
}
- (void) setDelegate:(id)newDelegate {
delegate = newDelegate;
}
@end
@interface SampleClass:NSObject<PrintProtocolDelegate>
- (void)startAction;
@end
@implementation SampleClass
- (void)startAction {
PrintClass *printClass = [[PrintClass alloc]init];
[printClass setDelegate:self];
[printClass printDetails];
}
-(void)processCompleted {
NSLog(@"Printing Process Completed");
}
@end
int main(int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
SampleClass *sampleClass = [[SampleClass alloc]init];
[sampleClass startAction];
[pool drain];
return 0;
}
Teraz, kiedy kompilujemy i uruchamiamy program, otrzymamy następujący wynik.
2013-09-22 21:15:50.362 Protocols[275:303] Printing Details
2013-09-22 21:15:50.364 Protocols[275:303] Printing Process Completed
W powyższym przykładzie widzieliśmy, jak są wywoływane i wykonywane metody delgate. Rozpoczyna się od startAction, po zakończeniu procesu wywoływana jest metoda delegata processCompleted w celu intymnego zakończenia operacji.
W żadnej aplikacji na iOS lub Maca nigdy nie będziemy mieć programu zaimplementowanego bez delegata. Dlatego ważne jest, abyśmy rozumieli użycie delegatów. Obiekty delegatów powinny używać właściwości unsafe_unretained, aby uniknąć wycieków pamięci.
Dynamiczne powiązanie określa metodę do wywołania w czasie wykonywania, a nie w czasie kompilacji. Dynamiczne wiązanie jest również nazywane późnym wiązaniem.
W Objective-C wszystkie metody są rozwiązywane dynamicznie w czasie wykonywania. Dokładny wykonywany kod jest określany zarówno przez nazwę metody (selektor), jak i przez obiekt odbierający.
Dynamiczne wiązanie umożliwia polimorfizm. Na przykład rozważmy zbiór obiektów, w tym Rectangle i Square. Każdy obiekt ma własną implementację metody printArea.
W poniższym fragmencie kodu rzeczywisty kod, który powinien zostać wykonany przez wyrażenie [anObject printArea], jest określany w czasie wykonywania. System runtime używa selektora dla metody run, aby zidentyfikować odpowiednią metodę w dowolnej klasie obiektu anObject.
Przyjrzyjmy się prostemu kodowi, który wyjaśniałby dynamiczne wiązanie.
#import <Foundation/Foundation.h>
@interface Square:NSObject {
float area;
}
- (void)calculateAreaOfSide:(CGFloat)side;
- (void)printArea;
@end
@implementation Square
- (void)calculateAreaOfSide:(CGFloat)side {
area = side * side;
}
- (void)printArea {
NSLog(@"The area of square is %f",area);
}
@end
@interface Rectangle:NSObject {
float area;
}
- (void)calculateAreaOfLength:(CGFloat)length andBreadth:(CGFloat)breadth;
- (void)printArea;
@end
@implementation Rectangle
- (void)calculateAreaOfLength:(CGFloat)length andBreadth:(CGFloat)breadth {
area = length * breadth;
}
- (void)printArea {
NSLog(@"The area of Rectangle is %f",area);
}
@end
int main() {
Square *square = [[Square alloc]init];
[square calculateAreaOfSide:10.0];
Rectangle *rectangle = [[Rectangle alloc]init];
[rectangle calculateAreaOfLength:10.0 andBreadth:5.0];
NSArray *shapes = [[NSArray alloc]initWithObjects: square, rectangle,nil];
id object1 = [shapes objectAtIndex:0];
[object1 printArea];
id object2 = [shapes objectAtIndex:1];
[object2 printArea];
return 0;
}
Teraz, kiedy kompilujemy i uruchamiamy program, otrzymamy następujący wynik.
2013-09-28 07:42:29.821 demo[4916] The area of square is 100.000000
2013-09-28 07:42:29.821 demo[4916] The area of Rectangle is 50.000000
Jak widać na powyższym przykładzie, metoda printArea jest wybierana dynamicznie w czasie wykonywania. Jest to przykład dynamicznego wiązania i jest bardzo przydatny w wielu sytuacjach, gdy mamy do czynienia z podobnymi obiektami.
Możemy stworzyć podklasę w klastrze klas, który definiuje klasę, która osadza w niej obiekt. Te obiekty klas są obiektami złożonymi.
Więc możesz się zastanawiać, co to jest klaster klas. Więc najpierw zobaczymy, co to jest klaster klas.
Klastry klas
Klastry klas to wzorzec projektowy, z którego w szerokim zakresie korzysta platforma Foundation. Klastry klas grupują szereg prywatnych podklas konkretnych w ramach publicznej abstrakcyjnej nadklasy. Grupowanie klas w ten sposób upraszcza publicznie widoczną architekturę struktury zorientowanej obiektowo bez zmniejszania jej funkcjonalnego bogactwa. Klastry klas są oparte na wzorcu projektowym Abstract Factory.
Aby to uprościć, zamiast tworzyć wiele klas dla podobnych funkcji, tworzymy jedną klasę, która zajmie się obsługą na podstawie wartości danych wejściowych.
Na przykład w NSNumber mamy wiele klastrów klas, takich jak char, int, bool i tak dalej. Grupujemy je wszystkie w jedną klasę, która zajmuje się obsługą podobnych operacji w jednej klasie. NSNumber w rzeczywistości opakowuje wartości tych typów pierwotnych w obiekty.
Czym właściwie jest obiekt złożony?
Osadzając prywatny obiekt klastra w obiekcie naszego własnego projektu, tworzymy obiekt złożony. Ten obiekt złożony może polegać na obiekcie klastra ze względu na jego podstawową funkcjonalność, przechwytując tylko komunikaty, które obiekt złożony chce obsłużyć w określony sposób. Ta architektura zmniejsza ilość kodu, który musimy napisać, i pozwala wykorzystać przetestowany kod dostarczony przez Foundation Framework.
Jest to wyjaśnione na poniższym rysunku.
Obiekt złożony musi zadeklarować, że jest podklasą abstrakcyjnej nadklasy klastra. Jako podklasa musi przesłonić prymitywne metody nadklasy. Może również przesłonić metody pochodne, ale nie jest to konieczne, ponieważ metody pochodne działają przez metody pierwotne.
Przykładem jest metoda count klasy NSArray; implementacja metody, którą przesłania, w interweniującym obiekcie może być tak prosta, jak -
- (unsigned)count {
return [embeddedObject count];
}
W powyższym przykładzie osadzony obiekt jest w rzeczywistości typu NSArray.
Przykład obiektu złożonego
Teraz, aby zobaczyć pełny przykład, spójrzmy na przykład z dokumentacji Apple, który jest podany poniżej.
#import <Foundation/Foundation.h>
@interface ValidatingArray : NSMutableArray {
NSMutableArray *embeddedArray;
}
+ validatingArray;
- init;
- (unsigned)count;
- objectAtIndex:(unsigned)index;
- (void)addObject:object;
- (void)replaceObjectAtIndex:(unsigned)index withObject:object;
- (void)removeLastObject;
- (void)insertObject:object atIndex:(unsigned)index;
- (void)removeObjectAtIndex:(unsigned)index;
@end
@implementation ValidatingArray
- init {
self = [super init];
if (self) {
embeddedArray = [[NSMutableArray allocWithZone:[self zone]] init];
}
return self;
}
+ validatingArray {
return [[self alloc] init] ;
}
- (unsigned)count {
return [embeddedArray count];
}
- objectAtIndex:(unsigned)index {
return [embeddedArray objectAtIndex:index];
}
- (void)addObject:(id)object {
if (object != nil) {
[embeddedArray addObject:object];
}
}
- (void)replaceObjectAtIndex:(unsigned)index withObject:(id)object; {
if (index <[embeddedArray count] && object != nil) {
[embeddedArray replaceObjectAtIndex:index withObject:object];
}
}
- (void)removeLastObject; {
if ([embeddedArray count] > 0) {
[embeddedArray removeLastObject];
}
}
- (void)insertObject:(id)object atIndex:(unsigned)index; {
if (object != nil) {
[embeddedArray insertObject:object atIndex:index];
}
}
- (void)removeObjectAtIndex:(unsigned)index; {
if (index <[embeddedArray count]) {
[embeddedArray removeObjectAtIndex:index];
}
}
@end
int main() {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
ValidatingArray *validatingArray = [ValidatingArray validatingArray];
[validatingArray addObject:@"Object1"];
[validatingArray addObject:@"Object2"];
[validatingArray addObject:[NSNull null]];
[validatingArray removeObjectAtIndex:2];
NSString *aString = [validatingArray objectAtIndex:1];
NSLog(@"The value at Index 1 is %@",aString);
[pool drain];
return 0;
}
Teraz, kiedy kompilujemy i uruchamiamy program, otrzymamy następujący wynik.
2013-09-28 22:03:54.294 demo[6247] The value at Index 1 is Object2
W powyższym przykładzie widzimy, że walidacja jednej funkcji tablicy nie pozwoliłaby na dodanie pustych obiektów, co doprowadzi do awarii w normalnym scenariuszu. Ale nasza tablica walidacyjna dba o to. Podobnie każda metoda walidacji tablicy dodaje procesy walidacji oprócz normalnej sekwencji operacji.
Jeśli odsyłasz do dokumentacji Apple, możesz zobaczyć szczegóły struktury Foundation, jak podano poniżej.
Framework Foundation definiuje podstawową warstwę klas Objective-C. Oprócz dostarczenia zestawu przydatnych prymitywnych klas obiektów, wprowadza kilka paradygmatów, które definiują funkcje, których nie obejmuje język Objective-C. Struktura Foundation została zaprojektowana z myślą o tych celach -
Zapewnij mały zestaw podstawowych klas użytkowych.
Ułatw tworzenie oprogramowania, wprowadzając spójne konwencje dotyczące takich rzeczy, jak zwalnianie przydziałów.
Obsługa ciągów Unicode, trwałości obiektów i dystrybucji obiektów.
Zapewnij poziom niezależności systemu operacyjnego, aby zwiększyć przenośność.
Framework został opracowany przez NeXTStep, który został przejęty przez Apple i te podstawowe klasy stały się częścią systemów Mac OS X i iOS.
Ponieważ został opracowany przez NeXTStep, ma przedrostek klasy „NS”.
Użyliśmy Foundation Framework we wszystkich naszych przykładowych programach. Używanie Foundation Framework jest prawie koniecznością.
Generalnie używamy czegoś takiego jak #import <Foundation/NSString.h> aby zaimportować klasę Objective-C, ale aby uniknąć importowania zbyt wielu klas, wszystko jest importowane #import <Foundation/Foundation.h>.
NSObject jest klasą bazową wszystkich obiektów, w tym klas zestawów fundamentowych. Zawiera metody zarządzania pamięcią. Zapewnia również podstawowy interfejs do systemu wykonawczego i możliwość zachowywania się jak obiekty Objective-C. Nie ma żadnej klasy bazowej i jest katalogiem głównym dla wszystkich klas.
Klasy podstawowe oparte na funkcjonalności
Sr.No. | Typ i opis pętli |
---|---|
1 | Przechowywanie danych NSArray, NSDictionary i NSSet zapewniają przechowywanie obiektów Objective-C dowolnej klasy. |
2 | Tekst i ciągi NSCharacterSet reprezentuje różne grupy znaków, które są używane przez klasy NSString i NSScanner. Klasy NSString reprezentują ciągi tekstowe i zapewniają metody wyszukiwania, łączenia i porównywania ciągów. Obiekt NSScanner jest używany do skanowania liczb i słów z obiektu NSString. |
3 | Daty i godziny Klasy NSDate, NSTimeZone i NSCalendar przechowują godziny i daty oraz reprezentują informacje z kalendarza. Oferują metody obliczania różnic dat i godzin. Wraz z NSLocale zapewniają metody wyświetlania dat i godzin w wielu formatach oraz dostosowywania czasu i dat w oparciu o lokalizację na świecie. |
4 | Obsługa wyjątków Obsługa wyjątków służy do obsługi nieoczekiwanych sytuacji i jest oferowana w Objective-C z NSException. |
5 | Obsługa plików Obsługa plików odbywa się za pomocą klasy NSFileManager. |
6 | System ładowania adresów URL Zestaw klas i protokołów zapewniających dostęp do typowych protokołów internetowych. |
Szybkie wyliczanie to funkcja Objective-C, która pomaga w wyliczaniu w kolekcji. Aby więc wiedzieć o szybkim wyliczaniu, musimy najpierw wiedzieć o zbieraniu, co zostanie wyjaśnione w następnej sekcji.
Kolekcje w Objective-C
Kolekcje są konstrukcjami podstawowymi. Służy do przechowywania i zarządzania innymi obiektami. Cały cel kolekcji polega na tym, że zapewnia wspólny sposób efektywnego przechowywania i pobierania obiektów.
Istnieje kilka różnych typów kolekcji. Chociaż wszystkie spełniają ten sam cel, jakim jest możliwość trzymania innych obiektów, różnią się głównie sposobem wyszukiwania obiektów. Najpopularniejsze kolekcje używane w Objective-C to -
- NSSet
- NSArray
- NSDictionary
- NSMutableSet
- NSMutableArray
- NSMutableDictionary
Jeśli chcesz dowiedzieć się więcej o tych strukturach, zapoznaj się z przechowywaniem danych w Foundation Framework .
Szybka składnia wyliczenia
for (classType variable in collectionObject ) {
statements
}
Oto przykład szybkiego wyliczenia.
#import <Foundation/Foundation.h>
int main() {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSArray *array = [[NSArray alloc]
initWithObjects:@"string1", @"string2",@"string3",nil];
for(NSString *aString in array) {
NSLog(@"Value: %@",aString);
}
[pool drain];
return 0;
}
Teraz, kiedy kompilujemy i uruchamiamy program, otrzymamy następujący wynik.
2013-09-28 06:26:22.835 demo[7426] Value: string1
2013-09-28 06:26:22.836 demo[7426] Value: string2
2013-09-28 06:26:22.836 demo[7426] Value: string3
Jak widać na wyjściu, każdy z obiektów w tablicy jest drukowany w kolejności.
Szybkie wyliczanie wstecz
for (classType variable in [collectionObject reverseObjectEnumerator] ) {
statements
}
Oto przykład dla reverseObjectEnumerator w szybkim wyliczaniu.
#import <Foundation/Foundation.h>
int main() {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSArray *array = [[NSArray alloc]
initWithObjects:@"string1", @"string2",@"string3",nil];
for(NSString *aString in [array reverseObjectEnumerator]) {
NSLog(@"Value: %@",aString);
}
[pool drain];
return 0;
}
Teraz, kiedy kompilujemy i uruchamiamy program, otrzymamy następujący wynik.
2013-09-28 06:27:51.025 demo[12742] Value: string3
2013-09-28 06:27:51.025 demo[12742] Value: string2
2013-09-28 06:27:51.025 demo[12742] Value: string1
Jak widać na wyjściu, każdy obiekt w tablicy jest drukowany, ale w odwrotnej kolejności w porównaniu do zwykłego szybkiego wyliczania.
Zarządzanie pamięcią jest jednym z najważniejszych procesów w każdym języku programowania. Jest to proces, w którym pamięć obiektów jest przydzielana, gdy są wymagane, i zwalniana, gdy nie są już potrzebne.
Zarządzanie pamięcią obiektów to kwestia wydajności; jeśli aplikacja nie zwalnia niepotrzebnych obiektów, jej zużycie pamięci rośnie i spada wydajność.
Techniki zarządzania pamięcią typu Objective-C można ogólnie podzielić na dwa typy.
- „Manual Retain-Release” lub MRR
- „Automatyczne liczenie referencji” lub ARC
„Manual Retain-Release” lub MRR
W MRR jawnie zarządzamy pamięcią, samodzielnie śledząc obiekty. Jest to realizowane przy użyciu modelu zwanego zliczaniem odwołań, który zapewnia klasa NSObject Foundation w połączeniu ze środowiskiem wykonawczym.
Jedyną różnicą między MRR i ARC jest to, że zatrzymanie i zwolnienie jest obsługiwane przez nas ręcznie w pierwszym przypadku, podczas gdy w drugim przypadku jest to obsługiwane automatycznie.
Poniższy rysunek przedstawia przykład działania zarządzania pamięcią w Objective-C.
Cykl życia pamięci obiektu klasy A pokazano na powyższym rysunku. Jak widać, licznik zachowań jest pokazany poniżej obiektu, gdy licznik zachowań obiektu osiąga wartość 0, obiekt jest całkowicie zwalniany, a jego pamięć jest zwalniana do użycia przez inne obiekty.
Klasa Obiekt jest najpierw tworzony przy użyciu metody przydzielania / inicjowania dostępnej w NSObject. Teraz liczba zachowań wynosi 1.
Teraz klasa B zachowuje obiekt klasy A, a liczba zachowań obiektu klasy A wynosi 2.
Następnie klasa C tworzy kopię obiektu. Teraz jest tworzony jako kolejna instancja klasy A z takimi samymi wartościami dla zmiennych instancji. W tym przypadku liczba zachowań wynosi 1, a nie liczba zachowań oryginalnego obiektu. Przedstawia to przerywana linia na rysunku.
Skopiowany obiekt jest zwalniany przez klasę C przy użyciu metody Release, a licznik przechowywania wynosi 0, a zatem obiekt jest niszczony.
W przypadku początkowego obiektu klasy A licznik zatrzymania wynosi 2 i należy go dwukrotnie zwolnić, aby został zniszczony. Odbywa się to poprzez oświadczenia o zwolnieniu klasy A i klasy B, które zmniejszają liczbę zatrzymań odpowiednio do 1 i 0. Ostatecznie obiekt zostaje zniszczony.
Podstawowe zasady MRR
Jesteśmy właścicielami każdego utworzonego przez nas obiektu: tworzymy obiekt przy użyciu metody, której nazwa zaczyna się od „przydziel”, „nowy”, „kopiuj” lub „mutableCopy”
Możemy przejąć na własność obiekt używając retain: otrzymany obiekt ma zwykle gwarancję, że pozostanie ważny w metodzie, w której został odebrany, a metoda ta może również bezpiecznie zwrócić obiekt do jego wywołującego. Używamy retain w dwóch sytuacjach -
W implementacji metody akcesora lub metody init, aby przejąć na własność obiekt, który chcemy przechowywać jako wartość właściwości.
Aby zapobiec unieważnieniu obiektu jako efekt uboczny innej operacji.
Kiedy już go nie potrzebujemy, musimy zrzec się prawa własności do obiektu, który posiadamy: Zrzekamy się prawa własności do obiektu, wysyłając mu komunikat o zwolnieniu lub automatyczną wiadomość o zwolnieniu. W terminologii Cocoa zrzeczenie się własności obiektu jest zatem zwykle określane jako „zwolnienie” obiektu.
Nie możesz zrzec się prawa własności do obiektu, którego nie jesteś właścicielem: jest to po prostu konsekwencja wcześniejszych zasad polityki określonych wyraźnie.
#import <Foundation/Foundation.h>
@interface SampleClass:NSObject
- (void)sampleMethod;
@end
@implementation SampleClass
- (void)sampleMethod {
NSLog(@"Hello, World! \n");
}
- (void)dealloc {
NSLog(@"Object deallocated");
[super dealloc];
}
@end
int main() {
/* my first program in Objective-C */
SampleClass *sampleClass = [[SampleClass alloc]init];
[sampleClass sampleMethod];
NSLog(@"Retain Count after initial allocation: %d",
[sampleClass retainCount]);
[sampleClass retain];
NSLog(@"Retain Count after retain: %d", [sampleClass retainCount]);
[sampleClass release];
NSLog(@"Retain Count after release: %d", [sampleClass retainCount]);
[sampleClass release];
NSLog(@"SampleClass dealloc will be called before this");
// Should set the object to nil
sampleClass = nil;
return 0;
}
Kiedy kompilujemy powyższy program, otrzymamy następujący wynik.
2013-09-28 04:39:52.310 demo[8385] Hello, World!
2013-09-28 04:39:52.311 demo[8385] Retain Count after initial allocation: 1
2013-09-28 04:39:52.311 demo[8385] Retain Count after retain: 2
2013-09-28 04:39:52.311 demo[8385] Retain Count after release: 1
2013-09-28 04:39:52.311 demo[8385] Object deallocated
2013-09-28 04:39:52.311 demo[8385] SampleClass dealloc will be called before this
„Automatyczne liczenie referencji” lub ARC
W automatycznym zliczaniu referencji lub ARC system używa tego samego systemu zliczania referencji, co MRR, ale wstawia wywołania odpowiedniej metody zarządzania pamięcią w czasie kompilacji. Gorąco zachęcamy do korzystania z ARC przy nowych projektach. Jeśli używamy ARC, zwykle nie ma potrzeby rozumienia podstawowej implementacji opisanej w tym dokumencie, chociaż w niektórych sytuacjach może to być pomocne. Aby uzyskać więcej informacji na temat ARC, zobacz Przejście do informacji o wersji ARC.
Jak wspomniano powyżej, w ARC nie musimy dodawać metod release i retain, ponieważ zajmie się tym kompilator. W rzeczywistości podstawowy proces Celu C jest nadal taki sam. Wykorzystuje wewnętrznie operacje zachowywania i zwalniania, co ułatwia programistę kodowanie bez martwienia się o te operacje, co zmniejszy zarówno ilość napisanego kodu, jak i możliwość wycieków pamięci.
Istniała inna zasada zwana garbage collection, która jest używana w Mac OS-X wraz z MRR, ale od czasu jej wycofania w OS-X Mountain Lion nie była omawiana razem z MRR. Ponadto obiekty iOS nigdy nie miały funkcji czyszczenia pamięci. A w przypadku ARC nie ma również zastosowania czyszczenia pamięci w OS-X.
Oto prosty przykład ARC. Zauważ, że to nie zadziała na kompilatorze online, ponieważ nie obsługuje ARC.
#import <Foundation/Foundation.h>
@interface SampleClass:NSObject
- (void)sampleMethod;
@end
@implementation SampleClass
- (void)sampleMethod {
NSLog(@"Hello, World! \n");
}
- (void)dealloc {
NSLog(@"Object deallocated");
}
@end
int main() {
/* my first program in Objective-C */
@autoreleasepool {
SampleClass *sampleClass = [[SampleClass alloc]init];
[sampleClass sampleMethod];
sampleClass = nil;
}
return 0;
}
Kiedy kompilujemy powyższy program, otrzymamy następujący wynik.
2013-09-28 04:45:47.310 demo[8385] Hello, World!
2013-09-28 04:45:47.311 demo[8385] Object deallocated