vbali

<%= "addicted to code" %>

Key-value observing

Minden Objective-C oktatási anyag már a kezdetek kezdetén tárgyalja a KVO témakört, amely szorosan kapcsolódik a KVC-hez:

Key-value coding is a mechanism for indirectly accessing an object’s attributes and relationships using string identifiers. It underpins or is related to several mechanisms and technologies special to Cocoa programming, among them Core Data, application scriptability, the bindings technology, and the language feature of declared properties. (Scriptability and bindings are specific to Cocoa on OS X.) You can also use key-value coding to simplify your program code.

Az Observer pattern egy elég erőteljesen alkalmazott tervezési minta a Cocoa keretrendszerben. Amikor minden az elvártnak megfelelően működik, olyankor a KVO egy áldás, amely óriási terhet vesz le a fejlesztő válláról:

Key-value observing provides a mechanism that allows objects to be notified of changes to specific properties of other objects. It is particularly useful for communication between model and controller layers in an application. (In OS X, the controller layer binding technology relies heavily on key-value observing.)

Például: megváltozik az egyik objektum valamely tulajdonsága, amelyet az őt figyelő másik objektum észrevesz és ennek következtében frissíti valamely saját tulajdonságát. Ez utóbbit figyeli egy másik observer, amely mondjuk a UI megjelenítéséért felelős. Észreveszi, hogy változás történt a megfigyelt objektumban és frissíti a felületet. Külön kód megírása nélkül, automatikusan. Egy tulajdonság megváltozása értesíti a másik objektumot, amely értesít egy harmadikat stb. és végül a felhasználói felületen megjelenik a változás, mindenféle spagetti kód nélkül. Super simple!

Amikor azonban valami nem úgy működik ahogyan azt a fejlesztő szeretné, olyankor könnyen rémálommá válik az observer pattern használata, mert irtó nehéz nyomon követni, hogy pontosan mi történik a háttérben. Az objektumok üzengetnek egymásnak, ezáltal fel van építve egyfajta logikai riadólánc. Majd egyszer csak “eltörik” valami a kódban és nem frissül a felület, vagy nem az jelenik meg ami elvárt volna. Hol lehet a probléma? Sok fejfájást tud ez okozni, amikor már egy kicsit összetettebb az adatszerkezet.

A minap óriásit szívtam a fent említett példával. Kicsit változott egy adatszerkezet és valahol megszakadt a riadólánc. Egy bizonyos osztályban a KVO kifogástalanul működött, míg ennek a leszármazottjában valami rendellenesség lépett fel és a hozzá kapcsolódó felület nem reprezentálta a változásokat. Az NSKeyValueObserving protokollnak van egy osztálymetódusa, amely automatikusan regisztrál egy (vagy több) observert a paraméterben megadott tulajdonsághoz. Ha a megfigyelt tulajdonságok valamelyike változik, akkor azok automatikusan értesítést küldenek a változásról:

+ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key

When an observer for the key is registered with an instance of the receiving class, key-value observing itself automatically observes all of the key paths for the same instance, and sends change notifications for the key to the observer when the value for any of those key paths changes.

Bár a megfelelő osztálymetódus létre volt hozva az osztály implementációjában, mégsem történt meg az automatikus KVO. Kis debuggolást követően kiderült, hogy a fent említett osztálymetódus eleve meg sem hívódik, így be sincs regisztrálva a megfelelő observer a tulajdonsághoz. De miért nem hívódik meg az osztálymetódus? Pár órás küzdelem után legyűrt a fáradtság és munkámat végezetlenül hagytam. Reggel ismét nekiültem, hogy kiderítsem a probléma okát. Átolvastam a kapcsolódó dokumentációt és pár percen belül kiszúrtam a nyilvánvalót, amely feketén fehéren le van írva a doksiban: When an observer for the key is registered with an instance of the receiving class …

