code

2017年10月30日 星期一

Stanford ios 10 with Swift 筆記6 - Memory Management

ARC (Automatic Reference Counting)

reference types (i.e. classes) 是儲存在heap中,要如何從heap釋放這些不使用的objects?
Java使用garbage collection,但是swift並不使用garbage collection,而是ARC

每次某個object被referenced,ARC就會增加此object的reference count,當然如果reference消失也會減少reference count,一個object的reference count為零的時候,就能從heap中釋放。

ARC是全自動的,programmer不能控制heap release。


影響ARC的方法

strong: 這是標準的reference,也就是某個var / let reference到的object,只在這個var/let 沒用到 (例如也deallocated / out of scope),才會decrease reference count。

weak: 這只能用在 var/let是一個 Optional,因為他的語意是“如果某個object在heap中已經沒有人reference,那就deallocate它吧,我不care。如果還有人reference他,那我就持續weak reference他”。所以是一種消極的reference。當這個object因為沒人reference而被deallocated from heap的話,這個var/let 會被設定成nil,這也是為什麼只能用在Optional的關係。

unowned: 強制設定某個heap中的object不要再做ARC,這個等於破壞了ARC機制,非常少使用,通常用來打破memory cycle situation。

Memory Cycle

這有點類似deadlock,就是兩個object互相strong reference到彼此。


Closures and ARC

closures是一個function object,是一個reference type所以存在heap中,他對他code block中的任何reference types會有strong reference注意這個隱性的事實容易造成memory cycle,如果code block中的reference types又有strong reference到這個enclosing closure的話。

假設有以下一個method:


client ViewController 假設傳入一個closure使用如下:


但是如果closure是trailing parameter,swift允許使用以下syntactic sugar:



甚至更進一步利用type inference簡化:


這樣寫很容易讓人忘記這事實上是一個trailing closure parameter,所以也就忘記了closure會對所有其內的reference types做strong reference。

還好swift compiler不允許這樣被compile,他希望programmer要明確知道這個closure有strong reference to self  (outer class, i.e. ViewController):


注意如果不幸ViewController又有strong reference到這個closure的話,memory cycle 就會形成了!這種現象很可能發生,因為reference關係通常不明顯,需要考慮清楚


解決此memory cycle:
1. 在closure中宣告一個local variable me,指向self (ViewController),這樣當然無助於break memory cycle,因為這又對self增加了一個strong reference(closure -> me -> self)。

不過如果採用unowned宣告me指向的self不要增加reference count:



me被使用的時候,如果self已經不在heap的話,此時就會crash,但是self是一個viewcontroller,所以這個情況crash也是合理的 (也是啦~)

不過為什麼不用weak? 其實用weak是比較preferred way,不過注意weak 一定是Optional才能宣告,所以要用?來防止self真的在heap中被deallocated:




2017年10月29日 星期日

Stanford ios 10 with Swift 筆記5 - Chain of MVCs & Viewcontroller life cycle

MVCs of MVCs

某個MVC的view,邏輯可責任可以放在另一組MVC上,例如以下例子:



TabController負責切換每個tabicon所代表的view,但是每個被切換的view怎麼運作則是相對應的MVC來負責(畫出黃色的區域,以及互動邏輯)。

所以這個UI總共有五個MVCs(包括Tab bar MVC):



下面的UISplitViewController有三個MVCs (包括UISplitViewController):



 下面這個UINavigationController有兩個MVCs,上方area是由UINavigationController畫出,其他則是被包含的SettingsVC畫出:



Segue

發音就是滑板車segway。

UINavigationController是stack-based 來儲存每個要畫出的MVC,push and pop。
每次push或是pop一個MVC,這個動作稱為segue。
注意每個push進來的MVC是allocated in heap,而整個navigation的邏輯是stack
當然如果popped,就會deallocated。

基本上從一個MVC移到另一個MVC就叫做segueing。



seque有一些方法要去override,用來決定下一個進來的MVC要做些什麼事。


