310 likes | 422 Views
Symbian ohjelmointi. 2. Luento Juha Turunen (juha.turunen@lut.fi). Perusfilosofiat Symbian ympäristössä. §1 Luotettavuus Ei muistivuotoja Kunnollinen poikkeuskäsittely §2 Pieni muistinkulutus Static footprint Run-time footprint §3 Nopeus. Sisältö. Poikkeustilanteiden käsittely
E N D
Symbian ohjelmointi • 2. Luento • Juha Turunen (juha.turunen@lut.fi)
Perusfilosofiat Symbian ympäristössä • §1 Luotettavuus • Ei muistivuotoja • Kunnollinen poikkeuskäsittely • §2 Pieni muistinkulutus • Static footprint • Run-time footprint • §3 Nopeus
Sisältö • Poikkeustilanteiden käsittely • Muistinhallinta • Cleanup stack • Olioiden kaksivaiheinen konstruktointi
Miksei C-tyylinen poikkeuskäsittely riitä? • Jokainen tapaus tarvitsee tarkastaa erikseen tai käyttää try catch -rakennetta • Vie paljon tilaa (§2) if ( ( obj = new CObj() ) == NULL ) HandleException(); • Try-catch generoi runsaasti koodia • C++ konstruktoreilla ei ole paluuarvoa => ei voida tietää menikö jokin vikaan CMyObj()::CMyObj() { iSomething = new CSomething(); if ( iSomething == NULL ) // ?!? } • Keskeneräisesti konstruktoidut oliot (§1)
Poikkeuskäsittely • Leave & TRAP harness • Symbian OS:ssa ei käytetä C++:ta tuttua try-catch-rakennetta poikkeustilanteiden hallintaan. • Try-catch-rakenteen tilalla käytetään ns. TRAP harness-mekanismia. • TRAP harness asetetaan seuraavasti: TRAPD( error, DoSomethingL() ); • Kun ohjelman suorituksessa sattuu poikkeus (leave), hyppää suoritus lähimpään TRAP harnessiin ja leave-koodi talletetaan muuttujaan (error). • Leaven aiheuttaminen • User::Leave( TInt aReason ); • ylikirjoitettu new-operaattori new( ELeave ) • tästä lisää muistinhallinnan yhteydessä • Metodien jotka saattavat aiheuttaa leaven pitää päättää nimensä L-kirjaimeen!
Poikkeuskäsittely • Panic • Käytetään vakavan virheen sattuessa • Lopettaa threadin suorituksen • Applikaation pääthreadin kuoleminen tappaa muut threadit • Ohjelman suorituksen loputtua systeemi tulostaa infon, jossa lukee panicin aiheuttajan määrittelemä teksti sekä panic-koodi • Panicin aiheuttaminen • User::Panic(const TDesC& aCategory, TInt aReason ); • Panic on virheiden paikallistamista varten • Jos kaikki menee kuin elokuvissa, ei loppukäyttäjä koskaan nää panic-ilmoituksia
Muistinhallinta • Muistin varaaminen • Käytetään C++ operaattoria new • Ylikirjoitettu new()-operaattori tukee leave-mekanismia • myStuff = new( ELeave ) CStuff; • mikäli muistinvaraus epäonnistuu, aiheutuu leave • Varattu muisti on nollattu systeemin toimesta • Jos muisti on nollattu jos systeemin toimesta, ei applikaatioiden tarvitse tyhjentää sitä => vähemmän koodia (§2)
Muistinhallinta • Muistin vapauttaminen • Käytetään C++ operaattoria delete • delete-operaattorille voi antaa myös NULL-pointerin • Applikaatioiden ei tarvitse tarkistaa erikseen NULL-pointtereita > vähemmän koodia (§2)
Muistinhallinta • Stack vs. Heap • Stack • Pino • Muistia ei varata kernelin kautta vaan pino-osoitinta kasvatetaan • Destruktoria kutsutaan, kun poistutaan skoopista • Muisti vapautuu pino-osoitinta vähentämällä • Ei C-luokkia stackiin! • Stackin määrä melko rajallinen • Heap • ”Kasa” • Muisti varataan kernelin kautta • Muisti vapautetaan ja destruktoria kutsutaan eksplisiittisesti delete-operaattorilla
Stack & heap • Pointterit stackissa osoittavat olioihin heapissa { TInt i; CClass* c = new(ELeave) CClass; ... Stack Heap c CClass i
Muistivuodot • Muistivuoto tapahtuu kun muistia varataan, mutta sitä ei vapauteta { Tint i; CClass* c = new(ELeave) CClass; c.Use(); // delete c; } Stack Heap CClass
Muistivuodot • Symbian applikaatioissa muistivuodot ovat erityisen haitallisia, koska mobiililaitteissa muistia on vähän ja ohjelmat saattavat olla katkotta ajossa jopa kuukausia • Symbian-sovellus ei saa vuotaa muistia! • Loppukäyttäjät eivät miellä puhelinta tietokoneeksi • Muistivuotojen havaitseminen • Symbian OS tarjoaa mekanismin muistivuotojen havaitsemiseen mahdollisimman aikaisessa vaiheessa • Varattujen muistisolujen määrän täsmääminen voidaan varmistaa tietyissä pisteissä ohjelman suoritusta
Pieni muistutus C++ konstruktoinnista • Luotaessa uusi olio new –operaattoria käyttäen, kutsutaan C++ luokan erityisiä konstruktorimetodeja • Class::Class() • Vastaava pätee myös olion tuhoamiseen jolloin metodeja kutsutaan destruktoreiksi • Class::~Class() • Luokan ollessa peritty kutsutaan kantaluokan konstruktoria ensin • Tuhottaessa käänteinen järjestys
Cleanup Stack • Symbianin ratkaisu resurssien vapauttamiseen poikkeustilanteissa • Cleanup stackia tarvitaan, jotta varattu muisti voidaan vapauttaa hallitusti leaven sattuessa • ... • CStuff* myStuff = new( ELeave ) CStuff; • myStuff->DoStuff(); • someObject->MightLeaveL(); • CFoo* myFoo = new( ELeave ) CFoo; • ... • Mitä tapahtuu jos MightLeaveL() tainew ( ELeave ) CFoo aiheuttaa leaven?
Cleanup Stack • Cleanup stack on nimensä mukaisesti pino • Leaven tapahtuessa, CleanupStack tuhoaa kaikki sinne laitetut oliot. • CleanupStack::PushL() • Cleanup stackiin voi laittaa ainoastaan CBasesta perittyjä olioita, jos on tarpeen kutsua destruktoria • CBase:lla on virtuaalinen destruktori, jota Cleanup stack tarvitsee osatakseen tuhota sinne laitetut oliot
Cleanup Stack • Tehdään edellisen esimerkin koodista ns. ”leave safe” • ... • CStuff* myStuff = new( ELeave ) CStuff; • CleanupStack::PushL( myStuff ); • myStuff->DoStuff(); • someObject->MightLeaveL(); • CFoo* myFoo = new( ELeave ) CFoo; • CleanupStack::PushL • ... • CleanupStack::PopAndDestroy( 2 ); • // myStuff & myFoo • ... • Jos MightLeaveL() tai new ( ELeave ) CFoo aiheuttaa leaven, Cleanup stack tyhjennetään ja myStuff sekä myFoo tuhoutuvat => ei muistivuotoa
Cleanup Stack • CleanupStack::PushL() • PushL() voi tehdä leaven?! • ... • CStuff* myStuff = new( ELeave ) CStuff; • CleanupStack::PushL( myStuff ); • PushL() tekee leaven vasta kun cleanup stackissa ei ole enää tilaa yhdellekkään osoittimelle • PushL():lle annettu osoitin pääsee aina cleanup stackiin ja leave tapahtuu vasta tämän jälkeen
Kaksivaiheinen konstruktointi • Mitä tapahtuu jos C++ konstruktorissa on koodia, joka voi suorittaa leaven? • Esim CStuff::CStuff() { iTimeStamp = SomeLibrary::GetTime(); iFoo = new( ELeave ) CFoo; // oops.. iBar = new( ELeave ) CBar; // I did it again... } // somewhere in code CStuff* myStuff = new( ELeave ) CStuff; • Mitä tapahtuu jos CBar tai CFoo olion luonti suorittaa leaven? • Ohjelmoijalla ei ole mahdollisuutta laittaa oliota Cleanup stackiin ennen kuin konstruktorin koodi suoritetaan!
Kaksivaiheinen konstruktointi • Ongelma kierretään käyttämällä kaksivaiheista konstruktointia • Kaksivaiheisen konstruktoinnin 1. vaiheessa olio luodaan normaalisti C++:n new –operaattoria käyttäen • 1. vaiheen jälkeen olio laitetaan cleanup stackiin • Tämän jälkeen suoritetaan 2. vaihe, jossa voidaan suorittaa koodia, joka voi tehdä leaven • Loppuun asti konstruktoitu olio jätetään cleanup stackiin tai otetaan sieltä pois riippuen siitä, mitä jatkossa on tarkoitus tehdä
Kaksivaiheinen konstruktointi • CStuff::CStuff() // constructor { iTimeStamp = SomeLibrary::GetTime(); } void CStuff::ConstructL() { iFoo = new( ELeave ) CFoo; // it’s OK to leave iBar = new( ELeave ) CBar; } CStuff::~CStuff() // destructor { delete iFoo; delete iBar; } // Somewhere in code CStuff* myStuff = new( ELeave ) CStuff; CleanupStack::PushL( myStuff ); myStuff->ConstructL(); // 2nd phase CleanupStack::Pop();
Kaksivaiheinen konstruktointi • Jos CBar-luokan luonti aiheuttaa leaven, on myStuff Cleanup stackissa ja sen destruktoria kutsutaan. • Destruktori tuhoaa iFoo:n ja iBar:n • iBar:n luontihan jäi kesken, mutta se ei haittaa, koska delete-operaattori hyväksyy myös NULL pointterin • Usein kaksivaiheinen konstruktointi kirjoitetaan staattiseen metodiin, jolloin sitä ei tarvitse kirjoittaa uudelleen joka kerta kun luokasta tehdään instanssi. • CStuff* CStuff::NewL() { CStuff* self = new( ELeave ) CStuff; CleanupStack::PushL( self ); self->ConstructL(); CleanupStack::Pop(); return self; }
Kaksivaiheinen konstruktointi • NewL():n käyttö • CStuff* myStuff = CStuff::NewL(); // easy... • CleanupStack::Pushl( myStuff ); • DoSomethingL(); • myStuff->SomethingElse(); • CleanupStack::PopAndDestroy(); • Jos NewL():n jälkeen tehdään leaven aiheuttavia operaatioita kannattaa NewL():n jättää olio valmiiksi cleanup stackiin => NewLC() • C-kirjain metodin lopussa on merkki siitä, että metodi jättää jotain cleanup stackiin
Kaksivaiheinen konstruktointi • Metodit, jotka jättävät olioita cleanup stackiin päättävät nimensä C-kirjaimeen. Luokan dokumentaatio kertoo mitä CS:iin jätetään. • NewLC() • static CStuff* CStuff::NewLC() • { • CStuff* self = new( ELeave ) CStuff; CleanupStack::PushL( self ); self->ConstructL(); return self; } • Säästää koodia (§2) mikäli NewL():n jälkeen kutsuttaisiin kuitenkin CleanupStack::PushL(). Nyt osoitin valmiiksi cleanup stackissa
Kaksivaiheinen konstruktointi • Jos luokassa on sekä NewL() että NewLC() voidaan optimoida NewL() CStuff* CStuff::NewL() // Before { CStuff* self = new( ELeave ) CStuff; CleanupStack::PushL( self ); self->ConstructL(); CleanupStack::Pop(); return self; } CStuff* CStuff::NewL() // Optimized { CStuff* self = CStuff::NewLC(); CleanupStack::Pop(); return self; }
Kaksivaiheinen konstruktointi • Luokan jäsenmuuttujia EI laiteta cleanup stackiin, koska destruktori tuhoaa ne • Mitä tapahtuu, jos näin kuitenkin tehdään? • Riittää että luokan käyttäjä huolehtii siitä, että joko luokan instanssi on cleanup stackissa tai siihen viitataan jäsenmuuttujassa jolleka kutsutaan deleteä destruktorissa
Cleanup Stack ja ei-C-luokat • Cleanup Stackiin voi laittaa myös muitakin kuin C-luokkiin osoittavia pointtereita TText* buffer=( TText* ) User::Alloc( 100 * sizeof( TText ) ); CleanupStack::PushL( buffer ); // do something that might Leave CleanupStack::PopAndDestroy(); • Huomaa että ainoastaan varattu muisti vapautetaan! Destruktoria ei kutsuta • Cleanup Stack ei tiedä miten destruktoria kutsutaan
Cleanup Itemit • RSomeServerSession session; • session.Connect(); • ... • // Something that might leave • ... • session.Close(); • Ongelma: session jää sulkematta mikäli kesken suorituksen tapahtuu leave
Cleanup Itemit • TCleanupItem • Sisältää referenssin olioon, jota kutsutaan kun cleanup stack tyhjennetään • Mahdollistaa minkä tahansa resurssin sitomiseen Cleanup Stack mekanismiin • Template funktiot • CleanupClosePushL( T& ); • CleanupReleasePushL( T& ); RSomeServerSession session; session.Connect(); CleanupClosePushL( session ); ... // Something that might leave ... CleanupStack::Pop() session.Close();
Konstruktointiin liittyviä asioita • Jos kaksivaiheinen konstruktori on tarjottu, kannattaa C++ konstruktori asettaa privateksi • Helpottaa binary compatibility asioissa • Jos peritään CBase:sta on jäsen data valmiiksi nollattu • Jos konstruktoinnissa pitää tehdä jotain mikä voi aiheuttaa leaven, käytä kaksivaiheista konstruktointia • Muista käyttää cleanup stackia automaattisia muuttujia ja dynaamista varausta käytettäessä • C++ konstruktori ei saa tehdä leavea!
Destruktointiin liittyviä asioita • Tuhoa vain kerran • Uudelleen allokoitaessa aseta osoitin = NULL • delete iSomething; • iSomething = NULL; • iSomething = CSomething::NewL(); • Destruktorit eivät saa olettaa täydellistä konstruktointia • Destruktori ei saa tehdä leavea! • Muista vapauttaa kaikki varatut resurssit luokan destruktorissa