As Sloth As Possible

可能な限りナマケモノでありたい

タグ:Mac

ちょっと前からディスクドライブが嫌な音するなーと思ってたら、DVDが読み込めなくなった。

てか、これは何気にピンチだ。何かの拍子に再インストールするはめになっても、データ自体はTime Machineでバックアップしてるから戻せるけど、DVDを読み込めないのではインストーラ起動させられないよな。

早急に修理しないとだけど、こういうときどうすればいいんだっけ。もう保証期間はとっくに過ぎてるし、Sofmap行ってもだめだよなぁ。ディスクドライブって自分で換装できるんだっけ。あれかな、iPhoneのアダプタ交換と新MacBookを見に行くついでにApple Storeに行けばいいのかな。どっちにしろ余計な出費だ…てかお金ないよ…。くそう。せめて冬のボーナスまで待ってくれれば新MacBook購入で万事解決だったのに…。

この間のiPhoto Exporter作りの続き。

先週の時点でX-WSSEヘッダ付けてAtomAPIのエンドポイントGETしてレスポンスからNSXMLDocumentを作る、ってとこまではやったのだけど、どうもWSSE認証に失敗してるらしくLogin Invalidとしか返ってこなかった。Rubyでだったらサクっと書けたんだけど、CocoaでSHA1とかBase64とか結構面倒臭い。むぅ。

で、その辺で挫けかけたので一週間ほど放置してたんだけど、ググったらopensslのライブラリでやればいいじゃんってことがわかった。こんな感じでNSStringとNSDataにメソッドを生やしてみた。あとはXcodeのビルドオプションで「その他のリンカフラグ」に「-lcrypto」と追加してコンパイルしてやるだけ。

// Crypto.h
#import <openssl/md5.h>
#import <openssl/sha.h>
#import <openssl/evp.h>
#import <openssl/bio.h>
#import <openssl/buffer.h>

#import <Foundation/Foundation.h>

@interface NSString (Crypto)

- (NSData *)dataHashedWithSHA1;
- (NSString *)stringHexHashedWithSHA1;
- (NSString *)stringEncodedWithBase64;

@end

@interface NSData (Crypto)

+ (NSData *)dataWithBase64String:(NSString *)pstrBase64;
- (NSData *)dataHashedWithSHA1;
- (NSString *)stringHexHashedWithSHA1;
- (NSString *)stringEncodedWithBase64;

@end
// Crypto.m
#import "Crypto.h"

@implementation NSString (Crypto)

- (NSData *)dataHashedWithSHA1
{
	return [[self dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:NO] dataHashedWithSHA1];
}

- (NSString *)stringHexHashedWithSHA1
{
	return [[self dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:NO] stringHexHashedWithSHA1];
}

- (NSString *)stringEncodedWithBase64
{
	return [[self dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:NO] stringEncodedWithBase64];
}

@end

@implementation NSData (Crypto)

- (NSString *)stringHexHashedWithSHA1
{
	unsigned char digest[20];
	char finaldigest[40];
	int i;
	
	SHA1([self bytes],[self length],digest);
	for(i=0;i<20;i++) sprintf(finaldigest+i*2,"%02x",digest[i]);
	
	return [NSString stringWithCString:finaldigest length:40];
}

- (NSData *)dataHashedWithSHA1
{
	unsigned char digest[20];
	
	SHA1([self bytes],[self length],digest);
	
	return [NSData dataWithBytes:&digest length:20];
}

+ (NSData *)dataWithBase64String:(NSString *)aString
{
	BIO *b64, *bmem;
	int length = [aString lengthOfBytesUsingEncoding:NSASCIIStringEncoding];

	char *buffer = (char *)malloc(length);
	memset(buffer, 0, length);
	b64 = BIO_new(BIO_f_base64());
	bmem = BIO_new_mem_buf((char *)[aString cStringUsingEncoding:NSASCIIStringEncoding], length);
	bmem = BIO_push(b64, bmem);

	BIO_read(bmem, buffer, length);
	BIO_free_all(bmem);
	
	return [NSData dataWithBytes:buffer length:length];
}

- (NSString *)stringEncodedWithBase64
{
	BIO *bmem, *b64;
	BUF_MEM *bptr;
	
	b64 = BIO_new(BIO_f_base64());
	bmem = BIO_new(BIO_s_mem());
	b64 = BIO_push(b64, bmem);
	BIO_write(b64, [self bytes], [self length]);
	BIO_flush(b64);
	BIO_get_mem_ptr(b64, &bptr);
		
	NSString *buff = [NSString stringWithCString:bptr->data length:bptr->length-1];
	BIO_free_all(b64);
	
	return buff;
}

@end

