Pages

Wednesday, August 29, 2012

UIViewController purgeMemoryForReason and crashes in iOS App

測試 iPhone App 時候,最怕測出自己無法預料的問題。所謂無法預料的就是很難重製,但是它就這樣存在 App 上。而當我們用心就知道,一定有問題存在。當 App 用 Ad hoc 方式發佈出去,這些 App 都裝在夥伴們的 iPhone, iPad 上,當真正發生 Crash 了,真的會覺得很不安與莫名其妙不知道從何下手。

好的 Crash Report 平台 如 TestFlight, Crashlytics 等等。這些 Tool SDK 都還算好安裝,再利用它們在當 App Crash 後,
收集 Crash 的 Log 將它們送回平台上,再利用網頁的優勢來做統計,讓我們知道最後僅有線索記錄。

ISSUE UIKit-[UIViewController purgeMemoryForReason:]

終於收到問題的通知信件了,當收到這樣的標題再進去看內容,還真的讓人不解。因為現在手頭上開發的 App 已經採用 ARC (Automatic Reference Counting in Objective-C and Xcode) 的寫法了,所有的物件釋放時機都交給這個機制去運作,我都不用太擔心應該釋放時機點問題,怎麼會出現最後 Memory warning 才會出現狀況呢?

到網路上找尋大家經驗,在這則三則找到了問題主因:

主要問題所在,當 App 操作上難免會有記憶體上升,所以系統會發出低記憶體的警告 Notification。但是如果在 UIViewController 寫了 [[NSNotificationCenter defaultCenter] removeObserver:self] 如此一來會把 UIViewController 還要接收註冊的 Observer 也一帶 Release 掉,因此 UIViewController 就不會進入 didReceiveMemory,也跟著不會去釋放該要被釋放的物件,導致最後記憶體下不來,而最後整個 App 直接 Crash 收掉。


而會這樣寫法有兩個主因:
  1. 懶惰。
  2. 對於 NSNotificationCenter 了解深度不夠。

正確的寫法是像這樣 [[NSNotificationCenter defaultCenter] removeObserver:self name:NotificationName object:nil] 也就是自己註冊的 Observer 就一對一在 View 要被移除掉時候一起移除掉。這樣才不會將不屬於自己的 Observer 也被移除掉。

寫程式最怕的就是亂處理非屬於自己掌控自己建立的物件、指令,這樣整個邏輯都會亂掉。最佳寫法就是要低藕合,只處理屬於自己掌控的。對的寫法就不能偷懶。

Saturday, August 25, 2012

iOS App 自行評估上的五個重要指標

身為一個開發出 N 個 iOS App 的你,對於一個 App 的好壞是否有心中的定數呢?怎麼樣的 App 在問市以前就可以先讓自己及贊助者、團隊都滿意呢?希望推出的 App 是一問市就獲得五顆星的佳評,還是要到後面自己在從一兩顆星慢慢追趕自己理想的五顆星呢?

根據我們的經驗,會發現每個使用者在針對 App 作出 Review 的時候不外乎就圍繞在五個主題上去描述他使用的經驗。而這五個主題是我們每天在追求要更上一層樓的。


1. User Interface

有好的 App 界面設計需要兩種專長有經驗的人才在一起合作才可行。App Designer 針對現在開發中的 App 的畫面,透過自己的設計能力去規劃畫面上每個地方的細節。套用各種 Designer 在美化上的公式,例如標齊對正、字體大小掌握、圖片材質的設計。這邊需要非常細心、創意、巧思才能完成理想圖。另外一種專長則是懂得實作進來的 App Developer,他的角色需要將 Designer 的用心根據自己程式的經驗將它實作起來,並且活化它,讓原本很美的靜態 App 理想圖變成實際看到、摸到、感覺到的 App,這時候開始後面的修改於調整,很多設計不是一次就能定數的。

2. User Experience

同樣的功能,同樣的企劃相信交由不同的開發團隊設計出來的使用體驗絕對會不一樣。是否需要很多步驟才能完成操作、還是操作的過程很不順利,要使用者等待很久、或者在操作過程讓使用者覺得干擾過多,很難達到使用者心中想要操作的指令。好的 User Experience 要能在操作上即反應出使用者心中所想要、所期待下一秒會出現的結果。並且可以依序的操作下去。這方面除了用心得開發之外,也要搭配好的程式技術來做各方面進一步的調整。往往 User Experience 的改進是在 App 開發出樣貌後,開始要精進的。

3. Information Architecture

iOS App 的寸土寸金的畫面上,要擺上哪些資訊是個極大的任務,這個畫面的出現是否讓使用者看的覺得有用、資訊是否足夠、資訊是否過多過少、資訊的上到下是否都對使用者而言很有意義,是否會讓使用者迷路,不知道怎麼進行上一頁、下一頁的各種操作。Information Architecture 的用心在於一個 App 的畫面有幾個,這些畫面上的資訊呈現與彼此的關聯性。要追求的是讓使用者覺得這樣的資訊呈現剛剛好。

