2014年9月10日星期三

iOS screen resolutions (with iPhone6 & iPhone6 plus)

其實 3.5" 的 iPhone 現在買不到了, 理論上不太需要支援, 而且在 xCode 裡寫, 設為 full screen 時, 大多數的情況下, 3.5" 和 4" 在 UI 控制和調整上不會差太多. iPad mini / iPad2 / iPad retina 其實在開發時沒有特別的感覺, 因為位置是一樣的, xCode 都自己做掉. 這次 iPhone 6 & iPhone 6 plus 的解析度是滿怪的, 但很合理, 這麼做是必然的, 和 Android 在開發上有相同的問題, 下面紅色圈圈是目前用到的, 需要下載新版的 xCode 6.0 來看看 iOS 8 裡怎麼處理 iPhone 6 的解析度.



2014年9月9日星期二

nsstring trim space begin end

奇怪,trim space 不是很常用嗎?為什麼 objective-C 裡寫起來這麼麻煩。

解法1:
NSString *string = @" this text has spaces before and after ";
NSString *trimmedString = [string stringByTrimmingCharactersInSet:
                                  [NSCharacterSet whitespaceCharacterSet]];

解法2:
UPDATE: A quick benchmark showed that Matt's own adaption, based on Max' & mine, performs best.
@implementation NSString (TrimmingAdditions)

- (NSString *)stringByTrimmingLeadingCharactersInSet:(NSCharacterSet *)characterSet {
    NSUInteger location = 0;
    NSUInteger length = [self length];
    unichar charBuffer[length];    
    [self getCharacters:charBuffer];

    for (location; location < length; location++) {
        if (![characterSet characterIsMember:charBuffer[location]]) {
            break;
        }
    }

    return [self substringWithRange:NSMakeRange(location, length - location)];
}

- (NSString *)stringByTrimmingTrailingCharactersInSet:(NSCharacterSet *)characterSet {
    NSUInteger location = 0;
    NSUInteger length = [self length];
    unichar charBuffer[length];    
    [self getCharacters:charBuffer];

    for (length; length > 0; length--) {
        if (![characterSet characterIsMember:charBuffer[length - 1]]) {
            break;
        }
    }

    return [self substringWithRange:NSMakeRange(location, length - location)];
}

