高見龍

iOS app/Ruby/Rails Developer & Instructor, 喜愛非主流的新玩具 :)

RubyKaigi 2013

本篇文章已刊載於 OpenFoundry 電子報自由專欄,刊登之內容由專業的 OpenFoundry 團隊潤稿,此篇為原文。

今年日本的 RubyKaigi 2013 在 5/29(四)、5/30(五)、6/1(六)舉辦,議程是雙軌同時進行,共計有超過五十場演講及十場 Lightning Talk,並包括三場主題演講 (Keynote),跟上一屆一樣也是三天。這次有超過 500 位 Ruby 開發者參加,其中有超過 100 位是從國外來的,這樣的規模短期內在台灣可能還是沒辦法達到(全台灣真的有在用 Ruby 工作的開發者加起來總數說不定都還不到 500 人)。 image photo by @eddiekao

Code Reading @ RubyKaigi 2013

很高興這次能有機會到日本參加 RubyKaigi 2013,我分享的題目是:"Code Reading, Learning More about Ruby by Reading Ruby Source Code“,算是個滿冷門的題目,主要是分享我自己從閱讀 Ruby 原始程式碼中學習到的一些經驗。不過當天還有不少朋友來捧場,讓場子看起來不會太冷清,真是太意外了(其實我也搞不清楚到底有多少人,我當時已經緊張到沒辦法算數了)。
Really glad to go to Japan and join the RubyKaigi 2013, the topic of my talk is ”Code Reading, Learning More about Ruby by Reading Ruby Source Code“, which I don’t think it’s a popular topic, and it’s about my experience while reading Ruby source code. Thanks to those friends who are willing to come to my talk.(Actually I was too nervous to count how many people already)

在進入主題之前,我問大家有多少人曾經來過台灣,結果在場有十來個人舉手,就以現場人數的比例來說不算少。
Before I jump into my topic, I asked everyone how many of you have been to Taiwan before, to my surprise, there’re more than 15 people raised their hand, which is about 20% of the attendees in this hall(I think).

image View on Speaker Deck | Download PDF

雖然之前有一些些上台演講的經驗,但這是第一次出國演講,而且還是全程用英文演講,我知道有很多的大神甚至 Ruby Committer 就坐在台下,所以我相當緊張。我那個「上台的前一天不會睡」的魔咒果然還是依舊存在,所以演講當天凌晨四點,我拿著筆電到飯店的大廳練習對著空氣講,練到早上七點,但還是覺得不夠穩。
Although I have a some experience about having public speech, but it’s my first time to have a presentation abroad and do my speech in English, and I also know there might be lots of awesome rubyists sitting there, including some Ruby core committer, so I really feel nervous. And I have a curse that “I won’t sleep if I have a public presentation on the next day”, so I practiced in the lobby of the hotel from 4 A.M. to 7 A.M., but still not stable enough.

我的演講廳是小間的,所以壓力有稍微小一點點。
My presentation hall is the smaller one, so my pressure is also slightly smaller, too.

為了怕現場出包,所以在 code review 展示的部份我是用預錄的,不過事後想想好像直接現場操作比較容易掌控時間
I think I might make some mistakes while presenting, so I recorded the code review demo in advance, but I think the real live demo might be easier for me to control time.

上午的演講結束後,下午發生的小插曲讓我抖了好大一下。我在演講內容裡有提到一些在 Ruby 的原始碼裡面有一些小地方的命名不太優,當下大家笑得很開心,雖然娛樂效果達到了,但我其實也有點擔心會不會有什麼不好的副作用。結果上午的講場結束,下午就看到這則
After finishing my talk, there’s something surprised me. In my speech, I mentioned some interesting naming in the Ruby source code just for fun. I know it might be entertaining but still a little worried about if there would be any bad consequence. Then in the afternoon, I saw this tweet:

image

哇!! 我有嚇到的感覺了,不過因為我個人覺得這樣好像有些失禮,所以當晚的 Official Party,我就親自去跟 @nobu 說聲不好意思,希望沒有造成他的困擾。其實我也是想趁這個機會認識一下只有在網路上才能看得到的傳說大神,而且他好像一點也不在意。
Wow! That’s really really surprised me!! and I don’t know if this would cause any trouble for committer, so I went to @nobu and said sorry to him on the official party in the night, and hope didn’t cause extra trouble for him. In fact, I went to him also want to know him by this chance, and actually he didn’t mind at all.