ViewController Life Cycle

(記得永遠都要呼叫super.xxxx())


1. creation: 通常由storyboard創造instance
2. preparation: 如果是由sequed,就會進入preparation。
3. outlet setting
4. viewDidLoad() : 進入這邊確定了所有的outlet set,所以適合做ViewController的setup / initialization,但是不要做任何關於geometry / device-specific的計算

viewWillAppear() : 在進入前景前會呼叫的callback,這邊可以做關於model modification的事情(例如重新從遠端抓資料),因為許多MVCs只需要在畫在前景時去update。另外由於這邊確定要畫在螢幕上了,所以computation expensive也可以考慮放在這邊(看情況也可以放在viewDidAppear)。

viewWillDisappear(): 可以做一些clean up動作,但是也要看情況是否做computation expensive (通常不會clean up還需要大量計算)。



Geometry changed

view bounds不能在viewWillAppear / viewDidAppear 確定,所以不要在此兩個callback使用。通常我們依賴autolayout去設定geometry,不會直接coding相關設定。

不過如果真的要做geometry computation的話,當view bounds/frame改變的時候(例如rotation,但不止bound/frame改變才會觸發,有很多可能會觸發這個callback),會經歷以下callbacks:

viewWillLayoutSubviews()
.. AutoLayout在此中間時刻發生 ..
viewDidLayoutSubviews()

如果想要參與rotation animation,則可以使用 viewWillTransition 這個callback。

Memory Low

則didReceiveMemoryWarning 這個callback會被呼叫。
在app中最好盡量去release站著大量記憶體,但是可以快速allocate / deallocate的物件。


Life Cycle Summary




2017年10月28日 星期六

Agile Software Development 2 - Agile Principles

Waterfall (1970, Royce) :

就是五十年後我們仍然最常用的model。




這是很理想性的,甚至只能是教育性的,主要問題:
(a) coding phase來的太晚,software只關乎coding成果,結果真的實作卻是放在如此後面的流程
(b) requirements一旦決定之後,基本上進入program design之後就不能改變
(c) 現實中的維護maintenance phase沒考慮到,但實際上佔了軟體工程成本的70% (?)


Agile view on Requirements

對requirement的agile 方面的批評是
1. requirement實務上是常改變(changing)的,太早固定只是自以為是
2. requirement文件基本上是個浪費(waste)

XP: 蒐集requirements是一個經常性的動態活動,而非產出一個靜態固定的文件

Scrum: 沒有前期的analysis / design phase,而是一直處於不斷的sprints cycle來蒐集requirements

Lean: 看不懂!


總之,agilist 認為requirement gathering phase不該太長,也不該是靜態性的結束。(不過要注意很多自認apply agile methods的人,過分低估前期的requirements gathering,造成工程上的大災難)。


Common Agile Principles (只是參考,不該形成教條)

1. 顧客為主:XP希望引入顧客成為team member (embedded customer),但實務上很難找到這樣的顧客,而且顧客有可能代表性不足。Scrum: 定義product owner來取代傳統manager,這是體制內的角色,比起embedded customer來說較為權責相符。

2. Accept Change: 這對應到OOP中的extendibility,但是實務上不一定能簡單做到。

3. self-organized team: 這仍是對manager角色的弱化,manager主要功能在agilist眼中:
  • 鼓勵進度
  • 幫忙找出問題
  • 移除障礙
  • 對困境提供協助
  • 鼓舞成員,抵消負面批評
  • 授權team member能直接面對客戶

4. 可持續步伐:不要壓榨才能長久

5. 只做出使用者要的feature,不要做自己想像的: 降低複雜度,也省非常多錢 (亂做一堆features會使得cost非線性成長)

Toyota的車廠管理衍生出Lean management,以下是幾種可能的"software waste":


(a) 就是產能過剩,做出沒人要的東西
(b) 庫存太多,做了不賣浪費時間浪費錢,打擊士氣
(c) 做一堆對產出產品無用的步驟
(d) 技能或經驗不足所以花一堆時間在蒐集資訊
(e) 沒被找出的bug costs money!
(f) 等待相關人士給予意見
(g) 不同framework之間的協調時間