@end
and then:
NSString *trimmedString = [yourString stringByTrimmingTrailingCharactersInSet:[NSCharacterset whitespaceAndNewlineCharacterSet]];
or for leading whitespace:
NSString *trimmedString = [yourString stringByTrimmingLeadingCharactersInSet:[NSCharacterset whitespaceAndNewlineCharacterSet]];
It's implemented in an abstract fashion so you can use it with any possible NSCharacterSetwhitespaceAndNewlineCharacterSet being just one of them.
For convenience you might want to add these wrapper methods:
- (NSString *)stringByTrimmingLeadingWhitespace {
    return [self stringByTrimmingLeadingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
}

- (NSString *)stringByTrimmingTrailingWhitespace {
    return [self stringByTrimmingTrailingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
}

- (NSString *)stringByTrimmingLeadingWhitespaceAndNewline {
    return [self stringByTrimmingLeadingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
}

- (NSString *)stringByTrimmingTrailingWhitespaceAndNewline {
    return [self stringByTrimmingTrailingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
}
Edit: reverted back to initial version using charBuffer for better performance.

2014年8月21日星期四

Waiting until asynchronous blocks are executed 水為什麼不走直路

在寫程式時有很多的 completion block, 都是 asynchronous, 一開始的解法是想透過 dispatch_group_t 的 wait 去等, 後來一直試不出來, 經神人Lyle Wang 的指點後, 就是多傳一個參數進 completion block 裡, 當是最後一個 node 進來, 並且 completion 就去執行畫 UI 的工作, 執行出來的結果是正確的, 而且沒有多餘的等待.

只是要特別注意的是,這個 last node 的判斷不能在一開始匯入的地方做判斷,而是要在每一個 completion block 裡去判斷,因為有可能 第1個先執行的 completion block 卻在最後才跑完。

這個讓我想起來水為什麼不走直路的文章, 我的直覺是要 synchronous 等傳回值後, program flow 再往下走, 實際上在 asyncronous 下的 completion block 裡做事情也ok.

每件事情的發生必有其原因, 河流為什麼是彎的? 下面故事裡的用觀點滿有趣的, 他說彎是必然的就和人生會遇到無法跨愈的障礙一樣, 需要繞過去.

我覺得河流彎有彎的好, 缺點是水太大時容易淹水, 有時候需要截彎取直.













佛學院的一名禪師在上課時把一幅中國地圖展開,問:「這幅圖上的河流有什麼特點?」

「都不是直線,而是彎彎的曲線。」「為什麼會是這樣呢?也就是說,河流為什麼不走直路,而偏偏要走彎路呢?」禪師繼續問。

學僧們七嘴八舌地議論開了,有的說,河流走彎路,拉長了河流的流程,河流也因此能擁有更大的流量,當夏季洪水來臨時,河流就不會以水滿為患了;還有的說,由於河流的流程拉長,每個單位河段的流量就相對減少,河水對河床的衝擊力也隨之減弱,這就起到了保護河床的作用……

「你們說的這些都對。」禪師說,「但在我看來,河流不走直路而走彎路,最根本的原因就是,走彎路是自然界的一種常態,走直路而是一種非常態,因為河流在前進的過程中,會遇到各種各樣的障礙,有些障礙是無法逾越的。所以,它只有取彎路,繞道而行,也正因為走彎路,讓它避開了一道道障礙,最終抵達了遙遠的大海。

說到這裡,禪師突然把話題一轉,說:「其實,人生也是如此,當人們遇到坎坷、挫折時,也要把曲折的人生看做是一種常態,不悲觀失望,不長吁短歎,不停滯不前,把走彎路看成是前行的另一種形式、另一條途徑,這樣你就可以像那些走彎路的河流一樣,抵達那遙遠的人生大海。」

把走彎路看成是一種常態,懷著平常心去看待前進中遇到的坎坷和挫折,您會像河流一樣,抵達到人生的目標。




The best is yet to come.

Waiting until two async blocks are executed before starting another block

寫的程式裡有很多 connection request 都是 async 但有時候 main block 需要等 return 回來的值再繼續 process flow. sample code 如下:

dispatch_group_t group = dispatch_group_create();

dispatch_group_async(group,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {
    // block1
    NSLog(@"Block1");
    [NSThread sleepForTimeInterval:5.0];
    NSLog(@"Block1 End");
});


dispatch_group_async(group,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {
    // block2
    NSLog(@"Block2");
    [NSThread sleepForTimeInterval:8.0];
    NSLog(@"Block2 End");
});

dispatch_group_notify(group,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^ {
    // block3
    NSLog(@"Block3");
});

dispatch_release(group);
and could produce output like this:
2012-08-11 16:10:18.049 Dispatch[11858:1e03] Block1
2012-08-11 16:10:18.052 Dispatch[11858:1d03] Block2
2012-08-11 16:10:23.051 Dispatch[11858:1e03] Block1 End
2012-08-11 16:10:26.053 Dispatch[11858:1d03] Block2 End
2012-08-11 16:10:26.054 Dispatch[11858:1d03] Block3

========================

// create a group
dispatch_group_t group = dispatch_group_create();

// pair a dispatch_group_enter for each dispatch_group_leave
dispatch_group_enter(group);     // pair 1 enter
[self computeInBackground:1 completion:^{
    NSLog(@"1 done");
    dispatch_group_leave(group); // pair 1 leave
}];

// again... (and again...)
dispatch_group_enter(group);     // pair 2 enter
[self computeInBackground:2 completion:^{
    NSLog(@"2 done");
    dispatch_group_leave(group); // pair 2 leave
}];

// dispatch your final block after all the dispatch_group_enters
dispatch_async(dispatch_get_main_queue(), ^{
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    NSLog(@"finally!");
});
In this example, computeInBackground:completion: is implemented as:
- (void)computeInBackground:(int)no completion:(void (^)(void))block {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
        NSLog(@"%d starting", no);
        sleep(no*2);
        block();
    });
}
Output (with timestamps from a run):
12:57:02.574  2 running
12:57:02.574  1 running
12:57:04.590  1 done
12:57:06.590  2 done
12:57:06.591  finally!

from:
http://stackoverflow.com/questions/11909629/waiting-until-two-async-blocks-are-executed-before-starting-another-block