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.
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 ,że 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.
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 bazowego 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.
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ę niemiłym przeżyciem. To właśnie tutaj pojawia się 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 się, 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”.
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 ,że 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.
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.