StartCzęść NR.1 Zawodowo programuje w C# od 2 lat. Licząc czas spędzonych na studiach z C# ten czas wydłuża się przynajmniej dwukrotnie.  Aby pisać aplikacje na iOS bez pomocy firmy Xamarin trzeba oczywiście znać Objective-C.

Czasu do nauki tego języka mam niewiele i jest to spore wyzwanie dla mnie biorąc pod uwagę, że jest praca na drugi etat.

Jak więc przyspieszyć czas naukę języka z miesiąca do kilku dni (nie żartuje). Na pewno szybkie porównanie języków programistycznych jest dobrym pomysłem. Im więcej języków programowania człowiek zna tym lepiej ponieważ widzi pewne podobieństwa.

Sprawa z Objective-C mimo to wciąż nie jest prosta. Znam Jave, C# i JavaScript ale te języki programowania nie mają dużo wspólnego z Objective-C.

Objective-C Date

Na swoim blogu nawet roku temu wyśmiałem trochę składnie języka Objective-C ponieważ język  wywodzi się od SmallTalk i C. Wtedy nawet byłem wielkim fanem rozwiązania Xamarin ,ale te rozwiązanie nie likwiduje problemu związanego z  zrozumieniem filozofii platformy iOS. Jednym słowem nie ma bata trzeba poznać technologie od źródła.

Na pierwszy rzut oka wydaje się ,że C# i Objective C nie powinny mieć nic ze sobą wspólnego ,ale…są to języki obiektowe a to też jakiś początek. Zauważyłem także ,ze Objective-C ma podobne mechanizm jak C#:

  • Metody rozszerzeniowe
  • Właściwości

Jakby spojrzeć o tej strony to porównanie Objective-C do C# ma przynajmniej większy sens niż  porównanie Javy do Objective-C.

W tym wpisie nie będę owijał w bawełnę. Omówię w tym wpisie: klasy, pamięć, protokoły , metody , właściwości oraz kategorie.

Jedziemy ,ale zaraz zaraz czym w ogóle jest Objective-C.

Czym jest Objective-C

Objective-C jest głównym językiem programowania używanym do pisania aplikacji na Mac OS X i platformy iOS. Jest to zbiór obiektowo zorientowany klas zbudowany w języku C. Co oznacza ,ze możesz użyć języka C wewnątrz klasy Objective-C.

Objective-C z oczywistych względów jest inny od C#. Jednak nie jest on aż taki strasznym językiem jak się wydaje (patrz powyższa ilustracja).

Dzięki ACR o którym zrobiłem wpis nie musimy już tak ściśle zarządzać pamięcią w Objective-C. Brzmi obiecująca ,a nie strasznie.

Klasy

Klasa Objective-C potrzebuje interfejsu i implementacji. Z góry warto zaznaczyć ,że “interfejs” nie referuje się do mechanizmu interfejsu z C#

Interfejs w Objective-C jest częścią deklaracji samej klasy.

Oto przykład klasy w C# i Objective-C. Szkoda ,że nie mogę koloryzować składni Objective-C jak C# ponieważ na Mac-u nie mogę oczywiście zainstalować Windows Live Writera.

public class TextDocument
{
    public TextDocument(string text)
    {
        // Inicjowanie właściwości itp
    }
}

Plik interfejsu klasy TextDocument – TextDocument.h

#import <Foundation/Foundation.h>

@interface TextDocument : NSObject<Printing>

- (id)initWithText:(NSString *)aText;

@end

Plik implementacji klasy TextDocument – TextDocument.m

#import "TextDocument.h"

@implementation TextDocument

- (id) initWithText:(NSString *)aText {
    
    if (self = [super init]) {
         }
    return self;
}

@end

Implementacja i interfejs  w Objective-C są zwykle trzymana w oddzielnych plikach. W interfejsie definiujemy instancje zmiennych, właściwości i metody które nasza klasa będzie miała. W implementacji  piszemy ich użycie.

Xcode

Metody “Init” są używane jako konstruktor. Zwracają one instancje obiektu . Typ zwrotny “id” jest typem dynamicznym i może on się referować do każdego typu obiektu. Dla mechanizmu konstruktora jest to wygodne.

