Tavoite-C | |
---|---|
Kieliluokka | olio- , moniparadigma : heijastava |
Esiintyi | 1983 |
Tekijä | Brad Cox |
Tiedostotunniste _ | .h, .m, .mmtai.C |
Vapauta |
|
Tyyppijärjestelmä | heikko , staattinen / dynaaminen |
Tärkeimmät toteutukset | Kaakao , Cocoa Touch , gcc , LLVM + Clang |
Vaikutettu | Smalltalk , C |
vaikutti | Java , Objective-J , Swift |
Verkkosivusto | developer.apple.com/libr… |
Mediatiedostot Wikimedia Commonsissa |
Objective-C on Apple Corporationin käyttämä käännetty olio- ohjelmointikieli , joka on rakennettu C -kielen ja Smalltalk - paradigmien päälle . Erityisesti objektimalli on rakennettu Smalltalk -tyyliin - eli viestejä lähetetään objekteille .
Objective-C-kieli on C-kielen superjoukko , joten Objective-C-kääntäjä ymmärtää C-koodin täysin.
Objective-C-kääntäjä sisältyy GCC :hen, ja se on saatavilla useimmille tärkeimmille alustoille. Kieltä käytetään ensisijaisesti Mac OS X ( Cocoa ) ja GNUstep , OpenStep oliokäyttöliittymän toteutuksissa . Kieltä käytetään myös iOS :lle ( Cocoa Touch ).
1980-luvun alussa strukturoitu ohjelmointi oli suosittua , mikä mahdollisti algoritmin jakamisen pieniin lohkoihin. Tehtävien monimutkaistuessa rakenteellinen ohjelmointi johti kuitenkin koodin laadun heikkenemiseen. Jouduimme kirjoittamaan yhä enemmän toimintoja, joita voitiin hyvin harvoin käyttää muissa ohjelmissa.
Monet ohjelmoijat näkivät olio-ohjelmoinnin mahdollisena ratkaisuna ongelmaansa. Toisaalta Smalltalkia käyttivät lähes kaikki enemmän tai vähemmän monimutkaiset järjestelmät. Toisaalta virtuaalikoneiden käyttö lisäsi resurssivaatimuksia.
Brad Cox loi Objective-C :n 1980-luvun alussa yrityksessään Stepstone . Hän yritti ratkaista koodin uudelleenkäytön ongelman.
Coxin tavoitteena oli luoda kieli, joka tukee ohjelmisto-IC:n käsitettä, mikä tarkoittaa kykyä koota ohjelmia valmiista komponenteista (objekteista), aivan kuten monimutkaisia elektronisia laitteita voidaan koota valmiista integroiduista piireistä .
Samalla kielen tulee olla yksinkertainen ja perustua C-kieleen, jotta kehittäjien siirtyminen siihen helpottuu.
Yksi tavoitteista oli myös luoda malli, jossa luokat itse ovat täysimittaisia objekteja, tuettaisiin itsetutkiskelua ja dynaamista viestien käsittelyä.
Objective-C on C:n laajennus: mikä tahansa C-ohjelma on Objective-C-ohjelma.
Yksi Objective-C:n tunnusmerkeistä on sen dynaaminen: päätökset, jotka tavallisesti tehdään käännösaikana, siirretään ajon aikana.
Objective-C on viestisuuntautunut kieli, kun taas C++ on funktiosuuntautunut: Objective-C:ssä metodikutsuja ei tulkita funktiokutsuksi (vaikka se yleensä johtuu siitä), vaan viestinä (jossa on nimi ja argumentit) -objekti, aivan kuten Smalltalkissa.
Mikä tahansa esine voidaan lähettää minkä tahansa viestin. Objekti voi viestin käsittelyn sijaan välittää sen toiselle objektille käsittelyä (delegointia) varten, erityisesti näin voidaan toteuttaa hajautettuja (eli eri osoiteavaruudessa ja jopa eri tietokoneissa sijaitsevia) objekteja.
Viestin sitominen vastaavaan toimintoon tapahtuu ajon aikana.
Objective-C-kieli tukee metatiedon kanssa työskentelyä - esimerkiksi ajon aikana saat selville objektin luokan, luettelon sen menetelmistä (syötetyillä argumenttityypeillä) ja ilmentymämuuttujat, tarkistaa onko luokka annetun protokollan jälkeläinen ja tukeeko se annettua protokollaa jne. .
Kielessä on tuki protokollille (objektirajapinnan ja protokollan käsitteet erotetaan selvästi). Perintöä tuetaan (ei useita); protokollat tukevat moniperintöä. Objekti voidaan periä toisesta objektista ja useista protokollista kerralla (vaikka tämä ei todennäköisemmin ole protokollan periytymistä, vaan sen tukea).
Clang- ja GCC-kääntäjät tukevat tällä hetkellä Objective-C:tä ( Windowsissa sitä käytetään osana MinGW :tä tai cygwiniä ).
Jotkut kielitoiminnot on siirretty ajonaikaiseen kirjastoon ja riippuvat siitä suuresti. Gcc-kääntäjän mukana tulee minimaalinen versio tällaisesta kirjastosta. Voit myös ladata ilmaiseksi Applen ajonaikaisen kirjaston: Applen Objective-C runtime.
Nämä kaksi ajonaikaista kirjastoa ovat samanlaisia (pääasiallinen ero on menetelmän nimissä). Muut esimerkit keskittyvät Applen ajonaikaiseen kirjastoon.
Objective-C-kieli käyttää erityistä ID-tyyppiä objektien osoittamiseen (tämä on analoginen Object-tyypin kanssa Javassa ). Id-tyypin muuttuja on itse asiassa osoitin mielivaltaiseen objektiin. Vakio nolla (= NULL) tarkoittaa nollaosoittimen objektiin.
Tässä tapauksessa id:n sijasta voit käyttää tutumpaa nimitystä, jossa on selkeä luokka. Erityisesti jälkimmäinen antaa kääntäjälle mahdollisuuden suorittaa jonkin verran objektien viestituen varmentamista - jos kääntäjä ei pysty päättelemään muuttujan tyypistä, että objekti tukee tiettyä viestiä, se antaa varoituksen.
Siten kieli tukee tyyppitarkistusta, mutta ei-tiukassa muodossa (eli löydetyt epäjohdonmukaisuudet palautetaan varoituksina, ei virheinä).
Viestien lähettämiseen käytetään seuraavaa syntaksia:
[ vastaanottajaviesti ] ;Tässä konstruktissa vastaanottaja on osoitin objektiin ja viesti on menetelmän nimi.
Toisin kuin C++, viestin lähettäminen nollaan on laillinen toimenpide, joka palauttaa aina nollan.
Viesti voi sisältää myös parametreja:
[ myRect setOrigin : 30.0 : 50.0 ];Tässä esimerkissä menetelmän (viestin) nimi on setOrigin::. Huomaa, että jokainen hyväksytty argumentti vastaa täsmälleen yksi kaksoispiste. Tässä esimerkissä ensimmäisellä argumentilla on otsikko (teksti ennen kaksoispistettä), mutta toisella ei ole.
Objective-C-kielen avulla voit merkitä jokaisen argumentin, mikä parantaa huomattavasti koodin luettavuutta ja vähentää väärän parametrin välittämisen todennäköisyyttä. Tämä on useimpien kehittäjien omaksuma tyyli.
[ myRect setWidth : 10,0 korkeus : 20,0 ];Tässä esimerkissä viestin nimi on setWidth: height:.
Se tukee myös mahdollisuutta välittää mielivaltainen määrä argumentteja viestissä:
[ myObject makeGroup : obj1 , obj2 , obj3 , obj4 , nolla ];Kuten funktiot, viestit voivat palauttaa arvoja, ja toisin kuin C:ssä, oletuspalautustyyppi on id.
float area = [ myRect area ];Yhden viestin tulosta voidaan käyttää välittömästi toisessa viestissä:
[ myRect setColor :[ otherRect color ]];Kuten aiemmin mainittiin, Objective-C:ssä luokat ovat itse objekteja. Tällaisten objektien (joita kutsutaan luokkaobjekteiksi) päätehtävä on luoda tietyn luokan esiintymiä ( tehdasmetodimalli ) [2] .
Tässä tapauksessa itse luokan nimellä on kaksoisrooli - toisaalta se toimii tietotyyppinä (eli sitä voidaan käyttää kuvaamaan osoittimia tämän luokan objekteihin). Toisaalta luokan nimi voi toimia objektina, jolle viesti lähetetään (viesteissä luokan nimi voi osallistua vain vastaanottajana).
Rect * myRect = [[ Rect alloc ] init ];Objective-C:ssä ei ole sisäänrakennettua tyyppiä loogisille arvoille, joten tämä tyyppi otetaan yleensä käyttöön keinotekoisesti. Lisäksi loogisille arvoille käytetään BOOL-tyyppiä mahdollisten YES- ja NO-arvojen kanssa (kuten tehdään NextStepissä, Mac OS X -käyttöjärjestelmissä).
Ensimmäinen vakava Objective-C-kielen käyttö oli sen käyttö NextStep-käyttöjärjestelmässä. Tätä järjestelmää varten on kirjoitettu suuri määrä erilaisia Objective-C-luokkia, joista monet ovat edelleen käytössä Mac OS X:ssä.
Kaikkien näiden luokkien nimet alkavat NS-etuliitteellä, mikä osoittaa, että ne kuuluvat NextStep-käyttöjärjestelmään. Nyt ne sisältyvät Foundation-kirjastoon, johon on rakennettu OS X:n ja iOS:n sovelluksia.
Yhden niistä - NSStringin - kohtaamme tässä artikkelissa. Tätä luokkaa käytetään työskentelemään merkkijonojen kanssa (tässä tapauksessa Unicodea käytetään merkkien sisäisenä esityksenä).
Kääntäjä tukee tätä tyyppiä kääntämällä automaattisesti rakenteet, kuten @"my string" osoittimeksi NSString-luokan objektiin, joka sisältää annetun merkkijonon (tarkemmin sanoen sen vakiomerkkijonoja vastaavan alaluokan).
OminaisuudetOletetaan, että Yritys-luokassa on ilmentymän muuttujan nimi.
@interface Yritys : NSObject { NSString * nimi ; }Jos haluat käyttää sitä ulkopuolelta, on parasta käyttää ominaisuuksia , jotka ilmestyivät Objective-C 2.0:ssa. @property-avainsanaa käytetään ominaisuuksien ilmoittamiseen.
@omaisuus ( säilyttää ) NSString * nimi ;Suluissa luetellaan ilmentymämuuttujan käyttöoikeusattribuutit. Ominaisuudet on jaettu 3 pääryhmään.
Aksessorien ja mutaattorien nimet
Luku-/kirjoitusrajoitus
Nämä attribuutit ovat toisensa poissulkevia. Ja viimeinen ryhmä ovat mutaattoriattribuutit .
GC :ssä työskennellessäsi ei ole eroa osoita, säilytä tai kopioi. Luodaksesi koodin ominaisuuksille niiden ilmoituksessa kuvatulla tavalla voit käyttää koodin automaattista generointia:
@synthesize nimi ;Automaattisesti luotu koodi ei ole aina hyvä ratkaisu, ja saatat joutua luomaan ilmentymän muuttujan aksessorit manuaalisesti.
Kieltä kritisoidaan usein sen ylikuormitetusta syntaksista muihin kieliin verrattuna. Usein sen kuitenkin todetaan olevan parempi luettavuus.
Kaikki Objective-C-avainsanat, joita ei löydy C:stä, alkavat @-symbolilla.
Kuten C++:ssa, luokan kuvaus ja toteutus erotetaan toisistaan (yleensä kuvaus sijoitetaan otsikkotiedostoihin, joiden tunniste on h ja toteutukset tiedostoihin, joiden pääte on m).
Seuraava on uuden luokkailmoituksen yleinen rakenne:
@interface ClassName : SuperClass { instanssimuuttujien ilmoitukset _ } menetelmäilmoitukset _ @loppuApplen ajonaikaisessa versiossa kaikilla luokilla on yhteinen esi-isä, NSObject-luokka, joka sisältää useita tärkeitä menetelmiä.
Muuttujien ilmoittaminen ei eroa muuttujien ilmoittamisesta rakenteissa C-kielellä:
Jos et käytä Applea, tarvitset todennäköisesti Objectin (#import <objc/Object.h>) NSObjectin sijaan.
@interface Rect : NSObject { float -leveys ; kelluntakorkeus ; _ BOOL on täytetty ; NSColor * väri ; } @loppuMenetelmien kuvaukset eroavat huomattavasti C++:ssa hyväksytyistä ja ovat hyvin samanlaisia kuin Smalltalk-kielen menetelmäkuvaukset.
Jokainen kuvaus alkaa plus- tai miinusmerkillä. Plus-merkki osoittaa, että tämä menetelmä on luokkametodi (eli se voidaan lähettää vain luokan objekteille, ei tämän luokan esiintymille). Itse asiassa luokkamenetelmät ovat analogisia staattisten menetelmien kanssa luokissa C++-kielellä.
Miinusmerkkiä käytetään osoittamaan objektien menetelmiä - tämän luokan esiintymiä. Huomaa, että Objective-C:ssä kaikki menetelmät ovat virtuaalisia , mikä tarkoittaa, että ne voidaan ohittaa.
Seuraavassa on kuvauksia mahdollisista menetelmistä Rect-luokassa.
@interface Rect : NSObject { float x , y ; float -leveys ; kelluntakorkeus ; _ BOOL on täytetty ; NSColor * väri ; } + uusiRect ; - ( tyhjä ) näyttö ; - ( kellunta ) leveys ; - ( kellunta ) korkeus ; - ( kellunta ) - alue ; - ( void ) setWidth: ( float ) theWidth ; - ( void ) setHeight: ( float ) theHeight ; - ( void ) setX: ( float ) theX y: ( float ) theY ; @loppuHuomaa, että menetelmän nimi voi olla sama kuin tämän luokan ilmentymämuuttujan nimi (esimerkiksi leveys ja korkeus).
Metodin palautustyyppi määritetään suluissa välittömästi plus- tai miinusmerkin jälkeen (mutta ennen menetelmän nimeä). Jos tyyppiä ei ole määritetty, katsotaan, että arvo tyyppiä id palautetaan.
Seuraavaksi tulee menetelmän nimi, jossa jokaisen kaksoispisteen jälkeen ilmoitetaan argumentin tyyppi (suluissa) ja itse argumentti.
Objective-C-kielen avulla voit myös määrittää yhden seuraavista menetelmäargumenttien kuvaajista - oneway, in, out, inout, bycopy ja byref. Näitä kuvaajia käytetään määrittämään tiedonsiirron suunta ja siirtotapa. Niiden läsnäolo yksinkertaistaa merkittävästi toteutusta ja työtä hajautettujen objektien kanssa (jotka otettiin käyttöön NextStep-käyttöjärjestelmässä viime vuosisadan 90-luvun alussa).
Menetelmä, joka ottaa mielivaltaisen määrän parametreja, voidaan kuvata seuraavasti:
- makeGroup: ( id ) objekti , ...;Otsikkotiedoston sisällyttämiseksi Objective-C:hen #include- direktiivin sijaan käytetään #import-direktiiviä, joka on samanlainen kuin #include, mutta takaa, että tämä tiedosto sisällytetään vain kerran.
Joissakin tapauksissa on tarpeen ilmoittaa, että etunimi on luokan nimi, mutta sitä ei nimenomaisesti kuvata (tällainen tarve syntyy kuvattaessa kahta luokkaa, joista jokainen viittaa toiseen luokkaan).
Tässä tapauksessa voit käyttää @class-direktiiviä, joka ilmoittaa, että sitä seuraavat nimet ovat luokan nimiä.
@class Muoto , Suora , Soikea ;Luokkamenetelmien toteutus näyttää tältä:
#tuonti "Luokannimi.h" @implementationClassName _ menetelmän toteutukset @loppuSeuraavassa on esimerkkitoteutus edellä kuvatuista Rect-luokan menetelmistä.
#tuonti "Rect.h" @toteutus Ret + uusiRect { Rect * rect = [[ Rect alloc ] init ]; [ rect setWidth : 1.0f ]; [ rect setHeight : 1.0f ]; [ rect setX : 0.0f y : 0.0f ]; paluu suoraan ; } - ( kellunta ) leveys { paluu leveys ; } - ( kellunta ) korkeus { paluukorkeus ; _ } - ( float ) -alue { return [ oma leveys ] * [ oma korkeus ]; } - ( void ) setWidth: ( float ) theWidth { leveys = leveys ; } - ( void ) setHeight: ( float ) theHeight { korkeus = Korkeus ; } - ( void ) setX: ( float ) theX y: ( float ) theY { x = X ; y = Y ; } @loppuKuten yllä olevasta esimerkistä näet, kaikki esiintymän muuttujat ovat käytettävissä menetelmissä. Kuitenkin, kuten C++:ssa, muuttujien näkyvyyttä (menetelmien näkyvyyttä ei voi hallita) on mahdollista ohjata @private-, @protected- ja @public-käskyillä (jotka toimivat täsmälleen kuten C++-kieli).
@ käyttöliittymätyöntekijä : NSObject { char * nimi ; @yksityinen int ikä ; char * arviointi ; @suojattu int job ; kelluva palkka ; @julkinen id pomo }Samalla julkisiin luokkamuuttujiin pääsee suoraan operaattorilla -> (esim. objPtr -> fieldName).
Kääntäjä kääntää jokaisen viestin lähettämisen, eli konstruktion, kuten [object msg], kutsuksi objc_msgSend-funktiolle. Tämä funktio ottaa ensimmäiseksi parametriksi osoittimen viestin vastaanottajaobjektiin ja toiseksi parametriksi ns. valitsin, jota käytetään lähetettävän viestin tunnistamiseen. Jos viestissä on argumentteja, ne välitetään myös objc_msgSendille kolmantena, neljäntenä jne. parametreina.
Jokainen Objective-C-objekti sisältää isa-attribuutin, joka on osoitin kyseisen objektin luokkaobjektiin. luokkaobjektin luo automaattisesti kääntäjä ja se on olemassa yhtenä esiintymänä, johon kaikki tietyn luokan esiintymät viittaavat isa:n kautta.
Jokainen luokkaobjekti sisältää välttämättä osoittimen pääluokan (superclass) ja lähetystaulukon luokkaobjektiin. Jälkimmäinen on sanakirja, joka yhdistää viestivalitsijat niitä toteuttavien menetelmien (toimintojen) todellisiin osoitteisiin.
Siten objc_msgSend-funktio etsii menetelmää annetulla valitsimella annetun objektin lähetystaulukosta. Jos sitä ei ole, haku jatkuu lähetystaulukossa sen yläluokkaan ja niin edelleen.
Jos menetelmä (eli sitä vastaava funktio) löytyy, sitä kutsutaan siirtämällä kaikki tarvittavat argumentit.
Muussa tapauksessa objektille annetaan viimeinen mahdollisuus käsitellä viesti ennen poikkeuksen antamista - viestin valitsin parametrien kanssa kääritään erityiseen NSInvocation-tyyppiseen objektiin ja forwardInvocation: -sanoma lähetetään objektille, jossa NInvocation-luokan objekti toimii parametrina.
Jos objekti tukee forwardInvocation:a, se voi joko käsitellä lähettämänsä viestin itse tai välittää sen toiselle objektille käsittelyä varten:
- ( void ) forwardInvocation: ( NInvocation * ) anInvocation { if ( [ someOtherObject responsesToSelector : [ an Invocation selector ]] ) [ anInvocation invokeWithTarget : someOtherObject ]; muu ........ }Viestien etsimisen nopeuttamiseksi lähetystaulukossa käytetään välimuistia, mikä voi merkittävästi vähentää viestien lähetyskustannuksia. Se helpottaa myös menetelmän etsimistä taulukoista käyttämällä ns. valitsimia tavallisten nimien sijaan. Tyypillisesti valitsin on 32-bittinen arvo, joka yksilöi menetelmän.
Valitsintyyppiä kutsutaan SEL:ksi, ja on useita toimintoja ja rakenteita, joiden avulla voit muuntaa nimen valitsimeksi ja päinvastoin.
Joten saadaksesi viestivalitsimen suoraan nimellä, käytä @selector() -rakennetta:
SEL setWidth = @valitsin ( setWidth :); SEL setPos = @valitsin ( setPosition : y :);NSSelectorFromString- ja NSStringFromSelector-funktioita käytetään valittimen hankkimiseen merkkijonon perusteella (ajon aikana) ja valitsimen muuntamiseen merkkijonoksi:
SEL setWidth = NSSelectorFromString ( @"setWidth:" ); NSString * methodName = NSStringFromSelector ( setPos );Tehokas metatietojen tuki Objective-C:ssä antaa sinun tarkistaa suorituksen aikana, tukeeko objekti menetelmää tietyllä valitsimella lähettämällä sille answersToSelector: -viestin:
if ( [ anObject responsesToSelector : @selector ( setWidth :) ] ) [ anObject setWidth : 200.0 ];On melko helppoa lähettää tiettyä valitsinta vastaava viesti (ei argumentteja, yksi, kaksi tai kolme argumenttia) käyttämällä performSelector:, performSelector: withObject:, performSelector: withObject: withObject:, performSelector: withObject: withObject: withObject: menetelmää. , ja niin edelleen.
[ myObject performSelector : sel withObject : nolla ];Huomaa, että performSelector: -menetelmät palauttavat aina tyypin id arvon.
Voit saada luokan tietylle objektille lähettämällä sille luokkaviestin. Tämä viesti palauttaa luokan osoittimena Class-tyypin objektiin.
Luokka * cls = [ anObject class ]; NSString * clsName = NSStringFromClass ( cls );Toisaalta voit myös helposti saada vastaavan luokkaobjektin luokan nimellä:
Luokka * cls = NSClassFromString ( clsName );Jokainen menetelmä on itse asiassa funktio, jossa on kaksi näkymätöntä argumenttia - self ja _cmd.
Ensimmäinen on analoginen tämän kanssa, eli se osoittaa itse objektiin - viestin vastaanottajaan. Toinen sisältää tämän menetelmän valitsimen.
Itse argumenttia voidaan käyttää viestien lähettämiseen itselleen, kuten seuraavassa menetelmässä:
- ( float ) -alue { return [ oma leveys ] * [ oma korkeus ]; }Itsen lisäksi on kuitenkin vielä yksi arvo, jolle viestejä voidaan lähettää - super. Itse asiassa super ei ole normaali muuttuja - se on vain toinen merkintä osoittimelle nykyiseen kohteeseen. Mutta kun supersanoma lähetetään, metodihaku ei ala nykyisen objektin lähetystaulukosta, vaan pääobjektin lähetystaulukosta.
Lähettämällä viestejä superille, kutsumme siten tämän luokan ohittamia metodien vanhoja versioita.
Objective-C-kielessä saat sen toteuttavan funktion osoitteen menetelmävalitsimella (täsmälleen kuten C-kielen funktio).
Tällainen funktio eroaa menetelmän kuvauksesta vain lisäämällä kaksi lisäparametria argumenttiluettelon alkuun - osoitin itse objektiin (self) ja tämän menetelmän valitsin (_cmd).
Lähettämällä objektille viestin methodForSelector: saamme vastauksena tämän menetelmän toteuttavan funktion osoitteen.
typedef float ( * WidthFunc )( id , SEL ); typedef void ( * SetWidthFunc )( id , SEL , float ); WidthFunc widthFunc = ( WidthFunc ) [ myRect methodForSelector : @selector ( leveys )]; SetWidthFunc setWidthFunc = ( SetWidthFunc ) [ myRect methodForSelector : @selector ( setWidth :)]; ( * setWidthFunc )( myRect , @selector ( setWidth :), 27.5f );Tämä mahdollistaa tarvittaessa toistuvasti kutsua samaa menetelmää tietylle objektille, välttää täysin kaikki viestin edelleenlähetykseen liittyvät kustannukset.
Objective-C-kieli sisältää täyden tuen protokollille (se on analoginen Java-rajapinnan ja C ++:n abstraktin luokan kanssa, jota joskus kutsutaan myös käyttöliittymäksi). Protokolla on yksinkertaisesti luettelo menetelmän määrittelyistä. Objekti toteuttaa protokollan, jos se sisältää toteutukset kaikista protokollassa kuvatuista menetelmistä.
Protokollat ovat käteviä siinä mielessä, että niiden avulla voit korostaa heterogeenisten objektien yhteisiä piirteitä ja siirtää tietoa aiemmin tuntemattomien luokkien objekteista.
Protokollan yksinkertaisin kuvaus on seuraava:
@protocol ProtocolName -metodimääritykset _ @loppuJoten Serialisoitavaa protokollaa voidaan kuvata seuraavasti:
@protokolla Serialisoitavissa - ( id ) initWithCoder: ( NSCoder * ) kooderi ; - ( void ) encodeWithCoder: ( NSCoder * ) kooderi ; @loppuProtokolla voidaan periä mielivaltaisesta määrästä muita protokollia:
@protokolla MyProto < Protokolla1 , Protokolla2 , Sarjasoitava , Piirrettävä >Samalla tavalla luokkaa kuvattaessa voit määrittää pääluokan lisäksi myös joukon protokollia:
@interface MyClass : SuperClass < Protokolla1 , Protokolla2 , Sarjasoitava , Piirrettävä >Voit tarkistaa suorituksen aikana, tukeeko objekti tiettyä objektiprotokollaa, käyttämällä conformsToProtocol: -sanomaa:
if ( [ myObject conformsToProtocol : @protocol ( Serialisoitava ) ] ) [ myObject encodeWithCoder : myCoder ];Lisäksi protokollan (protokollan) nimiä voidaan käyttää muuttujia määritettäessä osoittamaan kääntäjälle, että vastaavat objektit tukevat protokollaa.
Joten jos muuttuja myObject sisältää osoittimen aiemmin tuntemattoman luokan objektiin, mutta joka samalla täyttää Serializable- ja Drawable-protokollat, se voidaan kuvata seuraavasti:
id < Serialisoitava , Piirrettävä > myObject ;Vastaavasti, jos etukäteen tiedetään, että myObject sisältää osoittimen Shape-luokasta perittävään ja Serializable-protokollaa tukevaan objektiin, tämä muuttuja voidaan ilmoittaa seuraavasti:
Muoto < Serialoitavissa > * myObject ;Huomaa, että tämä kuvaus vain kertoo kääntäjälle, mitä viestejä tämä objekti tukee.
Kuten luokat, kaikki Objective-C:n protokollat esitetään objekteilla (protokollaluokka):
Protokolla * myProto = @protokolla ( Serialisoitavissa );Voit käyttää seuraavaa rakennetta protokollien ennakkoilmoittamiseen:
@protocol MyProto , sarjoitettava , piirrettävä ;Tämä rakenne kertoo kääntäjälle, että MyProto, Serializable ja Drawable ovat protokollien nimiä, jotka määritellään myöhemmin.
Objective-C tukee poikkeusten käsittelyä, joka on hyvin samanlainen kuin C++ ja Java.
Tämä tehdään @try-, @catch-, @finally- ja @throw-käskyjen avulla.
Cup * cup = [[ Cup alloc ] init ]; @yrittää { [ kupin täyttö ]; } @catch ( NSException * exc ) { NSLog ( @"Poikkeus kiinni:%@" , erillinen ); } @catch ( id exc ) { NSLog ( @"Tuntematon poikkeus kiinni" ); } @vihdoin { [ kupin vapauttaminen ]; }Poikkeuksen tekemiseen käytetään @throw - direktiiviä , joka ottaa argumenttina osoittimen poikkeusobjektiin. Tyypillisesti Mac OS X/NextStep käyttää tähän tarkoitukseen NSException-luokan objekteja.
NSException * exc = [ NSException poikkeusWithName : @"oma-poikkeus" syy : @"tuntematon-virhe" userInfo : nolla ]; @heittä exc ;@catch-lohkoissa @throw-direktiiviä voidaan käyttää uudelleenheittopoikkeuksen uudelleenheittämiseen ilman parametria.
Objective-C-kieli tukee monisäikeisten sovellusten synkronointia. @synchronized () -direktiivin avulla voit suojata koodinpätkän useiden säikeiden suorittamiselta samanaikaisesti .
@synchronized() ottaa syötteenä osoittimen Objective-C-kielen objektiin (voit käyttää mitä tahansa objektia tähän tarkoitukseen, mukaan lukien itse), joka toimii mutexina .
Kun säiettä yrittää aloittaa suojatun fragmentin suorittamisen, se tarkistaa, onko jokin säiettä jo suorittamassa fragmenttia. Jos kyllä, näiden säikeiden @synchronized():lle välittämiä objekteja verrataan.
Jos nämä osoittimet täsmäävät, suojattuun lohkoon pääsyä yrittävä säie keskeytetään, kunnes ensimmäinen säie poistuu lohkosta. Sitten toisen säikeen suoritus jatkuu, ja se "kieltää" jo tämän lohkon kaikille muille säikeille.
Tällaisen mahdollisuuden olemassaolo tekee elämästä paljon helpompaa kirjoitettaessa monisäikeisiä sovelluksia, kun on tarpeen seurata yrityksiä muuttaa samoja tietoja samanaikaisesti useilla säikeillä kerralla.
- ( void ) kriittinen menetelmä { @synkronoitu ( itse ) { // tehdä muutoksia jaettuihin objekteihin . . . } }On suositeltavaa määrittää ulkoisesti saavuttamaton objekti mutexiksi (eli @synchronized-käskyn parametriksi), koska tämä voi johtaa lukkiutumiseen , jos kaksi toisistaan riippuvaista säiettä käyttää samaa objektia mutexina. Erityisesti @synchronized(self) on vanhentunut.
Itse Objective-C-kielessä ei ole erityisiä komentoja objektien luomiseen ja tuhoamiseen (kuten uusiin ja poistamiseen). Tämä tehtävä kuuluu ajonaikaiseen kirjastoon ja toteutetaan viestin lähetysmekanismilla.
Tosiasiallisesti käytetty ja laajimmin käytetty malli objektien luomiseen ja tuhoamiseen Objective-C:ssä on NextStep- ja Mac OS X -käyttöjärjestelmissä käytetty malli, joka kuvataan alla.
Uuden objektin luominen on jaettu kahteen vaiheeseen - muistin varaus ja objektin alustus. Ensimmäinen vaihe toteutetaan alloc-luokkamenetelmällä (toteutettu NSObject-luokassa), joka varaa tarvittavan määrän muistia (tätä menetelmää käytetään muistin varaamiseen paitsi NSObject-luokan objekteille, myös mille tahansa siitä peritylle luokalle ). Samanaikaisesti isa-attribuutille kirjoitetaan osoitin vastaavan luokan luokkaobjektiin.
Huomaa, että alloc-sanoma lähetetään vaaditun luokan luokkaobjektille, ja tämä viesti palauttaa osoittimen objektin varattuun muistiin.
Itse asiassa itse objektin alustus (eli sen ilmentymämuuttujien arvojen asettaminen, lisäresurssien allokointi jne.) suoritetaan muilla menetelmillä, perinteen mukaan näiden menetelmien nimet alkavat initillä. Tyypillisesti tällainen viesti lähetetään välittömästi allok-sanoman jälkeen kyseisen viestin palauttamaan osoitteeseen.
id anObject = [[ Suorakaide alloc ] init ];Yllä oleva rakenne on oikea tapa luoda objekti. Huomaa, että seuraava rakenne ei välttämättä toimi joissain tapauksissa:
id anObject = [ Suorakaide alloc ]; [ anObject init ];Tämä johtuu siitä, että useille luokille init-metodi voi palauttaa täysin erilaisen osoittimen (ennen kuin itsensä).
Yksinkertaisimpia esimerkkejä siitä, milloin tämä tilanne voi tapahtua, ovat singletonit (jos yksi esiintymä luokasta on jo olemassa, niin init-metodi vapauttaa allocilla varaaman muistin ja palauttaa osoittimen jo luotuun yksittäiseen esiintymään) ja objektin välimuisti, kun Kohteiden allokointi suorituskyvyn lisäämiseksi tapahtuu välittömästi lohkoissa ja esineitä ei tuhota, vaan ne tallennetaan uudelleenkäyttöä varten.
Uutta luokkaa luotaessa ei yleensä tarvitse ohittaa alloc-metodia, mutta tarve ohittaa init-metodi tulee esiin melko usein.
Huomaa, että init-metodi(t) on vain tavallinen menetelmä, ei mitään erikoista (toisin kuin C++:ssa, jossa konstruktori on erityinen metodi, jolle ei esimerkiksi voi antaa osoitetta).
Siksi uutta luokka- ja init-metodia luotaessa kutsu ohitetulle init-menetelmälle (käyttäen [super init]) on tehtävä nimenomaisesti metodin alussa.
Usein objekteissa on useita menetelmiä, jotka alkavat initillä, kuten init, initWithName:, initWithContentsOfFile: jne.
Vakiintunut käytäntö tässä tapauksessa on erottaa kaikista aloitusmenetelmistä yksi, nimeltään nimetty alustus. Kaikkien muiden init-menetelmien on kutsuttava sitä, ja vain se kutsuu perittyä init-menetelmää.
- ( id ) initWithName: ( const char * ) theName // määritetty alustus { itse = [ super init ]; // kutsu peritty menetelmä if ( itse ) { nimi = strdup ( theName ); } palauttaa itsensä ; } - ( id ) init { return [ self initWithName : "" ]; }Joissakin tapauksissa on kätevää yhdistää muistin varaus ja objektin alustus yhdeksi (luokka) menetelmäksi, esimerkiksi NSString-luokassa on useita luokkamenetelmiä, jotka palauttavat jo valmistetun (alusoidun) objektin:
+ ( id ) stringWithCSstring: ( const char * ) cString- koodaus: ( NSStringEncoding ) enc + ( id ) stringWithFormat: ( NSString * ) muoto , ...Mac OS X (kuten NextStep) käyttää viitelaskentaa objektien eliniän hallintaan - jokaisen objektin sisällä on tietty laskuri, joka asetetaan luodessaan yhteen.
Säilytyssanoman lähettäminen objektiin kasvattaa laskuria yhdellä (esimerkiksi kaikki Foundation-kirjaston säilöluokat lähettävät objektille säilytysviestin, kun objekti sijoitetaan niihin).
Vakiintuneena käytäntönä on, että kaikki siitä kiinnostuneet osapuolet (objektit) lähettävät objektille säilytysviestin, eli jos muistat viittauksen kohteeseen, sinun tulee lähettää sille säilytysviesti.
Kun objektia ei enää tarvita, sille lähetetään yksinkertaisesti vapautusviesti.
Tämä viesti pienentää laskurin arvoa yhdellä ja jos tämä arvo on pienempi kuin yksi, se tuhoaa annetun kohteen.
Ennen kuin objekti tuhoutuu, sille lähetetään dealloc-sanoma, jonka avulla objekti voi suorittaa alustuksen poistamisen. Tämä on kuitenkin myös normaali viesti, ja siinä sinun täytyy nimenomaisesti kutsua vanhaa toteutusta [super dealloc]:n lopussa.
- ( mitätön ) dealloc { . . . [ super dealloc ]; }Muistinhallinta Objective-C:ssä perustuu "objektin omistajuuden" periaatteeseen. Objective-C:n muistinhallinnan perussäännöt voidaan kirjoittaa näin:
Nämä säännöt perustuvat Objective-C-nimeämiskäytäntöön ja ovat samalla tämän sopimuksen perusta.
Oletetaan, että ohjelmassa on luokka Yritys, jolla on työntekijöiden menetelmä.
@interface Yritys : NSObject { NArray * työntekijät ; } -( NSArray * ) työntekijät ; @loppuHarkitse pientä esimerkkiä tällaisen luokan käytöstä:
Yritys * yritys = [[ Yritys alloc ] init ]; // ... NSArray * työntekijät = [ yrityksen työntekijät ]; // ... [ yritysjulkaisu ] ;Koska yritysobjekti on luotu eksplisiittisesti, se on hävitettävä, kun sitä ei enää käytetä ([yhtiön tiedote]). Samaan aikaan työntekijöiden menetelmän nimi ei kerro, kenen pitäisi poistaa taulukko. Tällaisessa tilanteessa katsotaan, että työntekijäluetteloa hallinnoi Yritysobjekti, eikä sitä tarvitse poistaa.
MukavuusrakentajatMonissa luokissa voit yhdistää objektin luomisen sen alustukseen käyttämällä mukavuuskonstruktoreiksi kutsuttuja menetelmiä. tällaisten menetelmien nimi on yleensä +className... Voit olettaa, että kutsuja on vastuussa objektin eliniän hallinnasta, mutta tällainen käyttäytyminen olisi vastoin Objective-C-nimeämiskäytäntöä.
Yritys * yritys = [ Yritysyritys ] ; [ yhtiön tiedote ];Yllä olevassa koodissa [company release] -kutsu ei ole sallittu , koska tässä tapauksessa objektin käyttöikää on hallittava automaattisen julkaisuvarannon avulla.
Seuraavassa on esimerkki yrityksen menetelmän oikeasta toteutuksesta:
+( Yritys * ) yritys { id ret = [[ Yritys alloc ] init ]; palauttaa [ ret autorrelease ]; } automaattinen julkaisuPalataan Yritys-luokan työntekijöiden menetelmään. Koska paluu on taulukko, jonka elinikää kutsuja ei hallitse, työntekijöiden menetelmän toteutus näyttäisi tältä:
-( NSArray * ) työntekijät { NSArray * copy = [[ NSArray alloc ] initWithArray : työntekijät ] ; return [ copy autorrease ]; }Autorelease-kutsu lisää kopioobjektin automaattisen julkaisun pooliin, jolloin palautettu objekti saa vapautussanoman, kun pooli, johon se lisättiin, poistetaan. Jos automaattiseen julkaisuun lisätty objekti lähettää julkaisusanoman itsestään, tapahtuu virhe, kun automaattisen julkaisuvarannon poistaminen tapahtuu.
Objektin palauttaminen viittauksellaJoissakin tapauksissa objektit palautetaan viittauksella, esimerkiksi NSData-luokkametodi initWithContentsOfURL:options: error: ottaa (NSError **)errorPtr virheparametriksi. Tässä tapauksessa myös nimeämiskäytäntö toimii, josta seuraa, että nimenomaista pyyntöä kohteen omistuksesta ei ole, joten sitä ei tarvitse poistaa.
Objektien poistaminenKun objektin viiteluku laskee nollaan, objekti poistetaan. Tässä tapauksessa -(void)dealloc-menetelmää kutsutaan objektille. Jos objekti sisältää tietoja, se on poistettava tässä toiminnossa.
-( void ) dealloc { [ työntekijöiden vapauttaminen ]; [ super dealloc ]; }Kun julkaisusanoma on lähetetty kaikille luokkamuuttujille, on kutsuttava perusluokan dealloc-metodi. Tämä on ainoa tapaus, jossa on hyväksyttävää kutsua dealloc-menetelmää suoraan.
Ei ole takeita siitä, milloin dealloc-menetelmää kutsutaan. Joissakin tapauksissa sitä ei ehkä kutsuta ollenkaan, kun sovellus päättyy ajan säästämiseksi, koska käyttöjärjestelmä vapauttaa varatun muistin joka tapauksessa, kun sovellus päättyy. Vastaavasti dealloc-menetelmä ei saa sisältää menetelmiä, jotka vastaavat pisteiden, tiedostojen jne. sulkemisesta.