A telefonok memória- és tárkapacitása a számítógépéhez képest nagyságrendekkel kisebb, ahogyan a processzor teljesítménye is, ezért a Symbian-os szoftverfejlesztés egyik legfontosabb szempontja a kivétel- és memóriakezelés. A memóriaszivárgás kiküszöbölésére kifejezetten nagy gondot célszerű fordítani.
A Symbian-ban a hagyományos C++ kivételkezelés nem használható, ennek oka, hogy a Symbian (elődjének) fejlesztésekor a C++ fordítók még nem ismerték a kivételeket, a másik, hogy a C++ kivételek jóval több erőforrást és memóriát igényelnek, mint, ami megengedhető egy mobiltelefonon. Ezzel szemben a Symbian-os kivételkezelés során nem történik stackrewind, egy feltétel nélküli ugrással kerül a vezérlés a kezelési pontra, így sokkal hatékonyabban tudja kezelni a problémákat.
Hiba jelzésére a User könyvtár (statikus osztály) Leave metódusa szolgál. Azokat a függvényeket, amelyek a User::Leave metódust hívják leave-elő függvényeknek nevezzük. A User::Leave hívás logikailag megfelel a C++-ban megszokott throw-nak, mely az adott típusú kivételt dobja. Symbian-ban a kivétel típusa egy számot (hibakódot) jelent.
Például a new operátornak létezik egy felüldefiniált new (ELeave) metódusa, amely a memória elfogyása esetén leavel. Az L betűt a metódusok végére tehát nekünk kell odatennünk. Ez logikailag megfelel a Java-ban megszokott throws-nak vagy a C++-beli throw-nak. A különbség köztük azonban, hogy a throw deklarációját a fordítók ellenőrzik, míg a függvények végén az L betű helyes használatára nekünk magunknak kell figyelnünk, illetve egy külső eszköz, az ún. LeaveScan alkamazás segítségével ellenőrizhetjük, hogy kódunk megfelel-e a függvények leavel kapcsolatos konvenciójának.
A Fenti kód esetén amenyiben elfogy a rendelkezésre álló memória az obj2 objektum példányosítása folyamán, a new(Eleave) használat miatt kivétel váltódik ki. Azonban az obj1 objektum destruktora nem hívódik meg, így az általa lefoglalt erőforrások nem fognak felszabadulni. Szükséges tehát a leave-elős függvényhívásokat a C++-ban megszokottakhoz hasonlóan kritikus szakaszokba foglalni.
Természetesen a C++-beli catch utasításának is létezik a Symbian-ban megfelelője: ezek a TRAP illetve TRAPD makrók. Leave-lés esetén a legközelebbi ilyen TRAP-hez kerül a vezérlés, egy változóba írva a hibakódot. Ha nem történt leave, akkor a hibakód KErrNone lesz. A TRAPD dekralálja a TInt típusú hibakezelő változót, míg a TRAP esetén egy általunk deklarált változót használhatunk.
Ahogy azt korábban láttuk, a new operátorral (heap-en) létrehozott objektumoknak leavelés esetén nem hívódik meg a destruktora. Ez memóriaszivárgáshoz vezet. Ezért az így létrehozott objektumok mutatóit valahol el kell tárolni, hogy ha hiba történik, akkor elérhessük őket, és meghívhassuk a destruktoraikat. Ez, ahol tároljuk a CleanupStack lesz. Ahogy a neve is mutatja, ez egy Verem adatszerkezet, a megfelelő műveletek nevei is utalnak az általuk végzett tevékenységre.
A CleanupStack-re tehát valamilyen dinamikusan létrehozott erőforrások hivatkozásait tesszük fel. Ezek elsősorban C osztályú objektummutatók és R osztályú referenciaobjektumok. A felszabadítás menetét, a változó típusának megfelelően adhatjuk meg, a C osztályok esetén a CBase (kötelező) ősosztály által garantált virtuális destruktor, míg az R osztályok esetén, a speciálisan a CleanupStack kezelésére kialakított metódushívások (pl. CleanupClosePushL és a CleanupReleasePushL) használatával. További, az előbbi módszerek által nem megoldható, erőforrásfelszabadítást a TCleanupItem osztály segítségével valósíthatunk meg. Az osztály konstruktora két paramétert vár, egy tetszőleges típusú , az erőforrásra hivatkozó pointert (TAny*), illetve egy függvényreferenciát, melyben a felszabadítási mechanizmust definiálhatjuk.
Az alábbi példában különböző típusú objektumokat helyezünk a CleanupStack-re.
A CleanupStack segítségével sikerült felkészülni egy esetleges Leave hívás utáni takarításra. Viszont problémába ütközhetünk, ha az objektum lefoglalása után, de még a mutató CleanupStackre történő helyezése előtt történik a Leave hívás. Nézzük meg a következő kód-részletet:
Nem feltétlenül lesz probléma, a CKulsoOsztaly-nak nem sikerül helyet foglalni, akkor az új new operátorunk leave-el, ás minden rendben van; egyéb esetben pedig a Cleanupstackre kerül a külső mutató. Ha a PushL metódus leavel sem lesz gond. A PushL mindig előre lefoglal egy helyet egy mutatónak, ezért amikor leave-el, akkor ez az előre lefoglalás nem sikerül, és a korábbi PushL híváskor lefoglalt területre ekkorra már bekerült az argumentumban átadott mutató.
Vegyük azonban észre, hogy a PushL hívás előtt a CKulsoOsztaly konstruktora is lefut:
Ha viszont ekkor a CBelsoOsztaly számára nem sikerül helyet foglalni, akkor az eredmény egy félig létrehozott CKulsoOsztaly, amely számára le van foglalva a memória, és még sem került fel a CleanupStackre. Egyszerűen megfogalmazva: az a baj, hogy a konstruktor végére nem rakhatunk L betűt, pedig leave-el.
Tehát ezt nem szabad megengedni. A megoldás egy olyan kétlépéses konstruktor, ahol az egyszerű tagváltozók inicializálásának feladatát az „igazi” konstruktor végzi, míg a C osz-tálybeli tagváltozók helyfoglalását és az egyéb veszélyes feladatokat egy második metódus végzi.
Nézzünk most tehát egy helyes CKulsoOsztaly megvalósítást, azt feltételezve, hogy CBelsoOsztaly-nak nincs szüksége a kétfázisú konstrukcióra.
Ez a példa rendkívül fontos a CleanupStack és a kétfázisú konstrukció megértése szempontjából. Ezért nézzük meg, hogy mi is történik. A kiindulópont a ValamilyenFuggvenyL-ben a CKulsoOsztaly létrehozása. Ez a NewLC nevű statikus metódussal történik. Az LC végű függvények olyan allokálás jellegű műveletet végző függvények, amelyek a műveletsor végén a lefoglalt memóriaterületre mutató pointert a CleanupStackre helyezve hagyják. A különbség tehát a NewL és a NewLC között, hogy az első a kétfázisú létrehozás után (a ConstructL hívás miatt) a CleanupStackre tett objektumot onnan leveszi, míg a második rajta hagyja.
A statikus példányosító metódus először a new (ELeave) operátort használva létrehoz egy példányt a saját típusából. Mivel a konstruktor nem végez veszélyes műveletet, nem történhet leave-elés, csak ha nem sikerült helyet foglalni (ez esetben viszont nincs memóriaszivárgás sem. A következő lépés a CleanupStackre helyezni az újonnan létrehozott objektumot. Így már biztonságosan meghívható a ConstructL, mely a kétfázisú konstrukciót nem igénylő CBelsoOsztaly-t a new operátorral példányosítja (nem pedig CBelsoOsztaly::NewL-lel). Ha a CBelsoOsztaiy-nak nem sikerül helyet foglalni, a new operátor leave-el, és CleanupStackről levéve a CKulsoOsztaly megsemmisíthető. Ha nem történik hiba, akkor a NewLC visszatér, míg a NewL előbb leveszi a CleanupStackről az objektumot, és a kétfázisú konstrukció sikeresen befejeződik.
A CKulsoOsztaly példánya a CleanupStack::PopAndDestroy hívással megsemmisíthető, ha már nincs rá szükség, illetve ha nem leave-elő metódusait szeretnénk használni, akkor elég levenni (CleanupStack::Pop, és csak később megsemmisíteni (delete).
A fenti példa jól megjegyezendő, mert a konstruktorok esetleges paramétereitől eltekintve a NewL és NewLC metódusok implementációja gyakorlatilag mindig ugyanez.
Még egy fontos dolog maradt hátra. Bizonyára feltűnt már, hogy a CKulsoOsztaly destruktora akkor is meghívódhat, ha az iTagValtozo inicializálása nem sikerült. A destruktornak tehát oda kell figyelnie, hogy esetleg félig inicializált objektumot kell megszüntetnie. Mivel CKulsoOsztaly CBase-ből származik, az összes tagváltozója 0-val van inicializálva, így az iTagValtozo is. Ez a cBase viselkedés megszabadít minket sok felesleges inicializálás kiírásától, és ebben az esetben a destruktorban sem kell különösebb változtatásokat végezni, hiszen a C++ delete operátora bátran meghívható egy 0-t tartalmazó mutatóra is. Ha viszont olyan tagváltozóról van szó, amit néha megszüntetünk, néha pedig újrafoglalunk, akkor fordítsunk figyelmet a felszabadítás után a mutató 0-ra állítására. Általános szabályként szűrhetjük le tehát: ha a tagváltozónkra a destruktoron kívül memória-felszabadító műveletet végzünk, mindig állítsuk be utána az értékét 0-ra.