極簡者 / 短視者 觀點: minimum viable product / 不需要中間產物 (文件 圖片等)。但這也招致了不少software engineering從業者的批評,本課程認為這些觀點過於極端,不是好的software engineering。

6. iteration時間縮短但是更多,iteration過程中要freeze requirement

傳統的development iteration的模式是先建立infrastructure,然後一層層往上疊起,但這就讓usable product/code 能出現的時間往後延長了不少:


agilists preferred的方式是快速但partial functional的release:


不過這門課老師覺得可以採用dual development,首先對risk最高的底層infrastructure花全力去完成 (比一般agile iteration更長),之後才進入agile iteration。


7. 所有tests未通過前,不要implement new features
這個被列為最重要的agile教條之一,但是實務上我覺得要怎麼推動是一個問題,如果tests都是類似unit tests,那應該是比較容易推動。

8. use cases / scenarios 來描述requirements
好處是這讓測試的規格確定,同樣老師批評:




Lean specific principles

Lean的waste觀點:


這是一個不錯的negative impact checklist,可以實際用來檢視專案是否有浪費的情況。

Lean的learning觀點 (這是積極採取行動,透過資訊蒐集):


1. test給我們關於code的更多資訊
2. 嘗試各種solution會比寫文件或是花時間規畫能得到更多的資訊

以上五點其實就是讓我們盡量在開發時獲得資訊最大化的方法,agilist也希望能delay decision making,直到更明確information獲得之後,所以他們不喜歡大量的事前planning,但是要小心可能造成拖延症! 這個拿捏可能跟經驗有關



Crystal specific principles

Crystal方法主要focus在"focus" XD

1. 不要讓developer multitasking,避免分心
2. 不要中斷developer的開發連續流程,至少保持兩小時不打擾
3. team需要明確知道goal是什麼,只focus在goal

4. 人本尊重 (鼓勵發表意見的安全感,不受嘲弄)


XP specific principles

1. 人本尊重

  • 安全感
  • 成就感
  • 歸屬感
  • 成長性
  • 親密感(?!)



Lean / Scrum specific principles

天下武功,為快不破。要對要建立在快速information gathering
每個iteration中,盡快給出end product,獲得feedback,進入下一個iteration。

Scrum specific principles

user stories (requirements)認為是可以獨立存在的,不依賴其他的user stories。
這就是所謂的feature independence。

不過這還是case by case。


2017年10月25日 星期三

Stanford ios 10 with Swift 筆記4 - Gestures

辨識Gestures

有兩種方法:
1. 在Controller class中對某個UIVIew加入gesture recognizer


UIPanGestureRecognizer是一個abstract base class UIGestureRecognizer的concrete subclass。此abstract base class定義了各種states:



注意Xcode利用 #selector( method )來註解 這邊target的event handler是一個ViewController instance中的pan method,事實上"ViewController."這個naming是不需要的,因為default就是self,target handler的可能定義如下:



當然一個gesture recognizer會有一些有用的methods:


setTranslation比較奇特,算是重新設定判定的起始點,看情況可能會需要這個功能。



2. 或是提供某個gesture callback





Design Patterns 1 - Creational Patterns

軟體工程中的食譜

業界專家行之有年的經驗證明出這些patterns是經常性出現的設計難題的最好解答,所以是很實用的。

工程師之間了解design patterns的話,溝通也更精準,例如"這邊使用一個singleton pattern"。


Gang of Four

總共有23種,分成以下幾類:
1. creational patterns: 講述如何製造物件
2. structural patterns: 講述物件之間的關係
3. behavioral patterns: 講述物件間的責任,如何達成common goal

類別沒有那麼清楚,也是有模糊空間。


Singleton Pattern

某個class只能創造唯一一個instance,這種creational pattern稱為Singleton。
通常用在一個globally accessible object,而且合理的只能存在唯一的instance,例如系統的preference。