OMG, egész egyszerűen nem volt regisztrált observer a UI frissítését végző osztályban az adott tulajdonsághoz, így az osztálymetódus meg sem hívódott, így az adott tulajdonság nem frissült automatikusan egy másik tulajdonság függvényében és így állt elő az anomália. A két soros javítás (observer regisztráció és felszabadítás) pár másodpercet vett igénybe és működik minden ahogy annak kell.

Erről beszéltem. Amikor minden az elvártnak megfelelően működik, akkor a KVO rengeteg időt és energiát spórol meg a fejlesztőnek, de az adatszerkezetek változásával ez könnyen megváltozhat, úgyhogy csak ésszel!

_MASReceipt

A ReadKit kapcsán kicsit kutakodni kezdtem, hogy hogyan függetlenítették az App Store-tól a programot. Meglepő módon a binárist és minden egyéb, az alkalmazáshoz kapcsolódó állományt érintetlenül hagytak, mindössze egy könyvtárat (_MASReceipt) és annak tartalmát törölték ki az alkalmazáscsomagból.

Kipróbáltam, hogy mi történik az eredeti, MAS-ból letöltött alkalmazással, ha ezt a könyvtárat eltávolítom. Bár az áruházból letöltött alkalmazások DRM-mel védettek, ez csak a bináris módosíthatósága ellen véd. A könyvtár eltávolítását követően az alkalmazás probléma nélkül elindult bármelyik gépen. Kipróbáltam más csapatok által készített alkalmazásokkal a “trükköt”, ami néhány esetben működött, de a népszerűbb alkalmazások a módosítást követően nem voltak hajlandóak elindulni. Ebből sikerült levonnom azt a kézenfekvő következtetést, hogy a _MASReceipt-tel a fejlesztőnek kell kezdenie valamit. De mi is pontosan ez a _MASReceipt?

Amikor megvásárolunk egy alkalmazást az Store-ból, akkor a letöltési folyamat részeként az alkalmazáscsomagban létrejön a fent említett könyvtár egy receipt nevű állománnyal. Ez egy Apple által digitálisan aláírt állomány, amely az alkalmazásról (csomagazonosító, verziószám stb.), valamint a letöltő számítógépről (IOMACAddress) tartalmaz információkat. A receipt pontos leírását az ide vonatkozó Apple Developer cikk tartalmazza: Validating Mac App Store Receipts.

A lényeg, hogy a receipt ellenőrzése a fejlesztőre vár. Nincs arra vonatkozóan semmilyen előírás, hogy ezt az állományt ellenőrizni kell, de ha ezt mellőzzük, akkor a fent ismertetett egyszerű trükkel egyszerűen lehet függetleníteni az alkalmazást. Ha pedig beépítjük a megfelelő ellenőrzést, akkor a cracker ilyen módon oldja meg a problémát:

3

Akkor mégis mi lehet a megfelelő védelem? Egyszerű:

  • jó minőségű szoftvert kell írni,
  • megfelelően kell beárazni,
  • és meg kell hallgatni a felhasználókat.

Ha valaki nem akar, úgysem fog fizetni az alkalmazásért. A meglévő felhasználókra és a potenciális vásárlókra kell koncentrálni!

A két kalap

Kevés dolog tud jobban zavarni a ”bűzlő kódnál”, pedig időnként együtt kell élni ezzel a kellemetlenséggel. Naponta szembesülök az újratervezés szükségességével amikor egy-egy új szolgáltatás megvalósításán dolgozok. Ebből kifolyólag gyakran esek abba a hibába, hogy nem tudom épp melyik kalapot viselem, az újratervezőét vagy a szolgáltatás bővítőét. Mivel a két tevékenység végzéséhez teljesen eltérő szabályokat kell alkalmazni, így egyáltalán nem mindegy, hogy épp melyik kalap van rajtunk.

Napok óta törtem a fejem, hogy hol olvastam erről a problémáról korábban, míg végül bevillant Martin Flower Refactoring című könyve:

