2015年1月31日 星期六

在iOS上開發UPnP相關App的資源

說實在的iOS上的UPnP資源有夠難找,大概是因為比較少人做的關係吧,加上如果下錯關鍵字保證是找好幾天找不到什麼東西,就算找到了也不知道能不能達成自己需要的功能,因此就產生了這一篇,希望幫助那些仍在迷途中的靈魂們。

UPnP Control Point Library

顧名思義這邊列出的library主要是做control point的

CyberLink4C
用C語言實現的library,有提供objective-c的wrapper,但並沒有包含全部功能,例如event的部份就需要使用到c api。

Platinum
這是一個以C++實現的解決方案,一樣有提供objective-c的wrapper,control point跟DMS都可以做。注意這個library預設是GPL授權的,如果商業使用需要申請商業授權。

upnpx
核心是C++,加上Objective-C API。VLC for iOS使用了這套library。年代有點久遠,不過堪用。簡單範例可以參考https://github.com/basti2310/upnpx-classes

個人推薦程度 Platinum > upnpx > CyberLink4C

Edit:
兩個新library
UPnAtom
使用swift實現的版本,可以用在Objective-C以及swift專案,目前swift變動很快,production環境不建議使用這個library。

CocoaUPnP
某個開發者因為upnpx無法滿足需求而自己寫的新library,還在非常早期的階段。

UPnP Media Server

Platinum
很多App的Local DMS都是用這套做的,應該沒什麼大問題。
coevo-upnp-server
這個source只能當參考用,年代已經有點久遠,而且裡面有非常多的bug,相信光是要掃雷就會花掉很多時間。

個人推薦程度 Platinum > coevo-upnp-server

順便附上用Platinum做DMS的demo project
https://github.com/vampirewalk/MediaServer

2015年1月29日 星期四

Realm 0.89.0先期研究

近期出現了Realm這個新的mobile database,先做點筆記。
Document: http://realm.io/docs/cocoa/0.89.0/

Subclass RLMObject

// Dog.h
 @interface Dog : RLMObject
 @property NSString *name;
 @property BOOL      adopted;
 @end

 // Dog.m
 @implementation Dog
 @end //none needed

目前Model物件必須subclass RLMObject。RLMObject只能在創建它的thread內使用。

Supported property types

NSString
NSInteger, CGFloat, int, long, float, and double
BOOL or bool
NSDate
NSData
RLMObject subclasses, so you can have many-to-one relationships.
RLMArray < X >, where X is an RLMObject subclass, so you can have many-to-many relationships.

Write

// Create object
Person *author = [[Person alloc] init];
author.name    = @"David Foster Wallace";

// Get the default Realm
RLMRealm *realm = [RLMRealm defaultRealm];
// You only need to do this once (per thread)

// Add to Realm with transaction
[realm beginWriteTransaction];
[realm addObject:author];
[realm commitWriteTransaction];

// Update an object with a transaction
[realm beginWriteTransaction];
author.name = @"Thomas Pynchon";
[realm commitWriteTransaction];

所有對物件的新增修改刪除都必須包在WriteTransaction裡,因為在RLMObject裏的所有ivar都是跟資料庫直接相連的,而不是一般我們習慣的memory。http://stackoverflow.com/a/25002895/1060971 說明了所有對物件property的修改都會被同步進database。

PrimaryKey

在設計Model物件時,PrimaryKey絕對是不可或缺的property之一。在不同的thread之間無法互相傳遞RLMObject使用,因為RLMObject只能在創建它的thread內使用。所以我們只能透過在非main thread查詢到物件的PrimaryKey,然後把PrimaryKey傳到main thread,然後再

[MyObject objectsWhere:"primaryId IN %@", idArray];

獲得一個新的RLMObject,然後才在main thread使用這個新的RLMObject。
參考:https://groups.google.com/d/msg/realm-cocoa/naRBh-WMet0/ONPvTCGaSUkJ

產生primary key的方式常見有兩種,一是流水號,二是uuid。
產生流水號的method在multi-thread環境下記得保證其thread-safe,否則很可能會產生相同的流水號。
uuid的話,在multi-thread環境下直接產生就好,因為衝突機率很小。

[[NSUUID UUID] UUIDString];

參考:http://stackoverflow.com/a/26257616/1060971

Multi async task completion block

假設現在有一些task需要去執行,在method裡面我們想等所有task完成再call completionBlock,那麼可以分兩種情況
1. task是synchronous的
2. task是asynchronous的

1比較好解決,用NSOperationQueue加dependency就OK了,2的話需要用到dispatch_group_t group。

範例:AVAssetExportSession的exportAsynchronouslyWithCompletionHandler方法是用來把iPod Library中的音樂轉成檔案用的。

- (void)exportAsynchronouslyWithCompletionHandler:(void (^)(void))handler;

現在我們選擇了一批歌曲轉出,想要在全部歌曲轉完之後收到通知並呈現UI,但是AVAssetExportSession不提供一次轉多個歌曲,我們只能知道個別task完成的時間,但是不知道哪時候全部完成,那麼要如何在全部轉完之後才收到通知呢?
這時候就可以使用dispatch_group_t group等待group裡的task統統完成再執行收尾工作。

- (void) convertMediaItemsToMp3:(MPMediaItemCollection *) collection completionBlock:(void(^)()) block
dispatch_group_t group = dispatch_group_create();

    NSArray *songs = collection.items;
    for (MPMediaItem *song in songs) {

        dispatch_group_enter(group);

        AVAssetExportSession *exporter = [[AVAssetExportSession alloc]
                                      initWithAsset:asset
                                      presetName: AVAssetExportPresetAppleM4A];
        exporter.outputFileType = @"com.apple.m4a-audio";
        exporter.outputURL = ...; 

        [exporter exportAsynchronouslyWithCompletionHandler:^{
            dispatch_group_leave(group);
        }];
    }
    dispatch_group_notify(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        if (block) {
            block();
        }
    });

http://stackoverflow.com/a/20910658/1060971

2015年1月16日 星期五

使用Instrument找出被retain無法釋放的instance

使用⌘+I叫出Instrument
選擇allocation
allocation

看右邊的面板,勾選Record reference counts
Record reference counts

然後開始檢測程式,執行那些你懷疑有問題的動作。執行完後在搜尋框輸入你要找的Class名稱。
滑鼠移到在Category那欄的物件名稱後面會有小箭頭,點進去,出現所有instance,再找要研究的instance一樣點小箭頭。
Search

進去之後會出現所有retain跟release的紀錄,上方標籤選unpaired可以列出沒有成對的retain或是release。
選擇懷疑有問題的那一行,在右邊面板選擇第三個頁籤,可以看call stack。
reference count

這次我遇到的問題是KVOController的block retain自己所在的instance,下次要記得unobserve。
還有使用[[FBKVOController alloc] initWithObserver:self retainObserved:NO]才不會retain被觀察的物件。