Back to site
Since 2004, our University project has become the Internet's most widespread web hosting directory. Here we like to talk a lot about web servers, web development, networking and security services. It is, after all, our expertise. To make things better we've launched this science section with the free access to educational resources and important scientific material translated to different languages.

Mike Ash - Objective-C Literals


Petak Pitanja i odgovori 2012-06-22: Objective-C Literals

Dobrodošli! Nakon kraće pauze za WWDC, vreme je za još jednu avanturu. Današnja tema je o novim object literals syntax što se uvodi u Objective-C, koji je predložio čitalac Frank McAuleina.

Literals
Za one koje ne poznaju termine u kontekstu programskih jezika, "literal" se odnosi na bilo koju vrednost koja se može direktno pisati u source kodu. Na primer, 42 je literal u C ( I takođe za mnogo druge jezike). Uobičajeno je da se odnosi na vrstu vrednost koju proizvode, tako da 42 je literal sa celim brojem, "hello" je string literal, a 'x' je literal znak.

Literal-ali su temelj za većinu programskih jezika, pošto mora da postoji neki način pisanja konstantne vrednosti u kodu. Literals nisu neophodni jer možete ih izgraditi u bilo koju željenu vrednost u runtime-u, ali uglavnom napravi kod koji se lakše piše. Na primer, možemo konstruisati 42 a da ne koristimo bilo kakve literals:

    int fortytwo(void)
    {
        static int zero; // statics are initialized to 0
        static int fortytwo;
        if(!fortytwo)
        {
            int one = ++zero;
            int two = one + one;
            int four = two * two;
            int eight = four * two;
            int thirtytwo = eight * four;
            fortytwo = thirtytwo + eight + two;
        }
        return fortytwo;
    }

Međutim, ako bismo to radili za svaki celi broj koji koristimo, verovatno bismo svi odustali od programiranje računara i pronašli bismo neku drugu profesiju gde se alatke mogu lakše koristiti. Isto tako, možemo izgraditi C string ručno, koristeći znakove, ali se strings koriste često tako da jezik ima sažet način da ih napiše.

Kolekcije se takođe često koriste. C prvobitno nije imao uslove za prikupljanje literals, ali je približno blizu sposobnost da pokrene promenljive podataka složenog tipa:

    int array[] = { 1, 2, 3, 4, 5 };
    struct foo = { 99, "string" };

Ovo nije uvek u pogodno, tako da je C99 dodao složene literals, koji omogućavaju pisanje bilo gde direktno u kodu:

    DoWorkOnArray((int[]){ 1, 2, 3, 4, 5 });
    DoWorkOnStruct((struct foo){ 99, "string" });

Kolekcija literala su prilično česte u drugim jezicima. Na primer, popularni JSON serialization format je samo kodifikacija Javascript literal sintakse. Ovaj JSON je validna sintaksa gde može da se napravi niz rečnika u JavaScript, Python i verovatno nekih drugih jezika:

    [{ "key": "obj" }, { "key": "obj2" }]

Objective-C do nedavno nije imao nikakvu sintaksu za Objective-C kolekcije. Ekvivalent za gore navedeno je bio:

    [NSArray arrayWithObjects:
        [NSDictionary dictionaryWithObjectsAndKeys:
            @"obj", @"key", nil],
        [NSDictionary dictionaryWithObjectsAndKeys:
            @"obj2", @"key", nil],
        nil];

Ovo je zaista opširnp,toliko da je bolno za kucanje i zamagljuje ono što se dešava. Ograničenja za C variable argument passing takođe zahteva nultu vrednost sentinel na kraju svakog container creation call, koji može propasti ukoliko se zaboravi. Sve u svemu, nije dobra situacija.

Container literals
Najnoviji clang sada ima podršku za container literals u Objective-C. Sintaksa je slična onoj JSON i savremene skript jezike, ali sa tradicionalnim Objective-C @ koji ubačen. Naš niz/rečnik primer izgleda ovako:

    @[@{ @"key" : @"obj" }, @{ @"key" : @"obj2" }]

Postoji malo preopterećenje sa @ ali je veliko poboljšanje od prethodnog stanja. Znak @[] sintaksa kreira array od sadržaja, gde sve mora da bude objekat. Znak @{} sintaksa kreira rečnik iz sadržaja, koji se piše kao key : value umesto samo value, key sintaksa kao što se nalazi u NSDictionary metod.