Amikor szoftverfejlesztésre használjuk az újratervezést, két különböző tevékenység között osztjuk fel az időnket: a szolgáltatások bővítése és az újratervezés között. Amikor új szolgáltatásokat adunk a programhoz, nem változtatjuk meg a meglévő kódot, csak új lehetőségeket adunk hozzá. Előrehaladásunkat tesztek létrehozásával és működőképessé tételével mérhetjük. Amikor újratervezünk, akkor szándékosan nem veszünk fel új tevékenységeket, csak átépítjük a kódot. Nem készítünk új teszteket (kivéve ha egy korábban kihagyott esetet találunk), és csak akkor változtatunk meg egy tesztet, ha erre mindenképpen szükségünk van a felület megváltoztatásának kezelése érdekében.

A szoftver fejlesztése során valószínűleg gyakran kapjuk magunkat kalapcserén. Először megpróbálunk a kódhoz adni egy új szolgáltatást, és rájövünk, hogy ez sokkal egyszerűbb volna, ha más lenne a kód szerkezete. Tehát kalapot cserélünk, és egy ideig újratervezünk. Amikor a kódnak már jobb a szerkezete, kalapot cserélünk, és megírjuk az új szolgáltatást. Amint működőképes lesz, rájövünk, hogy túl bonyolultan kódoltunk, így ismét kalapot cserélünk, és újratervezünk. Mindez talán csak tíz percig tart, de fontos, hogy mindvégig tisztában legyünk vele, hogy éppen melyik kalapot viseljük.

Oké, mostantól tisztában vagyunk azzal, hogy ha kalapot váltunk akkor mindig észben tartjuk, hogy épp melyiket viseljük. Ám az újratervezés során mindig szembejön egy olyan korábban írt kódrészlet, amelyre - bár nem kapcsolódik közvetlenül az aktuális újratervezési folyamathoz, - mégiscsak ráférne egy kis átalakítás. Bár épp az újratervező kalapjában tevékenykedünk, mégsem tehetjük meg büntetlenül, hogy ész nélkül nekiesünk a szemünk elé kerülő összes problémás részletnek, ez ugyanis újabb virtuális kalapcserével járna, majd végül egy feneketlen rekurzióban találjuk magunkat. Tudom, ez elmondva egyszerűnek hangzik, a gyakorlatban nagyon nehéz ellenállni a kísértésnek.

Szerencsére az agilis fejlesztőknek minden problémára van egy alkalmazható szabálya, törvénye vagy javaslata:

Sose szakíts meg egy megszakítást

Kent Beck — Test-Driven Development

Adj ki korán, adj ki gyakran

Az év elején elhatároztam, hogy nyilvántartást fogok vezetni a munkaidőmről. Egy egyszerű időnyilvántartó alkalmazásra volt szükségem amelyben nyilvántarthatom az elvégzett feladataimat és lekérdezhetem, hogy az egyes projektekkel illetve az egyes tevékenységekkel mennyi időt töltöttem ténylegesen és arányaiban. Nem feladatkezelőben és todo listában gondolkodtam, hisz olyanom már van ami jól bevált. Sokkal inkább egy olyan alkalmazást szerettem volna melyet a todo lista mellett, annak kiegészítéseként tudok használni és pontos képet ad arról, hogy milyen hatékony a munkavégzésem. Ezen túlmenően mindenképp egy natív Mac-es alkalmazás illeszkedett volna legjobban az elképzeléseimhez.

Hosszas keresgélést követően sem találtam megfelelő szoftvert. A legtöbb időnyilvántartó programmal az a baj, hogy az elszámolásra és a számlázásra megy rá. Ezzel semmi gond sincs, ha valakinek erre van szüksége, engem azonban sokkal jobban érdekelt, hogy az egyes projektjeimmel mennyi időt töltök és a munkaórák hogyan oszlanak meg az egyes projektek között. Ezt a gondolatmeneten tovább boncolgatva arra is kíváncsi voltam, hogy a munkaidő milyen tényleges tevékenységekkel telik és ezek a tevékenységek milyen arányt képviselnek napi, heti illetve havi bontásban. Mivel nem találtam ilyen megoldást, így szabad óráimban nekikezdtem a Durations alkalmazás tervezésének, majd a fejlesztésnek.

