2014年4月4日 星期五

[CS193P] 2. Xcode 5

由CardGame作引導

在這堂課中,Hegarty老師以一個撲克牌遊戲(類似摸雲)來作為範本,從中引導學習Xcode的IDE使用方式。當然我們在這篇文章中,並不會以講解CardGame的內容為主,僅以其中提到較為重要或特別需要注意之處來做筆記。

Xcode的特殊符號

在Xcode中,我們可以插入特殊的符號(正確名稱為「顏文字」,Kaomoji),而且可以正確的在IDE中顯示,相當有趣。

 

看見程式碼裡的足球和哈姆太郎了吧!


init初始化

當我們要一個物件做實體化,我們通常需要針對該型別進行alloc與init動作。例如:

NSMutableArray *cards = [[NSMutableArray alloc] init];

當進行完alloc與init後,該物件才算在heap中完成空間的分配作業。尤其需要注意的是,init需立即在alloc後使用,也千萬不要僅使用了alloc後不進行init的呼叫。

覆寫init

我們若要覆寫init,則需要在回傳型態中,給定一個新的關鍵字「instancetype」。

- (instancetype)init
{
    self = [super init];
    
    if (self) {

    }
    return self;
}

instancetype是一種標記型態,為了讓compiler識別這個回傳型態會與該類別相同,其好處可在設計階段即確認型別是否正確。以往我們會使用id(可能是任何型別),但這樣會導致型別的錯誤會在run-time階段才發現,且非常不容易偵錯。

[可參考]高見龍文章:http://blog.eddie.com.tw/2013/12/16/id-and-instancetype/

我們在方法內的第一行self = [super init];可看到藉由呼叫super的初始化方法init作為self的基元。若有寫過其他OOP語言的朋友,對這樣的寫法應該不陌生。

Xcode介面初探



在Xcode中的介面,左邊為Navigator區,中間為主要工作編輯區,右邊為Utilities區,下方為Debug區。

可以記幾個有關上面的區域開啟與關閉的快速鍵,相當方便。


  • 左邊Navigator區的開關:Command (⌘) + 0  
  • 右邊Utilities區的開關:Command (⌘) + Option + 0
  • 下方Debug區的開關:Command (⌘) + Shift + Y  (這個就不是很好記了)


前兩個的記法很簡單,其實邊按的時候,感受一下Command和Option鍵的相對位置,就發現這不需要太硬背。


  • 而Navigator區上有8個子功能的切換,則使Command + 1、2、...、8進行切換。
  • Utilities區上的子功能,則是多加上Option鍵後,再加數字鍵即可切換。


如何加上圖片?

之前的版本,圖片會直接放在Supporting Files目錄中,在Xcode 5會採用全新的Images.xcassets來作管理。


其中只要把想要的圖片直接從Finder中拖進去就可以完成。但需要注意的是,圖片會有兩種尺吋,1x與2x,2x是給retina高解析度使用的,這版的xcode相當聰明管理,僅需使用相同名稱,iOS依據執行環境的規格會自行載入對應的圖片。

如何將View上的UI元件與程式碼做關聯?

在Xcode中,從iOS 5.0後採用了一種全新的UI設計方式,稱為storyboard。在沒有storyboard以前,要開發UI僅能在單一的.xib檔上編輯,但UI的View之間無法一目了然,僅能造程式碼的分析與研究才知道其中的視圖轉換。(有時候自己寫的程式沒多久後自己也無法了解時,會相當痛苦)

而iOS 7的一個最大的變化,就是採用了平圖設計,尤其影響甚大的是原本的Button按鈕元件,採用的是無邊框(border)的設計,老實說我不是很喜歡,因為有時候到底是只供閱讀Label還是可按的Button,一時間難以辯識(當然Button預設顏色是藍色,但Label總也可能設計成藍的吧)。

話說回來,iOS開發採用的是MVC,那麼我們就要讓View和Controller之間是可以做溝通,因此我們會做的有2件事:一是希望View上元件被操作時,相對的Controller的程式碼要做一些我們需要處理的動作。二是我們的程式碼運作後,希望能將新的結果反應在View上作變動。