第三天的議程,我本來就打算要來聽 Jim Gay 的主題(他是 Clean Ruby 一書的作者 ),沒想到坐下來沒多久竟然看到自己的名字跟前一天講的東西出現在投影片上:
In the last day, I was planning to attend Jim Gay’s talk, whihc is the author of the book “Clean Ruby”. After sitting in the hall, I suddenly found my name was quoted in the slide:

image

再度有被嚇到的感覺。
Yes, Supprised me again.

活動結束後,RubyKaigi 2013 的頭目角谷也推了一篇
the Organizing Director of RubyKaigi 2013 Shintaro Kakutani also tweeted this:

image

再次看到自己的名字被提到,有些驚訝也有些不好意思,不過這個"Conference-Driven Development(CDD)“ 聽起來好像不錯,而且我喜歡 "RubyKaigi made Ruby Better",如果有朝一日我有能力,我也希望可以多貢獻一些心力。
I feel surprised again and also little embarrassing, but the "Conference-Driven Development(CDD)” sounds workable, and I love the “RubyKaigi made Ruby Better”, I hope I can do more contributions to Ruby and this community someday if possible.

感謝高井さん(@takai)幫我拍的照片,讓我在 Rubyist 時計上也可以有一張漂亮的照片。
Thanks to Naoto Takai(@takai) to take a photo for me so that I can have a nice picture on the Rubyist Tokei.

image photoed by @takai

這趟日本之行有學習到不少東西,收獲很多,會在接下來的幾篇文章介紹。最後,有講的不好的地方,還請多多指教。
Anyway, I learned a lot on this trip to Japan, and I’ll write them down in next blog posts. At last, if there’s any bad or something wrong about my speech, please feel free to comment.

Thank you all, RubyKaigi team, you’re all AWESOME!

HHKB Pro 2 鍵盤入手

image

其實沒什麼,只是敗家紀錄一下,就前幾天去日本參加 RubyKaigi 時候順便帶回來的。而且還特別挑了個無刻的回來:

image

鍵盤很小一個,只有 60 顆鍵帽;沒有上下左右鍵,沒有 Page UpPage Down 鍵;Ctrl 鍵的位置不一樣(跟日本鍵盤一樣,在一般鍵盤的 Caps Lock 的位置),ESC 鍵在數字 1 的左手邊;沒有最上面一排的 F1 ~ F12 等種種跟一般鍵盤的差異,我也不太懂為什麼我要練這種折磨人的鍵盤,因為練這個可能得把之前的習慣整個砍掉重練,跟練 Vim 有點像,一但只要習慣它,就沒辦法習慣一般鍵盤了。

不過我很喜歡它打起來的手感,所以.. 喜歡就不需要太多理由了,還在習慣中 :)

一個月後的使用心得

Delegation in Objective-C

在開發 iOS app 的過程中,Delegation(委任) 幾乎是避不掉的東西,例如在 ViewController 裡處理 UITableView 的時候,大家一定都寫過像這樣的程式碼:

1
self.tableView.delegate = self

坊間的書本大多會教要這樣寫,但不一定有說明為什麼要這麼寫。其實 delegation 的概念並不困難,只是要用程式碼來表達的時候,對新手來說可能就需要多一點的想像力了。

Delegation,中文翻譯成「委任」,委任兩字講的好聽是拜託別人做事,講白一點就是自己不想做或不會做,所以外包出去叫別人做。

但是,就算是要叫別人做也不能隨便找一個路人就可以,舉個例子,我想要把「撰寫 Ruby 程式」這件事委任給別人,要有能力處理這份工作的人至少得知道 Ruby 程式怎麼寫。

那要判斷對方是否有「能力」來接受我的委任,就是問這個被委任的人是否有符合(Conform)我訂的條件(Protocol),然後這個條件就跟面試新人一樣,某些技能是必須的(Required),但其它條件是非必須的(Optional)。

一樣以 UITableView 來舉個例子,如果我希望某個 ViewController 可以有能力接受 TableView 的委任,假設這個 UITableView 是設定成一個叫做 tableView 的 IBOutlet 的話,我們可以直接在 ViewDidLoad 的地方這樣寫:

1
2
3
4
5
- (void)viewDidLoad
{
    [super viewDidLoad];
    _tableView.delegate = self;
}

或是直接在 Storyboard 用拉的也行:

image

這樣就完成了 delegation 的設定啦!

但是,剛剛才講到,我怎麼知道這個 ViewController 有能力可以完成我想要委任的工作? 其實就是讓這個 ViewController 實作 UITableViewDelegate 這個 Protocol 就行了。

