NOTE: Dette er kapitel 2. Du kan se en lister over alle kapitlerne ved at klikke her.
Sidst kiggede vi en del på Xcode, og vi introducerede også Objective-C. I dette kapitel fortsætter vi med at introducere Objective-C og vil kigge mere på de helt grundlæggende ting som er ret vigtige at forstå helt førend man begiver sig videre med sproget. Jeg vil nu antage at du ved hvordan du opretter et projekt og hvordan du kører det. Det er også vigtig at du lavede alle øvelserne fra sidste kapitel – programmering lærer man kun hvis man rent faktisk udøver det; man lærer det ikke af kun at læse en bog. Hvis du havde svært ved øvelserne forslår jeg at du bruger kommentarfunktionen for at få hjælp.
Indhold
- Erklæring og tildeling
- Navngivning
- Læsevenlige programmer
- Operatorerne + go *
- Kør, ret, kør, ret og vedligehold: Udviklingsfaserne
- Flere erklæringer på samme linje
- Nøgleord
- Funktioner
- Programstruktur
Eklæring og tildeling
Start Xcode, opret et kommandolinjeprogram der hedder Alder (under type vælg Foundation). Lad os starte med at tilrette main. Erstat main med følgende:
int main (int argc, const char * argv[]) { @autoreleasepool { int age; age = 4; NSLog(@"Jeg er %d år gammel", age); } return 0; }
Før main, som vi lige har ændrede, har vi nogle kommentarer:
// // main.m // Alder // // Created by Jacob Rohde on 05/02/12. // Copyright (c) 2012 __MyCompanyName__. All rights reserved. //
Vi kiggede på kommentarer i kapitel 1, så der skulle ikke være noget nyt her. Hvis du ønsker kan du rette i kommentarene så de bedre beskriver programmets formål. Jeg har for eksempel rettet dem til:
// // Prints my age to the console // by Jacob Rohde. //
Af vane skriver jeg altid mine kommentarer på engelsk. Faktisk bruger jeg altid engelske ord i min kode når jeg programmerer, men det behøver du selvfølgelig ikke. Som vi også så i kapitel 1, finders der også flerlinje kommentarer. Så jeg kunne også havde skrevet mine kommentarer som her:
/*
* Prints my age to the console
* by Jacob Rohde.
*/Hvad man vælger er stort set et spørgsmål om smag.
Efter vores kommentarer har vi følgende linje:
#import <Foundation/Foundation.h>Denne linje genkender vi også fra kapitel 1. Det er et importdirektiv, som sørger for at vi har adgang til alt hvad Foundation kan byde på.
Hvis du er lidt eventyrlysten så hold musen over linjen med import, og hold cmd knappen nede (den har også et ⌘ symbol på tasten). Linjen bliver nu blå med en streg under. Hvis du klikker på linjen navigerer du nu til filen Foundation.h og kan se hvad den indeholder. Som du kan se er Foundation.h blot en lang række importdirektiver der importerer headerfilerne for de filer der udgør Foundation-kodebiblioteket.
Lad os klikke tilbage ved at bruge tilbage-pilen øverst til venstre i editoren:
Ok – nu er vi så tilbage i main.m filen. De næste to linjer af vores kode er:
int main (int argc, const char * argv[]) {
Dem kender vi også. Først har vi funktionen mains funktionshoved som fortæller os at den returnerer en int (altså et heltal), at den hedder main, og tager to argumenter: en int, og en tabel af const char * (hvis alt det med en tabel og const char * virker forvirrende, så tænk ikke over det; vi kommer til det i et senere kapitel) . Tubog-klammen på næste linje angiver begyndelsen på funktionens kodeblok.
Derefter har vi:
@autoreleasepool {Som nævnt i kapitel 1 er vi her inde på noget med hukommelsesstyring, og det vil vi ikke komme ind på før meget senere. Så glem det indtil da – bare sørg for at du har kopieret det i din egen main. Vi ved dog at Tuborg-klammen angiver starten på en kodeblok.
Nu kommer vi endelig til noget nyt:
int age;Her definerer vi en variabel. Variablen har vi givet navnet age og er af typen int - dvs. heltal så vi kan bruge variablen til at gemme tal såsom 3 og 7, men ikke kommatal. Vi har adgang til denne variabel via navnet age fra stedet hvor vi erklærer den til slutningen af den omgivende kodeblok. Sådan en linje kalder man også for en variabelerklæring, og den bruger oversætteren til at gøre den påkrævede plads i hukommelesen klar.
int er et nøgleord, hvilket er en række af ord som der er reserveret af C og Objective-C. Vi kan ikke anvende nøgleord til andre formål. Så fx. kunne vi ikke erklære en int variabel der også hedder int, med andre ord er følgende ulovlig syntaks:
int int;
I vores erklæring er age det navn vi har givet vores variabel. Dette kalder man en identifikator. Der er regler for udformningen af identifkatorer, det kommer vi til om lidt.
Men hvad er en variabel? En variabel er blot et stykke hukommelse som vi kan anvende via et navn. I vores variabelerklæring beder vi altså oversætteren om at klargøre et stykke hukommelse til os på en sådan måde at den kan indeholde et heltal, og så vi kan tilgå den via navnet age. En variabel indeholder typisk forskellige værdier i løbet af et programs udførsel, med andre ord kan dets værdi variere – det er derfor de hedder variabler.
Bemærk også at vi som altid afslutter vores udsagn med semikolon.
Lad os kigge på næste linje:
age = 4;
Når vi erklærer en variabel kan vi på ingen måde være sikker på hvad den indeholder. Den kan muligvis indeholde 0 men den kan ligeså godt bare indeholde noget skrald, hvad der nu end var i hukommelsen på det tidspunkt. Når man erklærer en variabel er det næste derfor altid at give den en meningsfuld værdi. Det er det vi gør på denne linje. Det kaldes en tildeling, fordi vi tildeler variablen age en værdi. Vi tildeler her age værdien 4. I C og Objective-C anvendes et lighedstegn til tildeling. Når oversætteren ser denne linje vil den generere et stykke maskinkode som ved udførsel vil putte værdien 4 i den del af hukommelsen som age henviser til. Som du kan se har vi here at gøre med en meget ung programmør.
Efter vores tildeling, har vi et NSLog-kald:
NSLog(@"Jeg er %d år gammel", age);
Som vi så i kapitel 1 er NSLog navnet på en funktion fra Foundation-kodebiblioteket, og i parenteserne har vi angivet vores argumenter til funktionskaldet. Der er dog noget nyt her. I kapitel 1 havde vi kun et argument til NSLog og det var den streng vi ønskede skulle udskrives til konsollen. Nu har vi to argumenter, adskilt med et komma. Første argument er @”Jeg er %d år gammel”, og andet argument er age.
Det første argument kender vi som en Objective-C tekststreng (det ses på @ og gåseøjnene). Når vi har mere end et argument til NSLog kalder man sådanne en tekststreng for en formatstreng, da den fortæller NSLog hvordan den skal formattere outputtet til konsollen (hvis man kun har et enkelt argument til NSLog, som i kapitel 1, bliver strengen bare kopieret til konsollen som output) ved at gøre brug af resten af argumenterne. Sådan en formatstreng kan indeholde format-angivere som alle starter med et %-tegn og de fortæller hvordan de andre argumenter til NSLog skal fortolkes og vises. I vores eksempel har vi brugt %d som fortæller at det andet argument til NSLog er et heltal og skal udskrives som sådan. Eller sagt på en anden måde: % angiver at her skal der står værdien af en variabel, og d fortæller at det skal fortolkes som et heltal (dvs. det er et heltalsargument). Da vi kun har en format-angiver har vi kun et ekstra argument: age. Når vores program kører vil %d derfor blive erstattet med værdien gemt i variablen age.
Resten af vores program er som følger:
} return 0; }
Disse tre linjer behøver stort set ingen forklaring – dem kender vi. Den første klamme afslutter autorelease-kodeblokken. return 0; returnerer 0 til kalderen af main (som er operativsystemet) for at indikere succes, og den sidste klamme afslutter funktionen main.
Lad os nu køre programmet – du skulle gerne se følgende i konsollen:
Vi kan også set outputtet i Log navigator. Klik på log navigator i navigationsområdet:
I venstre side ser vi en liste over alle de gange vi har bygget og kørt vores program. Klik på det øverste debug-resultat for at se outputtet fra vores sidste kørsel:
Som du kan se er dette en del mere overskueligt end at bruge debug-området til at se konsollens output.
Variabler og konstanter
I vores program havde vi slet ikke behøvet at bruge en variabel; vi kunne istedet bare havde skrevet tallet 4 direkte i NSLog. Se følgende:
int main (int argc, const char * argv[]) { @autoreleasepool { NSLog(@"Jeg er %d år gammel", 4); } return 0; }
Hvorfor skulle vi bruge en variabel i stedet for bare et tal direkte i NSLog? Fordi variabler tilføjer mening og hensigt til din kode. Vores program er godt nok så lille at det er helt klart hvad tallet 4 er (nemlig en alder), men et program skal ikke være meget større førend det bliver svært at tyde betydningen af tal der er strøet ud over koden. Sådanne tal kalder man magiske tal eller talkonstanter. Ved at anvende variabler i stedet for talkonstanter bliver betydningen meget mere klar. Hvis vi et sted i koden ser tallet 8 er det uklart hvad det betyder, men hvis vi i stedet ser variabelnavnet priceInDollars ved vi hvad det betyder.
Funktioner og parametre
I en funktion fungerer en parameter ligesom en variabel, dvs. at vi i main-funktionen kan anvende argc – mains første parameter - som have vi en variabel ved det navn.
Erklæring og tildeling på samme linje
I vores program erklærede vi vores heltalsvariabel på én linje og lavede tildelingen på den næste. Da dette er sådan en almindelig ting, kan man gøre det hele i ét trin. Ret main så den ser sådanne ud:
int main (int argc, const char * argv[]) { @autoreleasepool { int age = 4; NSLog(@"Jeg er %d år gammel", age); } return 0; }
Som du kan se har vi nu én linje som både er en erklæring og en tildeling. Denne konstruktion er klart at fortrække da den sikrer at vores variabel er i en meningsfuld tilstand, og at vi ikke glemmer at give den en værdi.
For at se hvordan en variabel kan variere i løbet af et program, ret main så den ser sådanne ud:
int main (int argc, const char * argv[]) { @autoreleasepool { int age = 4; NSLog(@"Jeg er %d år gammel", age); age = 36; NSLog(@"Nu er jeg er %d år gammel", age); } return 0; }
Det eneste nye i vores program er dette:
age = 36; NSLog(@"Nu er jeg er %d år gammel", age);
Den første linje tildeler værdien 36 til vores age variabel. Det betyder at den gamle værdi – 4 – nu er helt tabt og uigenkaldeligt “død”. Derefter udskriver vi vores nye alder med et kald til NSLog.
Flere format-angivere i én formatstreng
I vores alder-program havde vi kun en format-angiver i vores format-streng. Vi kan dog have så mange vi ønsker.
Opret et nyt Xcode project kaldt Mig (som altid et kommandolinjeprogram af typen Foundation), og erstat main med:
int main (int argc, const char * argv[]) { @autoreleasepool { int age = 35; int weightInKilograms = 77; NSLog(@"Jeg er %d år gammel og vejer hele %d kg", age, weightInKilograms); } return 0; }
Som det første i vores autorelease-blok har vi:
int age = 35; int weightInKilograms = 77;
Her erklærer vi to variabler samtidig med at vi tildeler dem en værdi. Læg også mærke til de selvforklarende variabelnavne. Det er altid en god idé at give så forklarende navne til variabler som muligt. Til vægten kunne vi sagtens i stedet have valgt navnet w som er meget kortere at skrive, men vores weightInKilograms er helt selvforklarende – det fremgår endda at vægten er i kg. Især i Objective-C gør man meget ud af at bruge selvforklarende navne.
Lad os se på vores NSLog kald, for her bruger vi nu to format-angivere i vores formatstreng:
NSLog(@"Jeg er %d år gammel og vejer hele %d kg", age, weightInKilograms);
Begge vores format-angivere er %d da de to variable vi ønsker udskrevet i vores streng, begge er ints (altså heltal). Da vi nu har to format-angivere, skal vi selvfølgelig give NSLog to argumenter (udover vores formatstreng, i alt er det tre argumenter), og det vi gør med age og weightInKilograms. Rækkefølgen af format-angivere matcher rækkefølgen af argumenterne. Så den første %d erstattes af age, og den anden format-angiver erstattes af weightInKilograms.
Når vi kører programmet ser vi følgende:
Prøv at bytte rundt på de to sidste argumenter, dvs:
NSLog(@"Jeg er %d år gammel og vejer hele %d kg", weightInKilograms, age);
Kan du gætte hvad outputtet bliver? Prøv at regn det ud uden at udføre programmet først. Husk at når format-angiverne skal erstattes med værdien af et af argumenterne skal rækkefølgen matche mellem format-angiveren og argumentet.
Med andre ord vil konsollen nu vise at jeg er 77 år gammel og vejer 35 kg! Sikke noget.
Hvad sker der hvis der er for får argumenter? Prøv at fjerne det sidste argument til NSLog. Hvis du nu prøver at oversætte og køre programmet, vil Xcode vise dig en masse gule advarselstrekanter, så det er tydeligt noget er galt. Lad os se hvad outpuutet bliver – hos mig ser det sådanne ud:
Som du kan se er det rent nonsens. NSLog kan ikke finde en parameter til den anden format-angiver. I stedet udskrives blot noget skrald fra hukommelsen. Så hvis dit konsoloutput ser underlig ud, kan det være på grund af et manglende argument.
Hvad hvis der er for mange argumenter, dvs. flere argumenter end format-angivere? Well, i det tilfælde sker der ikke noget – de overflødige argumenter ignoreres blot.
Argumenter og parametre: Tit bruges ordene argument og parameter i flæng. Men helt korrekt er et argument det udtryk der bruges når funktionen kaldes. En parameter derimod er den variabel der er en del af funktionshovedet.
Navngivning
Jeg har tidligere berørt vigtigheden af at give selvforklarende navne til variabler. Det gælder dog ikke kun variabler men alle identifikatorer, dvs. også funktionsnavne (vi kigger lidt mere på funktioner senere i dette kapitel, og der vil også være et helt kapitel om funktioner). Så navngiv hellere en variabel countOfGoblins end count. Selvforklarende identifikatorer er også selvdokumenterende og altså ligeså vigtige som gode kommentarer.
Spørgsmålet er så, hvad er reglerne for navngivning? Antallet af tegn du kan bruge, skal du ikke tage dig af – de kan være op til 63 tegn hvilket er rigeligt. Derudover er det vigtigt du er klar over begrænsningerne på hvilke tegn du kan anvende. Følgende er lovlige i identifikatorer:
- bogstaver (store som små)
- tal
- en nedre streg, dvs. tegnet _ (men ikke bindestreg: -).
Selvom du kan anvende tal, må et tal ikke være det første tegn i navnet.
Følgende er eksempler på lovlige navne:
- count
- lion4
- myCar
- vatOfGoods
- _price
- price_
- vat_of_goods
Og her har vi nogle ulovlige navne:
- 1price
- $yo
- my name
Derudover er der forskel på store og små bogstaver. Dvs. myAge og myage og MyAge er tre helt forskellige identifikatorer. Det er vigtigt at du forstår dette!
Navngivningskonventioner
Udover regler vedrørende navngivning, er der også nogle konventioner, som man gør klogt i at følge. De er som følger:
- Variabel og funktionsnavne starter med lille bogstav, men hvert ord derefter starter med stort. Fx.: myName, shootPhotonTorpedo.
- Klassenavne starter med stort. Fx. WarShip og NSString (vi kommer til klasser senere i bogen når vi kigger på objekt-orienteret programmering).
Der er også andre navngivningskonventioner der er værd at kende, men dem nævner jeg når vi kommer til dem. Dem nævnt her er de vigtigste.
Læsevenlige programmer
Som tidligere nævnt i kapitel 1, er der som sådan ingen krav til formateringen af koden. Fx. kunne al koden stå på én linje, som illustreret med main her:
int main (int argc, const char * argv[]) {@autoreleasepool{int age=4;NSLog(@"Jeg er %d år gammel", age);age=36;NSLog(@"Nu er jeg er %d år gammel", age);}return 0;}
Det kræver ingen Einstein for at se at dette er virkelig dårlig kode på den måde at det er stort set umuligt at læse og forstå. Men det er helt lovlig kode. Udover visse krav, som fx. at der skal være mellemrum før og efter et nøgleord (som fx. int, char osv.) er vi helt fri til at formatere koden som vi ønsker.
Her er dog ingen grund til at blive kreativ. Du burde følge de konventioner der er i Objective-C og C miljøet. Den kode du ser i denne bog vil også følge konventionerne for god læsevenlig kode. Der er dog ikke én korrekt konvention men flere forskellige, og hvilken man tillæger sig er udelukkende et spørgsmål om smag og behag. Det vigtigste er at du er konsistent i din kode.
Lad mig give nogle eksempler for forskellige måder at formatere sin kode på.
Lad os starte med Tuborg-klammerne. Kig på definitionen af funktionen main:
Som du kan se står Tuborgklammerne på hver sin linje. Det gør det nemt – synes jeg – at får et overblik over hvor én Tuborg-klammes matchende “bror” findes; hvilket jeg har prøvet at illustrere med den blå streg på billedet ovenfor. For stort set alle kodeblokke anvender jeg denne konvention. En anden måde er dog at placere Tuborg-klammerne som det ses på autorelease-blokken:
Her er start-klammen på samme linje som autorelease-nøgleordet, hvorimod slut-klammen får sin egen linje. Denne konvention er også meget udbredt, også for funktioner. Fordelen skulle være at den sparer en linje. Personligt er jeg ikke så vild med den, så jeg benytter den slet ikke (udover for autorelease blokken). Det vigtigste her er ikke hvilken man benytter, men at man er konsistent omkring det.
Et andet punkt hvor der er forskellige konventioner er ved navngivning af identifikatorer såsom variabel- og funktionsnavne. I C (og til dels C++) angiver nogle variabler ved at adskille ord med en nedre streg såsom price_in_dollars. I Objective-C er denne navngivningsform stort set aldrig brugt, i stedet bruger man stort bogstav for hvert ord bortset fra det første, dvs. priceInDollars.
Som jeg har nævnt er det vigtigere at man vælger sin egen konvention og så følger den konsistent mere end hvad man vælger. Når det så er sagt, så er der en stor fordel i at følge de samme konventioner som alle andre Objective-C programmører. Det vil gøre det meget nemmere for andre at læse din kode.
Operatorerne + og *
Op til nu har vi lært hvordan vi kan udskrive tekst og heltal til konsollen. Vi har også lært at erklære en variabel og tildele en værdi til den. Det er dog begrænset hvad vi kan udrette med vores nuværende viden. Så lad os hælde lidt mere på. Derfor vil vi nu introducere to operatorer (i næste kapitel kigger vi meget mere på operatorer).
Hvad er en operator? I et programmeringssprog er en operator en handling. Nogle operatorerne er lige til at forstå da de minder om de matematiske ditto. Lad os kigge på to operatorer du helt sikkert kender rigtig godt:
- Operatoren for addition, som er et plustegn: +
- Operatoren for multiplikation, som er en asterisk (stjerne): *
Vi kan bruge disse operatorer til at addere og multiplicere talkonstanter og variable lige så tosset vi vil.
Fx. kan resultatet af en addition gemmes i en vairabel:
int resultOfOperation = 3 + 4;
resultOfOperation har nu værdien 7 (som er resultatet af additionen af tallene 3 og 4). Vi kan så senere i programmet kan vi tildele variablen resultatet af en multiplikation:
resultOfOperation = myMonthlySalary * 12;
her skal det antages at monthlySalary er erklæret tidligere i programmet og tildelt en fornuftig værdi (som jo så er min månedsløn).
En operator har brug for nogle operander for at udføre sin handnling. Additionsoperatoren for eksempel forventer to tal som den skal udføre sin handling på (som jo altså er at lægge dem sammen og give os resultatet tilbage). Så i vores første eksempel ovenfor er 3 og 4 operanderne og + er operatoren, og resultatet af handlingen (additionen) er 7. Det er totalt ligegyldigt hvorfra operanderne kommer: det kan være talkonstanter som i vores første eksempel ovenfor hvor begge operander er talkonstanter, eller det kan være variabler som i andet eksempel hvor den ene operand er en variabel (den anden er en talkonstant), men det kan også være fx. resultatet af et funktionskald (ser vi senere i bogen). Dog kan der være nogle krav til operandernes type. Fx. kan additionsoperatorerne ikke lægge et tal og en tekststreng sammen, men kræver to taloperander (fx. heltal som en int variabel eller heltalskonstant som 3 eller kommatal som 3.14 – mere om kommatal i næste kapitel).
Nu vi snakker om operatorer, er tildelingstegnet (altså =) faktisk også en operator. Den forventer også to operander: en operand på højre side som er et udtryk (som kan være arbitrært langt) og en variabel på venstre side som den skal tildele resultatet af udtrykket. (Vi berører både udtryk og operatorer mere i næste kapitel)
Lad os nu se på et program som viser os en masse +’er og *’er. Opret et Xcode projekt – jeg har kaldt mit Operatorer – (Command Line Tool af typen Foundation), og ret main til:
int main (int argc, const char * argv[]) { @autoreleasepool { int myAge = 35; int yourAge = 28; int totalAge = myAge + yourAge; NSLog(@"Den totale samlede alder er %d", totalAge); NSLog(@"Den totale samlede alder er %d", myAge + yourAge); int a = 4 + myAge; NSLog(@"Min alder er stadig %d", myAge); NSLog(@"Variablen a er deridmo %d", a); myAge = myAge + 4; NSLog(@"Nu er min alder %d", myAge); myAge += 4; NSLog(@"Nu er min alder %d", myAge); int b = 4; int c = 2; int d = c * b; NSLog(@"%d * %d = %d", b, c, d); myAge *= 2; NSLog(@"Nu er min alder %d", myAge); NSLog(@"4 + 5 = %d", 4 + 5); } return 0; }
Lad os nu gå programmet igennem, linje for linje. Først erklærer vi to variabler, myAge og yourAge og tildeler dem en værdi. Derefter erklærer vi en ny variable kaldt totalAge og tildeler dem resultatet af additionen af myAge og yourAge.
Næste linje er et NSLog-kald som udskriver værdien af totalAge. Næste linje igen er endnu et kald af NSLog som udskriver samme værdi som totalAge, men denne gang ved at springe mellemregningen over og lave regnestykket direkte i funktionskaldet. For så vidt NSLog angår er de to linjer helt identiske, da den modtager værdien 63 (summen af myAge og yourAge) som argument i begge tilfælde.
Derefter oprettet vi en variabel a og tildeler den summaen af myAge og 4. Derefter udskriver vi værdien af myAge for at pointere at myAge ikke er blevet ændret af disse regnstykker. Derefter udskriver vi værdien af variablen a.
Vores næste linje er:
myAge = myAge + 4;
Her ændrer vi faktisk værdien af myAge. Først udregnes summen af myAge og 4, og resultatet af dette tildeles tilbage i myAge. Med NSLog udskriver vi værdien af myAge og vi kan se at den nu har en ny værdi.
Lad os se på næste linje:
myAge += 4;
Denne linje er blot en nemmere måde at skrive myAge = myAge + 4. Denne linje lægger altså 4 til værdien i myAge og gemmer det i myAge. Denne forkortelse bruges rigtig tit, så prøv at huske den udenad. Den virker med rigtig mange operatorer, også * som vi ser om lidt.
De næste linje er som følger:
int b = 4; int c = 2; int d = c * b; NSLog(@"%d * %d = %d", b, c, d);
Her opretter vi to heltalsvariabler, b og c. Herefter opretter vi en tredje heltalsvariabel, d, som tildeles resultatet af at multiplicere b og c. Derefter udrkrives resultatet med NSLog.
Lad os rykker videre til næste linje. Her ganger vi myAge med 2 (og gemmer resultatet tilbage i myAge). Denne multiplikation laver vi på den smarte måde:
myAge *= 2;
Vi kunne i stedet havde benyttet den lidt længere metode:
myAge = myAge * 2;
Den følgende NSLog udskriver vores nu pænt fremskredne alder.
Den sidste linje er en NSLog som udskriver resultatet af 4 + 5, men uden brug af variabler.
Jeg håber at dette program har vist dig en masse om brugen af operatorer. Det du har set her kommer du til at bruge igen og igen og igen. Så sørg for du forstår essensen af det, og med øvelse skal det nok komme.
Kør, ret, kør, ret og vedligehold: Udviklingsfaserne
Hvis du har indtastet programmerne vi har kigget på indtil nu, har du muligivs lavet en fejl på et tidspunkt. Eller måske havde du problemer med nogle af øvelserne i kapitel 1, også med en eller flere fejl til følge. For at få dit program til at oversætte og køre, har du måtte rettet fejlene.
Denne process kommer du som programmør til at gennemleve igen og igen og igen. Det kan skrives op som her:
- Skriv eller ret program.
- Oversæt program.
- Kan program oversættes? Hvis ja gå til trin 4, ellers gå til trin 1.
- Kør program.
- Virker program efter hensigten? Hvis ja gå til trin 6, ellers gå til trin 1.
- Frigiv program til dets brugere.
Fejl i trin 2, skyldes syntaktiske fejl, dvs. du har brudt computersprogets egne syntaksregler; det kan fx. være et manglende semikolon (en typisk begynderfejl). Fejl i trin 4 kaldes for semantiske fejl. Dit program kan oversættes og udføres af computeren, og fungerer så vidt computeren angår helt perfekt, men måske har du lavet en forkert matematisk udregning, eller du har misfortolket en lydbuffer så lyden fra mikrofonen som du prøver at gemme i en fil, er i en forkert frekvens etc.
I løbet af denne process, vil du opdage hvorfor gode kommentarer er vigtige: en programmør læser meget mere kode end hun skriver. Derfor er dårlige kommentarer også ligeså irriterende som ingen kommentar.
Flere erklæringer på samme linje
Lad os kigge lidt mere på variabelerklæringer. Vi har set hvordan vi kan erklære en variabel på en linje:
int hz; // Frequency of the input wave
Og vi har også set at vi kan tildele en værdi til vores variabel – hvilket altid er en god idé:
int hz = 440;
Faktisk kan vi erklære flere variabler på én linje. Det ser sådanne ud:
int a, b, c;Her erklærer vi tre vairabler af typen int (dvs. heltalt), der hedder a, b og c. Men vi tildeler ingen af dem nogle værdi. Det er det samme som:
int a; int b; int c;
Vi kan dog godt tildele en eller flere af dem en værdi:
int a = 5, b, c;
Her tildeler vi a – og kun a – værdien 5. Brugen af denne konstruktion er et spørgsmål om smag. Nogle fraråder dog brugen af den, da ovenstående tildeling kan foranledige nogle til at tro at også b og c bliver tildelt værdien 5.
Lad os se det i et program. Opret et nyt program (jeg kalder det Erklæringer), og ret main til:
int main (int argc, const char * argv[]) { @autoreleasepool { int a = 7, b; NSLog(@"a har værdien %d", a); NSLog(@"b har værdien %d", b); } return 0; }
Hvis du kører dette program, ser du følgende i konsollen:
Som du kan se bliver variablen b ikke tildelt værdien 7.
Nøgleord
Som berørt tidligere, er der en del nøgleord i Objective-C. Et nøgleord er et ord som har en speciel betydning i sproget. Som oftest er det en rigtig dårlig ide at bruge et nøgleord som en identifikator (fx. variabelnavn) i et program, og typisk er det endda syntaktisk ulovligt.
Hvilke nøgleord er der så i Objective-C? I modsætning til de fleste andre sprog, er det ikke så nemt at sige. Objective-C tilføjer nogle specielle ord ovenpå C, såsom fx. @autoreleasepool osv., men der er i Objective-C ingen klar definition om sådan et ord er et nøgleord eller ej. Udover disse Objective-C tilfælde, er der de nøgleord som sproget har arvet fra C. Hvis vi laver vores egen definition af nøgleord, og inkluderer Objective-C “nøgleord” og nøgleordene fra C, får vi følgende liste:
void, char, short, int, long, float, double, signed, unsigned, id, const, volatile, in, out, inout, bycopy, byref, oneway, self, super, return, @autoreleasepool, @interface, @end, @implementation, @end, @protocol, @class.
En del af disse kan ikke benyttes som identifikatorer i dit program – du vil ikke kunne oversætte det; derimod kan andre anvendes uden problemer. Men hold dig på dydens smalle sti, og lad være med at bruge nogle af dem som identifikatorer. Selv i tilfælde hvor det er muligt, vil det forvirre andre Objective-C programmører som er vant til en anden brug af ordene.
Funktioner
Lad os skue lidt fremad og intrdocuere funktioner. Vi vil senere i bogen have et helt kapitel om funktioner, så frygt ikke hvis alt ikke giver mening nu. Men hvad er en funktion? Du har muligvis en intuitiv idé om det, især hvis du har hørt efter i matematikundervisningen, hvor du sikkert har stødt på funktioner. Den matematiske definition på en funktion er dog faktsik mere komplicerede end man skulle tro, og ikke en vej vi vil begå os ned af. Der er dog visse ligheder hvorfor din intuition højst sandsynligt er på rette spor.
For at forstå hvad en funktion er, så se på den som en sort boks. Du kommer noget ind i den, og den giver dig noget tilbage (eller som programmører siger: den returnerer noget). Vi kunne fx. have en sinus-funktion. Vi giver den et argument som er en vinkel i radianer, og den returnerer os sinus af dette argument.
Funktioner kan tage alt fra nul til flere argumenter (hvis man har funktioner der tager 17 argumenter, er det dog nok tegn på dårligt design). Nogle funktioner returnerer en værdi (fx. i vores sinus-eksempel returnerede funktionen sinus af argumentet, og vi har set i alle vores programmer, at main returner et heltal der signalerer programmets status), men en funktion kan også returnerer ingenting (hvilket man kalder void - som er engelsk for ‘tomt’).
En funktion der ikke returnerer noget? Det lyder måske mærkeligt, men det bruges tit i programmering. Her er vi ikke interesseret i en eller anden værdi som funktionen finder frem til os, men interesseret i en handling den gør for os, hvor den ændrer et eller andet i vores program (det kalder man en sidevirkning, eller på engelsk en side effect). Se bare på NSLog-funktionen. Den returner ingenting (dvs. void); vores interesse i den er udelukkende at den kan skrive til konsollen for os.
Når vi skal definere en funktion, skal vi kode to ting:
- Funktionshovedet.
- Funktionens kode – omgivet af et par Tuborg-klammer.
Vi har allerede i kapitel 1 kigget på mains funktionshoved, så kig der hvis du ikke kan huske det. Vi kommer ind på det igen i kapitlet om funktioner. Uanset hvad har vi allerede nu den nødvendige viden om hvordan vi kan skrive en funktion. Vi vil derfor lave en funktion der returnerer arealet af et rektangel.
Her er koden for funktionen:
int areaOfRectangle(int width, int length) { return width * length; }
Vores funktionshoved fortæller os at funktionen hedder areaOfRectangle. Vi kan også se at den returnerer et heltal. Som argument tager den to heltal. Med andre ord vil vores “sorte boks” her modtage to heltal, og give os tilbage et heltal. Selve funktionen udregner arealet ved at multiplicere de to argumenter og returner dem til kalderen.
Lad os prøve at bruge denne funktion. Opret et nyt projekt i Xcode – kald det fx. AreaOfRectangle. Skriv ovenstående funktion ind ovenover main-funktionen – dette er en vigtig detalje! I kapitlet om funktioner kommer vi tilbage til det, men indtil da, skal du altid skrive funktioner ovenover din main-funktion.
I din main kan du tilføje et eller flere NSLog-kald der udkskriver arealet af nogle firkanter. Min fil ser fx. sådanne ud (jeg har her udeladt kommentarene øverst i filen og #import-direktivet):
int areaOfRectangle(int width, int length) { return width * length; } int main (int argc, const char * argv[]) { @autoreleasepool { NSLog(@"Arealet af firkanten der er 7 gange 8 meter er: %d", areaOfRectangle(8, 7)); int width = 17; int length = 45; NSLog(@"Arealet af firkanten der er %d gange %d meter er: %d", width, length, areaOfRectangle(width, length)); } return 0; }
Udover at vi har defineret vores egen funktion, er der ikke meget nyt her. Du studser måske over at vi har kaldet at vores funktion direkte i NSLog, men det er der intet galt i. Når NSLog kaldes bliver vores funktion udført først, og den værdi den returnerer bliver givet til NSLog. Det eneste NSLog kræver er at vi skal give den et heltal her (da vi i vores formatstreng har brugt %d), men hvordan vi gør det er helt op til os: det kan være en talkonstant, en heltalsvariabel, eller et funktionskald der returnerer et heltal. Så længe udtrykket ender med at give et heltal er alt i den fineste orden.
Programstruktur
Til sidst i kapitlet, vil jeg kort berøre programstruktur.
De fleste af de programmer vi har lavet og kommer til at lave i denne bog har en meget lineær struktur: det hele starter i main, og herefter udføres instruktionerne i main en linje ad gangen fra top til bund. Vi vil måske gøre brug af nogle funktioner – enten nogle vi selv har lavet eller nogle der kommer med C og Objective-C implementationen – men programmet er i sin natur ret lineær. Selv når vi begynder at anvende objekter vil strukturen være den samme.
Sådan er de fleste programmer i virkelighedens verden ikke. De programmer du støder på når du bruger din Mac eller din iOS-enhed er hændelsesstyret (ligesom Windows programmer, Android programmer osv. er det). Det vil sige at programmet starter – også i main – og gør sig herefter klar til input fra brugeren. Men herefter sker ting kun på brugerens ønske. Med andre ord venter programmet på at nogle hændelser forekommer. Disse hændelser kan være en finger der tapper på en knap, en mus der klikker på et vindue, eller input fra et tastatur.
I denne bog arbejder vi mest med kommandolinjeprogrammer for at kunne fokusere på de vigtige principper, dog vil vi også kigge lidt på de hændelsesstyret programmer for at give dig en smagsprøve.
Øvelser
1. Find en fejl i følgende kodeuddrag:
int price = 500; NSLog(@"Prisen er %d", Price);
2. Find en fejl i følgende kodeuddrag:
int a = 5 int b = 6; NSLog(@"%d + %d = %d", a, b, a+b);
3. I programmet med funktionen areaOfRectangle, prøv da at flytte den ned under main. Hvad sker der? Vi kigger mere på dette, og hvad vi kan gøre for at undgå det. Men indtil da, sørg for altid at have dine funktioner før.
4. Lav en funktion der returner volumen af en terning. Brug funktionen lidt i main, og skrive nogle volumener til konsollen.
5. Lav et program der opretter variablerne hourPerDay, minutesPerHour, og secondsPerMinute og tildeler dem de korrekte værdier (fx. skal hourPerDay tildeles værdien 24). Brug derefter disse variabler til at udregne og udskrive til konsollen antallet af sekunder der er på et døgn.
6. Ret programmet fra #5 ved at tilføje dayPerYear, myAge, og udregn og udskriv herefter antallet af sekunder du har levet (tallet vil ikke være helt præcist da du næppe laver denne øvelse på din fødselsdag, men tæt nok på).
7. Skriv en funktion der kan udskrive dit navn til konsollen. Kald denne funktion fra main.
8. Skrive et program hvor du erklærer to variabler. Tildel dem en værdi. Udskriv derefter til konsollen deres sum og produkt.
9. Skriv et program der udskriver en tabel af tallene 1-10 samt deres kvadrattal og kubiktal. For at rykke til et tabstop brug \t i formatstrengen til NSLog. For at give dig lidt hjælp er her fx. linjen for tallet 4:
4 16 64
Der er flere måder at gøre det på. Du kan fx. oprette en funktion som du kalder ti gange. Men det vil jeg lade være op til dig.

