先講一下在Xcode中非常常用的左右分割的中央編輯區,單一及分割的切換快速鍵為:Command + Enter 以及 Command + Option + Enter

而左邊的檔案,若直接以滑鼠點選,會將選擇的檔案內容顯示在左邊的分割區中。
若按住Option鍵,再點選檔案,就會將檔案顯示在右邊的分割區中。

若你在編輯區中,相要在.h和.m檔中切換,則按住Ctrl + Command + 上或下鍵

那麼我們了解了後,則可將storyboard的View畫面開在分割區左邊,而右邊開的是ViewController.m檔,接著要用非常直接的方式來做關聯。

首先先按住Ctrl鍵,再點選某UI,拖曳出去時會出現一條線,則可將線連到程式碼的
@implementation的區域中。在彈跳出建立關聯的視窗中,若我們確認這個事件一定是特定型別時,可將「type」由id改為與UI元件相同的型別,例如下圖的UIButton。


執行後,我們會看到剛剛的動作Xcode幫我們建立好了回傳型別為IBAction的方法。


- (IBAction)touchCardButton:(UIButton *)sender 
{

}

IBAction一樣是一個標記型別,其真正的內容為void,用意僅為提供給Compiler作為辯識用途,知道這是一個與UI有相關的方法。

若我們要建立一個可從Controller程式碼修改回View的UI元件內容,則一樣使用Ctrl+滑鼠拖曳方式,但這次是要將線拉至@interface區域裡,則會建立IBOutlet的關聯。

@property (weak, nonatomic) IBOutlet UILabel *flipsLabel;

其中我們需要了解的是weak設定,因為UI元件會在View上做strong關聯,我們在Controller中取用其值,僅僅是一種「他存在時我就使用,他不存在時我也被設成nil,我並不需要握有該物件最後生命的能力」,這就是使用weak的道理所在。

另外,在Debug區中,我們可以透過NSLog()方法來將一些需要測試或追綜的輸出顯示出來,以方便開發過程中運用。

2014年1月7日 星期二

[CS193P] 1. Class Logistics, Overview of iOS, MVC, Objective-C

MVC (Model / View / Controller)

MVC一直是這五、六年來我非常推崇的開發觀念,早期從Java的Spring MVC開始著手,就深深愛上這種UI與程式邏輯獨立的開發方式。在iOS的開發裡,更是非常徹底的實踐了MVC。當然MVC有多種不同的變型,在此處的MVC我們可以簡單的來說明:

掌管UI背後運作的程式碼為Controller(在iOS開發,會稱為ViewController),是MVC中的運作核心。Controller掌控了最主要App流程的運作,除了接收使用者透過UI(View)傳遞而來的操作應如何有對應的動作,以及向Model端取得資料與商業邏輯的運算,再將運算結果反饋回UI做畫面上的改變。

而在iOS中的View,若開發的是Universal(可用於iPhone與iPad,但可依其不同特性而有不同的UI展現方式,但背後邏輯是相同一套Controller和Model),則可搭配兩個storyboard(一個iPhone用,一個iPad用,每個storyboard中由多個View所組成)呈現不同的UI。

這個設計方法說實話,有其優點也有其缺點,優點是真正落實了MVC的彈性,針對不同的顯示方式只要分開設計,可簡單搭配同一組背後的邏輯運算,而且可產生成相同的一套App,使用者不論是iPhone或是iPad,安裝後會以對應的UI來呈現。但缺點是兩種View若是有絕大部份都是相同的設計(90%),但卻要分兩個storyboard來設計,雖然可用copy/paste將大部份的UI元件複製再調整,但若當App愈來愈複雜時,若要調整某個既有的View的元件時,也得兩邊都更動,就UI模組化的觀念來看,改一處就要改多處(若有設計多國語言的storyboard,會更惱人),對UI人員是一種不會太開心的作業方式。