Durations draft

A projekt remek esettanulmányul szolgált. Egyrészt sikerült magam jobban belevetni a Cocoa mélységeibe, másrészt megtanultam, hogy amilyen korán csak lehet ki kell adni a szoftver első verzióját.

A programozók maximalisták, gyakran szélsőségesen. Hajlamosak egy megoldásra váró problémára sokkal több időt szentelni mint amennyit az ténylegesen megérdemel. Nem azért mert képtelenek az adott megoldással záros határidőn belül előrukkolni, inkább azért, mert nem találják az adott megoldást elég frappánsnak vagy letisztultnak, esetleg kicsit csúnya vagy bugos. Úgy gondolom, hogy ez így rendjén is van. Nincs azzal semmi gond, ha egy fejlesztő egészségesen törekszik a maximalizmusra, sokkal rosszabb ha valaki igénytelen a kódjára. Van azonban egy határ, melyet átlépve ez a törekvés már inkább hátráltató.

Van egy szoftverfejlesztési bölcselet, miszerint adjuk ki hamar, adjuk ki gyorsan. Ezen filozófia szerint adjuk ki a szoftver első verzióját amilyen hamar csak lehet, majd gyakran adjunk közre frissítéseket (az Apple jóváhagyási szokásait figyelembe véve ez akár problémás is lehet), s közben figyeljük a felhasználók visszajelzéseit. Ez az a filozófia melyet most, hogy a Durations hamarosan elérhetővé válik, sikerült magamévá tennem, tekintve, hogy az alkalmazás csaknem három hónapig porosodott, miközben olyan feature-ök hibáitól szenvedett, melyek nem is szerepeltek az eredeti célkitűzések között.

Mint említettem, a célkitűzés egy natív Mac-es alkalmazás elkészítése volt. Menet közben azonban felmerült, hogy milyen király lenne, ha a jövőben iPad-en és iPhone-on is lehetne mérni a munkaidőt, illetve riportálni. Ehhez azonban szükség lesz az adatok szinkronizálására, melyre a legkézenfekvőbb megoldás az iCloud. Bár nem első körös célkitűzés volt, mégis napokat öltem bele az iCloud szinkronizáció megvalósításába, amely végül nem váltotta be a hozzá fűzött reményeket. Mindez hónapokkal meghosszabbította az első kiadás elkészülését. Az iCloud szinkronizációhoz hasonlóan, eszméletlen mennyiségű időt fektettem a felület csiszolgatásába, csinosítására is. Bár a végeredménnyel elégedett vagyok és úgy gondolom, hogy az alkalmazás előnyére váltak ezek a finomítások, mégsem ez fogja meghatározni, hogy a szoftver sikeres lesz-e.

Durations 1.0

Szerencsére menet közben sikerült ráeszmélni, hogy rossz irányba halad a fejlesztés. Szükségem volt egy kis pihenőre, hogy ismét megtaláljam a fókuszt és a motivációt és megújult erővel folytassam a fejlesztést. Ebben a kis pihenőben készült el az iCacti for Mac. Mindössze az iOS-es feature set implementálása volt a cél, megfejelve a Mountain Lionban debütált Notification Center figyelmeztetésekkel. Ahogy Jason Fried mondja: Less is less.

A korai kiadásnak megvannak a maga előnyei és hátrányai, de ha mérlegre teszük ezeket az érveket, akkor nagy valószínűséggel a korai kiadás irányába billen a mérleg nyelve. Persze ez alól kivételt képeznek azok a speciális rendszerek, melyek emberéletekért felelősek: az orvosi műszerek szoftverei vagy például a légi közlekedést irányító rendszerek, de ezektől most tekintsünk el.

A Successful Software kellően kivesézte a témát. A lényeg:

A korai kiadás hátrányai