implementation可以簡單的利用private constructor,以及private static instance variable:



至於globally accessible的部分,就在於提供了一個class method來得到這個private instance variable。

另外這邊也用到了lazy creation,這對instance creation非常耗resource的class來說是個節省資源的好方法。

注意! 上面的implementation不是thread-safe,當多個thread嘗試獲取這個singleton instance的時候,race condition就會產生,單純對getInstance()做synchronization保護的話,只能保護object creation是synchronized,但不能確保這個singleton屬性的改變是thread-safe。


Factory Object Pattern (Not GoF pattern!)

簡單來說就是讓client把建立object 的責任delegate出去給一個factory object,在client選型號之後,一個工廠object就把東西丟出來給client,以下面例子:


這個pattern依賴subclassing,而顧客指定的型號就是各種不同的subclasses,真正創建一個class instance的過程稱為concrete instantiation。

client使用方式如下:



優點: 工廠新增或是刪除或是改變instantiation的方式,都不會動到client code。

限制: 不過client只能面對base class interface,舉例來說,各種Knife的subclass都被client用Knife type variable去承接,除非約定好工廠出來的東西一定是那個subclass,那就可以利用type casting去安全使用之。



Factory Method Pattern

Factory Method pattern不使用一個工廠物件來製造商品,而是在商品的abstract base class定義了一個abstract factory method,所有的concrete sublcasses都得implement這個factory method,所以把製造細節留給subclasses去決定,其實就是一般OOP的概念。




2017年10月23日 星期一

Stanford ios 10 with Swift 筆記3 - Custom Views

View Hierarchy

view可以有subviews,最後組成一個hierarchy,最上層是UIWindow,通常不會碰到它。而ViewController有一個property "view",這就指向了這個hierarchy的最上層view (但是不是UIWindow? 不確定)。

interface builder會把storyboard存成xml,所以要programmatically create一個view,必須要用:


CGFloat

跟UIView drawing有關的float / double 都應該要用CGFloat 當作UIView座標系統內的單位。


UIView 座標系統

很瞎的是,原點在螢幕左上角,往右增加x,往下增加y。

point: 座標系統內的個體點,是座標系統內的最小單位,這是一個抽象的單位。
pixel:螢幕上實體能產生一個顏色的最小單位,是實體單位,有可能多個pixel組成一個point,也有可能是1對1的關係。

Bounds (CGRect)

UIView在自己的view coordinate system中所佔(畫出)的points範圍。

center and frame 

容易混淆的是,UIView的這兩個屬性事實上是說在superview coordinate system中的位置與大小。由於有可能在superview中被rotate或是scale,所以UIView的frame != UIView的bounds,這個要注意:



如何畫出一個UIVIew?

override UIView的draw function,系統要填滿UIView所佔的bounds中某個CGRect (小於等於bounds) 的時候,他就會來呼叫這個draw function。

這是系統callback,我們不能呼叫,如果要redraw,就要利用setNeedsDisplay()

1. 繪製UIView時,我們需要一個context,這定義了畫在哪個buffer (有可能是screen / printing / off-screen等),在draw(CGrect) callback中 利用 UIGraphicsGetCurrentContext function 獲得。

2. 創建paths
3. 設定paths 屬性
4. fill or stroke


畫出Text

通常我們用UILabel,但客製化一個view的時候,可能需要製作自己的text,可以用以下的code:


NSAttributedString 不是 NSString,總之是給custom draw用的,不過這邊有一些眉角,看起來似乎很煩,用到再說吧。



畫出image

通常我們也是用UIImageView,但是要自己畫的時候就使用UIImage。



當UIView bounds改變的話?

UIView bounds改變的時候,系統預設不會去呼叫draw function,而是會根據此UIView 的 contentMode:UIViewContentMode 來決定是要scale或是translate,但是這通常會看起來很怪,所以如果要在bounds change的時候自己決定如何重劃的話,就要把contentMode設定成 redraw,如此我們的draw function就又會被系統呼叫。


