Upload
taro-matsuzawa
View
2.395
Download
6
Embed Size (px)
DESCRIPTION
Citation preview
どう考えてもdisconなものをiPhoneに移植してみた
Taro Matsuzawa aka btm (@smellman)at sumaben kanto #11
自己紹介的年表
1981
• 千葉県千葉市に生まれる
1988
• 白髪が生える
1989
• フライデーのヌード写真を強制的に見せられ女性恐怖症に
1991
• BL本鑑賞
1997
• 初めてのパソコン
• Windows 95 OSR2
• MMX Pentium 200Mhz
1999
• 東京電機大学理工学部入学
• English Speaking Society (E.S.S.)に入部
• 初恋
2000• デ・ジ・キャラットで萌えに目覚める
• 初恋の相手を秋葉に誘う
• Mozilla Party JP 1.0参加(初のOSSイベント参加)
• 彼女ができる
• コンピュータクラブ入部
• 埼玉西地区Linux研究会参加
• 彼女と別れる
• 腹いせにE.S.S.の部長に就任
2001
• オープンソース祭り参加
• アイスランド日本大使館設立に伴う留学生の招待を担当
• 鬱病
• Tokyo Linux Entertainment Community(TLEC)に参加
• WEB標準プロジェクト参加
2002• 二度目の恋を見つける
• 留年発覚、半年休学
• アルバイトでVBプログラマ(休学期間のみ)
• Mozilla Party 3.0 JPスタッフ参加
• もじら組スタッフに
• 復学
• 二人目の彼女ができる
2003
• 前就職先にアルバイト入社、入って二週間目でデスマ
• はめられてもじら組組長になる
• デスマで就活できなかったら勝手に内定がでる
• Mozilla Party JP 4.0 スタッフ参加
• 彼女と別れる
• デスマで時給1000円で月給40万
2004
• 東京電機大学理工学部情報科学科卒業(5回生)
• 神奈川県川崎市宮前区に引っ越す
• 借金する方法を覚える
• 日経ビジネスの取材をうけるも酷い扱いを受ける
• 「日経ビジネス買うぐらいならばんがいち買ったほうがいい」
• 某オラ○リーの本の翻訳の影武者をやるも出版されなかった
2005
• C Magazine 2005/10 ブラウザプログラミング特集2 「Firefoxのすべて」をPiroさんと共同執筆、執筆に伴いC++を初めてちゃんと勉強してコードを読む
• 仕事に限界を感じて退職を決意するも阻止される
2006
• 仕事に限界を感じるキッカケとなった上司が逃亡
• ほとんどの仕事がおれのところに
• 仕事に本当に限界を感じる
2007
• Software Design 2007/04 Firefox拡張機能開発チュートリアル(共著)
• 鬱病
• ゆ○○事件
• 辞めることを決意
• ヘッドハントの人と会うもその後連絡なし、まじむかつく
• 辞表提出
2008
• 最後の仕事が停電対応なので一人しか労を労ってくれない><
• 株式会社KBMJ入社
• オライリー・ジャパン「Firefox 3 Hacks」(共著) 執筆
• 実家へ引っ越す
• 寝取られ発覚
2009
• もじら組脱退
• 鬱病から復帰
• Software Design 「Firefoxステップアップ講座」2009.11 ~ 2010.04 まで半年間連載執筆
• OSS ECパッケージ「エレコマ」開発
2010
• iPhone開発の手伝い、In App Purchase 関係で2週間
• 副社長から「ちょっとベトナム行ってきて」と言われる
• ベトナム出張(一回目)
2011
• ベトナム出張(二回目)
• デング熱感染
• 二度と東南アジアに行けない体に
ベトナム出張前• 第一回目はiPhoneプログラミングの教育
• 以下副社長との会話
• 副社長「iPhoneプログラミングできるよね?」
• 俺「二週間しかやったことない上に、GUIほとんどわからないんですよ」
• 副社長「大丈夫だよ、出張まであと一週間ある」
• 俺「え?」
ベトナム出張前• 以下部長との会話
• 俺「ベトナムでiPhoneプログラミングっていうけど、今回のは業務持ち込みなんですよね?」
• 部長「そうそう、業務として仕事渡すからそれをこなしながらやってほしい」
• 俺「わかりました、で、何を実装するんでしょうか?」
• 部長「IMAPとかどうかな?」
• 俺「え?」
IMAP?
IMAP
• メールの受信箱に相当するプロトコル
• i.softbank.jpとかで標準
• 部長「まだメール受信機能ないじゃん」
• 俺「ちょっと無謀じゃないですか?」
• 部長「太郎ちゃんならできるよ」
• 俺「えー」
挫折
• ベトナムに来てしまったのでとりあえずわからないなりにやってみる
• ログインまではなんとかなる
• SSLとかちょっと無理すぎる
• とてもじゃないけど二週間の量じゃない
転換
• 相談してみる
• 俺「無理すぎるんだけど...」
• 同僚「他のライブラリとか見つからないの?」
• 俺「(見つからないから俺に話が来たんじゃ)えっと、探してみます」
なんかある• PantomimeというMac OS X/GNUStep向けのライブラリを発見、ライセンスはLGPL2
• 俺「2007年ぐらいから開発止まってるんだけど」
• 同僚「でも、ソースはあるんだよね」
• 俺「ライセンス的に問題が無いか調べる必要あるけど...」
• 同僚「じゃ調べて」
• 俺「えー」
LGPL2とiPhone• LGPL2自体は基本使うのは問題ない(ex. 昔のcocos2d-iphoneとか)
• 静的リンクすればいい
• ライブラリ自体のソースを添付するもしくは入手方法を明記
• リバースエンジニアリングを許可しないといけない
• 俺「リバースエンジニアリングってOKかな?」
• 同僚「一部のお客さんは嫌がったり組み込めないかもね」
• 俺「で、やるの?」
• 同僚「やって(はぁと」
• 俺「Σ(゚д゚lll)」
というわけで移植してみた話です
Pantomime
• Objective-Cで記述されたIMAP/POP/SMTPのライブラリ
• ターゲットはMac OS XとGNUStep
• 多くのソフトで動かされているという記述がサイトにある
• 最終リリースは 2007 / 2 / 5
• どう見てもdisconです。本当に(ry
移植のポイント 1
• 非推奨メソッドを通常のメソッドへ変更
• Mac OS X 10.2とかがターゲットのため非推奨メソッドだらけなので一つ一つ新しくしていく
• いらなそうな部分のコメントアウト
• NSDebugとかもういらないだろ
• 依存するライブラリへの対応
• がんばって依存するライブラリを動かせるようにする
移植のポイント 2
• GNUStepに依存するコード
• いらないので削除する
• SMTPのコード
• 今回のプロジェクトでは実装しなくていいのでコメントアウト(ひどい
• iOSに存在しないメソッドの代替案を探す
• 代替案がAppleが提供している場合はそれを利用
• 代替案がAppleが提供してない場合は自作する
非推奨メソッド
• 分かりやすい例だと以下のものが該当
• NSString cString
• NSFileManager fileAttributesAtPath:traverseLink:
• 非推奨メソッドはxcodeで警告が出ている
• デベロッパドキュメントで非推奨メソッドを検索すると推奨されるメソッドの情報が必ず載っている
NSFileManager
fileAttributesAtPath:traverseLink:
attributesOfItemAtPath:error:
- attributes = [[NSFileManager defaultManager] fileAttributesAtPath: thePath traverseLink: NO];+ NSError *error = nil;+ attributes = [[NSFileManager defaultManager] attributesOfItemAtPath: thePath error: &error];
大体ドキュメント読めばわかる
依存するライブラリ
• Pantomimeではopensslが利用されている。当然無いとビルドできない。
• openssl-xcodeというopensslを静的ライブラリにするものを使うとそのままiPhoneで利用できる
• https://github.com/sjlombardo/openssl-xcode
• ただし、ビルド対象が libcrypt.a のみなのでこれだけだとlibssl.aがないのでコンパイルが通りませんorz
無いなら作る
• libssl.aをビルドできるopenssl-xcodeを作ってしまう
• https://github.com/smellman/openssl-xcode
• 作った!
• libssl.a と libcrypt.a の両方が生成されるので多い日も安心
GNUStep対応のコード
• GNUStepに対応させるためのコードは今回は明らかに邪魔になっている
• そのせいで #ifdef MACOSX とかがあるけどそんなマクロ用意したくない(iOSだから)
• GNUStepとMac OS Xの実装を分離している部分を見つけたらMac OS
Xの方だけ残して削除するようにする。
• 見つけ次第削除していくだけでOK (Search and Destroy)
例
-#ifdef MACOSX #include <Pantomime/CWMacOSXGlue.h>-#endif
Mac OS X一択という考え方をする
例-#ifdef MACOSX #include <CoreFoundation/CFString.h> #include <CoreFoundation/CFStringEncodingExt.h>-#else-#include <GNUstepBase/GSCategories.h>-#endif
elseで処理されるのはGNUStepのコードとなる
iOSに存在しないもの
• iOSではそもそも存在しないものがある
• デバイス上の制限によるものが中心
• 代替案をとにかく探さないといけない
• 似たような機能があるかどうか?
• あればそれを採用
• 無ければ作る
NSHost
• 今回使われていたのは [[NSHost currentHost] name]
• 自分のホスト名を返すだけ
• gethostname で代用できる
• 返り値がNSStringなのでそれに合わせる必要がある
gethostname+ char s[65];+ memset(s, 0, sizeof(s));+ gethostname(s, sizeof(s)-1);+ NSString *hostName = [NSString stringWithCString:s encoding:NSUTF8StringEncoding]; aMailFile = [NSString stringWithFormat: @"%@:%@", [NSString stringWithFormat: @"%d.%d%d%d.%@", time(NULL), getpid(), rand(), [_cacheManager count],- [[NSHost currentHost] name]],+ hostName], ((id)theFlags ? (id)[theFlags maildirString] : (id)@"2,")];
よくあるパターンですね
NSCalendarDate
• iOSで存在しない上に、Mac OS XでもLegacy API扱いされている要らない子
• Pantomimeでは広範囲に使われていてかなり面倒
• パターンを分けて対処する
NSCalendarDateのパターン
• NSCalendarDateのインスタンスを保持しているケース
• NSDateを使うようにする
• descriptionWithCalendarFormatを使っているケース
• NSDateFormatterを使うようにする
• 時間の比較をしているケース
• NSCalendarとNSDateCompomentsを使うようにする
• timeIntervalSince1970 を使っているケース
• NSDateに同じメソッドがあるのでそれを使う
時間を比較- NSCalendarDate *aCalendarDate;+ NSCalendar *calendar = [NSCalendar currentCalendar];+ NSDate *aCurrentDate = [[NSData alloc] init]; int days; // We get the days interval between our two dates- aCalendarDate = [NSCalendarDate calendarDate];- [aCalendarDate years: NULL- months: NULL- days: &days- hours: NULL- minutes: NULL- seconds: NULL- sinceDate: aDate];- + NSDateComponents *comps;+ NSUInteger flags = NSDayCalendarUnit;+ comps = [calendar components:flags + fromDate:aDate+ toDate:aCurrentDate+ options:0];+ days = (int) [comps day];
あれ?w
NSMapTable
• NSMapTableというクラスではなく、typedef struct _NSMapTable NSMapTable;
• Mac OS X 10.0 ~ 10.4 までに使われていたもの
• 格納する型を指定できる
• オブジェクトのみとか、int型のみとかができる
• NSMapTable クラスで代用できるかと思ったら iOS にない。
オブジェクトの格納
• NSCreateMapTableでオブジェクトを格納するケース
• NSCreateMapTable(NSObjectMapKeyCallBacks, NSObjectMapValueCallBacks, 128);
• オブジェクトを格納してMapインターフェイスを使うには、NSMutableDictionaryが使える
NSMutableDictionary
//宣言- NSMapTable *_table;+ NSMutableDictionary *_table;
//初期化- _table = NSCreateMapTable(NSObjectMapKeyCallBacks, NSObjectMapValueCallBacks, 128);+ _table = [[NSMutableDictionary alloc] initWithCapacity:128];
//値挿入- NSMapInsert(_table, aUID, aDate);+ [_table setObject:aDate forKey:aUID];
int型の格納
• NSCreateMapTableでint型を格納するケース
• NSCreateMapTable(NSIntMapKeyCallBacks, NSObjectMapValueCallBacks, 128);
• NSMutableDictionaryはオブジェクト専門
• |←樹海| ┗(^o^ )┓三
よく考えよう
• 必要なのはMap的な何か
• Javaとかも型強制(Generic)するやつあるよね
• Genericってなんかの機能に似てるとかいう話を聞いたことがある
• 記憶をたどる
• std::map
• C++ !!!!!!
C++とiPhone
• iPhoneではC++でも書ける
• ただし、Objective-Cから叩きたい
• Objective-C++ というものがある
• ( ; ゚д)ザワ(;゚д゚;)ザワ(д゚; )
• 実装してみる
• なんか動いた
• 初めて自分で考えて書いたC++のコードがObjective-C++とか...
Objective-C++でMap (1)
#ifndef _Pantomime_H_StdMapIntWrapper#define _Pantomime_H_StdMapIntWrapper
#import <Foundation/Foundation.h>
#ifdef __cplusplus#include <map>@interface StdMapIntWrapper : NSObject {@private std::map<int, int> *wrapper_map;}#else@interface StdMapIntWrapper : NSObject {@private void *wrapper_map;}#endif
- (int) valueForKey:(int)aKey;- (void) setValue:(int)aValue forKey:(int)aKey;- (void) removeValueForKey:(int)aKey;
@end
#endif // _Pantomime_H_StdMapIntWrapper
Objective-C++でMap (2)
#import <Pantomime/StdMapIntWrapper.h>
@implementation StdMapIntWrapper
- (id) init { self = [super init]; if (self) { wrapper_map = new std::map<int, int>; } return self;}
- (int) valueForKey:(int)aKey{ return (* wrapper_map)[aKey];}
- (void) setValue:(int)aValue forKey:(int)aKey{ (* wrapper_map)[aKey] = aValue;}
- (void) removeValueForKey:(int)aKey{ std::map<int, int>::iterator it; it = (* wrapper_map).find(aKey); if (it != (* wrapper_map).end()) { (* wrapper_map).erase(it); }}
- (void) dealloc{ wrapper_map = NULL; [super dealloc];}
@end
そんなに大変なものでもない
こんな風に//include+#include <Pantomime/StdMapIntWrapper.h>
//宣言-static NSMapTable *fd_to_cfsocket;+static StdMapIntWrapper *fd_to_cfsocket;
//初期化- fd_to_cfsocket = NSCreateMapTable(NSIntMapKeyCallBacks, NSIntMapValueCallBacks, 16); + fd_to_cfsocket = [[StdMapIntWrapper alloc] init];
//値挿入- NSMapInsert(fd_to_cfsocket, (void *)[_connection fd], (void *)_socket);+ [fd_to_cfsocket setValue:(void *)_socket forKey:(void *)[_connection fd]];
//値取得- socket = (CFSocketRef)NSMapGet(fd_to_cfsocket, (void*)fd);+ socket = (CFSocketRef)[fd_to_cfsocket valueForKey:(void*)fd];
おまけ: Modified UTF-7
• Pantomimeの本体では Modified UTF-7 の処理空っぽ
• メソッドだけある状態
• とりあえず実装してみた
こんな感じ- (NSString *) modifiedUTF7String{ NSString *str = self; unichar c; uint index = 0; BOOL nowBase64 = NO; NSMutableString *toBase64String = [[NSMutableString alloc] initWithString:@""]; NSMutableString *result = [[NSMutableString alloc] initWithString:@""]; for (; index < [str length]; index++){ c = [str characterAtIndex:index]; if ((0x20 <= c && c <= 0x7e)) { if (nowBase64) { NSString *base64edstr = [self getUTF7Parts:toBase64String]; toBase64String = [[NSMutableString alloc] initWithString:@""]; [result appendString:base64edstr]; [result appendString:@"-"]; nowBase64 = NO; } if (c == 0x26) { [result appendString:@"&-"]; } else { [result appendString:[NSString stringWithCharacters:&c length:1]]; } } else { if (!nowBase64) { nowBase64 = YES; [result appendString:@"&"]; } [toBase64String appendString:[NSString stringWithCharacters:&c length:1]]; } } if (nowBase64) { NSString *base64edstr = [self getUTF7Parts:toBase64String]; [result appendString:base64edstr]; [result appendString:@"-"]; nowBase64 = NO; } return result;}
まとめ
• Mac OS X用のライブラリを移植するのはわりとなんとかなる
• Appleのデベロッパードキュメントは情報の宝庫
• 使えるものはなんだって使う根性があるとよい
• Objective-C++とか恐れないで!
URLとか
• Pantomime本家
• http://www.collaboration-world.com/pantomime
• Pantomime iOS fork 版
• https://github.com/smellman/Pantomime
質問など(スリーサイズ以外)