Objective-Cなので見慣れない[]とか@とか入ってるけど、中身をよくよく見るとどう見てもCです。本当に(ry。Cのコードやライブラリをそのまま転用できるのは素敵だなぁ。Objective-Cで便利なフレームワークとか見つけられなくてもCで探せば大概ある。こりゃいいや。

APIを叩く準備が出来たので、早速iPhoto Exporterに取り掛かってもいいんだけど、どっちかというとフレームワーク作りに興味が向き始めた。livedoorの各種サービスのAPIをCocoaから簡単に使えるようにするフレームワークでも作ろうかなぁ。うふふ。

ここのところ久しくObjective-Cをいじってなかったので、急にCocoaと戯れたい衝動に駆られた。そうだ、iPhotoライブラリのExporterを作ろう、と思い付きでいろいろ調べてみた。iPhotoとかろくに触ったことないけど、Objective-C+Cocoa環境だと、動的にロードしたバンドルでランタイムのクラスを書き換えるとかもできてしまうので、既存のアプリに新しい機能を付けるとかが比較的簡単にできる。その気になれば(Cocoaアプリでさえあれば)どんなものにでもプラグインを作れるので、まぁなんとかなるだろと楽観的発想をしてみる。

で、見付けた参考になりそうな情報はitok’s Labさんとこの一連の記事とそのリンク先。iPhotoには組み込みで写真のExporterを追加できる機能があるらしく、思ったより簡単にできそう。大雑把に言うと、「Exporterのプロトコルに準拠したバンドルを作って、拡張子を.iPhotoExporterにして、/Applications/iPhoto.app/Contents/PlugIns以下につっこむ」などすればいいらしい。

元記事は詳細に作り方が書いてある上サンプルコードもあって素敵なのだけど、上で挙げた記事の通り、若干内容が古い。Leopard+iPhoto'08環境で使えるプラグインを作りたいので、自力でclass-dumpを使ってクラスを調べあげてExportPluginProtocolのヘッダファイルを作るところから挑戦することにする。

が。

これがなかなか厄介で、まずここで挫折しかけた。ヘッダファイルを作りはじめてはみたものの、何かサンプルコードの時点から大分増えたり減ったりしてて何が必要なのかわからないし、クラスやプロトコロルの定義はともかくCの構造体の定義とかまではちゃんとはdumpできない。途方に暮れかけながらExportPluginProtocolとかExportImageProtocolとかでググってたら、GoogleCodeにiPhotoPluginのプロジェクトがあるのを見つけた。ひゃー。欲しかったヘッダファイル全部ある。現在の環境で動くサンプルが作れる。これは素敵。

お陰で「書き出し」メニューの中にカスタムビューを追加するだけのプラグインを作れるところまでは出来たので、あとは写真共有サイトのAPIを叩くロジックを書くだけ。これは単にHTTPでGETとかPOSTとかするだけなのでそんなに難しくない。ほほぅ。やっぱりCocoaは面白いな。

Objective-Cランタイムを使うように書き換えられたMacRubyとやらが出たそうなので試しに入れてみた。目的としては今RubyCocoaが抱えている問題の解消、およびOSXのより深い部分までRubyで扱いやすくする、ってとこなんだろうか。

何が違うの?

  • 実装が1.9のものになっている(Leopard標準搭載のRuby+RubyCocoa環境は1.8.6)
  • メモリ管理をObjective-CランタイムのGCを使うようにしている
  • Cocoaとのブリッジ機能を使うのに特別なライブラリをrequireする必要がない
  • Frameworkの読み込みはKernel#frameworkで
  • 全てのRubyクラスはNSObjectのサブクラス
  • もっといろいろ違うとこあるんだろうけど、まだちゃんと見てないのでこんな感じ。

例えば、現行のRuby+RubyCocoaだと、

require 'osx/cocoa'
OSX.require_framework 'coredata'

class AppController < OSX::NSObject
# ほげほげ
end

とかしてるところを

framework 'coredata'

class AppController
# もともとNSObjectのサブクラス
end

と書けるようになってた。

ObjectがNSObjectのサブクラス

Rubyの全てのクラスはObjectクラスから派生しているのだけど、MacRubyではNSObjectがObjectクラスのスーパークラスになってた。

$ ruby --version
ruby 1.8.6 (2007-09-24 patchlevel 111) [universal-darwin9.0]
$ irb
>> Object.ancestors
=> [Object, Kernel]
>> require 'osx/cocoa'
=> true
>> OSX::NSObject.ancestors
=> [OSX::NSObject, OSX::OCObjWrapper, OSX::NSKeyValueCodingAttachment, OSX::NSKVCAccessorUtil, OSX::ObjcID, Object, Kernel]
$ ruby --version
MacRuby version 0.1 (ruby 1.9.0 2008-02-18 revision 0) [i686-darwin9.2.0]
$ irb
>> Object.ancestors
=> [Object, NSObject, Kernel]
>> NSObject.ancestors
=> [NSObject, Kernel]

ということはつまり、MacRubyでは全てのクラスはCocoaのクラスであって、RubyCocoaで言うところのOSX::NSObjectから使えるメソッドが全てのクラスで使えるということか。しかしObjectよりNSObjectのが上位にあるってなんか気持ち悪いなぁ、ちょっと。

実装はあとで見る

Objective-Cってことなんでソースが.mのファイルがいっぱいあるんだと思ったんだけど、流石にそういうわけではなくて(Objective-Cで全面書き直ししたわけではなくて)、大体普通のRuby1.9っぽかった。まぁそりゃそうか、わざわざObjCで全面書き直しする意味もないよなぁ。あとで何がどう違うのかちゃんと見てみよう。読める範囲で。

この間Cocoaセミナーに行って来たときに、面白い現象を見つけたので忘れないうちにメモ。

最近はもうずっとことえりを使わずにAquaSKKを使っている。SKKに慣れてしまうともう他のIMEを使う気になれないのだけど、Unixは言うにおよばずMacでもWinでもSKKがあるので、どこでも同じ操作感で日本語入力が出来て快適!もう手放せない!

Leopardにアップグレードしたときは若干不安ではあったんだけど、今日に至るまでこれと言った問題もなく動いて来たので特に気にせず使ってきた。

GCを有効にするとあれ…?

ところが、こないだついにAquaSKKが使えない事態に遭遇した。Cocoaセミナーでは演習と称してRSSリーダーを作っていたのだけど、Xcodeのコンソールでログを見てたら、どう見ても自分のアプリと無関係なエラーログが表示された。

2008-02-23 15:40:44.392 CoreRSSReader[625:10b] Error loading /Library/InputManagers/SIMBL/SIMBL.bundle/Contents/MacOS/SIMBL:  dlopen(/Library/InputManagers/SIMBL/SIMBL.bundle/Contents/MacOS/SIMBL, 265): no suitable image found.  Did find:
	/Library/InputManagers/SIMBL/SIMBL.bundle/Contents/MacOS/SIMBL: GC capability mismatch
2008-02-23 15:40:45.051 CoreRSSReader[625:10b] Error loading /Library/Components/AquaSKKInputMethod.component/Contents/MacOS/AquaSKKInputMethod:  dlopen(/Library/Components/AquaSKKInputMethod.component/Contents/MacOS/AquaSKKInputMethod, 262): no suitable image found.  Did find:
	/Library/Components/AquaSKKInputMethod.component/Contents/MacOS/AquaSKKInputMethod: GC capability mismatch

んん?SIMBL?AquaSKK?なんでなんで?

もちろんセミナーでSIMBLでプラグインを使うような高度なものを作るわけないし、テキストフォームだってIBでNSTextFieldを使ってるだけで別にオレオレカスタムViewでも何でもない。で、よく見たらGC capability mismatchと。ああなるほど。セミナーで作ったアプリはGCを有効にしてコンパイルするように言われたのだけど、SIMBLやAquaSKKはGC無効でコンパイルしてあるはずなので、それで読み込めてなかったのか。てことは、SIMBLやAquaSKKをGC有効にしてコンパイルすれば不整合が起きず普通に日本語入力できるようになるわけだ。

あれ、でもさぁ

確かLeopard発売当初はSIMBLが使えなかったのだけど、今はSIMBLもLeopard対応してるし、AquaSKKは当初からSafariやMail.appでも日本語入力できてた。てことはLeopardの標準アプリはGCを使ってないということだろうか。じゃあ、逆にAquaSKKをGC有効でコンパイルすると普通のアプリで使えなくなりそうだけど、その辺どうなんだろう。あとで調べてみるか。

昨夜はもちろんMacWorldの実況を追ってましたので、寝不足。危うい。寝惚けたままAppleStoreでPre-Order nowしそうになるし、車に轢かれそうになるし、AppleStoreでぽちっとやりそうになるし、電車に轢かれそうになるし、AppleStoreで(以下略)。落ち着け。あんなの、あんなの、たかが薄くなってカッコよくなって新デバイスが付いてSSDが搭載されただけのMacBookじゃないか!欲しい!やばい欲しいよ!多分今からでも遅くないからAppleStoreで(ry。そんな金無いっつーの。

でそんなわけでMacBook Air欲しいよ症候群に苦しんでたら、狙ったかのようなタイミングで開発用のマシンが届いた。昼飯から帰って席に付こうとしたらiMac様の箱が鎮座しておられました。ウホ。


素敵だよ。iMac。ちなみにiMacと一緒にぽつんとメモリが単体で置いてあったことも追記しておく。自分でやれメソッド発動。流石かわいい子を谷底に叩き落とす獅子の会社。違うって、そういう意味じゃないだろ。

あ、どうでもいいけどMacWorldで今回発表されたモノの中で個人的に一番ポイント高いのはTime Capsule。あの容量のネットワークストレージがあの値段で、加えてあのデザインで、あまつさえ無線LANのベースステーションで、極めつけがTime Machine対応だってよ。地味だけど今一番欲しい。

逆にAirは見送りの一手かな。今飛びつくのは林檎狂か真の漢だけだろう。ものすごく購買欲をそそるんだけども、冷静にスペックとコストパフォーマンスを見たらどう考えても最新型のMacBookのが上だし、これまでに無いものだから流石に不安すぎる。爆発はしないと思うけども。いや、違うよ。衝動買いするだけの財力が無いから負け惜しみしてるわけじゃないよ。本当にそれほど欲しくなんかないんだから。うん…欲しいよバカヤロウ、コンチクショウ。俺は林檎教徒だ悪いか!

CoreDataにつっこんであるデータを表示するViewを作ろうといろいろ試行錯誤してて、せっかくCoreDataでデータを管理してるのにいちいちそれを操作するコントローラを作るってのもなんだか泥臭いなと思ってたんだけど、調べたらちゃんとそれ専用のViewがあった。それがNSCollectionView。

使い方は簡単

  1. InterfaceBuilder上でNSCollectionViewを配置すると、一緒にNSCollectionViewltemとNSViewが作られる。
  2. NSCollectionViewのBindingを設定する。
    1. 「Content」を(使いたいCoreData entityを管理している)NSArrayControllerのarrangedObjectにBinding。「Model Key Path」は空のまま。
    2. 「Selection Indexes」をNSArrayControllerのselectionIndexesにBinding。「Model Key Path」は空のまま。
  3. 「Collection View Item」は既にOutletが設定されているので、特に変更することはない。
  4. Viewをいじる。
    1. 自動で作られた「View」はNSViewのインスタンスなので、カスタムビューを使う場合はこれのクラスを変更しておく。
    2. 「View」に表示したいものをぽんぽん置いてく。
    3. 配置した各項目のBindingを設定する。
      1. 「Collection View Item」にBindingする。「Model Key Path」は「representedObject.属性名」になる。

こんな感じで設定をすると、「CoreDataからデータをとってくる」→「そのデータをビューにはめこむ」→「スクロールビューに表示」までやってくれる。簡単簡単。ここまででコーディング一切なし。素敵だぜInterface Builder。残念ながらこれを使うとLeopardでしか動作しなくなっちゃうのだけど、これはかなり強力!

CoreDataとCocoaBinding

情報が少ないもんで何か難しいものと思われて敬遠されがちなんだけど、慣れてしまえばこれほど楽なものはない。データの扱いだとかUIの構築なんていう面倒なところを殆ど自作しなくてよくて、しかもそれが標準で使える機構だけでできるってのが素敵だよなー。

追記

参考にした記事を書いた人もTwitterクライアントを作ろうとしてCoreDataを弄ってらっしゃるのね。

正月休み中に形にするぞと息巻いてRubyCocoaのtwitterクライアントを作ってるのだけども、主にtwitterと関係ないところでハマってる*1。モデルの管理とIBとの連携が楽になると思って当初からCoreDataを使ってるんだけど、結構煩雑というかやぼったいというか。オブジェクトの追加は楽にできるんだけどオブジェクトの取得が面倒臭いんだよなぁ。

毎回NSFetchRequestを作ってNSEntityDescriptionを取得して…という処理を書くのがダルかったので、AR風のラッパを作ってみた。ベースのクラスを継承してモデル名と同名のクラスを作ると、そいつの操作が裏でCoreDataの操作になってるようなやつ。findとかdeleteとか、あとアクセサメソッドは定義しないでも勝手に拾ってよきにはからったりするとか、その辺は実装した。うーん、でも、このアプローチに自信が持てない。なんか車輪の再発明な気がしてならないし、多段ラッパになってて処理の効率悪そう。まぁ、勉強目的だから再発明上等だしtwitterクライアント程度でそんなにパフォーマンス気にすることない気もするけども…。RubyCocoaのdefine_wrapperとかARの拡張とかその辺りを補完するようなフレームワークを作れればいいんだけど。

こないだの補足

「CoreDataで管理してるデータは、正常終了時に自動保存されるのだけど、どこでどう処理されてるのかわからない」

テンプレートで自動生成されたAppDelegate.rbの中読んだらちゃんと書いてあった。applicationShouldTerminateメソッドの中で、managedObjectContextにsaveメッセージを送ってるのがそれですね。そこ以外ではデータの保存処理をしてないので、長時間つけっぱなしにしといて異常終了したりするとその間のログが全部ふっとぶのでどこかで定期的にsaveするようにしとかないとだ。まぁ、そもそも保存されなくても大して困らないけど。使えるものは使っておけと。

IBのチュートリアル

書こうと思ってたけど、むしろ俺自身が嵌ってるので書けそうにない。カスタムビューの作り方とか画像の扱いとかわかんないことだらけ。Objective-CやRubyCocoaはわからなくても、昔の資料から類推したりソース読んだりすればなんとでもなるんだけど、InterfaceBuilderの使い方は独力ではキツい。バージョン違うと資料が役に立たないし。今他のアプリのソース見て参考にしてるけど、誰か詳しい人がいたら助けて欲しいっす。

キーバリューコーディング

主にMVCのうちのCとVのバインディングに活躍してるキーバリューコーディングだけど、RubyCocoaと結構相性がいい。ハッシュかなんかをeachで回して全部ObjCのオブジェクトにobjc_send('setValue', value, 'forKey', key)とかしてやるだけでいいので、ラッパを作るのがすごい楽だった*2。素敵素敵。

いいから頑張れ俺

とかなんとか文章でいくら書いてもしょうがないので、作ったもの晒してみんなにツッコミ入れてもらった方が早い。さっさと投下しよう。

*1:二重の意味で

*2:具体的に言うとARでいうところのcreateあたりがまさにそう。あと、method_missingを捕捉してdelegate_object.objc_send('setValue', *args, 'forKey', name.to_s)とかやると後はよきにはからってくれるので、その辺も大して考えずに書けた。

RubyCocoaを極めるプロジェクト中でtwitterクライアントを作るという話が出てたので、正月休み中にみんなでいじって遊べる程度に動くものを作ってしまおうと目下がりがり書いてる最中なのですが、なかなか楽しいね、RubyCocoa。

e84fa15a.png

CoreDataが素敵

折角なのでCoreDataを活用してやろうと、HMDT本やリファレンスと格闘してました。CoreDataってなんぞ?ってレベルからのスタートだったので結構苦労したのだけども、CoreDataってのは要はCocoaアプリ中で簡易DBとO/Rマッパーを使えるようになるフレームワークなんだね。ActiveRecord+pstoreやsqlite、みたいなもんだと言っていいのかな。

で、これが非常に便利。Xcodeのモデリングツールでデータの定義をしておけば、勝手にDBとラッパオブジェクトを作ってくれるのでモデルクラスのとこのコーディングが格段に減る。モデル同士の関連もモデリングツールで作れるし、InterfaceBuilderとの連携でコントローラ部分も自分で作る必要がないので、簡単簡単。RubyCocoaから使う場合はCocoaから使う場合と若干構成が違うので最初躊躇ったけども。自分へのメモの意味でも、Xcode3でRubyCoca-CoreDataアプリを作る際のIBチュートリアル記事をあとで書いとこう。

ちなみに、twitterのデータの読み書きはこんな感じ

まずはデータの新規作成。

# NSManagedObjectのオブジェクトを新しく作ってDBに突っ込む
user = OSX::NSEntityDescription.objc_send("insertNewObjectForEntityForName",
            "User",
            "inManagedObjectContext",
            @managedObjectContext)
user.userId = '11111111'
user.screenName = 'rucotan'                                               
user.name = 'るこたん'

これでるこたんの情報がCoreData経由で使えるようになる。実はこの時点では「るこたんの情報が管理対象になった」だけで、自分でデータの保存処理をするか、CoreDataApplicationテンプレートからプロジェクト作ってるとアプリの正常終了時に自動で保存処理が呼びだされる*1か、そのタイミングで初めてファイルやDBに書き込まれるんだけど、「管理対象になった」時点でアプリ全体で使えるデータになるのでここではあんまり気にしない。

ちなみに、以前はNSManagedObjectのプロパティをいじるのに毎回valueForKeyやsetValue_forKeyを呼び出してたみたいなんだけど、RubyCocoaの改良でラッパオブジェクトを作るようにして、NSManagedObjectをまるでRubyのオブジェクトのように扱えるようになったらしい。軸がぶれてない、素敵*2。この辺がARっぽいとこ。

続いて既にあるデータの取得。

# NSFetchRequestを用意。RDBMSにSELECTクエリを発行するみたいな感じと言えばいいかな。
request = OSX::NSFetchRequest.alloc.init

# モデルを設定。SELECT文のFROMを指定するのに相当すると理解。
entity = OSX::NSEntityDescription.entityForName_inManagedObjectContext('User', @managedObjectContext)
request.setEntity(entity)

# 述語を設定。SELECT文のWHERE句相当、かな。
predicate = OSX::NSPredicate.predicateWithFormat('screenName == %@', 'rucotan')
request.setPredicate(predicate)

error = OSX::OCObject.new
users = @managedObjectContext.executeFetchRequest_error(request, error)

これで「screenNameがrucotanであるUserのリスト」を取得できる。NSFetchRequestとかNSPredicateってのが要はDBハンドラにプリペアードクエリを渡すのと手順は大して変わらないので、日頃DBを使うアプリに慣れてると違和感ないですね。注意点としては、executeFetchRequestの返り値はOSX::NSCFArrayなので、users.empty?とかusers.firstとかやると「そんなメッセージ、わたしが受け付けるとでも思ってるの!?この愚民は言葉もまともに話(ry」と怒られてしまう。Rubyで使うときは最初にto_aしておいた方が楽かも。

これだけだとRubyのARやPerlのClass::DBIに慣れきってる身としてはかったるくてしょうがないんだけど、Xcode上のモデリングツールで受信要求テンプレートというのを作ることができて、それを使うとNSFetchRequestの生成が大分楽になる。

variables = OSX::NSMutableDictionary.dictionary # 述語に渡すパラメータ
variables['screenName'] = 'rucotan'
request = @managedObjectModel.fetchRequestFromTemplateWithName_substitutionVariables('template_name', variables)
error = OSX::OCObject.new
users = @managedObjectContext.executeFetchRequest_error(request, error)

ちなみに、Xcode上で受信要求テンプレートを作る際には述語ビルダというのが使えて、複雑なリクエストでもiTunesやMailのスマートフォルダを作る感覚で述語を生成できてしまうので非常に簡単。だと思われる。いや、まだ使ったことないの。

キーバリューコーディング

に、ついても書こうと思ったんだけど、そろそろ長くなってきたのでまたの機会に。

仮名のRuchettaについて

Rubyの「る」とCocoaの「こ」がつくものがいいなあと考えて真っ先に浮んだのが「ルッコラ」だったんだけど、rucolaって開発環境があるのね、RubyCocoaの。ので、同じくルッコラを意味するイタリア語の「Ruchetta」にしてみた(安易)。ちゃんとr(uby)とc(ocoa)とt(witter)が入ったし、「ついった」と「るけった」で語感的にも悪くないかなと思ったんだけどどうだろう。みんなでいじれるようになったら名前も新しく考えてもらってもいいかな。

先を越された

これを書いてる最中に夏ライオン(OSXのtwitterクライアント)αリリースのお知らせが…。UIかっこいいなぁ。参考にさせてもらおう。

追記

夏ライオン見易くてなかなか良かった。自分でtwitterクライアント作ってる最中なのに当面夏ライオンユーザになりそうな予感。

*1:未確認。アプリ終了時に保存されるのは確かなんだけど、それがどこから何を呼び出してるのか実はわかってない。あとでちゃんと見る。

*2:そういえば絶望先生2期って今クールでしたっけ?

ここんとこMacPortsをメインに使ってたので気付いてなかったのだけど、GHCをインストールしたくなって、でも確かあれは「ソースからコンパイルはしてはいけない」って言われてたよな、MacPortsじゃない方法で入れたいな、と思って見に行ったらFinkのLeopard対応始まってた

まだバイナリのダウンロードはできないみたいだけど、どのみちいつもソースから入れてるのであんまり関係ない。何かと便利なので入れておこう。

秋学期始まって以来新システム導入やらメンテナンスやらで学校のPCルームのMacブースが全然使えなかったのだけど、久々に使えるようになってたので行ってみたら、PowerMac G4だったのが全部Intel iMacに変わってて驚いた。ついこの間まで明らかに型落ちのPowerMacに10.2が走ってるというなんだかなーな環境だったのが、Core 2 duo 2.16GHzでメモリ2GBで10.4.10ですって。快適快適。この間までどう見ても「よくわかんないけどとりあえず置いてます」って感じだったけど、Macに詳しいスタッフが入ったのかどうなのか、ちゃんと学内ネットワークとの連携もとれてる様子。こりゃいいや、卒業までずっとここ使おう。どうせみんなMacなんか使わないからほとんど俺専用席になる予感。

どうせみんなMacなんか使わない、というより大学の情報センターのスタッフがMacに関してはほとんどフォローしてくれないので、使わないではなくて使えないが正しい。Mac OS Xは正直Windows XPなんかよりよっぽど使いやすいインターフェースだとは思うんだけど、「知ってるものと違う」ってだけで思考が止まるらしく、大抵の場合席が埋まってて仕方なくMacブースに追いやられた可哀想な人たちは途方に暮れている。ので、たまに俺みたいな物好きがMacブースに座ってると、よく助けを求められる。謎の中国人に中国語入力の方法を教えてあげたり、以前はMacで学内のストレージにアクセスできなかったのでFTPでファイルを保存してあげたり、ログインもログアウトもわかんないで途方に暮れてるお姉さんに手取り足取りサポートしてあげたりしたこともある。

で、今日もそんな感じで隣に座った人に助けを求められたんだけど、今日はちょっといつもと違った。「Unixのコンソールを使いたいんだが、どうしたらいいか」と。そんなこと聞かれたの初めてだ。学校のPCルームでIEかWordかExcel以外のソフトを立ち上げてる人を見たことがなかったので、Terminal.appのありかを教えてあげながら、若干興味を持った。向こうもiMacをいじくってニヤニヤしてる俺が気になったらしく、いろいろ話しかけてきた。相手は電子工学の院生で海外のIT企業にインターンを考えてる人らしい。W-ZERO3の話とかiPod touchの話とか、学校の新システム開発の裏事情とか、そんなことをとりとめなく話して妙に意気投合して連絡先を交換した。

へぇ。ここ最近学校に行くのは時間の無駄としか思ってなかったけど、こういう予想外の面白いことがあるんならあと半年くらい我慢して通ってもいいかなとちょっとだけ思えた。まぁそれもMacの前に座ってたおかげなので、これからは暇つぶしするにしても積極的にMacブースに来ようかな。

うん、まぁ、授業に出る気は全然起きないんだけど。やる気の方向性が間違ってる。でもなぁ。実際何の意味もないんだもんな。

また新しいのが出てたので、こないだTigerを入れ直したばかりのMac miniをまっさらにして入れてみる。なんだか最近OSのセットアップが週末の習慣になってる気がする。なにかあったらすぐOS再セットアップ。気分転換にOS再セットアップ。なんだその変な趣味。おかげでMacOSXの再セットアップには大分手慣れてしまったよ。

で、Leopardの最新Preview版。すごい。興奮して鼻血出そう。新インターフェースはWWDCとかで発表されてる通りなんだけど、なかなかにインパクトあるよ。まだ最適化が足りないのか、1.83GHz Core Duoでメモリ512MBのMac miniでは若干重かったけども。あと、半透明のメニューバーは視認性悪いな。

具体的なことは多分ここには書けない*1のが残念なのだけども、もう発売間近なので皆さんお楽しみにということで。アプリの互換性とか検証してみよう。あとObjective-C 2.0だなぁ。あとでADCのリファレンス読んでみよう。

*1:ってかどこまで書いていいのかわかんない。規約確認しないと。

本家/.の記事より。今年1月にリリースされて以来批判が多く、発売直後のスタートダッシュもすぐに息切れしたと思われていたWindows Vistaだが、実はこのところ市場シェアを順調に伸ばしており、一方でVista批判CMを打つなど対抗心をあらわにしていたAppleのMac OS Xは横ばいが続いていると言う(ComputerWorldの記事)。

スラッシュドット ジャパン | Vistaのシェアは着実に増、Mac OS Xは横ばい?

コメントで触れてる人がいたけど、そもそも比較対象がおかしい。MacOSX*1とVista、ではなくてMacとWindowsって比較にするべきでしょ。

MacOSXのシェアが横這いでVistaのシェアが増えてるならその分どっかが減ってるわけで*2、トータルで見れば大して変動してない、ってだけの結論しかでない。大体、このところニューモデルのMacも発表されてない*3とか、Leopardの発売を前に買い控えが起きてる可能性にも触れてない。現行のMacOSX(Tiger)にはこのタイミングでシェアを伸ばす要因がないのだ。OSX→Vistaへの流入が有り得たのにそうならなかったのはむしろ堅調な証拠じゃないかな。一方Vistaは今後PCを買い替えるともれなくついてきます状態。増えはすれど減るわけがないだろう。それをさもVistaが頑張ってるから伸びてるんだよ、とでも言いたげな書き方はアンフェアというものだろう。それを言うならMacのCMも「それは間違いじゃないけど、ウソだよね」ってことを言ってるけど。

*1:参考のデータにはMacOSとMacIntelという項目しかなかったので、MacOSXというよりはMacOS全部合わせた数字じゃないのかな

*2:実際、XPや2000のシェアは減ってる。ちなみにMacOSとOtherも減ってるけど、MacIntelは増えてる

*3:マイナーバージョンアップはしてるけど、新型Macが出てないという意味で


最近Safariが不安定になったりやたら重くなったりと愛機のMacBookが不調。なんか妙なものでもインストールしちゃったかなといろいろ見てみたけどこれと言って原因が掴めない。そんなわけでアクティビティモニタをしばらく眺めてたら、時々mixi stationのCPU使用率が50%を越えてることに気付いた。えええ。


メモリ食いつぶすことは結構あるけど、CPUをフルに使うのはコンパイルの最中と動画のエンコーディング中くらいだったのに…こいつが半分以上もリソース食ってるとは思わなかった。SafariやiTunesですら普通にしてると10%にもならないこと考えたらこれは重過ぎだよなぁ。Rosettaで動いてるわけでもないし。


最近mixiログインすらしてないし、大体mixi station入れててもあんまり見てる人いないしな。とりあえず消しておこう。




今日はちょっと別なことに興味を引かれたので、一旦Cocoa&RSpecでBDD大作戦のほうはお休み。

さて、Dashboad Widgetなら開発もCUI上で完結できるんじゃね?なんて言ってしまったのでTerminalから手動でバンドルをビルドする方法を探してみることにした、のはいいんだけど何だかえらい面倒なことになりそうだ。

バンドルの実態

OSX上で実行可能なモノとして扱われるアプリケーションバンドル(拡張子が.appのやつら)にしろ、ウィジェット(.wdgt)にしろ、実態は単なるディレクトリで、中に決まった構成でファイルを配置して特別な拡張子を付けたものにすぎない*1。フレームワーク(.framework)やローダブルバンドル(.bundle)も同様。その証拠にターミナルでcd /Application/Safari.appとかやるとSafariの中に入れるし、Finderからでも「パッケージの内容を表示」で中身を見ることができて、そこでいろいろいじくることでアイコンを変えたりローカライズしたりアプリケーションそのものの挙動を変えてしまったりも簡単にできる。

で、あるならばそれを自力で作ってしまえばいいわけで、rakeとかmakeとかで自動化してしまえばCUIでサクサク開発も夢じゃない。と、ここまでは知ってはいた。けども、実際何が必要でどんな手順でやればいいのかはわかんない。さて、どうしたもんかね。

とりあえず本職に聞いてみる

不可解なことでも実際誰かがやっていることであるはずなので、そいつの仕事ぶりを見て技を盗めばいい。つまり、Xcodeの様子を観察してみればいいのだ。Xcodeの仕事はと言えばまさにgccでソースをコンパイルして、バンドル用のディレクトリを作り、必要なファイルをそこにコピーすることに他ならないので、要は見様見真似でそれをやってのければいいのです。多分。

というわけで、早速Xcode上でCocoa Bundleプロジェクトを作成してそれをビルドさせてみる。動作としてはこんな感じだった。

  1. Test.bundle/Contentsを作ってそこにInfo.plistをぶち込む
  2. Test.bundle/Contents/Resourcesを作ってそこにローカライズ可能なリソースたち(デフォルトのプロジェクトだとEnglish.lprojにInfoPlist.stringsがあるだけ)をぶち込む
  3. プリコンパイル済みプレフィックスヘッダとやらを生成、または処理する
    1. '-arch ppc'で
    2. '-arch i386'で
  4. ソースをコンパイル
    1. '-arch ppc'で
    2. '-arch i386'で
  5. リンク
    1. ppc用のやつをppc用のフォルダに入れとく
    2. i386用のやつをi386用のフォルダに入れとく
  6. ppc用バイナリとi386用バイナリをくっつけてUniversal Binaryを生成、Test.bundle/Contents/MacOSにぶち込む

これで出来上がり。うん、大体予想してた通りだ。このうちプリコンパイル済みヘッダとやらは多分なくてもいい。あと最初のInfo.plist、com.apple.tools.info-plist-utilityとかいうので処理してるんだけど、それがどこにある何のことなのかよくわからない。出来たバンドルの中見たけど単に元のInfo.plistのコピーが入ってるだけに見えるんだけどな。

で、それぞれの段階で二回ずつ処理してるのがちょっと面白い。Universal Binaryって本当に二つのアーキテクチャ用にコンパイルしたバイナリを一纏めにしてるだけなのか。ふぅん。何だか面倒なので差しあたって手を出したくないところなんだけど、大した機能があるわけでもないのにIntel Mac専用なウィジェットとか作ったところで面白くもないので、これもちゃんと覚えておこう。多分同じことを二回やってくっつけるだけだ。きっと。おそらく。

ということで、こんなRakefileを作ってみた。Rakefileの書きかたちゃんと調べてないけどまぁ失敗したら後で直せばいいか。(こら)

task :default => :build
desc 'Build loadable bundle'
task :build => ['Hello'] do |t|
  bundle_name = 'build/Hello.bundle'
  Dir.mkdir bundle_name
  Dir.mkdir "#{bundle_name}/Contents"
  Dir.mkdir "#{bundle_name}/Contents/Resources"
  Dir.mkdir "#{bundle_name}/Contents/MacOS"
  sh "cp Info.plist #{bundle_name}/Contents"
  sh "cp -R English.lproj #{bundle_name}/Contents/Resources"
  sh "cp Hello #{bundle_name}/Contents/MacOS"
end

file 'Hello' => ['Hello.ppc', 'Hello.i386'] do |t|
  sh "/usr/bin/lipo -create #{t.prerequisites} -output #{t}"
end

file 'Hello.ppc' => ['Hello.ppc.o'] do |t|
  sh "/usr/bin/gcc-4.0 -o #{t} -framework Cocoa -arch ppc -filelist #{t}.LinkFileList -bundle -mmacosx-version-min=10.4"
end

file 'Hello.i386' => ['Hello.i386.o'] do |t|
  sh "/usr/bin/gcc-4.0 -o #{t} -framework Cocoa -arch i386 -filelist #{t}.LinkFileList -bundle -mmacosx-version-min=10.4"
end

file 'Hello.ppc.o' => ['Hello.m'] do |t|
  sh "/usr/bin/gcc-4.0 -x objective-c -arch ppc -o #{t} -c #{t.prerequisites}"
  File.open('Hello.ppc.LinkFileList', 'w') { |f| f.write('Hello.ppc.o') }
end

file 'Hello.i386.o' => ['Hello.m'] do |t|
  sh "/usr/bin/gcc-4.0 -x objective-c -arch i386 -o #{t} -c #{t.prerequisites}"
  File.open('Hello.i386.LinkFileList', 'w') { |f| f.write('Hello.i386.o') }
end

お。ちゃんとビルドできた。class-dumpしてみる。

$ class-dump build/Hello.bundle/Contents/MacOS/Hello
/*
 *     Generated by class-dump 3.1.1.
 *
 *     class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2006 by Steve Nygard.
 */
This file does not contain any Objective-C runtime information.

クラスがない。思いっきり失敗しとるやんけ。 orz

念のため普通にXcodeでビルドしたやつもclass-dumpしてみるとちゃんとクラスの情報取れる。他のCocoaアプリからロードしてみても、自分で構成したやつはprincipalClassがnilになる。…うー。よくわからないまま適当にコンパイラのオプション省略するからいけないんだよね…。もうちょいいじってみよう。

追記

ちゃんとビルドできました。リンクのタイミングでXcodeは-filelistってのを使ってるんだけど、それやめて引数でファイル名与えたら通った。ってかやっぱり上のRakefile手順間違ってる?

*1:と、書いてあるっぽい。多分。


この間のインストール失敗のあとしばらく放置してたんだけど、よく考えたらAPRをインストールしてなかったことを思い出して再度挑戦。ちゃんとhttpd2.2.4に同梱のソースからコンパイルしてaprとapr-utilをインストールしたら今度は上手く動いた。それからexpatはなんら関係なかった。Fink版のAPRは一体いつの段階で入れてたんだろう。見たとこ特に依存してるのが見あたらないんだよなぁ。とりあえずいらないので消しておく。


あとついでにRailsの画面生成を10倍高速化してみる。ついでにと言いつつ、実はこれをいろいろいじってみてくれとの指令を受けているので全然ついでじゃないんだが。こっちは問題なく設定完了。バックエンドのRailsが吐いたPHPをApacheが処理して…って面白いんだけど何か妙な感じ。段々自分が何を書いてるんだか分かんなくなるなぁ。こうなると確かにRPHPというのが欲しいかも。


とにかくこれでdav+svnとapache+mongrelが使えるようになったのでいろいろ実験したいと思います。




Leopardのプレビュー版をMac miniに入れてたんだけど、そもそも「使って試せる」レベルのものじゃなかったし、既に発表されてる新機能もまだ取り込まれる前のものだし、本腰入れていじってる暇もないしなと思いもう一度Tiger入れなおしてサーバにすることにした。

vim、zsh、Ruby、Apache、Subversionあたりは最新版をソースからインストールして、その他の必要なものは面倒なのでFinkを使って入れてたんだけど、mod_dav_svnを組み込んだApacheを起動させようとしたところでこんなエラーが。

httpd: Syntax error on line 53 of /usr/local/apache2/conf/httpd.conf: Cannot load /usr/local/apache2/modules/mod_dav_svn.so into server: Library not loaded: /sw/lib/libexpat.0.dylib?n  Referenced from: /sw/lib/libaprutil-0.0.dylib?n  Reason: Incompatible library version: libaprutil-0.0.dylib requires version 6.0.0 or later, but libexpat.0.dylib provides version 2.0.0

libexpatが古いのか。Finkが勝手に取り込んでくれるライブラリは若干古いから気を付けてないと。となると依存ライブラリも自分で保守しなきゃならなくなって結構面倒くさいなぁ。でもFinkからだとコンパイル時の細かい設定できなそうだし、ApacheやSubversionは評価や導入の練習も兼てできれば最新のものを使いたい。さてどうしよう。

…あれ。いや、待て。そういえばApacheもSubversionもソースから入れたのになんで/sw/lib/libaprutilを見てるんだろう。ていうか/sw以下のライブラリ同士で依存関係の不整合が起きてるのは何故?なんかどっかで手順間違えたかな、俺。

木下さんの新しいCocoa本が出るということを今知った。あわわわ。プレゼント企画ももう終わってるじゃないか。しまったー乗り遅れたー。

今度は初心者向けの入門書らしい。そういえば俺がCocoaで遊んでみようと思ったときにはHMDT本を買ったけど、「Macってどうやっていじくるの?」「Objective-Cって何やのん?」ってレベルだった当時、率直に言って若干敷居が高かったし(いきなりCocoaバインディングやCoreDataを理解しようというのは無謀だったなぁ)、かと言って他の本はと言えばXcodeの使い方みたいな表面的なとこをなでる程度で物足りなかったし、数少ないCocoa関連書籍の中でも良書と言われていたものたちも、その当時(PantherからTigerへの移行期ぐらいだったかな)でさえもはや時代遅れになってしまっていた。

で、今、せっかくLeopardやらBootCampやらSafariやらiPhoneなんかで注目が集まってるというのに、Macでプログラミングしてみようと思ってる人の足掛りになりそうなドキュメントがほとんど無い。…まぁ、ADCのリファレンス読めって話だけどさ。でもそろそろまたこの手の本が必要な頃合いでしょう。

そんなわけでたのしいCocoaには期待。つか俺もそろそろなんかCocoaアプリでも作ろう。RubyCocoaも試したいし。

あと、プログラミング初心者じゃなくてCocoaをちゃんと知りたい人にはObjective-C Mac OS Xプログラミングが断然オススメ。Objective-CとCocoaについて体系的にかつ網羅的に知りたいと思ったら、現時点で発行されてる日本語書籍でこれに勝るものは無いと思う。

追記

ここまで書いた時点でもう発売してるってことに気が付いた。よく読め俺。あほう。なんだかすごく恥しくなりつつ、早速明日たのしいCocoa買ってきます。

AquaSKKというインプットマネージャを入れて見た。ことえりに別に不満があったわけでもないんだけど、何となく面白そうだったから。でも、これはなかなか快適だ。普通のIMに慣れてると確かにクセがあるし決して賢いわけじゃないんだけど、打ったはしからサクサクと変換してくれるのがビックリするほど気持いい。シンプルで余計なことは一切してくれないのが潔よいと言うか。

今まで知らなかったのがもったいないくらいだなぁ。後で会社のLinuxにも入れてみようっと。

この間から書こう書こうと思っててずっと忘れてたんだけど、最近のAppleのCM凄いね。あのラーメンズの出てるやつ。初めましてパソコンです、初めましてMacです。挑発してるなぁ。

ビジネスライクなお付き合い

あのCMのいまいち駄目そうなサラリーマン風の「パソコン」とカジュアルな格好の「Mac」というキャラクターの対比が素敵。「みんな僕をプライベートで使うから親しんでくれる(Mac)」「僕はビジネスライクな関係ばかりだからな…(パソコン)」と言わせてみたり、クールなアプリケーションを持っていると自慢する「パソコン」が挙げるのが電卓や時計だったり、ウイルスに感染して「ヤバい、クラッシュする」と言って倒れる「パソコン」を横目に「Mac」が平然と立ってたり。思わずぷふっと笑いが漏れてしまう。

ターゲットは何処?

MacのWindowsに対して優位に立っている部分を端的に表現して、かつ皮肉もかましてるあたりが素敵なCMだけども、若干疑念がわく。つまり、あれを見て果たして普通の人がMacを欲しくなるのかどうか、ということに。

あれが皮肉だと感じるのは少なくともWindows以外のOSを知っている人だろう。んでもって、Windows以外のOSに関心がある層は比較的ITに関するリテラシーの高い層に重なるだろうから、そういう人たちはあのCMを見なくともMacの長所短所を多少なりとも理解してるだろうし、その上でWindowsを選んでる人たちは、たとえ、安全性・安定性・インターフェースの使いやすさ・デザイン・先進的な機能・高性能なアプリケーション等々あらゆる面でWindowsに勝っていても、「Windowsではない」という理不尽だが致命的な理由で使わない(あるいは、使いたくても使えない)のだろうから、どうしようもない。あのCM程度では心は動かないでしょう。

一方で今までMacというもの自体を知らなかった人たちはあれを見ても皮肉だということすらわからないかもしれない。ソフトバンクの詐欺まがいの宣伝に飛びついちゃった連中が、未だにWILLCOMの定額プランなど知らないし説明してもその凄さを理解出来ないのと同じだ。そもそもWindows以外の環境を知らなければ、Windowsを相対化して他と比較するなんてことは出来ないし、しない。長所短所の問題以前に、比べる対象として見てない。「なんかよくわからん」で終わりだろう。とあれば、iMacが始めて出たときの戦略と同じように、「なんかよくわからないけどカッコいい」って思わせちゃった方が食いつきが良さそうだ。

してやったり、と思うのは「こっち側」だけなのかな

というわけであの素敵なCMが流れたところでその効果のほどはどうなんだろう、という感じ。いやわかんないけど。俺の素人考えなんかてんで見当違いで、もしかしたらこれがきっかけでバカ売れし始めるのかもしれないけどもさ。…うーんでもやっぱりそれはないだろうなぁ。あのCMもMac自体も大好きなんだが。個人的には。

↑このページのトップヘ