Pošto je ugrađen u jezik, nema potrebe za završni nil. U stvari, korišćenje nil bilo gde u ove literals će izbaciti grešku u runtime, jer Cocao kolekcije odbijaju da sadrže nil. Kao i obično, koristi [NSNull null] da zastupi nil u kolekciji.

Ne postoji ekvivalentna sintaksa za NSSet. Array literal sintaksa čini posao malo lakšim, jer možete da uradite nešto kao: [NSSet setWithArray: @[ contents ]], ali ništa ne može da se poredi sa sažetom literal sintaksom.

Sve što ste postavite u takav array ili rečnik uvek mora da bude objekat. Ne možete ispuniti array objekata sa brojevima ako ih pišete na sledeć način: @[ 1, 2, 3 ]. Ipak je mnogo lakše kad sa uvođenjem sledećeg:

Boxed Izrazi
Boxed izrazi u suštini omogućavaju literals koji odgovaraju primitivnim tipovima. Sintaksa je @(contents), koji proizvodi objekt boxing je rezultat izraza unutar zagrade.

Tip objekta zavisi od tip izraza. Numerički tipovi se pretvaraju u NSNumber objekate. Na primer, @(3) proizvodi NSNumber koji sadrži 3, kao kad napišete [NSNumber numberWithInt: 3]. C strings se pretvaraju u NSString objekte koristeći UTF-8 kodiranje, tako da je @("ovde nešto ubacite") proizvodi NSString sa tim sadržajem.

Ovo može da sadržati abitrary izraze, ne samo konstante, tako da oni prevazilaze jednostavne literals. Na primer, @(sqrt(2)) će proizvesti NSNumber koji sadrži kvadratni koren od 2. @(getenv("FOO")) je ekvivalentan [NSString stringWithUTF8String: getenv("foo")].

Jedna prečica je da broj literals mogu biti boxed bez upotrebe zagrade. Umesto @(3), možete samo napisati @3. Ovo se primenjuje na strings I daje nam poznatu i drevnu konstruktciju @"object string". Imajte na umu da izrazi ne funkcionišu na ovaj način.@2+2 i @sqrt(2) će proizvesti grešku i moraju biti u zagradi @(2+2) i @(sqrt(2)).

Kada koristimo ovo, možemo lakše da napravimo array objekata koji sadrži brojeve:

    @[ @1, @2, @3 ]

Ponovo imamo malo @ preopterećenja, ali je mnogo lepše nego ekvivalent bez nove sintakse.

Imajte na umu da boxed izraze samo funkcionišu za numeričke tipove i char *, i ne funkcionišu sa drugim pokazateljima ili strukturama. I dalje morate da koristite longhand da box-ujete svoj NSRectili SELs.

Object subscripting (objekat u stilu indeksa)
Čekajte, ima još! Sada postoji sažeta sintaksa za pronalaženje i postavljanje elemenata array i rečnika. Ovo nije striktno vezano za objekat literals, ali je u isto vreme stiglo u clang i nastavlja temu koja olakšava rad sa container-ima.

Poznata [] sintaksa za pristup array pristup sada takođe radi NSArray NSArray objekte:

    int carray[] = { 12, 99, 42 };
    NSArray *nsarray = @[ @12, @99, @42 ];

    carray[1]; // 99
    nsarray[1]; // @99

Takođe funkcioniše za postavljanje elemenata u mutable array:

    NSMutableArray *nsarray = [@[ @12, @99, @42 ] mutableCopy];
    nsarray[1] = @33; // now contains 12, 33, 42

Imajte na umu da nije moguće dodati elemente u array na ovaj način, samo se mogu zameniti postojeći elementi. Ako se array indeks nalazi van kraja array, onda taj array neće odgovarati i desiće se greška.

Isto tako važi i za rečnike, samo što je subscriptobjekat umesto indeks. Pošto rečnici nemaju nikakva indexing ograničenja onda takođe funkcioniše i za postavljanje nove stavke:

    NSMutableDictionary *dict = [NSMutableDictionary dictionary];
    dict[@"suspect"] = @"Colonel Mustard";
    dict[@"weapon"] = @"Candlestick";
    dict[@"room"] = @"Library";

    dict[@"weapon"]; // Candlestick

Što se tiče literals, ne postoji ekvivalentna notacija za NSSet, verovatno zato što nema smisla da se set subscript-uje.

Prilagođene Subscripting metode
Clang programeri su namestili da operateri objekta subscripting-a budu potpuno generični. Oni nisu vezani u NSArray ili NSDictionary na bilo koji način. Oni se jednostavno prevode na jednostavne metode koja svaka klasa može implementira.

