作者彙整: Riddle

《請君勿死》

《請君勿死》(部分節錄)by 與謝野晶子

啊!吾弟,我為汝哭泣。請汝萬萬不可送死。
汝生為家中么兒,爹娘寵愛於一身,
春暉庭訓遵正道,豈教持刃殺人耶?
取人性命身亦死,豈僅二十四載養育情?

旅順存亡為何物,可知商訓無此條。

天皇御駕不親征,徒令血流遍荒野,
若是天皇思慮密,豈信戰死顯榮耀。

哀痛未息又送汝,聽聞聖代太平世,
慈母白髮卻徒增。

恩愛廝守未十月,試想少女望夫心。

此生並非汝一人,究竟何人可請託,
啊!吾弟,請不要死!

在《日俄戰爭的時代》一書的序言讀到《請君勿死》一詩,覺得譯者何源湖翻譯的真好!
#有沒有台大歷史系畢業的人翻譯功力都很強的八卦?

Objective-C 筆記》貓王運算符

有一種常見的三元運算符用法如下:

NSString *aString = nil;
NSString *outString;

outString = aString ? aString : @"";   //=> outString: ""

aString = @"Hello";
outString = aString ? aString : @"";  //=> outString: "Hello"

對於這種三元運算符用法,GCC C Extension 增加了可省略中間運算式的 ?: 運算符來縮短這種寫法,用法如下:

NSString *aString = nil;
NSString *outString;

outString = aString ?: @"";  //=> outString: ""

aString = @"Hello";
outString = aString ?: @"";  //=> outString: "Hello"

我在《 Java 程序員修煉之道》這本書裡看到這種運算符稱作貓王運算符(Elvis operator)

因為這個符號看起來明顯很像貓王鼎盛時期梳的大背頭

重點來了,我寫這篇筆記只是想說:貓王運算符這個名字真是太可愛了! >////<

Objective-C 筆記》GCC 的 C 擴充功能: Code Block Evaluation

在 NSHipster 看到 Clang 有支援 GCC 的擴充功能:Code Block Evaluation,看到後覺得真是驚為天人(又在亂用成語了XD),這個功能真是太實用了,立馬就在正在寫的專案裡用了這種寫法 😀

這個功能是用圓括弧()包起一個代碼區塊{},然後這個代碼區塊最後一個表達式的值就是返回的值,寫法就像這樣:

self.nameLabel = ({
    UILabel *label = [UILabel alloc] initWithFrame:CGRectMake(10, 10, 100, 44)];
    label.text = @"Riddle";
    label.textColor = [UIColor greenColor];
    label;
});

self.blogLabel = ({
    UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(10, 50, 300, 40)];
    label.text = @"http://riddleapple.logdown.com";
    label.textColor = [UIColor redColor];
    label;
});

因為變數只能存活在宣告那個變數的大括號內,因此可以利用這個方式,重複使用一些通用的變數名稱而不會產生衝突!

Objective-C 筆記》NSSelectorFromString

在 Objective-C 裡,可以使用 NSSelectorFromString() 函式把一個 NSString 轉換成 Selector,這樣就可以在執行期才決定要傳送的訊息:

SEL aSelector = NSSelectorFromString(@"redColor");  // 把一個字串轉換成 Selector

例如,有一個儲存顏色字串的 Array :

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // color string array
        self.items = @[@"Red", @"Green", @"Blue", @"Yellow", @"Orange"];
    }
    return self;
}

然後在一個 TableView 裡顯示這些顏色字串與顏色:

#pragma mark - UITableView Data Source

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return _items.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
    }
    
    // 利用字串產生 Selector
    SEL colorMethod = NSSelectorFromString([NSString stringWithFormat:@"%@Color", [_items[indexPath.row] lowercaseString]]);

    if ([UIColor respondsToSelector:colorMethod]) {
        // 發送訊息
        cell.textLabel.textColor = [UIColor performSelector:colorMethod];
    }
    
    cell.textLabel.text = _items[indexPath.row];
    cell.selectionStyle = UITableViewCellSelectionStyleNone;

    return cell;
}

執行結果會像這樣:

Objective-C 筆記》使用 GCD 的 dispatch_apply 函數實現類似 Ruby 的 map 方法

Ruby 語言的 Array 類別有一個 map 方法,可以把一個 Block 應用到 Array 裡的每個元素上,並將 Block 的傳回值收集成一個新的 Array,例如:

a = ['a', 'b', 'c']
a.map {|e| e.upcase}   # => ['A', 'B', 'C']

在 Objective-C 裡可以利用 GCD 的 dispatch_apply 函數來實現 map 方法:

#import <Foundation/Foundation.h>

@interface NSArray (RAMethods)
- (NSArray *)map:(id (^)(id item))block;
@end
#import "NSArray+RAMethods.h"

@implementation NSArray (RAMethods)

- (NSArray *)map:(id (^)(id item))block {
    NSMutableArray *results = [NSMutableArray array];
    for (int i = 0; i < self.count; ++i) {
        [results addObject:[NSNull null]];
    }
    
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    dispatch_apply(self.count, queue, ^(size_t index) {
        [results replaceObjectAtIndex:index withObject:block([self objectAtIndex:index])];
    });
    
    return results;
}
@end

使用方式:

#import "NSArray+RAMethods.h"
...

NSArray *array = @[@"a", @"b", @"c", @"d", @"e"];
NSArray *tmpArray = [array map:^(id item) {
                        NSLog(@"%@", item);
                        return [item uppercaseString];
                    }];
NSLog(@"----------");
NSLog(@"tmpArray: %@", tmpArray);

輸出的結果:

a
c
d
e
b
----------
tmpArray: (
    A,
    B,
    C,
    D,
    E
)

dispatch_apply 函數有三個參數:第一個參數是迭代次數,第二個參數是要使用的 Dispatch Queue ,第三個參數是要執行的 Block。

可以注意到在輸出結果中,虛線上方的字母次序跟 array 裡的次序不同,這是因為 dispatch_apply 函數會把第三個參數的 Block 加到 Dispatch Queue 裡,各個 Block 的執行時間不定,所以虛線上方的字母次序才會跟 array 裡的不同。

iOS 筆記》UIColor

每次使用 UIColor 的 +colorWithRed:green:blue:alpha: 方法都要把 RGB 數值換算成 0 ~ 1.0 間的浮點數,覺得太麻煩了,可以使用下面這個方法,給一組 RGB 十進制數值的陣列:

#import <UIKit/UIKit.h>

@interface UIColor (RAMethods)
+ (UIColor *)colorWithRGB:(NSArray *)array alpha:(CGFloat)alpha;
@end
#import "UIColor+RAMethods.h"

@implementation UIColor (RAMethods)

+ (UIColor *)colorWithRGB:(NSArray *)array alpha:(CGFloat)alpha {
    if (array.count != 3) {
        return nil;
    }
    
    NSMutableArray *results = [NSMutableArray array];
    
    for (NSNumber *number in array) {
        float colorValue = ((float)[number intValue])/255;
        [results addObject:[NSNumber numberWithFloat:colorValue]];
    }

    return [self colorWithRed:[results[0] floatValue]
                        green:[results[1] floatValue]
                         blue:[results[2] floatValue]
                        alpha:alpha];
}
@end

使用方式:

#import "UIColor+RAMethods.h"

...

// R: 127, G: 127, B: 127 (灰色)
UIColor *color = [UIColor colorWithRGB:@[@127, @127, @127] alpha:1.0f];

// R:255, G: 0, B: 0 (紅色)
UIColor *color = [UIColor colorWithRGB:@[@255, @0, @0] alpha:1.0f];