Olen käyttänyt tänä keväänä paljon aikaa iPhone- ja iPad-ohjelmointiin. Tästä syystä en ole bloggaillut kovin paljon web-asioista, vaikka niitäkin yhä kehittelen.

iPad-ohjelmointi on sikäli haastavaa, että isolla ruudulla esitettävät käyttöliittymät ja sisällöt vaativat paljon muistia. iPadissa on DRAM-muistia vain 256MB eli saman verran kuin iPhone 3GS:ssä. Näytön resoluutio ja kuvien koot ovat iPhoneen nähden nelinkertaisia, joten muistin kanssa pitää olla tarkkana.

Eniten muistia vievät sellaiset käyttöliittymät, joissa näyttöä "flippaillaan" sormella oikealle ja vasemmalle. Muistissa täytyy pitää vähintään kolme näytöllistä tavaraa, jotta skrollaus toimii ripeästi kumpaan suuntaan tahansa. Aiempia näkymiä täytyy sitten jatkuvasti vapauttaa muistista sitä mukaa, kun niitä ei enää tarvita.

Muistinhallinnassa on muutama kohta, jotka menevät helposti pieleen. Käyn ne tässä läpi.

UIViewController: viewDidUnload

Käyttöjärjestelmä lähettää toisinaan UIViewController-objekteille viewDidUnload-viestin muistin käydessä vähiin. Tässä tilanteessa .xib-tiedostosta ladattu näkymä (UIView) ollaan vapauttamassa. UIViewControllerin velvollisuus on vapauttaa kaikki viittaukset kyseisen näkymän elementteihin, kuten tekstikenttiin ja nappeihin, jotta ne poistuvat muistista. Tyypillinen toteutus voi näyttää tältä:

- (void)viewDidUnload {
    [super viewDidUnload];
    self.textLabel = nil;
    self.pushButton = nil;
}

Propertyjen vapautus deallocissa

Propertyt ovat Objective-C 2.0:n kätevä ominaisuus. Niiden avulla ei tarvitse kirjoitella erikseen getter- ja setter-metodeja. Riittää, että luokkaan määritellään @property- ja @synthesize-direktiivit näin:

@interface OmaLuokka : NSObject {
    NSString *teksti;
}
@property (nonatomic, retain) NSString *teksti;
@end

//…

@implementation OmaLuokka @synthesize teksti; @end

Jujuna tässä on kuitenkin se, että Objective-C ei vapauta propertyjä automaattisesti. Jokainen property pitää erikseen vapauttaa dealloc-viestin yhteydessä, tai muuten muisti vuotaa:

- (void)dealloc {
    [teksti release];
    [super dealloc];
}

Vuotamisen voi todeta ajamalla iPhone/iPad-sovelluksensa läpi Xcoden Leaks Performance Toolilla. Se näyttää suorituksen lopuksi ne objektit, jota ei vapautettu kunnolla.

SQLite ja binääridata

Eräs iPhonen ja iPadin kätevimpiä ominaisuuksia on Core Dataan sisäänrakennettu SQLite-tuki. Sovellus voi varastoida kaiken datansa paikalliseen SQL-tietokantaan. Melko usein tulee sitten eteen tilanne, että tietokannasta ladataan muistiin esimerkiksi 100 riviä UITableView-listausta varten.

Ongelmia aiheuttavat tässä sellaiset tietokantataulut, joihin on tallennettu suuria binääriobjekteja, kuten JPEG- ja PNG-kuvia. Jos tietokannasta ladataan muistiin 100 riviä, joissa jokaisessa on mukana 500 kilotavun kuva, muistia kuluu hetkessä 50MB. Jos rivejä on 500, hakutulokset eivät enää mahdu muistiin ollenkaan.

Ratkaisu on tallentaa binääriobjektit erilliseen tauluun, jolla on one-to-one relationship alkuperäisen taulun riveihin. Tällöin Core Data lataa binääriobjektit faultingia käyttäen automaattisesti muistiin vasta sitten, kun niihin viitataan koodissa. Esimerkiksi silloin, kun käyttäjä klikkaa luettelonäkymästä yhtä riviä ja sovellus siirtyy detaljinäkymään.

Käytön jälkeen binääriobjektit voi poistaa muistista ja palauttaa faulteiksi refreshObjectilla tähän tapaan:

[managedObjectContext refreshObject:binaariObjekti mergeChanges:NO];

Tyypillisesti tämä tehtäisiin silloin, kun isoa binääriobjektia käyttänyt detaljinäkymä suljetaan ja palataan luettelonäkymään.