Reset to Suggested Constraints

interface builder可以幫我們自動根據snap to edges (藍色線距離四個邊界的長度)做constraints:


可以看到貼近四個edges的constraints已經被設定了:



export UIView 屬性到Interface Builder

利用Xcode preprocessor @IBDesignable, @IBInspectable


可以看到在IB中:


這一點比Android Studio 酷多了!!!!!!!!!!












2017年10月21日 星期六

Deep Learning 筆記2 - Logistic Regression

Logistic Regression for Binary Classification

LR是用來做二元分類的,例如判斷一張影像中的動物是貓或不是貓。假設有一張影像64x64大小,則以RGB 3個channel來說的話,總共有64x64x3 = 12288個數值,如果把這12288個數值當成feature vector的話(1D),定義 feature vector維度 = n = nx = 12288 :



Notation

ML的課很容易被數學符號搞混,先說明以下符號是本課程使用:
首先對classification來說,input x一定是一個nx維度的vector,而output y不是1就是0:


training sample 會是一個pair (x, y),每個data set總共有m個training samples

如果把所有m個training samples x(1) x(2) 依照column順序放入matrix X的話,會形成一個 m by nx matrix:


記住為了之後programming方便起見,不要把xi 弄成row vector放入X。
所以所有的training samples中的label y(1) y(2) ... 也可以變成一個1 by m的matrix Y:


Logistic Regression

這個之前的筆記都有了,簡單來說,就是一個input vector x,LR希望能吐出一個對class的預測y hat,但是是一個機率:


logistic regression簡單來說就是linear regression經過sigmoid function mapping之後,會落在[0,1]區間內,符合機率定義:


sigmoid function的圖如上,所以可以知道z在趨近無限大和無限小時候,會趨近於1和0:




這門課Andrew Ng不使用把bias b編寫成W0=1的notation,因為他說在programming的時候,最方便還是把bias和W分開來最好。


Logistic Regression Loss (Error) Function L

要train出w vector和bias b,我們需要知道LR對training example的預測y hat成效如何?
定義loss (error) function為單一sample x的預測值y hat和label值y 的預測誤差

一個可能的error function為squared difference:


不過由於之後要用gradient descent的演算法來找出cost function J的global minimum,而error function 使用squared difference的話會造成J形成曲折的幾何型態,會有多個local minimums,所以在LR中不予採用。

LR採用的error function如下:


採用上面這個function算是不難理解其效用,因為當:

label y = 1,則後項為零,前項要最小化的話,則log(y hat)必須要最大化,意即y hat要最大化,而y hat最大化為1,因為sigmoid function的關係,所以這邊的直覺就是當y = 1,我們希望y hat也趨近於1。

同理label y = 0的時候,上面的error function希望最小化-log(1 - y_hat),這會簡化成要最小化y hat,也就是希望y hat趨近於0。


Logistic Regression Cost Function J

Cost function定義為整個training set要找出最佳weight vector w和bias b所需要minimize的 function,合理的定義就是整個training set的平均loss:



LR 可以視為很簡單的一種NN。

2017年10月20日 星期五

Android Performance Enhancement 1 - Overdraw problem

第一守則:不要解決非瓶頸部分

除非你很閒,否則CP值很低。


Rendering通常是最大效能瓶頸

在android上安排UI或是畫圖,若是沒有理解Android的繪圖流程,很容易造成畫面效能低落。

如果要達成流暢的60 fps,我們需要在每16 ms的time frame中完成每一個frame的繪製:


算法就是螢幕refresh rate = 60Hz = 60 次/sec,所以相當於每 1/60 = 0.016秒 = 16ms refresh一次。

如果你的drawing無法在16 ms中完成的話,當然就無法達成60 fps,此時就稱為drop frame:

Rendering Pipeline

繪製一個frame需要兩個component去計算,一個是CPU,主要負責項目如下:


layout hierarchy過於龐大深度,會造成layout重複計算,是主要原因。