1
2
3
@interface ViewController()<UITableViewDelegate>

@end

關於 Protocol,大家可以參考之前寫的這篇

但是,UITableViewDelegate 這個 protocol 到底定義了哪些東西? 讓我們按著鍵盤的 Cmd 鍵加上滑鼠點擊 UITableViewDelegate 就可以連過去看一下它的定義(部份程式碼省略):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@protocol UITableViewDelegate<NSObject, UIScrollViewDelegate>

@optional

// ..省略

// Variable height support

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section;
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section;

// Section header & footer information. Views are preferred over title should you decide to provide both

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section;
- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section;

// ..省略

@end

UITableViewDelegate 這個 protocol 定義在 UIKit 的 UITableView.h 裡面,而且全部都是 optional 的,也就是說就算不實作任何這個 protocol 裡的方法也不會怎麼樣,它還是有自己預設的行為。

但如果你想要做一些 UI 客製化,例如想要動態的調整每個 cell 的高度,你就可以在這個 ViewController,也就是這個 tableview 所「委任」的對象,覆寫這個 method:

1
2
3
4
5
#pragma mark - UITableView delegate
- (CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return 100.0f;
}

上面的實作這個方法,你可以想像成以下情境:

tableview 問 view controller 說:「喂,我每個 cell 要設定多高啊?」

view controller 回答:「就 100 個點(point)吧。」

再舉個例子,如果你想要客製化這個 tableview 的 section header 或 footer,就是覆寫這兩個方法:

1
2
3
4
5
6
7
8
9
- (UIView *) tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section
{
    // 內容省略
}

- (UIView *) tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
    // 內容省略
}

以此類推。

UITableView 所需要的一些行為或是外觀描述,都是透過 delegate 的方式,請被委任的傢伙(通常是那個 tableview 所在的 view controller)告知。

Delegate 怎麼寫?

上面大概說明了怎麼用別人寫好的 delegate,如果也想自己仿著做一個,該怎麼做呢?

我這邊來做個簡單的範例,大概就是希望某個 UIView 在進行動畫完成之後透過 delegate 發送一個「喂,我已經搞定囉」的通知,並同時也回傳狀態。

我開了一個新的 Xcode 專案,選了 Single View Application,裡面應該只有預設的 ViewController.hViewController.m,接著我新增了一個繼承自 UIView 的類別,叫做 AnimationSquareView

image

並且在上面加了一個叫做 run 的 public method:

1
2
3
4
5
6
//== 檔案:AnimationSquareView.h ==
#import <UIKit/UIKit.h>

@interface AnimationSquareView : UIView
- (void) run;
@end
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// == 檔案:AnimationSquareView.m ==
#import "AnimationSquareView.h"

@implementation AnimationSquareView
- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self)
    {
        self.backgroundColor = [UIColor blackColor];
    }
    return self;
}

- (void) run
{
    [UIView animateWithDuration:3.0 animations:^{
        CGRect newFrame = CGRectMake(self.frame.origin.x + 150,
                                     self.frame.origin.y,
                                     self.frame.size.width,
                                     self.frame.size.height);
        self.frame = newFrame;
        self.alpha = 0.5;
    } completion:^(BOOL finished) {
        // do something later..
    }];
}

@end

這個 run 做的事情就是在 3 秒鐘完成一個簡單的動畫(其實就是讓它自己往右邊移動 150 個點,並且透明度變 50%)。

ViewController 的部份程式碼如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// == 檔案:ViewController.m ==

#import "ViewController.h"
#import "AnimationSquareView.h"

@interface ViewController()

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    AnimationSquareView* squareView = [[AnimationSquareView alloc] initWithFrame:CGRectMake(50, 50, 100, 100)];
    [self.view addSubview:squareView];
    [squareView run];
}

@end

執行一下,應該會看到一個黑色的方塊緩緩的往右程動,過三秒鐘之後就停下來了:

image

加上 delegate

如果,我想要讓這個 ViewController 知道這個方塊什麼時候做完它的動畫效果,該怎麼做?

一種方法是可以在那個動畫 block 的 completion 區塊利用 NSNotification Center 並且在 ViewController 搭配 KVO(Key-Value Observing) 來實作,不過我們這邊選擇用 delegation。首先,我先定義一個叫做 AnimationSquareViewDelegate 的 protocol,並且在原來的 AnimationSquareview 類別裡加了一個名為 delegate 的 property:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// == 檔案:AnimationSquare.m ==
#import <UIKit/UIKit.h>