不過在iOS的View的獨立性,的確是相當的出色。若寫過.Net的話,應該會了解要讓Controller與View溝通,要在View上對每個元件都指定ID,好讓Controller可以針對ID來做事。但在Xcode優異的IDE介面,View上的元件與Controller之間是以「連線」的概念將兩者關聯起來,若View元件需有操作事件要指派delegate給Controller,只需要用肉眼直覺將該元件拖曳至Controller程式碼中建立IBAction關聯;而若有到時Controller需要修改UI元件內容屬性,也只需要將該元件拖曳至程式碼建立IBOutlet關聯。但在View當中,仍然沒有ID的觀念,只有從Xcode GUI上才看得出到底是哪個元件連到哪個Controller的屬性或方法上,而且沒有要用的就不必做連線。這點和其他開發環境上是有蠻大的差異,但做法相當直覺且先進。

總之,在iOS的開發中,就是一堆的MVC與MVC的互動。例如行事曆點選了Year View的某月後,會進入Month View,而點選其中某一天,會進入Day View…。Year View其實就是由一組Year的MVC組成,而Month View也是由一組Month MVC組成,以此類推會有Day的MVC,甚至填寫新事件頁面的MVC。因此以上的流程轉換,就是前面所稱的「一堆的MVC與MVC的互動」

在iOS 5之前並無Storyboard設計方式,需在獨立的.xib檔中設計View的UI,不同組MVC之間的流程,很難直接看的出來,只能從code中去了解。但自從有了Storyboard後,MVC之間的流程化被具象化出來,就像是分鏡圖一樣,可以從Storyboard中一眼就把整個流程架構看完,無論是拿來與相關人員討論或事後維護都非常的有幫助。更別說再更早期UI的開發要在另一套Interface Builder (IB)設計UI,再和程式碼整合的困難了。

基本上,iOS的MVC中,和其他程式相比差異最小的就是Model的部份,但在程式碼上許多初學者會一不小心,就把程式邏輯直接寫在Controller裡面了,造成了MVC只剩下VC。(其他語言亦然)

因此在程式碼的結構上,要養成好的習慣將Model的部份另外抽離出來,如此在被Controller呼叫時才可做到較有彈性與擴充性的設計。


Objective-C初探

我們常說.h檔是定義檔,.m檔是實作檔。Hegarty教授給了一個更實務的說法,.h檔其實就是你的API (開放出來讓人看的規格),而.m檔就是你不讓別人看見,以及其他所有要實作的程式碼。

所以呢,和Java或.Net這類語言不同之處是,我們並沒有針對各個屬性或方法設定所謂的「存取範圍」,也就是沒有什麼private、public…這種東西,反正你放在.h的,就是public,其他的就放.m就是了。

在View去拉IBOutlet和IBAction時,我們常有一種認知,覺得從UI元件按cmd接出的那條連接線就是要拉到.h檔的慣性,其實若你不是要開放出來的方法或屬性,其實拉到.m檔就好了,我之前真的是沒想過呢。

小地方注意:

  • @interface其實指的是在.h檔的class,並非其他語言的interface(在iOS反而叫protocol)
  • 在.h檔要寫繼承的類別,在.m檔則不要
  • 以@property來存取變數

Strong vs Weak Pointer

ex.
@property (strong, nonatomic) NSString *content;

在設定@property時,有個屬性要嘛就strong,要嘛就weak,但到底什麼是strong和weak pointer?

strong pointer是一種reference counting技術,只要所有被設定strong property所指到的物件(置於heap中),有至少一個以上還指著,那該物件就不會被回收。直到最後一個strong指標刪除,則iOS會自動釋放掉該物件的資源。但是這種自動的reference counting技術,在iOS 5後出現,正確名稱為ARC(Automatic Reference Counting),並不是我們在其他語言所說的GC(Garbage Collector)。

而weak pointer指的是,若此指標指到在heap中的那個物件,只要有超過一個strong pointer指向他,則幫我連接著;但若沒有任何的strong pointer指向該物件了(該物件會被釋放),則此weak pointer會一併被設定成nil。

strong和weak指標往往在很多書和文件上都說的不清不楚,大部份的人也都一知半解,說穿了就是亂用一通,反正好像run起來沒有很明顯的說出哪裡有問題,就這樣隨心所欲使用,哪一天出現了 memory的錯誤時,會非常難抓bug。


@property與@synthesize的改變(變得更省事了)