Szolgáltatások hiánya: felesleges azon rugózni, hogy a konkurencia szoftverében több olyan feature is van, melyet az enyém nem tartalmaz. Lehet, hogy a felhasználók nem is használják azt, vagy nincs akkora jelentősége, hogy hónapokra megakadályozza a kiadást.

Hírnév: a korai kiadás nem tesz rosszat a márkának? Természetesen egy rossz minőségű szoftverre nincs bocsánat. Nem a minőséget, hanem a funkciók számát kell alacsonyan tartani a kezdeti kiadásban. Előre meg kell határozni azt a minimális követelményt, amellyel a szoftver elstartolhat, miközben jól használható marad. A többi pedig jöhet majd a frissítésekkel.

Támogatási többletmunka: amint megjelennek a vásárlók, támogatást kell nyújtani a szoftverhez. Minél korábbi a kiadás, annál több dolgod lesz a támogatással.

Kiadási többletmunka: a frissítések kiadása rengeteg időt vesz igénybe, még akkor is ha a legtöbb részfeladat automatizálva van. Minél több szoftverfrissítés jelenik meg az alkalmazáshoz, annál több időt kell teszteléssel és honlapfrissítéssel tölteni.

A korai kiadás előnyei

Felhasználói visszajelzések: Minden termékbejelentés egy nagy kérdés. Ha sok a versenytárs, felmerül a kérdés, hogy a vásárlók számára mennyire lesz sikeres a termék a konkurenciával szemben. Ha nincs konkurencia előfordulhat, hogy nincs is piaca a terméknek. Az is kérdéses, hogy a termék mely tulajdonságait szeretik a vásárlók és mennyit hajlandóak fizetni érte. A válaszokat a felhasználóktól lehet megtudni. Amint megjelennek a vásárlók, biztos lehetsz benne, hogy a tudtodra fogják hozni, hogy miben várnak előrelépést.

Motiváció: a valódi vásárlók nagyon jót tesznek a motivációnak. Remeteként fejleszteni hónapokig vagy évekig, mindenféle visszajelzés nélkül, könnyen alááshatja a motivációt és a termékbe vetett hitet.

Korai bukás: minden szakértelem és törekvés dacára sok termék megbukik. A te terméked lehet a következő. Ha pedig bukásra van ítélve, akkor ez történjen meg amilyen korán csak lehet, hogy minél hamarabb bele tudj kezdeni valami másba, valami újba.

Megtérülés: minél korábban kezdesz el értékesíteni annál korábban megtérül a befektetésed.

As a rule of thumb, I would say that if you aren’t embarrassed by the lack of features in v1.0, then you didn’t release it early enough.

Successful Software

Effective programming

A héten fejeztem be Jeff Atwood blogkönyvének, az Effective Programming: More Than Writing Code olvasását, amelyre Orcsik Antal bejegyzése hívta fel a figyelmemet. Jeff a Coding Horror blog szerzője, a StackExchange alapítója és igazi hardcore programozó, akit remek írási készséggel is megáldott a sors. A könyv, amely tulajdonképp a Coding Horror blogon megjelent bejegyzések kivonata, egy igazán lebilincselő olvasmány, szinte le sem tudtam tenni. Azokra a mindennapos kérdésekre keresi a választ, amelyek minden programozót érintenek, kezdve a megfelelő eszközök kiválasztásától (billentyűzet, szék, világítás), a hatékony programozási munkán át, egészen a marketing tevékenységig. Mindezt könnyen emészthető és olvasmányos formában, mellőzve minden Happy Talk bullshitet.

Korábban Jason Fried és DHH Rework könyvétől vártam hasonló élményt, de annál sajnos teljesen elmaradt a tűzijáték. Kicsit unalmas volt és leginkább a klasszikus self-help könyvekre hajazott, kevés konkrétummal, sok lelkesítő beszéddel. Szóval nem igazán tetszett. Ezzel szemben az Effective Programming szórakoztató, mégis kellőképp szakmai olvasmány, izgalmas, színes témákkal, melyek Joel Spolsky régi klasszikusait juttatták eszembe.