@class AnimationSquareView;
@protocol AnimationSquareViewDelegate <NSObject>

@optional
- (void) animationSquareView:(AnimationSquareView *) squareView didFinishAnimationWithStatus:(NSDictionary *)status;

@end

@interface AnimationSquareView : UIView

@property (nonatomic, weak) id <AnimationSquareViewDelegate> delegate;
- (void) run;

@end

這裡可能有些需要解釋的:

  1. delegate 這個 property 並不一定要命名為 delegate,只是慣例上通常會使用 delegate 這個名字,你硬是要把它叫 abc 也行,只是在待會我們要設定 delegate 的時候就要用 abc 了。另外,protocol 的定義也不一定要放在同一個 header 檔裡,也可以另外獨立的檔案,不過因為這個 delegate 的行為跟這個類別有關,我通常也會把它寫在同一個檔案裡面。

  2. 上面第 4 行,為什麼需要那個 @class AnimationSquareView;? 因為在定義 protocol 的當下,這個類別還沒被定義出來,所以 @class 是告訴編譯器說:「我這個 AnimationSquareView 是一個類別,反正我待會就會寫,你先別管這麼多」。

  3. Protocol 裡的方法有分兩種,一種是規定必須實作的(required),另一種則是不一定要實作的(optional),在這個範例裡,我認為完成動畫的這個方法並不是一個一定要實作的方法,所以我把 animationSquareView:didFinishAnimationWithStatus: 它設定成 optional (如果沒有特別標記,就是 required 的)。

  4. Protocol 的名字 AnimationSquareViewDelegate 也不一定要叫這個名字,只是在後面加上個 Delegate 似乎也是慣例。

再來,我們改寫一下剛剛那段動畫的 completion 區塊的程式碼:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// == 檔案:AnimationSquare.m ==
- (void) run
{
    [UIView animateWithDuration:3.0 animations:^{
        CGRect newFrame = CGRectMake(self.frame.origin.x + 150,
                                     self.frame.origin.y,
                                     self.frame.size.width,
                                     self.frame.size.height);
        self.frame = newFrame;
        self.alpha = 0.5;
    } completion:^(BOOL finished) {
        if ([_delegate respondsToSelector:@selector(animationSquareView:didFinishAnimationWithStatus:)])
        {
            NSDictionary* currentStatus = @{@"status": @"finished"};
            [_delegate animationSquareView:self didFinishAnimationWithStatus:currentStatus];
        }
    }];
}

這裡需要解釋的就應該就只有那段 respondsToSelector 了。這段的意思是問自己本身的這個 delegate 是不是有實作 animationSquareView:didFinishAnimationWithStatus: 這個方法,如果有的話,就呼叫它,並且把自己(self)以及狀態(一個NSDictionary)傳入。

設定 delegate

再來,回到 ViewController 裡,準備設定我們剛剛寫好的 protocol 跟 delegate:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// == 檔案:ViewController.m ==

#import "ViewController.h"
#import "AnimationSquareView.h"

@interface ViewController()<AnimationSquareViewDelegate>

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    AnimationSquareView* squareView = [[AnimationSquareView alloc] initWithFrame:CGRectMake(50, 50, 100, 100)];
    [self.view addSubview:squareView];
    squareView.delegate = self;
    [squareView run];
}

#pragma mark - AnimationSquareView Delegate
- (void)animationSquareView:(AnimationSquareView *)squareView didFinishAnimationWithStatus:(NSDictionary *)status
{
    NSLog(@"current status = %@", status);
}

@end

這邊多了幾行程式碼:

  1. 第 6 行,在類別後面加上了 <AnimationSquareViewDelegate>,就是告訴編譯器說:「我有遵守(conform) AnimationSquareView 規定的 protocol」,表示可以接受 AnimationSquareView 的委任。

  2. 第 17 行,設定 squareView.delegate = self,把 squareView 的 delegate 設定到自己身上,也就是目前這個 ViewController。

  3. 第 22 行到第 25 行,就是實作 protocol 裡定義的方法。

執行一下,應該可以在動畫執行結束的時候,看到輸出結果。

那,如果第 22 行到第 25 行的程式碼沒實作出來會怎樣? 在我們這個例子,不會怎樣,因為我們並沒有在 protocol 裡設定一定要實作的方法(required)。

透過 delegation,可以降低類別與類別之間的耦合,但仍然可方便的讓類別之間有"溝通"的效果,而且也很容易的可以把要溝通的"訊息"一併傳回。

範例檔下載

