Mål-C | |
---|---|
Sprog klasse | objektorienteret , multiparadigme : reflekterende orienteret |
Dukkede op i | 1983 |
Forfatter | Brad Cox |
Filtypenavn _ | .h, .m, .mmeller.C |
Frigøre |
|
Type system | svag , statisk / dynamisk |
Større implementeringer | Kakao , Cocoa Touch , gcc , LLVM + Clang |
Blev påvirket | Smalltalk , C |
påvirket | Java , Objective-J , Swift |
Internet side | developer.apple.com/libr... |
Mediefiler på Wikimedia Commons |
Objective-C er et kompileret objektorienteret programmeringssprog, der bruges af Apple Corporation , bygget oven på C -sproget og Smalltalk - paradigmerne . Især objektmodellen er bygget i Smalltalk -stilen - det vil sige, at beskeder sendes til objekter .
Objective-C-sproget er et supersæt af C -sproget , så C-koden er fuldt forståelig af Objective-C-kompileren.
Objective-C-kompileren er inkluderet i GCC og er tilgængelig på de fleste større platforme. Sproget bruges primært til Mac OS X ( Cocoa ) og GNUstep , implementeringer af den OpenStep objektorienterede grænseflade . Også sproget bruges til iOS ( Cocoa Touch ).
I begyndelsen af 1980'erne var struktureret programmering populært , hvilket tillod en algoritme at blive opdelt i små blokke. Men med stigende kompleksitet af opgaver førte struktureret programmering til et fald i kodens kvalitet. Vi skulle skrive flere og flere funktioner, som meget sjældent kunne bruges i andre programmer.
Mange programmører så objektorienteret programmering som en potentiel løsning på deres problem. På den ene side blev Smalltalk brugt af næsten alle mere eller mindre komplekse systemer. På den anden side øgede brugen af virtuelle maskiner ressourcekravene.
Objective-C blev skabt af Brad Cox i begyndelsen af 1980'erne hos hans firma Stepstone . Han forsøgte at løse problemet med genbrug af kode.
Cox' mål var at skabe et sprog, der understøtter konceptet software IC, hvilket indebærer evnen til at samle programmer fra færdige komponenter (objekter), ligesom komplekse elektroniske enheder kan samles fra et sæt færdige integrerede kredsløb .
Samtidig skal sproget være enkelt og baseret på C-sproget for at lette overgangen for udviklere til det.
Et af målene var også at skabe en model, hvor klasserne selv er fuldgyldige objekter, introspektion og dynamisk meddelelsesbehandling ville blive understøttet.
Objective-C er en udvidelse af C: ethvert C-program er et Objective-C-program.
Et af kendetegnene ved Objective-C er, at det er dynamisk: beslutninger, der normalt tages på kompileringstidspunktet, udskydes til runtime.
Objective-C er et meddelelsesorienteret sprog, mens C++ er funktionsorienteret: i Objective-C tolkes metodekald ikke som et funktionskald (selvom det normalt er det, det kommer ned til), men som et budskab (med en navn og argumenter) objekt, ligesom i Smalltalk.
Ethvert objekt kan sendes til enhver besked. Et objekt kan, i stedet for at behandle en besked, videresende det til et andet objekt til behandling (delegering), især på denne måde kan du implementere distribuerede (det vil sige placeret i forskellige adresserum og endda på forskellige computere) objekter.
Binding af en meddelelse til den tilsvarende funktion sker under kørsel.
Objective-C-sproget understøtter at arbejde med meta -information - for eksempel ved kørsel kan du finde ud af klassen for et objekt, listen over dets metoder (med de typer af argumenter, der er overført) og instansvariabler, kontrollere om klassen er en efterkommer af den givne og om den understøtter den givne protokol osv. .
Sproget har understøttelse af protokoller (begreberne objektgrænseflade og protokol er tydeligt adskilt). Nedarvning er understøttet (ikke flere); protokoller understøtter multipel nedarvning. Et objekt kan nedarves fra et andet objekt og flere protokoller på én gang (selvom dette mere sandsynligt ikke er protokolarv, men dets understøttelse).
Objective-C understøttes i øjeblikket af Clang- og GCC-kompilatorerne (på Windows bruges det som en del af MinGW eller cygwin ).
Nogle sprogfunktioner er blevet flyttet til runtime- biblioteket og er stærkt afhængige af det. Gcc-kompileren kommer med en minimal version af et sådant bibliotek. Du kan også gratis downloade Apples runtime-bibliotek: Apples Objective-C runtime.
Disse to runtime-biblioteker ligner hinanden (den største forskel er i metodenavnene). Yderligere eksempler vil fokusere på Apples runtime-bibliotek.
Objective-C-sproget bruger en speciel type-id til at udpege objekter (dette er analogt med objekttypen i Java ). En variabel af typen id er faktisk en pointer til et vilkårligt objekt. Konstanten nul (= NULL) bruges til at angive en nul-markør til et objekt.
I dette tilfælde kan du i stedet for id bruge en mere velkendt betegnelse med en eksplicit angivelse af klassen. Sidstnævnte giver især compileren mulighed for at udføre en vis verifikation af meddelelsesunderstøttelse af objekter - hvis compileren ikke kan udlede af typen af en variabel, at et objekt understøtter en given meddelelse, vil den udstede en advarsel.
Sproget understøtter således typekontrol, men i en ikke-streng form (det vil sige, fundne uoverensstemmelser returneres som advarsler, ikke fejl).
Følgende syntaks bruges til at sende beskeder:
[ modtagerbesked ] ;I denne konstruktion er modtageren en pointer til et objekt, og meddelelsen er et metodenavn.
I modsætning til C++ er det en lovlig handling at sende en besked til nul, der altid returnerer nul.
Meddelelsen kan også indeholde parametre:
[ myRect setOrigin : 30.0 : 50.0 ];I dette eksempel er metoden (meddelelses) navn setOrigin::. Bemærk, at hvert argument, der sendes, matches af nøjagtigt et kolon. I dette eksempel har det første argument en etiket (teksten før kolon), men det andet har ikke.
Objective-C-sproget giver dig mulighed for at mærke hvert argument, hvilket i høj grad forbedrer kodens læsbarhed og reducerer sandsynligheden for at sende den forkerte parameter. Dette er den stil, de fleste udviklere bruger.
[ myRect setWidth : 10,0 højde : 20,0 ];I dette eksempel er meddelelsesnavnet setWidth: height:.
Det understøtter også muligheden for at sende et vilkårligt antal argumenter i meddelelsen:
[ myObject makeGroup : obj1 , obj2 , obj3 , obj4 , intet ];Ligesom funktioner kan meddelelser returnere værdier, og i modsætning til i C er standardreturtypen id.
float area = [ myRect area ];Resultatet af én besked kan straks bruges i en anden besked:
[ myRect setColor :[ otherRect color ]];Som nævnt tidligere, i Objective-C, er klasser i sig selv objekter. Hovedopgaven for sådanne objekter (kaldet klasseobjekter) er at skabe forekomster af en given klasse (fabriksmetodemønster ) [ 2] .
I dette tilfælde spiller selve klassenavnet en dobbeltrolle - på den ene side fungerer det som en datatype (det vil sige, at det kan bruges til at beskrive pointere til objekter i denne klasse). På den anden side kan klassenavnet fungere som det objekt, som beskeden sendes til (i beskeder kan klassenavnet kun deltage som modtager).
Rect * myRect = [[ Rect alloc ] init ];Objective-C har ikke en indbygget type for booleske værdier, så denne type introduceres normalt kunstigt. Yderligere, for logiske værdier, vil BOOL-typen blive brugt med mulige værdier af YES og NO (som det gøres i NextStep, Mac OS X-operativsystemer).
Den første seriøse brug af Objective-C-sproget var dets brug i NextStep-operativsystemet. Et stort antal forskellige Objective-C-klasser blev skrevet til dette system, hvoraf mange stadig bruges i Mac OS X.
Navnene på alle disse klasser begynder med NS-præfikset, hvilket indikerer, at de tilhører NextStep-operativsystemet. Nu er de inkluderet i Foundation-biblioteket, som applikationer til OS X og iOS er bygget på.
Med en af dem - NSString - vil vi støde på i denne artikel. Denne klasse bruges til at arbejde med strenge (i dette tilfælde bruges Unicode som den interne repræsentation af tegn).
Compileren understøtter denne type ved automatisk at oversætte konstruktioner som @"min streng" til en pointer til et NSString-klasseobjekt, der indeholder den givne streng (mere præcist, dens underklasse svarer til konstante strenge).
EjendommeAntag, at der er et instansvariabelnavn i klassen Company.
@interface Firma : NSObject { NString * navn ; }For at få adgang til det udefra er det bedst at bruge egenskaberne , der dukkede op i Objective-C 2.0. Nøgleordet @property bruges til at erklære egenskaber.
@property ( retain ) NSString * navn ;Parenteser opregner adgangsattributter til en instansvariabel. Attributter er opdelt i 3 hovedgrupper.
Accessor- og mutatornavne
Læse-/skrivegrænse
Disse egenskaber udelukker hinanden. Og den sidste gruppe er mutatoregenskaber .
Når du arbejder under GC , er der ingen forskel på at bruge tildele, beholde, kopiere. For at generere kode til ejendomme, i overensstemmelse med den måde, de er beskrevet i deklarationen, kan du bruge kode autogenerering:
@synthesize navn ;Autogenereret kode er ikke altid en god løsning, og du skal muligvis manuelt oprette instansvariable accessorer.
Sproget bliver ofte kritiseret for dets overbelastede syntaks sammenlignet med andre sprog. Det bemærkes dog ofte, at det er højere læsbarhed.
Alle Objective-C nøgleord, der ikke findes i C, begynder med @-symbolet.
Som i C++ er beskrivelsen af en klasse og dens implementering adskilt (normalt placeres beskrivelsen i header-filer med en h-udvidelse, og implementeringerne placeres i filer med en m-udvidelse).
Følgende er den generelle struktur for den nye klasseerklæring:
@interface ClassName : SuperClass { instansvariableerklæringer _ _ } metodedeklarationer _ @endeI Apples runtime-version deler alle klasser en fælles forfader, NSObject-klassen, som indeholder en række vigtige metoder.
Deklarationen af variabler adskiller sig ikke fra deklarationen af variabler i strukturer på C-sproget:
Hvis du ikke bruger Apple, har du højst sandsynligt brug for Object (#import <objc/Object.h>) i stedet for NSObject.
@interface Rect : NSObject { flyde -bredde ; flydehøjde ; _ BOOL er fyldt ; NSColor * farve ; } @endeBeskrivelserne af metoderne adskiller sig markant fra dem, der accepteres i C++ og minder meget om beskrivelserne af metoder i Smalltalk-sproget.
Hver beskrivelse starter med et plus- eller minustegn. Plustegnet angiver, at denne metode er en klassemetode (det vil sige, at den kun kan sendes til klasseobjekter, ikke til forekomster af denne klasse). Faktisk er klassemetoder analoge med statiske metoder i klasser i C++-sproget.
Minustegnet bruges til at udpege metoder til objekter - forekomster af denne klasse. Bemærk, at i Objective-C er alle metoder virtuelle , hvilket betyder, at de kan tilsidesættes.
Det følgende er beskrivelser af de mulige metoder for Rect-klassen.
@interface Rect : NSObject { flyde x , y ; flyde -bredde ; flydehøjde ; _ BOOL er fyldt ; NSColor * farve ; } + newRect ; - ( ugyldig ) visning ; - ( flyde ) bredde ; - ( flyde ) højde ; - ( flyde ) område ; - ( void ) setWidth: ( float ) theWidth ; - ( void ) setHeight: ( float ) theHeight ; - ( void ) setX: ( float ) theX y: ( float ) theY ; @endeBemærk, at navnet på metoden kan være det samme som navnet på en instansvariabel af denne klasse (f.eks. bredde og højde).
En metodes returtype er angivet i parentes umiddelbart efter plus- eller minustegnet (men før metodenavnet). Hvis typen ikke er angivet, anses det for, at en værdi af type id returneres.
Dernæst kommer navnet på metoden, hvor der efter hvert kolon er angivet typen af argumentet (i parentes) og selve argumentet.
Objective-C-sproget giver dig også mulighed for at specificere en af følgende deskriptorer for metodeargumenter - oneway, in, out, inout, bycopy og byref. Disse deskriptorer bruges til at specificere retningen for dataoverførsel og overførselsmetoden. Deres tilstedeværelse forenkler implementeringen og arbejdet med distribuerede objekter betydeligt (som blev implementeret i NextStep-operativsystemet i begyndelsen af 90'erne af forrige århundrede).
En metode, der tager et vilkårligt antal parametre, kan beskrives som følger:
- makeGroup: ( id ) objekt , ...;For at inkludere en header-fil i Objective-C, i stedet for #include - direktivet , bruges #import-direktivet, svarende til #include, men garanterer, at denne fil kun vil blive inkluderet én gang.
I nogle tilfælde bliver det nødvendigt at erklære, at et givet navn er navnet på en klasse, men uden at det udtrykkeligt beskrives (et sådant behov opstår, når man beskriver to klasser, som hver refererer til en anden klasse).
I dette tilfælde kan du bruge @class-direktivet, som erklærer, at de efterfølgende navne er klassenavne.
@class Shape , Rect , Oval ;Implementeringen af klassemetoderne ser således ud:
#import "Klassenavn.h" @implementationClassName _ metodeimplementeringer _ @endeDet følgende er et eksempel på implementering af metoderne i Rect-klassen beskrevet ovenfor.
#import "Rect.h" @implementering Ret + newRect { Rect * rect = [[ Rect alloc ] init ]; [ ret sætBredde : 1.0f ] ; [ ret sætHøjde : 1,0f ] ; [ rekt sætX : 0.0f y : 0.0f ]; returnere rect ; } - ( flyde ) bredde { retur bredde ; } - ( flyde ) højde { returhøjde ; _ } - ( flyde ) område { return [ selv bredde ] * [ selv højde ]; } - ( void ) setWidth: ( float ) theWidth { width = theWidth ; } - ( void ) setHeight: ( float ) theHeight { højde = højden ; } - ( void ) setX: ( float ) theX y: ( float ) theY { x = X ; y = Y ; } @endeSom du kan se fra eksemplet ovenfor, er alle instansvariabler tilgængelige i metoderne. Men som i C++ er det muligt at kontrollere synligheden af variabler (synligheden af metoder kan ikke kontrolleres) ved hjælp af @private, @protected og @public direktiverne (som fungerer nøjagtigt som C++ sproget).
@interface Worker : NSObject { char * navn ; @privat int alder ; char * evaluering ; @beskyttet int job ; flydende løn ; @offentlig id chef }Samtidig kan offentlige klassevariabler tilgås direkte ved hjælp af -> operatoren (for eksempel objPtr -> fieldName).
Compileren oversætter hver meddelelse, der sendes, det vil sige en konstruktion som [object msg] til et kald til objc_msgSend-funktionen. Denne funktion tager som sin første parameter en pointer til modtagerobjektet for meddelelsen, og som sin anden parameter den såkaldte. en vælger, der bruges til at identificere den besked, der sendes. Hvis der er argumenter i meddelelsen, sendes de også til objc_msgSend som den tredje, fjerde osv. parametre.
Hvert Objective-C-objekt indeholder en isa-attribut, som er en pegepind til klasseobjektet for det pågældende objekt. klasseobjekt oprettes automatisk af compileren og eksisterer som en enkelt instans, som refereres af alle instanser af den givne klasse gennem isa.
Hvert klasseobjekt indeholder nødvendigvis en pointer til et klasseobjekt for den overordnede klasse (superklasse) og afsendelsestabel. Sidstnævnte er en ordbog, der matcher meddelelsesvælgere med de faktiske adresser på de metoder (funktioner), der implementerer dem.
Således leder funktionen objc_msgSend efter en metode med den givne vælger i afsendelsestabellen for det givne objekt. Hvis den ikke er der, fortsætter søgningen i afsendelsestabellen for dens overordnede klasse og så videre.
Hvis metoden (det vil sige funktionen svarende til den) findes, kaldes den med overførsel af alle nødvendige argumenter.
Ellers får objektet en sidste chance for at behandle meddelelsen, før der kastes en undtagelse - meddelelsesvælgeren er sammen med parametrene pakket ind i et særligt objekt af typen NSInvocation, og forwardInvocation: meddelelsen sendes til objektet, hvor objektet for NSInvocation-klassen fungerer som en parameter.
Hvis et objekt understøtter forwardInvocation:, så kan det enten behandle den besked, det sender selv, eller videresende det til et andet objekt til behandling:
- ( void ) forwardInvocation: ( NSInvocation * ) anInvocation { if ( [ someOtherObject respondsToSelector : [ anInvocation selector ] ] ) [ anInvocation invokeWithTarget : someOtherObject ]; andet .......... }For at fremskynde søgningen efter beskeder i afsendelsestabellen bruges caching, hvilket kan reducere omkostningerne ved at sende beskeder markant. Det gør det også nemmere at slå en metode op på tværs af tabeller ved at bruge såkaldte selectors i stedet for de sædvanlige navne. Typisk er en vælger en 32-bit værdi, der entydigt identificerer en metode.
Vælgertypen er betegnet som SEL, og der er en række funktioner og konstruktioner, der giver dig mulighed for at konvertere et navn til en vælger og omvendt.
Så for at få beskedvælgeren direkte efter navn, brug @selector()-konstruktionen:
SEL setWidth = @vælger ( setWidth :); SEL setPos = @vælger ( setPosition : y :);Funktionerne NSSelectorFromString og NSStringFromSelector bruges til at opnå en vælger ved hjælp af en tegnstreng (ved kørsel) og konvertere vælgeren til en streng:
SEL setWidth = NSSelectorFromString ( @"setWidth:" ); NSString * methodName = NSStringFromSelector ( setPos );Kraftig metainformationsunderstøttelse i Objective-C giver dig mulighed for at kontrollere under kørsel, om et objekt understøtter en metode med en given vælger ved at sende den en respondsToSelector: besked:
if ( [ anObject respondsToSelector : @selector ( setWidth :) ] ) [ anObject setWidth : 200.0 ];Det er ret nemt at sende en besked svarende til en given vælger (ingen argumenter, et, to eller tre argumenter) ved hjælp af performSelector:, performSelector: withObject:, performSelector: withObject: withObject:, performSelector: withObject: withObject: withObject: metoden , og så videre. Yderligere.
[ myObject performSelector : sel withObject : nil ];Bemærk, at performSelector:-metoderne altid returnerer en værdi af typen id.
Du kan få klassen for et givet objekt ved at sende den en klassemeddelelse. Denne meddelelse returnerer klassen som en pointer til et objekt af typen Klasse.
Klasse * cls = [ anObjektklasse ] ; NSString * clsName = NSStringFromClass ( cls );På den anden side kan du også nemt få det tilsvarende klasseobjekt efter klassenavn:
Klasse * cls = NSClassFromString ( clsName );Hver metode er faktisk en funktion med to usynlige argumenter - self og _cmd.
Den første er analog med dette, det vil sige, at den peger på selve objektet - modtageren af beskeden. Den anden indeholder vælgeren for denne metode.
Selvargumentet kan bruges til at sende beskeder til sig selv, som i følgende metode:
- ( flyde ) område { return [ selv bredde ] * [ selv højde ]; }Men udover sig selv er der en værdi mere, som beskeder kan sendes til - super. Faktisk er super ikke en normal variabel - det er bare endnu en notation for en pointer til det aktuelle objekt. Men når en supermeddelelse sendes, starter metodeopslaget ikke fra afsendelsestabellen for det aktuelle objekt, men fra afsendelsestabellen for det overordnede objekt.
Ved at sende beskeder til super kalder vi derved de gamle versioner af metoderne, der er tilsidesat af denne klasse.
I Objective-C-sproget kan du få adressen på den funktion, der implementerer det, ved hjælp af en metodevælger (præcis som en C-sprogfunktion).
En sådan funktion adskiller sig kun fra beskrivelsen af metoden ved at indsætte to yderligere parametre i begyndelsen af argumentlisten - en pointer til selve objektet (selv) og en vælger af denne metode (_cmd).
Ved at sende meddelelsen methodForSelector: til objektet, modtager vi som svar adressen på den funktion, der implementerer denne metode.
typedef float ( * WidthFunc )( id , SEL ); typedef void ( * SetWidthFunc )( id , SEL , float ); WidthFunc widthFunc = ( WidthFunc ) [ myRect methodForSelector : @selector ( width )]; SetWidthFunc setWidthFunc = ( SetWidthFunc ) [ myRect methodForSelector : @selector ( setWidth :)]; ( * setWidthFunc )( myRect , @selector ( setWidth :), 27.5f );Dette giver mulighed for, om nødvendigt, at gentagne gange kalde den samme metode på et givent objekt, helt undgå alle omkostninger forbundet med videresendelse af meddelelser.
Objective-C-sproget indeholder fuld understøttelse af protokoller (det er analogt med en grænseflade i Java og en abstrakt klasse i C++, som også nogle gange kaldes en grænseflade). En protokol er simpelthen en liste over metodeerklæringer. Et objekt implementerer en protokol, hvis det indeholder implementeringer af alle metoderne beskrevet i protokollen.
Protokoller er praktiske, fordi de giver dig mulighed for at fremhæve fælles træk i heterogene objekter og overføre information om objekter fra tidligere ukendte klasser.
Den enkleste beskrivelse af protokollen er som følger:
@protocol ProtocolName metodeerklæringer _ @endeSå den serialiserede protokol kan beskrives som følger:
@protocol Serializable - ( id ) initWithCoder: ( NSCoder * ) koder ; - ( void ) encodeWithCoder: ( NSCoder * ) coder ; @endeEn protokol kan nedarves fra et vilkårligt antal andre protokoller:
@protocol MyProto < Protocol1 , Protocol2 , Serializable , Drawable >På samme måde kan du, når du beskriver en klasse, angive ikke kun den overordnede klasse, men også et sæt protokoller:
@interface MyClass : SuperClass < Protocol1 , Protocol2 , Serializable , Drawable >For at kontrollere under kørsel, om et objekt understøtter en given objektprotokol, kan du bruge meddelelsen conformsToProtocol::
if ( [ myObject conformsToProtocol : @protocol ( Serialiserbar ) ] ) [ myObject encodeWithCoder : myCoder ];Desuden kan navnet på protokollerne bruges, når variabler deklareres for eksplicit at indikere over for compileren, at de tilsvarende objekter understøtter protokollerne.
Så hvis variablen myObject indeholder en pointer til et objekt af en hidtil ukendt klasse, men samtidig opfylder protokollerne Serializable og Drawable, så kan den beskrives som følger:
id < Serializable , Drawable > myObject ;På samme måde, hvis det er kendt på forhånd, at myObject vil indeholde en pointer til et objekt, der arver fra Shape-klassen og understøtter Serializable-protokollen, så kan denne variabel erklæres som følger:
Shape < Serializable > * myObject ;Bemærk, at denne beskrivelse kun tjener til at fortælle compileren, hvilke meddelelser dette objekt understøtter.
Ligesom klasser er alle protokoller i Objective-C repræsenteret ved hjælp af objekter (Protocol-klassen):
Protokol * myProto = @protokol ( Serialiserbar );Du kan bruge følgende konstruktion til at forhåndsannoncere protokoller:
@protocol MyProto , Serializable , Drawable ;Denne konstruktion fortæller compileren, at MyProto, Serializable og Drawable er protokolnavne, der vil blive defineret senere.
Objective-C understøtter undtagelseshåndtering meget lig C++ og Java.
Dette gøres ved at bruge @try, @catch, @finally og @throw-direktiverne.
Kop * kop = [[ Cup alloc ] init ]; @prøve { [ kopfyldning ] ; } @catch ( NSException * exc ) { NSLog ( @"Undtagelse fanget:%@" , exc ); } @fangst ( id exc .) { NSLog ( @"Ukendt undtagelse fanget" ); } @langt om længe { [ kopudløser ] ; }For at kaste en undtagelse bruges @throw- direktivet , der tager som argument en pointer til undtagelsesobjektet. Typisk bruger Mac OS X/NextStep objekter af NSException-klassen til dette formål.
NSException * exc = [ NSException exceptionWithName : @"min-undtagelse" årsag : @"ukendt-fejl" brugerinfo : nul ]; @kast exc ;Inden for @catch-blokke kan @throw-direktivet bruges uden en parameter til at genkaste en genkast-undtagelse.
Objective-C-sproget understøtter synkronisering til flertrådede applikationer. Ved at bruge @synchronized ()-direktivet kan du beskytte et stykke kode mod at blive eksekveret samtidigt af flere tråde på én gang .
@synchronized() tager som input en pointer til et Objective-C sprogobjekt (du kan bruge et hvilket som helst objekt til dette formål, inklusive selv), som spiller rollen som en mutex .
Når en tråd forsøger at begynde at udføre et beskyttet fragment, tjekker den, om fragmentet allerede udføres af en tråd. Hvis ja, så sammenlignes de objekter, der sendes af disse tråde til @synchronized().
Hvis disse pointere matcher, vil en tråd, der forsøger at komme ind i en beskyttet blok, blive suspenderet, indtil den første tråd forlader blokken. Så vil udførelsen af den anden tråd fortsætte, og den vil allerede "forbyde" denne blokering for alle andre tråde.
Tilstedeværelsen af en sådan mulighed gør livet meget lettere, når du skriver flertrådede applikationer, når det er nødvendigt at spore forsøg på at ændre de samme data samtidigt med flere tråde på én gang.
- ( ugyldig ) kritisk metode { @synkroniseret ( selv ) { // udføre ændringer af delte objekter . . . } }Det anbefales at angive et eksternt utilgængeligt objekt som en mutex (det vil sige en parameter for @synchronized instruktionen), da dette kan føre til en deadlock , hvis det samme objekt bruges som en mutex af to indbyrdes afhængige tråde. Især @synchronized(self) er forældet.
Der er ingen specielle kommandoer til at oprette og ødelægge objekter (som ny og slet) i selve Objective-C-sproget. Denne opgave falder på runtime-biblioteket og implementeres ved hjælp af mekanismen til afsendelse af meddelelser.
Det faktisk brugte og mest udbredte skema til at skabe og ødelægge objekter i Objective-C er det, der bruges i NextStep og Mac OS X operativsystemer, som vil blive beskrevet nedenfor.
Oprettelsen af et nyt objekt er opdelt i to trin - hukommelsesallokering og objektinitialisering. Det første trin implementeres af alloc-klassemetoden (implementeret i NSObject-klassen), som allokerer den nødvendige mængde hukommelse (denne metode bruges til at allokere hukommelse ikke kun til objekter i NSObject-klassen, men også for enhver klasse, der er arvet fra den ). Samtidig skrives en pointer til klasseobjektet for den tilsvarende klasse til isa-attributten.
Bemærk, at alloc-meddelelsen sendes til klasseobjektet for den påkrævede klasse, og denne besked returnerer en pointer til objektets allokerede hukommelse.
Faktisk udføres initialiseringen af selve objektet (det vil sige indstilling af værdierne for dets instansvariabler, allokering af yderligere ressourcer osv.) udføres af andre metoder, traditionelt begynder navnene på disse metoder med init. Typisk sendes en sådan besked umiddelbart efter allokeringsmeddelelsen til den adresse, der returneres af denne besked.
id anObject = [[ Rektangelallokering ] init ] ;Ovenstående konstruktion er den korrekte måde at skabe et objekt på. Bemærk venligst, at følgende konstruktion muligvis ikke fungerer i nogle tilfælde:
id anObject = [ Rektangelallokering ] ; [ anObject init ];Dette skyldes, at for en række klasser kan init-metoden returnere en helt anden pointer (i stedet for selv).
De enkleste eksempler på, hvornår denne situation kan opstå, er singletons (så, hvis en instans af klassen allerede eksisterer, så vil init-metoden frigøre hukommelsen allokeret af alloc og returnere en pointer til den enkelte instans, der allerede er oprettet) og objektcache, når allokering af objekter for at øge ydeevnen sker med det samme i blokke, og objekter ødelægges ikke, men gemmes til genbrug.
Når du opretter en ny klasse, er der normalt ikke behov for at tilsidesætte alloc-metoden, men behovet for at tilsidesætte init-metoden opstår ret ofte.
Bemærk, at init-metoden(e) bare er en almindelig metode, ikke noget særligt (i modsætning til i C++, hvor en konstruktør er en speciel metode, der f.eks. ikke kan gives en adresse).
Derfor, når du opretter en ny klasse og init-metode, skal opkaldet til den overstyrede init-metode (ved hjælp af [super init]) foretages eksplicit i begyndelsen af metoden.
Ganske ofte har objekter flere metoder, der starter med init, såsom init, initWithName:, initWithContentsOfFile: osv.
Den etablerede praksis i dette tilfælde er at udskille én blandt alle init-metoder, kaldet designated initializer. Alle andre init-metoder skal kalde det, og kun det kalder den nedarvede init-metode.
- ( id ) initWithName: ( const char * ) theName // designated initializer { self = [ super init ]; // kalder arvet metode hvis ( selv ) { navn = strdup ( navnet ); } returnere selv ; } - ( id ) init { return [ self initWithName : "" ]; }I nogle tilfælde viser det sig at være praktisk at kombinere hukommelsesallokering og objektinitialisering i én (klasse) metode, for eksempel har NSString-klassen en række klassemetoder, der returnerer et allerede forberedt (initialiseret) objekt:
+ ( id ) stringWithCString: ( const char * ) cString- kodning: ( NSStringEncoding ) enc + ( id ) stringWithFormat: ( NSString * ) format , ...Mac OS X (som NextStep) bruger referencetælling til at styre objekternes levetid - hvert objekt indeholder en bestemt tæller inde i det, som er indstillet til én, når det oprettes.
Sending af en bevaringsmeddelelse til et objekt øger denne tæller med én (for eksempel sender alle Foundation-bibliotekets containerklasser et objekt en bevaringsmeddelelse, når et objekt placeres i dem).
Den etablerede praksis er at sende en retain-meddelelse til et objekt af alle parter (objekter), der er interesserede i det, det vil sige, hvis du husker en reference til et objekt, så skal du sende det en retain-meddelelse.
Når et objekt ikke længere er nødvendigt, sendes der blot en frigivelsesmeddelelse til det.
Denne meddelelse formindsker tællerværdien med én, og hvis denne værdi er mindre end én, ødelægger den det givne objekt.
Inden et objekt ødelægges, sendes der en dealloc-meddelelse til det, som gør det muligt for objektet at udføre sin deinitialisering. Dette er dog også en normal besked, og i den skal du udtrykkeligt kalde den gamle implementering via [super dealloc] til sidst.
- ( ugyldigt ) dealloc { . . . [ super dealloc ]; }Hukommelsesstyring i Objective-C er baseret på princippet om "objektejerskab". De grundlæggende regler for hukommelseshåndtering i Objective-C kan skrives således:
Disse regler er baseret på Objective-C navngivningskonventionen og er samtidig grundlaget for denne konvention.
Antag, at der er et klassefirma i programmet, der har en arbejdermetode.
@interface Firma : NSObject { NSArray * arbejdere ; } -( NSArray * ) arbejdere ; @endeOvervej et lille eksempel på brug af sådan en klasse:
Company * company = [[ Company alloc ] init ]; // ... NSArray * workers = [ virksomhedsarbejdere ] ; // ... [ virksomhedsmeddelelse ] ;Da firmaobjektet er oprettet eksplicit, skal det bortskaffes, når det ikke længere bruges ([virksomhedsfrigivelse]). Samtidig siger navnet på arbejdermetoden ikke, hvem der skal slette arrayet. I en sådan situation anses det for, at listen over medarbejdere administreres af virksomhedens objekt, og det er ikke påkrævet at slette den.
Convenience konstruktørerMange klasser giver dig mulighed for at kombinere oprettelsen af et objekt med dets initialisering ved hjælp af metoder kaldet bekvemmelighedskonstruktører; sådanne metoder kaldes normalt +klassenavn... Du kan antage, at den, der ringer, er ansvarlig for at styre objektets levetid, men en sådan adfærd ville være imod Objective-C-navnekonventionen.
Firma * firma = [ Firmavirksomhed ] ; [ virksomhedsmeddelelse ] ;I ovenstående kode er [firmafrigivelse]-kaldet ikke tilladt , da objektets levetid i dette tilfælde skal administreres ved hjælp af autorelease-puljen.
Følgende er et eksempel på en korrekt implementering af virksomhedsmetoden:
+( Firma * ) firma { id ret = [[ Company alloc ] init ]; return [ ret autorelease ]; } autoreleaseLad os vende tilbage til arbejdermetoden i firmaklassen. Da returneringen er et array, hvis levetid ikke kontrolleres af den, der ringer, ville implementeringen af arbejdermetoden se nogenlunde sådan ud:
-( NSArray * ) arbejdere { NSArray * copy = [[ NSArray alloc ] initWithArray : arbejdere ] ; return [ kopi autoudgivelse ]; }Kaldet til autorelease føjer kopiobjektet til autorelease-puljen, hvilket får det returnerede objekt til at modtage en frigivelsesmeddelelse, når puljen, det blev føjet til, fjernes. Hvis et objekt føjet til en autofrigivelsespulje sender en frigivelsesmeddelelse alene, vil der opstå en fejl, når autofrigivelsespuljen fjernes.
Returnering af et objekt ved referenceI nogle tilfælde returneres objekter ved reference, f.eks. tager NSData-klassemetoden initWithContentsOfURL:options: error: (NSError **)errorPtr som fejlparameter. I dette tilfælde fungerer navnekonventionen også, hvoraf det følger, at der ikke er nogen eksplicit anmodning om ejerskab af objektet, derfor er det ikke nødvendigt at slette det.
Sletning af objekterNår et objekts referencetælling går til nul, slettes objektet. I dette tilfælde kaldes -(void)dealloc-metoden på objektet. Hvis objektet indeholder data, skal det fjernes i denne funktion.
-( ugyldig ) dealloc { [ frigivelse af arbejdere ]; [ super dealloc ]; }Efter frigivelsesmeddelelsen er blevet sendt til alle klassevariabler, skal basisklassens dealloc-metode kaldes. Dette er det eneste tilfælde, hvor det er acceptabelt at kalde dealloc-metoden direkte.
Der er ingen garantier for, hvornår dealloc-metoden kaldes. I nogle tilfælde kaldes det muligvis slet ikke, når applikationen slutter, for at spare tid, da operativsystemet alligevel frigør den tildelte hukommelse, når applikationen slutter. Derfor bør dealloc-metoden ikke indeholde nogen metoder, der er ansvarlige for lukning af stikkontakter, filer osv.