在iOS 5以前(換種說法,是在Xcode 4.4之前的版本),在.h檔設定了@property後,需要在.m檔加上對應的@synthesize,在.m檔中才可以使用self.的方式取用或設定property的值,或者重新覆寫getter或setter。


Emp.h中
@interface Emp : NSObject

@property (strong, nonatomic) NSString *name;

@end

Emp.m中
#import "Emp.h"

@implementation Emp

@synthesize name = _name; //這行

@end


但在iOS 6開始,在.h檔宣告了@property後,在.m檔已不需要再自行加入@synthesize,這些會由compiler幫我們自動加入(除了@synthesize語句外,也會幫我們自動加入預設的setter與getter內容)。

甚至我們要針對setter或getter重新改寫,也可以直接寫上改寫部份,也不會有問題。


@property的setter或getter的名稱變更

在Objective-C的property的setter與getter,和Java體系不一樣的一點是在於getter會與property名稱一致,前置詞不加上「get」字樣。setter則與Java相同。

也就是說,若你的property為name,則setter會叫"setName",而getter會叫"name"。

當然,若我們使用self.方式取用或設值,是不受影響直接都用self.name。但若是透過message傳遞時,就會使用原本message名稱。若有時候,我們有需要改變getter(或setter)的名稱時,讓程式閱讀上更清楚意思,我們則可在宣告@property時,直接做指定。

這種情況通常會發生在BOOL的property上,例如有個BOOL的property名稱為active。照預設,其setter會叫setActive,getter會叫active。但在Java中,我們通常會以較為符合英文文意的方式來看待布林值的結果,通常會以「is」作為前置詞,例如「isActive」。既清楚又好懂。

所以我們在Objective-C中也想要這樣使用的話,就是直接在@property宣告時指定名稱變更。

@property (nonatomic, getter = isActive) BOOL active;

這樣我們就輕鬆改變了一個較容易理解的getter名稱了!這種作法一般實務上,較常運用在BOOL的getter上,當然有時候也有其他的時機,是為了解決一些奇奇怪怪的問題時,也可以使用這個方法來調開名稱的衝突。(我之前開發的專案有遇到過,但我現在想不起來時機是什麼了)

2014年1月2日 星期四

來吧!成為史丹佛CS夜間部學生!

不得不為iTunesU歡呼一下!



當然最早推行開發式課程的MIT功不可沒,不過藉由iTunesU的資源整合,可更全面的搜尋與掌握更多精彩絕倫的課程。

其中課程排行榜居高不下,且課程不斷更新的當然首推史丹佛的iOS開發課程(Developing iOS 7 Apps for iPhone and iPad [CS193P]),由思路、技術與經驗超級豐富的Paul Hegarty講師主講。從之前幾年前開始的幾個版本開始我就看過了這個課程,但一直看個前幾課就停滯不前了。

Paul Hegarty的學經歷資料很難查的到,就我從各方網路資源查到的,他畢業於Stanford,畢業後進入NeXT工作,並且在矽谷創立了自己的公司。(資料實在不多)

直到最近出了iOS 7最新版本的課程,想想是時候該好好來看一看這門非常精彩又讓人獲益良多的課,除此之外,倒不如邊看邊做個筆記,把最精華的部份咀嚼吸收後,再轉化成為自己融會貫通的筆記,也可造福人群。


這個CS193P課程,雖然為全英文,但由Paul Hegarty精彩的講解,要不懂實在也很難。尤其他的投影片實在做的太出色了,這大概是我這輩子看過最清楚易懂的教學資源了。

不得不感嘆的是,史丹佛大學的課程也太精實了吧,這樣的課程若是用功一點的學生整套學完,真的直接上戰場沒問題呀!但也感謝有這樣完全免費佛心來的開放式課程,讓遠在台灣的我們可以直接學習到世界頂尖學校的課程,太感動了。

好吧,就利用下班的時間,把自己當成「史丹佛夜間部的學生」好好的上課吧!




iTunesU Link:
https://itunes.apple.com/us/course/developing-ios-7-apps-for/id733644550

CS193P Course WebPage by Paul Hegarty:
http://www.stanford.edu/class/cs193p/cgi-bin/drupal/