以上,希望這篇文章能讓大家對 delegation 有更進一步的了解。如果有哪邊寫錯,還請前輩先進指點。

CreateJS, 從 Flash 到 Javascript

這是我在本次 JSDC 的分享的題目,應該是本次議題裡少數跟 Javascript 比較沒直接關係,而且技術含量也相對較低的一場演講。

image View on Speaker Deck | View on Slideshare | Download PDF

認識我的朋友可能知道我是靠 Ruby 跟 Rails 在討生活的(目前改靠 iOS app 開發混飯吃)。不過,如果以寫程式的時間來說,佔我目前生命中最久時間的,應該是 Flash/ActionScript 了。

接觸 Flash 七、八年以來,看到了它的起起落落,當年在 Marcomedia 時代還因為喜歡那個閃亮小徽章而去考官方的認證,認證考過了,但 Macromedia 也被 Adobe 併購了,然後就沒有再發閃亮小徽章了。

也因為對 Flash 有愛,所以從 2007 年起就厚臉皮的去擔任 PTT Flash 的版奴到現在,去年以及前年也愛現的辦了十幾場的 Flash 聚會

這期間,有不少自稱或被稱為 Flash 殺手級的應用,例如 N 年前的 Silverlight,以及後期的 HTML5。我個人覺得,真正強大的 Flash 殺手應該是已經先回家的教主 Steve Jobs 吧,他重重的打了 Flash 一巴掌之後,然後大家一起跟上來打。

Flash 真的被殺死了嗎? 其實這是個很嚴肅的話題,特別是像我這種曾經對它有愛而且曾經依賴它過活的人.. 老實說,我覺得就算不死也半條命了..

不過,在台灣有不少的中小企業在做網站的時候,還是喜歡做那種會飛來飛去轉來轉去,首頁還放一個 skip 按鈕的 Flash 動態。所以,我想 Flash 在幾年內在台灣應該還是有它生存的空間的。

我常跟朋友說,大家都在喊 Flash 已死,但對我來說,死的其實是 Flash player 而不是 Flash。Flash 有另一個支線叫做 AIR,不只可以做一般的桌面應用程式,也可以轉成原生的 iOS app 到 App Store 上架。

如果你曾經是個 Flash 程式設計師,或是現在也還靠它在過活,聽到大家都在講什麼 Javascript、HTML5 而對自己的未來感到無助。同時客戶一個一個來跟你說"No Flash",也不管你會不會寫,就說他們的 event site 不要用 Flash 做,因為客戶說手機上看不到..

其實,我都很想回客戶說:「是會有多少人用手機看你的 event site?」。但為了生活,這句話我通常還是選擇吞下去 XD

CreateJS,也許可以說是目前對 Flasher 來說相對友善的 HTML5 library 了。除了語法熟悉之外,也有方便的 toolkit 可以讓設計師從 Flash 直接輸出成 HTML5。

對我這種沒美術細胞,Javascript 功能大概就是比會寫個 Alert 再複雜一點點的人來說,有個這樣的套件或工具讓我可以很快的把東西做出來,我已經相當感恩了 :)

推薦原因

  • CreateJS 是由 Grant Skinner 他們家開發的,光是這點我就已經足以說服我用它了。記得當年 GS 就是因為跟 Microsoft 的 HTML5 專案合作,在開發的過程中就順手做了 EaselJS (一開始只有這個,其它的是後來才慢慢加進來的)。如果說用這個做出來的東西,連 Microsoft 都能接受的話,相信應該是有一定品質的。重點是這個專案在 IE 上應該可以執行,畢竟是 MS 的專案.. 不過比較舊版本的 IE 就不一定了。

  • 其次,GS 大神本來也是寫 Flash/AS 的,所以他開發的這套東西的時候,也許也希望其它 Flash 開發者也可以較無痛轉換,於是在設計 API 的時候有特別讓它長得跟 Flash 的 API 有點像。

  • 有文件,有範例可以參考。不要以為這很理所當然,很多開源的專案是沒在寫文件的..

  • 最後,它是 MIT 授權的,基本上可以放心大膽用它沒問題。

以上,大概是這回 JSDC 大概分享的內容,希望對大家有幫助,如果有錯誤的地方,也請前輩先進不吝指教,感謝!

PS: 講到這裡我就想小小埋怨一下,Flash 是 A 公司的產品,是商業軟體沒錯,但寫 Flash 的人不表示就不開放,在 Flash 圈也是有非常多優秀的開源專案,但似乎只要跟 Flash 沾上邊就會被當做不開放..