Célkitűzés-C | |
---|---|
Nyelvóra | objektum-orientált , több paradigma : tükröző-orientált |
Megjelent | 1983 |
Szerző | Brad Cox |
Fájlkiterjesztés _ | .h, .m, .mmvagy.C |
Kiadás |
|
Típusrendszer | gyenge , statikus / dinamikus |
Főbb megvalósítások | Kakaó , Cocoa Touch , gcc , LLVM + Clang |
Befolyásolva | Smalltalk , C |
befolyásolta | Java , Objective-J , Swift |
Weboldal | developer.apple.com/libr… |
Médiafájlok a Wikimedia Commons oldalon |
Az Objective-C az Apple Corporation által használt lefordított objektum-orientált programozási nyelv , amely a C nyelvre és a Smalltalk paradigmákra épül . Konkrétan az objektummodell Smalltalk stílusban épül fel – vagyis üzeneteket küldenek az objektumoknak .
Az Objective-C nyelv a C nyelv szuperkészlete , így a C kód teljesen érthető az Objective-C fordító számára.
Az Objective-C fordító a GCC része, és a legtöbb nagyobb platformon elérhető. A nyelvet elsősorban a Mac OS X ( Cocoa ) és a GNUstep operációs rendszerekhez használják, amelyek az OpenStep objektumorientált felület megvalósításai . A nyelvet az iOS ( Cocoa Touch ) is használja.
Az 1980-as évek elején a strukturált programozás népszerű volt , lehetővé téve az algoritmusok kis blokkokra való felosztását. A feladatok egyre összetettebbé válásával azonban a strukturált programozás a kód minőségének csökkenéséhez vezetett. Egyre több olyan függvényt kellett írnunk, amit más programokban nagyon ritkán lehetett használni.
Sok programozó az objektum-orientált programozásban látott lehetséges megoldást problémájára. Egyrészt a Smalltalkot szinte minden többé-kevésbé bonyolult rendszer használta. Másrészt a virtuális gépek használata megnövelte az erőforrásigényt.
Az Objective-C-t Brad Cox hozta létre az 1980-as évek elején a Stepstone nevű cégénél . Megpróbálta megoldani a kód újrafelhasználásának problémáját.
Cox célja egy olyan nyelv létrehozása volt, amely támogatja a szoftveres IC koncepcióját, amely magában foglalja a programok kész komponensekből (objektumokból) való összeállításának lehetőségét, ahogyan összetett elektronikus eszközöket is össze lehet állítani kész integrált áramkörökből .
Ugyanakkor a nyelvnek egyszerűnek kell lennie, és a C nyelven kell alapulnia, hogy megkönnyítse a fejlesztők átállását arra.
Az egyik cél egy olyan modell létrehozása is volt, amelyben maguk az osztályok is teljes értékű objektumok, támogatva lenne az introspekció és a dinamikus üzenetfeldolgozás.
Az Objective-C a C kiterjesztése: bármely C program egy Objective-C program.
Az Objective-C egyik jellemzője, hogy dinamikus: a fordítási időben általában meghozott döntések a futásidőre halaszthatók.
Az Objective-C egy üzenet-orientált nyelv, míg a C++ funkció-orientált: az Objective-C-ben a metódushívások nem függvényhívásként értelmeződnek (bár általában ez jön le), hanem üzenetként (egy név és argumentumok) objektum, akárcsak a Smalltalkban.
Bármely objektum bármilyen üzenetet elküldhet. Egy objektum ahelyett, hogy egy üzenetet feldolgozna, továbbíthatja azt egy másik objektumnak feldolgozásra (delegálásra), így különösen elosztott (vagyis különböző címterekben és akár különböző számítógépeken elhelyezkedő) objektumokat valósíthat meg.
Egy üzenet hozzárendelése a megfelelő függvényhez futás közben történik.
Az Objective-C nyelv támogatja a metainformációkkal való munkát - például futás közben megtudhatja egy objektum osztályát, metódusainak listáját (az átadott argumentumtípusokkal) és példányváltozókat, ellenőrizheti, hogy az osztály megfelelő-e. az adott leszármazottja, és hogy támogatja-e az adott protokollt stb.
A nyelv támogatja a protokollokat (az objektum interfész és a protokoll fogalma egyértelműen elkülönül). Az öröklődés támogatott (nem többszörös); protokollok támogatják a többszörös öröklődést. Egy objektum örökölhető egy másik objektumtól és több protokolltól egyszerre (bár ez inkább nem protokoll öröklődés, hanem annak támogatása).
Az Objective-C-t jelenleg a Clang és a GCC fordítók támogatják ( Windows rendszeren a MinGW vagy a cygwin részeként használják ).
Egyes nyelvi funkciók átkerültek a futásidejű könyvtárba, és nagymértékben függenek tőle. A gcc fordító egy ilyen könyvtár minimális verziójával érkezik. Ingyenesen letöltheti az Apple futásidejű könyvtárát is: az Apple Objective-C futtatókörnyezetét.
Ez a két futásidejű könyvtár hasonló (a fő különbség a metódusnevekben van). A további példák az Apple futásidejű könyvtárára összpontosítanak.
Az Objective-C nyelv speciális típusú azonosítót használ az objektumok kijelölésére (ez hasonló a Java objektumtípusához ). Az id típusú változó valójában egy tetszőleges objektumra mutató mutató. A nulla konstans (= NULL) egy objektumra mutató nullmutatót jelöl.
Ebben az esetben az id helyett használhat egy ismerősebb megnevezést az osztály kifejezett megjelölésével. Ez utóbbi különösen lehetővé teszi a fordító számára, hogy bizonyos objektumokkal ellenőrizze az üzenettámogatást – ha a fordító a változó típusából nem tud arra következtetni, hogy egy objektum támogat egy adott üzenetet, akkor figyelmeztetést ad ki.
Így a nyelv támogatja a típusellenőrzést, de nem szigorú formában (vagyis a talált inkonzisztenciákat figyelmeztetésként ad vissza, nem hibaként).
A következő szintaxist használják az üzenetek küldésére:
[ vevő üzenet ];Ebben a konstrukcióban a vevő egy objektumra mutató mutató, az üzenet pedig a metódus neve.
A C++-tól eltérően az üzenet küldése nullára törvényes művelet, mindig nullát ad vissza.
Az üzenet paramétereket is tartalmazhat:
[ myRect setOrigin : 30.0 : 50.0 ];Ebben a példában a metódus (üzenet) neve setOrigin::. Vegye figyelembe, hogy minden átadott argumentum pontosan egy kettősponttal egyezik meg. Ebben a példában az első argumentumnak van címkéje (a kettőspont előtti szöveg), a másodiknak viszont nincs.
Az Objective-C nyelv lehetővé teszi az egyes argumentumok címkézését, ami nagymértékben javítja a kód olvashatóságát, és csökkenti a rossz paraméter átadásának valószínűségét. A legtöbb fejlesztő ezt a stílust alkalmazza.
[ myRect setWidth : 10.0 magasság : 20.0 ];Ebben a példában az üzenet neve setWidth: height:.
Támogatja azt is, hogy tetszőleges számú argumentumot adjunk át az üzenetben:
[ myObject makeGroup : obj1 , obj2 , obj3 , obj4 , nil ];A függvényekhez hasonlóan az üzenetek is visszaadhatnak értékeket, és a C-vel ellentétben az alapértelmezett visszatérési típus az id.
float area = [ myRect area ];Egy üzenet eredménye azonnal felhasználható egy másik üzenetben:
[ myRect setColor :[ otherRect color ]];Ahogy korábban említettük, az Objective-C-ben az osztályok maguk is objektumok. Az ilyen objektumok (úgynevezett osztályobjektumok) fő feladata egy adott osztály példányainak létrehozása (gyári metódusminta ) [2] .
Ebben az esetben maga az osztálynév kettős szerepet tölt be - egyrészt adattípusként működik (vagyis az osztály objektumaira mutató mutatók leírására használható). Másrészt az osztálynév működhet objektumként, amelyhez az üzenetet küldik (az üzenetekben az osztálynév csak fogadóként vehet részt).
Rect * myRect = [[ Rect alloc ] init ];Az Objective-C nem rendelkezik beépített típussal a logikai értékekhez, ezért ezt a típust általában mesterségesen vezetik be. Továbbá a logikai értékekhez a BOOL típust a rendszer a YES és NO lehetséges értékekkel használja (ahogyan a NextStep, Mac OS X operációs rendszereknél is történik).
Az Objective-C nyelv első komolyabb felhasználása a NextStep operációs rendszerben történt. Ehhez a rendszerhez számos különböző Objective-C osztályt írtak, amelyek közül sokat még mindig használnak a Mac OS X rendszerben.
Ezen osztályok nevei az NS előtaggal kezdődnek, jelezve, hogy a NextStep operációs rendszerhez tartoznak. Most bekerültek az Alapítvány könyvtárába, amelyre az OS X és iOS alkalmazások épülnek.
Az egyikkel - az NSStringgel - találkozunk ebben a cikkben. Ez az osztály karakterláncokkal való munkavégzésre szolgál (ebben az esetben a Unicode a karakterek belső reprezentációja).
A fordító támogatja ezt a típust azáltal, hogy a @"my string"-hez hasonló konstrukciókat automatikusan mutatóvá fordítja egy NSString osztály objektumra, amely tartalmazza az adott karakterláncot (pontosabban az állandó karakterláncoknak megfelelő alosztályát).
TulajdonságokTegyük fel, hogy van egy példányváltozónév a Vállalat osztályban.
@interface cég : NSObject { NString * név ; }Ha kívülről szeretné elérni, a legjobb, ha az Objective-C 2.0-ban megjelent tulajdonságokat használja. A @property kulcsszó a tulajdonságok deklarálására szolgál.
@ tulajdonság ( megtartása ) NSString * név ;A zárójelek a példányváltozóhoz tartozó hozzáférési attribútumokat sorolják fel. Az attribútumok 3 fő csoportra oszthatók.
Accessor és mutator nevek
Olvasási/írási korlát
Ezek a tulajdonságok kölcsönösen kizárják egymást. És az utolsó csoport a mutátor attribútumok .
Ha GC alatt dolgozik, nincs különbség a hozzárendelés, megőrzés és másolás között. A tulajdonságok kódjának generálásához, a deklarációban leírt módon, használhatja az automatikus kódgenerálást:
@szintetizáló név ;Az automatikusan generált kód nem mindig jó megoldás, és előfordulhat, hogy manuálisan kell létrehoznia a példányváltozó-elérőket.
A nyelvet gyakran kritizálják a többi nyelvhez képest túlterhelt szintaxisa miatt. Gyakran megjegyzik azonban jobb olvashatóságát.
Minden Objective-C kulcsszó, amely nem található a C-ben, a @ szimbólummal kezdődik.
A C++-hoz hasonlóan az osztály leírása és megvalósítása el van választva (általában a leírás h kiterjesztésű fejlécfájlokba, a megvalósítások m kiterjesztésű fájlokba kerülnek).
A következő az új osztálydeklaráció általános felépítése:
@interface ClassName : Szuperosztály { példányváltozódeklarációk _ _ } metódus deklarációk @végeAz Apple futásidejű verziójában minden osztálynak van egy közös őse, az NSObject osztály, amely számos fontos metódust tartalmaz.
A változók deklarálása nem különbözik a C nyelvben a struktúrákban lévő változók deklarálásától:
Ha nem Apple-t használ, akkor valószínűleg az Object-re (#import <objc/Object.h>) lesz szüksége az NSObject helyett.
@interface Rect : NSObject { float -width ; úszómagasság ; _ BOOL isFilled ; NSColor * szín ; } @végeA metódusok leírása markánsan eltér a C++-ban elfogadottaktól, és nagyon hasonlít a Smalltalk nyelvi metódusok leírásaihoz.
Minden leírás plusz vagy mínusz jellel kezdődik. A pluszjel azt jelzi, hogy ez a metódus egy osztálymetódus (azaz csak osztályobjektumoknak küldhető el, ennek az osztálynak a példányaihoz nem). Valójában az osztálymetódusok hasonlóak a C++ nyelv osztályainak statikus metódusaihoz.
A mínusz jel az objektumok metódusainak jelölésére szolgál - ennek az osztálynak a példányai. Ne feledje, hogy az Objective-C-ben minden metódus virtuális , ami azt jelenti, hogy felülbírálhatók.
Az alábbiakban a Rect osztály lehetséges metódusainak leírása található.
@interface Rect : NSObject { float x , y ; float -width ; úszómagasság ; _ BOOL isFilled ; NSColor * szín ; } + newRect ; - ( üres ) kijelző ; - ( úszó ) szélesség ; - ( úszó ) magasság ; - ( úszó ) terület ; - ( void ) setWidth: ( float ) theWidth ; - ( void ) setHeight: ( float ) theHeight ; - ( void ) setX: ( float ) theX y: ( float ) theY ; @végeVegye figyelembe, hogy a metódus neve megegyezhet az osztályba tartozó példányváltozó nevével (például szélesség és magasság).
A metódus visszatérési típusa közvetlenül a plusz vagy mínusz jel után zárójelben van megadva (de a metódus neve előtt). Ha a típus nincs megadva, akkor a rendszer id típusú értéket ad vissza.
Ezután következik a metódus neve, ahol minden kettőspont után megadjuk az argumentum típusát (zárójelben) és magát az argumentumot.
Az Objective-C nyelv lehetővé teszi a következő leírók egyikének megadását is a metódus argumentumokhoz: oneway, in, out, inout, bycopy és byref. Ezek a leírók az adatátvitel irányának és az átvitel módjának meghatározására szolgálnak. Jelenlétük jelentősen leegyszerűsíti a megvalósítást és az elosztott objektumokkal végzett munkát (amelyeket a múlt század 90-es évek elejére implementáltak a NextStep operációs rendszerben).
Egy tetszőleges számú paramétert felvevő módszer a következőképpen írható le:
- makeGroup: ( id ) object , ...;Ha fejlécfájlt szeretne beilleszteni az Objective-C-be, az #include direktíva helyett az #import direktíva kerül felhasználásra, hasonlóan az #include-hoz, de garantálja, hogy ez a fájl csak egyszer kerül bele.
Egyes esetekben szükségessé válik annak deklarálása, hogy egy adott név egy osztály neve, de anélkül, hogy kifejezetten leírnánk (ilyen igény két osztály leírásakor merül fel, amelyek mindegyike egy másik osztályra vonatkozik).
Ebben az esetben használhatja a @class direktívát, amely deklarálja, hogy az őt követő nevek osztálynevek.
@class Shape , Rect , Oval ;Az osztálymetódusok megvalósítása így néz ki:
#import "Osztálynév.h" @implementationClassName _ módszer megvalósításai @végeAz alábbiakban a Rect osztály fentebb ismertetett metódusainak megvalósítási példája látható.
#import "Rect.h" @implementation Rect + újRect { Rect * rect = [[ Rect alloc ] init ]; [ rect setWidth : 1.0f ]; [ rect setHeight : 1.0f ]; [ rect setX : 0.0f y : 0.0f ]; return rect ; } - ( úszó ) szélesség { visszatérési szélesség ; } - ( úszó ) magasság { visszatérési magasság ; } - ( úszó ) terület { return [ self width ] * [ self height ]; } - ( void ) setWidth: ( float ) theWidth { szélesség = theWidth ; } - ( void ) setHeight: ( float ) theHeight { magasság = theHeight ; } - ( void ) setX: ( float ) theX y: ( float ) theY { x = az X ; y = az Y ; } @végeAmint a fenti példából látható, az összes példányváltozó elérhető a metódusokban. A C++-hoz hasonlóan azonban lehetőség van a változók láthatóságának szabályozására (a metódusok láthatósága nem szabályozható) a @private, @protected és @public direktívákkal (amelyek pontosan úgy működnek, mint a C++ nyelv).
@interfész dolgozó : NSObject { char * név ; @magán int kor ; char * értékelés ; @védett int job ; lebegő bér ; @nyilvános id főnök }Ugyanakkor a nyilvános osztályváltozók közvetlenül elérhetők a -> operátor segítségével (például objPtr -> fieldName).
A fordító minden egyes üzenetküldést, azaz az [object msg]-hez hasonló konstrukciót az objc_msgSend függvény hívására fordítja. Ez a függvény első paraméterének egy mutatót vesz fel az üzenet címzett objektumára, második paramétereként pedig az ún. az elküldött üzenet azonosítására szolgáló választó. Ha vannak argumentumok az üzenetben, akkor azok harmadik, negyedik stb. paraméterként is átadódnak az objc_msgSendnek.
Minden Objective-C objektum tartalmaz egy isa attribútumot, amely egy mutató az adott objektum osztályobjektumára. osztály objektumot a fordító automatikusan létrehozza, és egyetlen példányként létezik, amelyre az adott osztály összes példánya hivatkozik az isa-n keresztül.
Minden osztályobjektum szükségszerűen tartalmaz egy mutatót a szülőosztály (szuperosztály) osztályobjektumára és a küldési táblára. Ez utóbbi egy olyan szótár, amely az üzenetválasztókat az azokat megvalósító metódusok (függvények) tényleges címeivel egyezteti.
Így az objc_msgSend függvény az adott objektumhoz keres egy metódust az adott szelektorral a küldési táblában. Ha nincs ott, akkor a keresés folytatódik a feladási táblában a szülőosztálya után, és így tovább.
Ha a metódus (vagyis a hozzá tartozó függvény) megtalálható, akkor az összes szükséges argumentum átvitelével meghívásra kerül.
Ellenkező esetben az objektum egy utolsó esélyt kap az üzenet feldolgozására, mielőtt kivételt dobna - az üzenetválasztó a paraméterekkel együtt egy speciális NInvocation típusú objektumba kerül, és a forwardInvocation: üzenet elküldésre kerül az objektumnak, ahol az NInvocation osztály objektuma paraméterként működik.
Ha egy objektum támogatja a forwardInvocation: funkciót, akkor vagy feldolgozhatja az általa küldött üzenetet, vagy továbbíthatja egy másik objektumnak feldolgozásra:
- ( void ) forwardInvocation: ( NInvocation * ) anInvocation { if ( [ someOtherObject responsesToSelector : [ an Invocation selector ] ] ) [ anInvocation invokeWithTarget : someOtherObject ]; más ........ }Az üzenetek feladási táblában történő keresésének felgyorsítása érdekében gyorsítótárazást alkalmaznak, ami jelentősen csökkentheti az üzenetküldés költségeit. Ezenkívül megkönnyíti a metódusok táblák közötti keresését az úgynevezett szelektorok használatával a szokásos nevek helyett. A szelektor általában egy 32 bites érték, amely egyedileg azonosít egy metódust.
A kiválasztó típusát SEL-ként jelölik, és számos olyan függvény és konstrukció létezik, amelyek lehetővé teszik egy név szelektorrá alakítását és fordítva.
Tehát az üzenetválasztó közvetlen név szerinti megjelenítéséhez használja a @selector() konstrukciót:
SEL setWidth = @selector ( setWidth :); SEL setPos = @selector ( setPosition : y :);Az NSSelectorFromString és az NSStringFromSelector függvények arra szolgálnak, hogy egy karakterlánc segítségével jelölőt kapjanak (futás közben), és a szelektort karakterláncsá alakítsák át:
SEL setWidth = NSSelectorFromString ( @"setWidth:" ); NSString * methodName = NSStringFromSelector ( setPos );Az Objective-C hatékony metainformáció-támogatása lehetővé teszi, hogy futás közben ellenőrizze, hogy egy objektum támogat-e egy metódust egy adott szelektorral azáltal, hogy egy answersToSelector: üzenetet küld neki:
if ( [ anObject responsesToSelector : @selector ( setWidth :) ] ) [ anObject setWidth : 200,0 ];Elég egyszerű egy adott szelektornak megfelelő üzenetet küldeni (nincs argumentum, egy, két vagy három argumentum) a performSelector:, performSelector: withObject:, performSelector: withObject: withObject:, performSelector: withObject: withObject: withObject: metódussal. , és így tovább.
[ myObject performSelector : sel withObject : nil ];Vegye figyelembe, hogy a performSelector: metódusok mindig id típusú értéket adnak vissza.
Egy adott objektum osztályát osztályüzenet elküldésével kaphatja meg. Ez az üzenet az osztályt mutatóként adja vissza egy Class típusú objektumra.
Osztály * cls = [ anObject class ]; NSString * clsName = NSStringFromClass ( cls );Másrészt könnyen megszerezheti a megfelelő osztályobjektumot osztálynév alapján is:
Osztály * cls = NSClassFromString ( clsName );Valójában mindegyik metódus egy függvény két láthatatlan argumentummal – self és _cmd.
Az első ezzel analóg, vagyis magára az objektumra – az üzenet címzettjére – mutat. A második tartalmazza ennek a módszernek a választóját.
A self argumentum használható üzenetek küldésére önmagának, például a következő módszerrel:
- ( úszó ) terület { return [ self width ] * [ self height ]; }A self mellett azonban van még egy érték, amelyre üzeneteket lehet küldeni - szuper. Valójában a super nem egy normál változó – ez csak egy újabb jelölés az aktuális objektumra mutató mutatóhoz. De szuperüzenet elküldésekor a metóduskeresés nem az aktuális objektum küldési táblájából indul, hanem a szülőobjektum küldési táblájából.
Így a szupernek küldve az osztály által felülírt metódusok régi verzióit hívjuk meg.
Az Objective-C nyelvben az azt megvalósító függvény címét metódusválasztóval kaphatjuk meg (pontosan úgy, mint egy C nyelvi függvénynél).
Egy ilyen függvény csak abban különbözik a metódus leírásától, hogy két további paramétert szúr be az argumentumlista elejére - egy mutatót magára az objektumra (self) és a metódus választóját (_cmd).
Az objektumnak a methodForSelector: üzenet elküldésével válaszul megkapjuk annak a függvénynek a címét, amely ezt a metódust megvalósítja.
typedef float ( * WidthFunc )( id , SEL ); typedef void ( * SetWidthFunc )( id , SEL , float ); WidthFunc widthFunc = ( WidthFunc ) [ myRect methodForSelector : @selector ( szélesség )]; SetWidthFunc setWidthFunc = ( SetWidthFunc ) [ myRect methodForSelector : @selector ( setWidth :)]; ( * setWidthFunc )( myRect , @selector ( setWidth :), 27.5f );Ez lehetővé teszi, hogy szükség esetén ismételten meghívjuk ugyanazt a metódust egy adott objektumon, és teljesen elkerüljük az üzenettovábbítással járó költségeket.
Az Objective-C nyelv teljes mértékben támogatja a protokollokat (hasonló a Java interfészével és a C ++ absztrakt osztályával, amelyet néha interfésznek is neveznek). A protokoll egyszerűen a metódusok deklarációinak listája. Egy objektum akkor valósít meg egy protokollt, ha az tartalmazza a protokollban leírt összes metódus megvalósítását.
A protokollok kényelmesek, mivel lehetővé teszik a heterogén objektumok közös jellemzőinek kiemelését és információk átvitelét a korábban ismeretlen osztályok objektumairól.
A protokoll legegyszerűbb leírása a következő:
@protocol ProtocolName metódus deklarációk @végeTehát a szerializálható protokoll a következőképpen írható le:
@protokoll Serializable - ( id ) initWithCoder: ( NSCoder * ) kódoló ; - ( void ) encodeWithCoder: ( NSCoder * ) kódoló ; @végeEgy protokoll tetszőleges számú más protokolltól örökölhető:
@protocol MyProto < Protokoll1 , Protokoll2 , Sorosozható , Rajzolható >Ugyanígy egy osztály leírásánál nemcsak a szülőosztályt, hanem egy protokollkészletet is megadhat:
@interface MyClass : SuperClass < Protokoll1 , Protokoll2 , Sorosozható , Rajzolható >A conformsToProtocol: üzenet használatával futás közben ellenőrizheti, hogy egy objektum támogatja-e az adott objektumprotokollt:
if ( [ myObject conformsToProtocol : @protocol ( Serializable ) ] ) [ myObject encodeWithCoder : myCoder ];Ezenkívül a protokoll(ok) neve használható változók deklarálásakor, hogy kifejezetten jelezze a fordítónak, hogy a megfelelő objektumok támogatják a protokoll(oka)t.
Tehát, ha a myObject változó tartalmaz egy mutatót egy korábban ismeretlen osztályba tartozó objektumra, de ugyanakkor megfelel a Serializable és Drawable protokolloknak, akkor azt a következőképpen írhatjuk le:
id < Serializálható , Rajzolható > myObject ;Hasonlóképpen, ha előre ismert, hogy a myObject tartalmazni fog egy mutatót egy olyan objektumra, amely a Shape osztályból örököl, és támogatja a Serializable protokollt, akkor ez a változó a következőképpen deklarálható:
Shape < Serializálható > * myObject ;Ne feledje, hogy ez a leírás csak arra szolgál, hogy megmondja a fordítónak, hogy az objektum mely üzeneteket támogatja.
Az osztályokhoz hasonlóan az Objective-C minden protokollja objektumokkal van ábrázolva (a Protokoll osztály):
Protokoll * myProto = @protokoll ( Serializálható );A következő konstrukciót használhatja a protokollok előzetes bejelentéséhez:
@protocol MyProto , Serializálható , Rajzolható ;Ez a konstrukció közli a fordítóval, hogy a MyProto, Serializable és Drawable olyan protokollnevek, amelyeket később definiálunk.
Az Objective-C támogatja a C++-hoz és a Java-hoz nagyon hasonló kivételkezelést.
Ez a @try, @catch, @finally és @throw utasításokkal történik.
Cup * cup = [[ Cup alloc ] init ]; @próbálja meg { [ csészetöltés ] ; } @catch ( NSException * exc ) { NSLog ( @"Kivétel elkapva:%@" , exc ); } @catch ( azonosító kiv .) { NSLog ( @"Ismeretlen kivétel elkapva" ); } @végül { [ pohárkioldás ] ; }Kivétel dobásához a @throw direktívát használjuk, argumentumként a kivételobjektumra mutató mutatót használva. A Mac OS X/NextStep általában az NSException osztály objektumait használja erre a célra.
NSException * exc = [ NSException kivételWithName : @ "saját kivétel" oka : @"ismeretlen hiba" userInfo : nil ]; @dobja exc ;A @catch blokkon belül a @throw direktíva paraméter nélkül használható az újradobás kivételének visszadobására.
Az Objective-C nyelv támogatja a többszálú alkalmazások szinkronizálását. A @synchronized () direktíva használatával megvédheti egy kódrészletet attól, hogy egyszerre több szál egyszerre hajtsa végre .
A @synchronized() bemenetként egy mutatót vesz egy Objective-C nyelvi objektumra (bármilyen objektumot használhat erre a célra, beleértve a self-t is), amely mutex szerepét tölti be .
Amikor egy szál megpróbálja elindítani egy védett töredék végrehajtását, ellenőrzi, hogy a töredéket már végrehajtja-e valamelyik szál. Ha igen, akkor a szálak által a @synchronized()-nek átadott objektumokat a rendszer összehasonlítja.
Ha ezek a mutatók egyeznek, akkor egy védett blokkba belépni próbáló szál felfüggesztésre kerül, amíg az első szál ki nem lép a blokkból. Ezután a második szál végrehajtása folytatódik, és máris „tiltja” ezt a blokkot az összes többi szál számára.
Egy ilyen lehetőség megléte nagyban megkönnyíti az életet többszálú alkalmazások írásakor, amikor nyomon kell követni az azonos adatok egyidejű, több szálon keresztüli megváltoztatására irányuló kísérleteket.
- ( érvénytelen ) kritikus módszer { @szinkronizált ( saját ) { // módosításokat hajt végre a megosztott objektumokon . . . } }Javasoljuk, hogy egy külsőleg elérhetetlen objektumot mutexként (vagyis a @synchronized utasítás paramétereként) adjunk meg, mivel ez holtponthoz vezethet, ha ugyanazt az objektumot két egymástól függő szál mutexként használja. Különösen a @synchronized(self) elavult.
Magában az Objective-C nyelvben nincsenek speciális parancsok objektumok létrehozására és megsemmisítésére (például új és törlés). Ez a feladat a futásidejű könyvtárra esik, és az üzenetküldési mechanizmus segítségével valósul meg.
A ténylegesen használt és legszélesebb körben használt séma az Objective-C objektumok létrehozására és megsemmisítésére a NextStep és Mac OS X operációs rendszerekben használt séma, amelyet az alábbiakban ismertetünk.
Egy új objektum létrehozása két lépésből áll: memóriafoglalás és objektum inicializálás. Az első lépést az alloc osztály metódussal valósítjuk meg (az NSObject osztályban implementálva), amely lefoglalja a szükséges memóriamennyiséget (ezt a módszert használják nem csak az NSObject osztály objektumaihoz, hanem bármely onnan örökölt osztályhoz is. ). Ezzel egyidejűleg az isa attribútumhoz egy mutatót írunk a megfelelő osztály osztályobjektumára.
Vegye figyelembe, hogy az alloc üzenetet a rendszer elküldi a kívánt osztály osztályobjektumának, és ez az üzenet egy mutatót ad vissza az objektum lefoglalt memóriájába.
Valójában magának az objektumnak az inicializálását (vagyis példányváltozói értékeinek beállítását, további erőforrások kiosztását stb.) más módszerekkel hajtják végre, a hagyomány szerint ezeknek a módszereknek a neve init-el kezdődik. Általában egy ilyen üzenetet közvetlenül az alloc üzenet után küldenek az üzenet által visszaadott címre.
id anObject = [[ Téglalap alloc ] init ];A fenti konstrukció a helyes módja egy objektum létrehozásának. Felhívjuk figyelmét, hogy a következő konstrukciók bizonyos esetekben nem működnek:
id anObject = [ Téglalap alloc ]; [ anObject init ];Ez annak a ténynek köszönhető, hogy számos osztály esetében az init metódus teljesen más mutatót adhat vissza (nem pedig önmagát).
A legegyszerűbb példa arra, hogy mikor fordulhat elő ez a helyzet, a szingutonok (ekkor, ha az osztály egy példánya már létezik, akkor az init metódus felszabadítja az alloc által lefoglalt memóriát, és visszaad egy mutatót a már létrehozott egyetlen példányra) és az objektum gyorsítótárazás, amikor Az objektumok kiosztása a teljesítmény növelése érdekében a blokkokban azonnal megtörténik, és az objektumok nem semmisülnek meg, hanem elmentik újrafelhasználásra.
Új osztály létrehozásakor általában nincs szükség az alloc metódus felülbírálására, de az init metódus felülbírálásának igénye elég gyakran felmerül.
Vegyük észre, hogy az init metódus(ok) csak egy szabályos metódus, semmi különös (ellentétben a C++-val, ahol a konstruktor egy speciális metódus, aminek pl. nem lehet címet adni).
Ezért egy új osztály és init metódus létrehozásakor a felülírt init metódus meghívását ([super init] használatával) kifejezetten a metódus legelején kell végrehajtani.
Az objektumoknak gyakran több metódusuk is van, amelyek init karakterekkel kezdődnek, például init, initWithName:, initWithContentsOfFile: stb.
A bevett gyakorlat ebben az esetben az, hogy az összes inicializálási módszer közül kiemelnek egyet, az úgynevezett kijelölt inicializálót. Minden más init metódusnak meg kell hívnia, és csak ez hívja meg az örökölt init metódust.
- ( id ) initWithName: ( const char * ) theName // kijelölt inicializáló { self = [ szuper init ]; // örökölt metódus meghívása if ( self ) { név = strdup ( theName ); } visszatérő én ; } - ( id ) init { return [ self initWithName : "" ]; }Bizonyos esetekben kényelmesnek bizonyul a memóriafoglalás és az objektum inicializálása egyetlen (osztály) metódusba kombinálni, például az NSString osztálynak számos osztálymetódusa van, amelyek egy már előkészített (inicializált) objektumot adnak vissza:
+ ( id ) stringCStringgel: ( const char * ) cString kódolás: ( NSStringEncoding ) enc + ( id ) stringWithFormat: ( NSString * ) formátum , ...A Mac OS X (mint a NextStep) referenciaszámlálást használ az objektumok élettartamának kezelésére – minden objektum tartalmaz egy bizonyos számlálót, amely létrehozáskor egyre van állítva.
Ha megtartási üzenetet küld egy objektumnak, ez eggyel növeli ezt a számlálót (például az összes Foundation library tárolóosztály megtartási üzenetet küld egy objektumnak, ha egy objektumot helyez el bennük).
A bevett gyakorlat az, hogy egy objektumnak minden érdekelt fél (objektum) megtartó üzenetet küld, vagyis ha emlékszik egy tárgyra való hivatkozásra, akkor megtartó üzenetet kell küldenie.
Ha egy objektumra már nincs szükség, a rendszer egyszerűen elküldi a feloldási üzenetet.
Ez az üzenet eggyel csökkenti a számláló értékét, és ha ez egynél kisebb, akkor megsemmisíti az adott objektumot.
Mielőtt egy objektum megsemmisül, dealloc üzenetet küldenek neki, amely lehetővé teszi az objektum inicializálásának végrehajtását. Azonban ez is egy normális üzenet, és ebben kifejezetten meg kell hívnia a legacy implementációt a [super dealloc]-on keresztül a végén.
- ( érvénytelen ) dealloc { . . . [ super dealloc ]; }Az Objective-C memóriakezelése az „objektumtulajdonlás” elvén alapul. Az Objective-C memóriakezelésének alapvető szabályai így írhatók fel:
Ezek a szabályok az Objective-C elnevezési konvención alapulnak, és egyúttal ennek az egyezménynek az alapját is képezik.
Tegyük fel, hogy van egy osztály Vállalat a programban, amely rendelkezik egy dolgozó módszerrel.
@interface cég : NSObject { NArray * dolgozók ; } -( NSArray * ) dolgozók ; @végeVegyünk egy kis példát egy ilyen osztály használatára:
Vállalat * cég = [[ Company alloc ] init ]; // ... NSArray * dolgozók = [ cég dolgozói ]; // ... [ cégkiadás ] ;Mivel a Vállalati objektumot kifejezetten létrehozták, meg kell semmisíteni, amikor már nem használják ([vállalati kiadás]). Ugyanakkor a workers metódus neve nem mondja meg, hogy kinek kell törölnie a tömböt. Ilyen helyzetben úgy tekintjük, hogy az alkalmazottak listáját a Vállalat objektum kezeli, és nem szükséges törölni.
Kényelmi kivitelezőkSok osztály lehetővé teszi egy objektum létrehozásának és inicializálásának kombinálását kényelmi konstruktoroknak nevezett metódusok segítségével; az ilyen metódusokat általában +className-nek nevezik... Feltételezhetjük, hogy a hívó felelős az objektum élettartamának kezeléséért, de ez a viselkedés ellentétes az Objective-C elnevezési konvencióval.
Vállalat * cég = [ Társaság cég ]; [ cégkiadás ] ;A fenti kódban a [company release] hívás nem engedélyezett , mivel ebben az esetben az objektum élettartamát az autorelease pool segítségével kell kezelni.
A következő példa a vállalati módszer helyes megvalósítására:
+( Társaság * ) cég { id ret = [[ Company alloc ] init ]; return [ ret autorelease ]; } automatikus kiadásTérjünk vissza a Vállalat osztály dolgozói metódusához. Mivel a return egy tömb, amelynek élettartamát a hívó nem szabályozza, a dolgozók metódusának megvalósítása valahogy így nézne ki:
-( NSArray * ) dolgozói { NNSarray * copy = [[ NNSarray alloc ] initWithArray : dolgozók ] ; return [ copy autorlease ]; }Az autorelease hívása hozzáadja a másolási objektumot az automatikus kiadási készlethez, így a visszaadott objektum kiadási üzenetet kap, amikor a készletet eltávolítják. Ha egy automatikus kiadási készlethez hozzáadott objektum önmagában kiadási üzenetet küld, akkor hiba történik az automatikus kiadási készlet eltávolításakor.
Objektum visszaadása hivatkozássalEgyes esetekben az objektumok hivatkozással kerülnek visszaadásra, például az NSData osztálymetódus initWithContentsOfURL:options: error: hibaparaméterként (NSError **)errorPtr. Ebben az esetben is működik az elnevezési konvenció, amiből az következik, hogy az objektum tulajdonjogára nincs kifejezett igény, ezért nem szükséges törölni.
Objektumok törléseAmikor egy objektum referenciaszáma nullára megy, az objektum törlődik. Ebben az esetben a -(void)dealloc metódus kerül meghívásra az objektumon. Ha az objektum bármilyen adatot tartalmaz, azt ebben a függvényben el kell távolítani.
-( void ) dealloc { [ dolgozók felszabadítása ]; [ super dealloc ]; }Miután a kiadási üzenetet elküldtük az összes osztályváltozónak, meg kell hívni az alaposztály dealloc metódusát. Ez az egyetlen eset, amikor elfogadható a dealloc metódus közvetlen meghívása.
Nincs garancia arra, hogy mikor hívják meg a dealloc metódust. Egyes esetekben előfordulhat, hogy az alkalmazás lejártakor egyáltalán nem hívják meg, hogy időt takarítsunk meg, mivel az operációs rendszer az alkalmazás lejártakor mindenképpen felszabadítja a lefoglalt memóriát. Ennek megfelelően a dealloc módszer nem tartalmazhat olyan módszereket, amelyek felelősek a socketek, fájlok stb. bezárásáért.