GPU的主要負責項目只有幫pixel上色 (rasterization,非常耗時):


GPU最可能發生的效能問題就是overdraw,重複畫在同一個區域。


Android UI & GPU

真正做rasterization的是GPU,但是CPU也扮演著把UI object計算過,並且透過openGL ES上傳到GPU memory的角色,這兩個動作都是非常耗時的:



所以最好要減少計算UI Object的時間,以及減少上傳到GPU的次數

Honeycomb 之後的android使用GPU rendering不需要programmer去操心了,除了overdraw的問題。

Overdraw problem

overdraw定義:一個frame中,某個pixel被重複畫了的次數。這可能發生在重疊的view,但是CPU和GPU不能罵他笨,他們只是忠實的重現programmer的意志:



上面這樣堆疊主要是浪費GPU 無謂rasterization的時間。
Android 使用者開發選項有一個 “show GPU overdraw”,打開後可以檢視目前螢幕上的app的overdraw程度:


可以藉由重新layout xml來調教:



fix overdraw problem

1. 我們的activity如果有apply theme,則他會有一個background color被指定要繪製,這可能跟actitvy本身的content layout的background color不一樣,所以壓在layout下面,看不到,此時應該要把這個theme本身的background 拿掉:

這樣可以減少一次重繪。

2. 檢查所有重疊view的background,只留下你真正需要的background設定。

3. 調整邏輯,某些時候重複繪製是可以靠程式邏輯去避免的。


Clipping

Android UI rendering基本上會自己計算那些UI 部分是重疊的而不去繪製被壓在下面看不見得部分,但是當我們自己extends View,並且override onDraw()的時候,等於就放棄了這個系統的判斷,所以要跟Android明確定義出visible rect:


這樣的動作稱為clipping。


舉例來說,以下是一個custom view:


可以看到onDraw如下:


卡片有重疊部分還是重畫了,沒有做任何處理。

現在來看使用clipRect要怎麼改:


所以基本上clipRect是改變canvas state,設定一個只能在clipRect,則rendering system只會畫在這個clipRect中間的顏色。但是由於每張卡片的clipRect都不一樣,所以要記得canvas.save()來儲存canvas原本的state,之後繪製目前clipRect之後還要再restore到沒有設定clipRect的state。

當然最後一張卡片由於是沒有被任何其他卡片覆蓋住的,所以不用設定clipRect,直接畫出整張卡片即可。

改善結果如下:




CPU optimization

CPU負責把layout xml file轉換成openGL需要的display list,如此GPU才知道要畫些什麼:



所以這個display list會被cache在GPU memory,任何只是單純改變屬性 (這邊定義不太清楚)的狀況,都不會改變這個display list,所以這個display list就是這個Button的繪製藍圖。

但是一旦Button的任何pixel內容改變了,例如多出陰影或是按下的動畫等等,這個view就需要被invalidate,告訴CPU要重新製作display list,所以上面的流程都要重新跑一次,耗費了CPU與GPU的資源。

以scale button為例:


scaling會觸發Measure過程,因為系統要知道你的最後大小與位置,另外也有可能影響到其他UI component的位置與大小,所以有可能觸發Layout過程,接下來就是invalidate被影響view去重新計算display list交給GPU去重畫。


注意Measure和Layout會讓Android重新評估所有受影響的UI components,這個過程耗費的資源和整個xml定義的view hierarchy息息相關,所以調整view hierarchy是重要的,不要設計無謂龐大的hierarchy。


Hierarchy Viewer

主要目的:
1. 用android rendering system的視角來看layout hierarchy
2. 提供hierarchy扁平化的可能性,加速rendering和memory consumption。

不過這個工具已經obsolete,這邊不做筆記了)

以下是兩個一模一樣的視覺呈現,但是layout hierarchy不一樣,可以看到上面那個row的階層比較深,performance的差異如下:



所以有機會的話,盡量可以使用relative layout,或是最近推出的constraint layout。