4. Quality

所謂的品質是達到我們所有想要提供給使用者的功能都能正常的操作,都能順利的完成。當 App 操作過程會 Crash、操作的過程等待過久、操作的過程因為某些特殊原因,且沒有處理好,而讓使用者的操作路線斷掉,上不去下不來的狀況。所以好的品質是要讓使用者不但能完成 App 提供的功能之外,還能操作的夠久到使用者自己自行離開關閉此 App。

5. Content, idea

回到最初的問題,我們為何要設計這個 App,希望讓使用者可以看到哪些功能、讀到哪些資訊、讓使用者可以解決他們生活上哪些問題。在最開頭的企劃上是否就走對了路。內容相當的重要,不希望當以上四點都做到了,但是最後使用者卻嘆了一口氣:“這個 App 好無聊噢!對我一點用處都沒有。” 所以 App 能跟使用者緊密關係是這個項目要努力與用心的地方。

最後

當我們 iOS App 開發團隊進行每天工作的時候,時常都在天人交戰的部分就是以上這些項目,這些項目都是關係著我們推出後的 App 會帶給使用者哪些體驗。而這些過程很多部分會互斥或者互補的。有互補的狀況當然是最好的,最讓人擔心就是互斥的部分。

當為了好的 Information Architecture 缺犧牲了 User Experience,那麼我們必須要停下來問自己,哪一個最重要,如果能解決 User Experience 而 Information Architecture 再用別種方式來解決是不是很好呢?

這五個項目都是我每天在問自己的問題,對於現在手頭上正在進行的 App ,他在各個項目拿到幾分了呢?我們應該要在 App 問市前,心中就已經有譜了。戰爭還沒開打前,勝負就已經揭曉,這才是身為資深專業的 iOS App PM, developer 及每一個成員該有的知識。

NSKeyedArchiver 讓 iOS App 方便儲存 Objects 的資料

在 iOS App 開發裡面,我們要儲存使用者的購物清單,而這些購物清單透過使用者在瀏覽的時候,將它們一一記錄下來,當隨後使用者要查看即可方便顯示出來。

我們會規劃 Objects 來封裝我們的各種值,這些 Object 在規劃上研究深入就要由 Domain Model 來分析了。但是還好我們的 iOS App 不會到這麼複雜,只要基本的切分清楚在使用上方便即可。而我們是否有方便的方式將這些物件儲存起來呢?

NSKeyedArchiver 可以幫我們做到,透過 archive 即可將整串從 Root Object 開始將裡面所有的 Object 儲存起來。而下次要使用的時候只要透過 unarchive 取出即可還原。


實作 NSCoding

我們有 PersonalShoppingList 這樣的 Object,而裡面有 Food 的 NSSet 和 FoodWithIngredient 的 NSDictionary,而要記得是在所有要存的 Object 都要實作 <NSCoding> 並且規劃寫 - (id)initWithCoder:(NSCoder *)aDecoder; 和 - (void)encodeWithCoder:(NSCoder *)aCoder。當然的,所有裡面會牽扯到的 Object 自己寫的都要這樣去實作。所以最後會看到自己要存的各種 Object 都會有寫好要存取的值。

儲存

回到 NSKeyedArchiver 去將 PersonalShopping 指定給 archivedDataWithRookObject。當取得 NSData 後,一起將要儲存的 Path 規劃好之後,即可將它 writeToFile 寫進檔案。

讀取

取出的時候是將 NSData 透過完整路經將檔案指定回來,再將 NSKeyedUnarchiver 的 unarchiveObjectWithData 還原回來,即可繼續使用了。

NSKeyedArchiver 對於 iOS App 有做好 Object 規劃方面,儲存和讀取方面變的很方便,只要實作了此功能,我們的任何資料即可隨時存取,相當的方便。

Sunday, August 12, 2012

挫敗的上一步

圖為 Edward In Action 所有
很多現象永遠跟你想的不一樣。不論是已經收集了滿滿線索、信心滿滿、問了所有有經驗的夥伴自己的做法,只為了證明自己沒有錯。但是問題就是這樣的攤在面前,告訴著當事人你,是的,他就是錯了,不論怎麼試還是錯了。

這個現象常常發生在我們 iOS App 開發上,每當信心滿滿的按下 Xcode 的 Build & Run,就是會去預期這一次在模擬器上開起來後的測試效果要如自己預期般的呈現,結果,很抱歉,就是沒有。請問該怎麼辦?