Postoje ukupno četiri metode: jedan setter i jedan getter za celobrojne subscripts, a jedan setter/getter za objekat subscripts. Celobrojni subscript getter ima ovaj prototip:

    - (id)objectAtIndexedSubscript: (NSUInteger)index;

Možete da implementirate ovo da biste uradili šta god želite kako biste podržali semantiku koju želite. Kod se jednostavno mehanički prevede:

    NSLog(@"%@", yourobj[99]);
    // becomes
    NSLog(@"%@", [yourobj objectAtIndexedSubscript: 99]);

Vaš kod može da donese indeks iz interne array, može da izgradi novi objekat na osnovu indeksa, da prijavi grešku, abort(), započne igru tenisa, ili šta god poželite.

Odgovarajući setter ima sledeći prototip:

    - (void)setObject: (id)obj atIndexedSubscript: (NSUInteger)index;

Dobijete indeks i objekat koji je se tu postavlja, a onda činite sve što treba da biste implementirali semantiku koju želite. Da ponovim, ovo je samo jednostavan mehanički prevod:

    yourobj[12] = @"hello";
    // becomes
    [yourobj setObject: @"hello" atIndexedSubscript: 12];

Dve metode za objekat subscripts su slični. Njihovi prototipi su sledeće:

    - (id)objectForKeyedSubscript: (id)key;
    - (void)setObject: (id)obj forKeyedSubscript: (id)key;

Moguće je sprovesti sve četiri metode na istoj klasi. Compiler odlučuje koji da pozove tako što ispitia tip subscript-a. Celobrojni subscript zovu indeksirane varijante, a objekti zovu ukucane varijante.

Ovo je zapravo mali deo preopterećenja operatora koji je sada dostupan u Objective-C, koji je obično uvek izbegavao. Dakle, budite oprezni da vaše uobičaejne implementacije ostanu tačni prema subscripting operateru. Nemojte da sprovodite subscripting sintaksu da dodavanje objekata ili slanje poruka preko mreže. Ako ih ostavite ograničenim samo na pronalaženje i dobijanje elemenata vašeg objekta, upotreba sintakse ostaje dosledna i možete lakše razumeti šta kod radi a da ne morate da znate sve detalje.

Initializers
C ima jedan čudan aspekat gde initializer-i koji imaju globalnu varijablu moraju biti compile-time constant. Ovo uključuje jednostavne izraze, ali ne function pozive. Na primer, sledeća deklaracija globalne varijable je dozvoljena:

    int x = 2 + 2;

Ali ova nije:

    float y = sin(M_PI);

C string literals su compile-time constants, tako da je ovo dozvoljeno:

    char *cstring = "hello, world";

NSString literals su takođ compile-time constants, tako da je Cocao ekvivalent dozvoljen:

    NSString *nsstring = @"hello, world";

Važno je napomenuti da nijedna nova literal sintaksa odgovaraju compile-time constant. Pod pretpostavkom da je array globalna variajbla, sledeće nije dozvoljeno:

    NSArray *array = @[ @"one", @"two" ];

To je zato što @[] sintaksa bukvalno prevodi to u poziv prema NSArray metodi. Compiler ne može da izračuna rezultat te metode u compile time, tako da nije dozvoljen initializer u ovom kontekstu.

Zanimljivo je kad se istraži zašto je to tako. Compiler izlaže globalne variajble u vašem binary, a on se direktno učitava u memoriju. Globalna varijable koja je inicijalizovana sa 2 + 2 rezultata u literal 4 koja je pisana u memoriju. Sa C string initialize, sadržaj stringa koji se piše u podacima programa, a zatim pokazivač tih sadržaja koji se pišu kao globalna vrednost varijable.

Važno je primtetiti da C + + i samim tim Objective-C + +, ne dozvoljava non-constant initializers za globalne varijable. Kada C + + compiler naiđe na takav izraz on upakuje to u funkciju i organizuje da se ta funkcija pozove kada se binary učita. Jer initialize kod brzo radi i to može biti pomalo opasno za korišćenje, jer drugi kodovi kao NSArray možda nisu još uvek spremni. U svakom slučaju, ako ste videli non-constant initializer compile i pitate se zašto, verovatno je sastavljeno kao C + +.