Upewniamy się także ,że super obiekt utworzony z konstruktora bazowanego został poprawnie utworzony dopiero wtedy zwracam nasz nowy obiekt ustawiają nasze właściwości.

Słowo kluczowe “self” w Objective-C oznacza to samo co słowo kluczowe “this” w C#. Jest to odwołanie do obecnej instancji klasy wewnątrz niej.

Pamięć i obiekty

Zapewne słyszałeś o tym ,że Objective-C wymaga manualnej zarządzania pamięci.

Sprawy się zmieniły wraz z iOS 5 i mechanizmem ACR. Więcej o nim możesz przeczytać poniżej. Nie widzę sensu omawiania jak zarządzać pamięci w Objective-C gdyż ten mechanizm robi to za nas.

ACR wpis

Protokoły

Protokoły to mechanizm podobny do .NET interfejsów . Protokół więc zawiera listę metod które klasa może zaimplementować. Warto zaznaczyć tutaj słowo może.

interface IPrintable
{
    void Print();
}

public class TextDocument : IPrintable
{
    public TextDocument(string text)
    {
        // Iinicjowanie właściwości itp
    }

    public void Print()
    {
        Console.WriteLine("Tutaj wstaw tekst");
    }
}

Oto jak wygląda protokół w Objective-C

@protocol Printing <NSObject>

- (void)print;

@optional
- (void) preview;

@end

Jak i jego implementacja i użycie. Plik interfejsu klasy TextDocument – TextDocument.h

#import <Foundation/Foundation.h>
#import "Printing.h"

@interface TextDocument : NSObject<Printing>

- (id)initWithText:(NSString *)aText;

@end

Plik implementacji klasy TextDocument – TextDocument.m

#import "TextDocument.h"

@implementation TextDocument

- (id) initWithText:(NSString *)aText {
    
    if (self = [super init]) {
         }
    return self;
}

- (void) print {
    NSLog(@"Printing %@",self.text);
}

@end

W tym przykładzie klasa TextDocument ma protokół “Printing” w Objective-C oraz interfejs  w C# “IPrintiable”.

Protokoły przeciwieństwie do interfejsów C# mogą posiadać opcjonalne metody. W tym przykładzie mamy opcjonalną metodę “preview”.

Metody

Wywołanie metod w Objective-C jest naprawdę nie miłym przeżyciem. To właśnie tutaj pojawia dziwna nie do końca jasna składnia tego języka. Jednak wiem ,że z czasem się do niej przyzwyczaję.

TextDocument document = new TextDocument("Mój tekst");
document.Print();

Kod Objective-C z aplikacji konsolowej.

Wydaje się proste.

Prawdziwy problem pojawia gdy chcemy przesłać parametry do metody. W Objective-C do pełnej nazwy metody włączamy także nazwę pierwszego parametru.

Plik interfejsu

#import <Foundation/Foundation.h>
#import "Printing.h"

@interface TextDocument : NSObject<Printing>

- (id)initWithText:(NSString *)aText;

- (BOOL)saveAS:(NSString *)fileName toPath:(NSString *)filePath;

@end

Plik implementacji

#import "TextDocument.h"

@implementation TextDocument

- (id) initWithText:(NSString *)aText {
    
    if (self = [super init]) {
         }
    return self;
}

- (void) print {
    NSLog(@"Printing %@",self.text);
}

-(BOOL)saveAS:(NSString *)fileName toPath:(NSString *)filePath {
    //Dodaj kod 
    return YES;
}

@end

Użycie metody “SaveAS”.

BOOL success = [document saveAS:@"MyTextFile" toPath:@"~/Temp/"];

Nie mamy więc  metody “SaveAS” która przyjmuje dwa parametry zamiast tego mamy metodę “saveAS:ToPath  która zawiera dwa argumenty. Dziwne nieprawdaż.

W Objective-C zazwyczaj wysyłamy wiadomość do obiektu niż wywołujemy metodę tego właśnie obiektu.