我最常解決這樣問題的方法是借鏡於實驗。實驗裡面有實驗組與對照組,實驗組是投入要觀察的項目,而對照組是用來對照出來的實驗結果。所以為什麼問題會發生,程式功能跑不出來。是否有成功的案例,他是怎麼做的。我們要將這兩者的所有影響要素一一條列出來,將一樣的部分在腦海中一一刪去,剩下不一樣的項目。例如根據不同的經驗是:
  1. 程式的撰寫方式有改變。
  2. 引用的元件套件版本的差異。
  3. 設定上的不同。
  4. 在某種多重參數、要素組合的條件下造成的錯誤。

在卡住過程我們是最容易急的,因為當時間一直一直過去,可是問題卻一直攤在面前。所以面對這種挫折感的時候,最需要的就是冷靜,唯有冷靜才能平心靜氣的來慢慢穩健的分析。回到上一次成功的情境,去想想看為什麼那樣下會成功,但是現在卻不一行。

很多時候,當問題卡住了,透過回到上一次成功的經驗,能帶給自己更多的信心,也才能在這過程中找出差異性。所以有時候我們想要急著前進三步遇到挫折時候,何不先退後一步,深呼吸一下呢!

Called the Shot 談每日工作生產力

圖片為 Edward in Action 所有
每當談到軟體預估,我就想到《人月神話》第八章〈預估〉[Calling the Shot] 的貝比魯斯。在 1932 年美國職棒世界大賽,全壘打王貝比魯斯在打擊前伸手指向中外野,接著隨即打出了一支全壘打。打擊前他做了預估,憑藉著自己的實力與精準度打擊出去,這就是著名的貝比魯斯的全壘打預告 (Babe Ruth's Called Shot)

每當上司、主管、業務任何非技術背景同仁會來跟我商討功能的複雜度,以及大約需要多久,這個時間就要根據自己的經驗與了解度來做分析了,這沒有標準的答案,因為中間牽扯到執行寫程式的人技術、用心程度、負責程度,再到軟體測試是否能符合功能的需求來作開發,更不用再提任何變動的因素,而被迫要做調整。這些事情只要是軟體開發,就一定會碰到。前輩以及書上經驗跟我們說,只有不斷的練習,了解自己、了解團隊才能越來越掌控所需要的時間。

Called the Shot 的精神我會將它融入到每天的工作。早上進入公司,開發團隊會做個簡單的溝通同步了解彼此在哪一個戰線上,而對於自己,就會訂下一個本日可以完成的大項目,或者本日可以完成的兩個以上小項目。以現在 iOS App 開發為例:
  • 某主題的 UITableViewController 開發,因為它是負責資訊列表的提供,所以會在本日將它所需要的資料串接,放入 UITableViewDataSource,最後再讓資料呈現在畫面上。而有兩個基本原則會一起伴隨開發的,重新整理與載入更多。一個是使用者會需要將現在畫面上的資料更新到最新,另外一個是使用者會一直瀏覽瀏覽需要更多後面的資料,而將這些資料載入畫面中。
  • 使用者登入 UIViewController 設計,它是負責讓使用者可以來登入,所以這部分要將它規劃出輸入欄位,例如 Email, password,而當使用者輸入過程可以做些輸入上驗證,送出到伺服器上作密碼驗證,再回來跟使用者表達是否登入成功與否。最後再將畫面收掉,並且讓整體的使用體驗是給使用者有登入後的感覺。
  • 整個 App 會共用的照片拍照上傳程式,這邊設計上會以方便給予照片物件集合,讓使用者在拍完照片或者挑選完照片,即可透過這隻程式來上傳,過程中表現出目前進度。最後處理上傳成功與失敗的狀況。

這些是作為每天開始工作前,去思索一遍,動筆前先動腦,敲鍵盤前先想過,在自己展開工作一天,要來完成每日小目標。

1. 有明確目標

透過這樣的思索,讓自己在一開始即可想完整體面貌,而當面貌越完整,即可知道自己手邊資料材料是否足夠,在接下來一天設定這樣目標達成的可行性提升。

2. 排除萬難

因為設定了這樣目標,所以在接下來的工作時段,就是要全心全意的來實作,任何的阻礙都會視為仇敵的方式來看待。任何的干擾包含:無關緊要的電話、無管緊要的 Email 、無關緊要的人過來你身旁找你講不相干的事情、任何突然會讓你被迫離開座位阻斷你前進的因素,這些能免就要免。

最後每天傍晚跟團隊夥伴做一天回顧,如果有順利的執行以上項目,在自己一天內能完成的目標,並且順利地達成,最後還可以展示給夥伴們看,因為預估而實踐做了哪些功能、哪些昨天沒有今天才出來,今天因為我的用心而完成的小任務。

當完成了當初自己的預估,除了可以讓夥伴們眼睛一亮之外,自己在每個重要的一天,又完成了一個小成就。