NSString literals su takođe compile-time constants, zbog povezanost između compilet i biblioteke. Postoji poseban NSString subclass koji se zove NSConstantString sa fiksnom ivar layout:

    @interface NSSimpleCString : NSString {
    @package
        char *bytes;
        int numBytes;
    #if __LP64__
        int _unused;
    #endif
    }
    @end

    @interface NSConstantString : NSSimpleCString
    @end

Ono samo sadrži isa (nasleđen iz NSObject), pointer za bajte i za dužinu. Kada se takav literal koristi kao globalni variajble initializer, compiler jednostavno ispiše string sadržaj i zatim piše ovu jednostavnu strukturu objekta i konačno pokreće globalnu varijablu sa pointer sa tom strukturom.

Možda ste primetili da ne morate da zadržite i oslobodite NSString literals kao sa drugim objektima (ali je poželjno da to i dalje činiti, čisto iz navike). U stvari, možete ih osloboditi onoliko puta koliko želite i neće ništa raditi. Zato što NSString literals se ne dodeljuju poput većina Objective-C objekata. Umesto toga, oni se dodeljuju u compile time kao deo vašeg binary i traju za ceo život vašeg procesa.

Ova povezanost ima prednosti, kao na primer proizvodnja dozvoljene globalne variajble initializers i ne zahteva dodatan kod da bi vršio objekat u runtime-u. Međutim, postoje i velike mane. NSConstantString layout je postavljen zauvek. Ta klasa mora da se održava upravo sa tim layout podacima, jer layout podaci su fiksirani u hiljade nezavisnih aplikacija. Ako Apple promeni layout, te nezavisne aplikacija bi se slomile, jer sadrže NSConstantString objekte sa starim layout.

Ako suNSArray literals compile-time constants, onda bi bio potreban sličan NSConstantArray klasa sa fiksnim layout koji compiler može da generiše i to bi moralo da se održava odvojeno od drugih NSArray implementacija. Ovakav kod nije mogao da se pokrene na starijim operativnim sistemima koji nisu imali ovu NSConstantArray klasu. Isti problem postoji i kod ostalih klasa koje mogu da proizvedu nove literals.

Ovo je posebno zanimljivo u slučaju NSNumber literals. Lion je uveo označene pointers koje omogućavaju NSNumber sadržaj da se direktno ugradi u pointer, eliminišući potrebu za posebno dinamički dodeljenom objektu. Ukoliko compiler emituje označene pointers, njihov format nikada ne može da se promeni i kompatibilnost sa starim operativnim sistemima bi se izgubili. Ako compiler emituje konstantne NSNumber objekte, onda NSNumber literals bi bili znatno drugačiji od drugih NSNumber.

Umesto toga, compiler jednostavno emituje pozive unutar okvira, izgrađuje objekte na isti način kao kad bi to uradili ručno. Ovo rezultira u runtime hit, ali ne ne bude gore da ste sami gradili bez nove sintakse i čini mnogo čistiji dizajn.

Kompatibilnost
Kada možemo početi da koristimo ovu novu sintaksu? Xcode 4.3.3 je najnovija verzija i još uvek ne uključuje ove dodatke. Razumno možemo očekivati da će sledeće izdanje, verovatno sa Mountain Lion, imati ove promene u svojoj verziji clang.

Za kompatibilnost operativnog sistema, literals jednostavno generišu kod koji poziva standardni Cocoa initializers. Rezultat je neprepoznatljiv kada se uporedi sa pisanjem koda ručno.

Priča vezana za subscripting je malo kompleksnija. Ovo zahteva nove metode koje trenutno ne postoje u Cocoa. Međutim, subscripting metode se direktno mapiraju na postojeći NSArray i NSDictionary metode. Tako da možemo očekivati kompatibilnost shim koji će biti dostupan na sličan način kao ARCLite koji omogućava korišćenje ARC na operativnim sistemima koji ga prethodi.

Zaključak
Novi objekat literals i subscripting sintakse u Objective-C mogu značajno smanjiti verbosity koda koji se najviše bavi arrays i rečnicima. Sintaksa je slična onoj u zajedničkim skript jezicima i čini kod mnogo lakšim za čitanje i pisanje, osim što ima višak @ simbola.

To je to za danas. Vrati se sledeći put na još jedno prijateljsko istraživanje sveta programiranja. Petkom pitanja i odgovroi uvek pokreću čitaoci sa sugestijama, pa sve do tada, ako imate neku temu koju želite da vidite ovde, pošaljite!


Published (Last edited): 20-03-2013 , source: http://www.mikeash.com/pyblog/friday-qa-2012-06-22-objective-c-literals.html