Ciężko to zrozumieć ,ale istnieje pewien prosty sposób aby to pokazać. Otóż jeśli wyślemy wiadomość do obiektu bez referencji ( w objective-C nil reprezentuje wartość null) nie otrzymamy żadnego wyjątku. Nie ma żadnej konsekwencji takiego działania.

Document *docNil = nil; [docNil print];

W C# jak i w Javie w takim wypadku otrzymujemy wyjątki “NullReferenceException” i “NullPointerException”.

NullReferenceException

Właściwości

Objective-C ma właściwości tak jak C#. Deklarujemy je w interfejsie klasy używając słowa kluczowego “@property”.

Cel właściwości jest hermetyzacja  dostępu do danych wewnątrz obiektu.

W celu możemy tworzyć metody “get” i “set” jednak właściwości są lepsze. Właściwości w Objective-C automatycznie za nas tworzą instancje zmiennej która przetrzymuje daną wartość. W C# w tym celu mamy mechanizm automatycznych właściwości.

public class TextDocument : IPrintable
{
    public string Text { get; set; }

    public TextDocument(string text)
    {
        // Inicjowanie właściwości itp
    }

    public void Print()
    {
        Console.WriteLine("Tutaj wstaw tekst");
    }
}

Plik interfejsu klasy TextDocument – TextDocument.h

#import <Foundation/Foundation.h>
#import "Printing.h"

@interface TextDocument : NSObject<Printing>

- (id)initWithText:(NSString *)aText;

- (BOOL)saveAS:(NSString *)fileName toPath:(NSString *)filePath;

@property (nonatomic,copy) NSString *text;

@end

Plik implementacja klasy TextDocument – TextDocument.m

#import "TextDocument.h"

@implementation TextDocument

- (id) initWithText:(NSString *)aText {
    
    if (self = [super init]) {
        self.text = aText;
    }
    return self;
}

- (void) print {
    NSLog(@"Printing %@",self.text);
}

-(BOOL)saveAS:(NSString *)fileName toPath:(NSString *)filePath {
    //Dodaj kod 
    return YES;
}

@end

Deklaracja “@property” definiuje mechanizm magazynowania.  W tym wypadku mówimy ,ze chcemy kopiować wartość “copy” niż przetrzymywać wskaźnik (referencje) do oryginału. Słowo kluczowe “nonatomic” oznacza ,że metody get i set będą generowane bez blokowania wątku czyli będą one działać szybciej.

Właściwości mają więcej słów kluczowych ,ale omówię je później.

Kategorie

Kategorie w Objective-C dodają funkcjonalność do klasy bez potrzeby dziedziczenia. Te zachowanie jest zbliżone do metody rozszerzeniowej w C#. Jest to bardzo użyteczna funkcjonalność gdyż w ten sposób możesz dodać funkcjonalność do klasy NSString do której nie masz dostępu jak  w C# do klasy String.

Objective-C Category

Oto przykład użycia kategorii w Objective-C. Do klasy NSString dodamy funkcjonalność cofania napisu.. W C# taką funkcjonalność też dodamy używając metody rozszerzeniowej.

public static class MyStringExtensionsMethods
{
    public static string Reverse(this string s)
    {
        char[] arr = s.ToCharArray();
        Array.Reverse(arr);
        return new string(arr);
    }
};

Kategorie jak klasy mają swój interfejs i implementacje.

#import <Foundation/Foundation.h>

@interface NSString (Reverse)

- (NSString *)reverse;

@end
#import "NSObject+Reverse.h"
#import <Foundation/Foundation.h>

@implementation NSString (Reverse)

- (NSString *)reverse {
    NSMutableString *reverseStr;
    
    int len = [self length];
    
    reverseStr = [NSMutableString stringWithCapacity:len];
    while (len > 0) {
        [reverseStr appendString:
         [NSString stringWithFormat:@"%C",[self characterAtIndex: --len]]];
        
    }
    
    return reverseStr;
}

@end
NSLog(@"Odwrócony tekst %@",[document.text reverse]);

Więcej chce więcej

To była szybka przejażdżka nieprawdaż.

Jeśli chce nauczyć się Objective-C używając odniesień do C# nie martw się to nie będzie pierwszy wpis tego typu.