2月2008

Cocoaで初めに覚えたい2つのこと

今日は、Cocoaで初めに覚えるべき二つの事柄について紹介しようと思います。きっとiPod touchのおかげでCocoaに興味を抱いている人は少なくないはずです。他でプログラミングをやったことがあって、Cocoaに興味があるという人向けに書いてみました。それぞれの項目については簡単にしか説明していませんので、どこか他のところで調べましょう。

1.メモリ管理

LeopardのCocoaからはガベージコレクションが使えるようになったので、もう不要かもしれませんが、以前はメモリ管理方法を知らなければプログラミングできませんでした。

キーワードは、alloc,copy,retain,release,autoreleaseです。Cocoaのメモリ管理は参照カウンタ方式を取っており、オブジェクトを生成(alloc,copy)、または保持(retain)するたびにカウントされ、逆に解放(release, autorelease)をするとカウントが減ります。カウントが0になったとき、すなわち誰も保持しなくなったときに解放されます。

また、メモリオーナーシップの概念も非常に重要です。オブジェクトを生成(alloc,copy)したとき、そのオブジェクトを解放(release)する責任を持っているのは、生成(alloc,copy)したオブジェクトとなります。逆に、自分が生成(alloc)や保持(retain)していないオブジェクトを勝手に解放(release)してはいけません。

参考資料:Objective-C 2.0プログラミング言語 メモリ管理

2.命名規則

Cocoaにおいて命名規則は非常に重要な意味を持ち、これが動的なObjective-Cの弱点を補っているとも言えるでしょう。具体的に命名規則が役立っている場面をあげるとすれば、

  • メモリ管理
  • キー値コーディング
  • デリゲート
  • 名前空間の代わり
  • クラス名とプロトコル名の識別
  • NSNotificationの通知

ざっとあげるとこんなものです。これらの規則が一体となって、結果的にコードの可読性を大きく向上させます。特に重要なところと言えば、キー値コーディングを使うには、アクセサメソッドやコレクションメソッドに対して特定の文法に従って命名する必要があります。キー値コーディングはCocoaの至る所で使われていて、これを使わない手はありません。

参考資料:Cocoaのためのコーディングガイドライン(ポッチンルーム)

最後に、Cocoaを始めるにあたって役立ちそうなドキュメントをいくつか並べておきます。すべてAppleの日本語訳ドキュメントです。どれも良質なドキュメントで、下の二つは量も多いので本にして出版してほしいくらいです。

Cocoaアプリケーションチュートリアルの紹介
Cocoa基礎ガイド
Objective-C 2.0プログラミング言語

5.0のコンパイラはほぼ完成してると思う…

久しぶりのActiveBasicの話題をどう切り出そうかと迷っているうちに、ついCocoaやニコニコの話題を書いて一週間たってしまったわけですが、そろそろ中断していたライブラリ開発を再開しているところです。

あんまり開発陣から音沙汰ないと、一般ユーザーさんが置いてけぼりにされている気がするので、リポジトリ上のActiveBasic 5.0今どんな感じのコードが書けるのかということを伝えようと思います。

たとえば、最近フォーラムに投稿されたこのトピック。
文字化けしてしまう

私もGetLgicalDrivesはつい最近Directory.GetLogicalDrivesで実装したばかりなので、何かアドバイスしようと思ったのですが、CP3の段階のStringの仕様とかよく覚えてないので、残念ながら何も回答できなかった次第です。まあ、そんことはどうでもよく、とりあえず最新のリポジトリ上のコンパイラとライブラリを用いると、ドライブ列挙がこんな風に書けます。

Imports System
Imports System.IO
Imports System.Collections.Generic

Try
	Dim drives = Directory.GetLogicalDrives() As List
	Show( drives )
Catch ex As Exception
	Show( ex )
End Try

Sub Show( aList As List )
	Dim s As String
	Dim enumerator = aList.GetEnumerator() As IEnumerator
	While enumerator.MoveNext()
		s += enumerator.Current.ToString() + Ex"¥r¥n"
	Wend
	MessageBox(0, s, aList.ToString, MB_OK)
End Sub

Sub Show( anException As Exception )
	ShowMessage( anException.ToString() )
End Sub

何か見慣れないものがありますが、とりあえずImportsは無視して、まずTry – Catchからです。この意味は、Tryの中で何かエラーが起きたら、Catchのところに飛びます。たとえば、Directory.GetLogicalDrives()で何らかのエラーにより取得に失敗すると、Catchへ飛びます。Catchのところで定義されているexというのにはエラー情報が入っていて、exからエラー情報を取得することができます。AB5では、いわゆる例外処理が実装されたわけです。ほかにもリストとかジェネリックとかいろいろあるわけですが、とりあえずほっといて、実行結果はこんな感じになります。

Directory.GetLogicalDrives()

せっかくなので、ここで手に入れた情報をDirveInfoに入れて、ドライブの情報も取得しましょう。

Imports System.IO
Imports System.Collections.Generic
Imports System

Try
	Dim drives = Directory.GetLogicalDrives() As List
	Dim list As List
	list.Add("ドライブ名 | フォーマット | 空き/容量")
	Dim s As String
	Foreach s In drives
		Dim drive = New DriveInfo(s)
		If drive.IsReady Then
			Dim name      = drive.Name As String
			Dim format    = drive.DriveFormat As String
			Dim freeSpace = drive.AvailableFreeSpace As QWord
			Dim totalSize = drive.TotalSize As QWord
			list.Add(name + " | " + format + " | " + freeSpace.ToString() + "/" + totalSize.ToString())
		End If
	Next
	Show(list)
Catch ex As Exception
	Show( ex )
End Try

/*Show()は上と同じ*/

そういえばForeach構文も実装されていたことを忘れてました。Enumeratorを使う必要がありませんね。実行結果は下の通り。
ドライブ情報

ちなみに、drive.IsReadyをコメントアウトして、準備できていないドライブに対しても取得しようとすると、drive.DriveFormatが失敗してCatchに飛びます。

失敗

というわけで、このようにコンパイラは最後に一般向けに公開されたCP3からずいぶんと進化しています。このほかにも、まだ誰も手をつけていませんが、デリゲートも搭載され、Windows.Formsのライブラリの開発にも着手できるようになったはずです。また、ライブラリ開発でコンパイラのバグがだいぶ減っていると思うので、今までのように公開して即座に重大なバグが見つかるといったことも、そんなにないかと思われます。

といってもコンパイラだけの話で、まだライブラリはメインのFormsに全然手をつけられていないし、エディタに関してはまだAB4のままです。しかし、上のコードを見ると、AB4からAB5へのバージョンアップは、相当なものということが見て取れると思います。

コメントの活用事例

ニコニコ動画が始まってから早くも一年以上が経ちますが、このたった一年という間で、書店の雑誌をみるとYouTubeと肩を並べて語られる位置まで上り詰めている急成長ぶりを伺うことができます。さて、約一年前に、私が最初にニコニコ動画について触れた記事があります。

ニコニコ動画の可能性

一年前に私が考えていたことは、ニコニコ動画のコメント活用の話です。動画と深くある関係のあるコメントを分析し、動画と結びつけることによって何かできないかという可能性を探っていました。これまでそういった試みはみたことなかったのですが、ここにきてようやく興味深い事例が出てきました。

[ニコニコ]NicoAutoRanking 0.0.0(ExceptionErrorMessage)

見てわかるとおり、コメントを活用したランキング動画自動生成プログラムです。これはコメントのあるニコニコ動画だからできることであり、ほかの動画サイトでこれと同様のことをすることはきわめて困難でしょう。コメントの活用方法にはまだまだ開拓の余地があると思うので、今後もこのようなおもしろい事例が出てくることを期待しています。そして私も、何かに利用できないかと常々考えていこうと思います。

ところで、ニコニコ動画全体の話になってきますが、3月5日にSP1となるようです。これまでの流れからすると、今回のバージョンアップは、ニコニコの発展に大きく貢献したと思われる、創作のサイクルを促す仕組みに何かしら関係のあるものではないかと考えています。具体的に何かとは予想ができませんが、少なくともこれまでよりユーザーの結びつきを強くするような機能が増えるのではないかと思ったりもしています。

もうひとつの予想としては、ビジネスの方向に行ってしまうのではないかというところです。こちらの予想は外れてほしいものです…

おまけ話として、ニコスクリプトやフィルター機能などの、主にRC2で追加された機能をうまく使いこなしている動画をほとんど見たことない、ただ自分が知らないだけなのかもしれませんが、この使い方は個人的におもしろかったです。ニコスクリプトは投稿してからでも変更できるという性質をうまく利用しているようです。

ただ曲をリンクしていっているだけなんですけどね。

10分でできるサービスメニューへの対応

Mac OS Xのユニークな機能として、システムサービスというものがあります。私はそれほど使っていませんが、便利といえば便利なものなので、ぜひ自分の作成したアプリケーションにも取り入れたいものです。そういうわけで、今回はサービスメニューへの対応の仕方を紹介しようと思います。

ちなみに、今回はサービスのみのアプリケーションについては説明しません。また、説明が結構適当になっているので、Appleのドキュメントとあわせてみるとより効果的だと思います。英語が苦手な方は、Cocoa Break!さんのところに日本語訳が置いてあります。

システムサービス

0. 概要

まずは一通り簡単な説明から行いましょう。サービスメニューはペーストボードを拡張したようなもので、データはペーストボードを経由し、アプリケーションへ渡されます。サービスメニューへ対応するために行う作業は主に2つあり、1つはOSヘサービスメニューがあることを知らせるための、アプリケーションのinfo.plistへのサービス情報の書き込み、もうひとつは、アプリケーション側でのサービスの登録、サービスに応答するメソッドの実装です。言葉にすると少し長くなりますが、やってみるとすぐに終わることなので、是非試してみましょう。

1. サービスに応答するメソッドを実装する

まず、サービスに応答するメソッドを実装しましょう。メソッドは次のような形式で定義します。

- (void)sendMessage:(NSPasteboard *)pboard userData:(NSString *)userData error:(NSString **)error

sendMessageのところを独自のメソッド名に変えて実装していきます。引数のpboardのところからペーストボードのデータを取得して、アプリケーションの処理を行っていきます。

2. サービスにアプリケーションを登録する

サービスに対応するアプリケーションは、アプリケーション起動時にサービスへ登録しなければなりません。ほとんどの場合、applicationDidFinishLaunching:で登録すれば問題ないでしょう。

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
	[NSApp setServicesProvider:self];

}

NSApplicationのsetServicesProvider:でサービスへ登録します。引数には、先ほど実装した、サービスに応答するメソッドが実装されているオブジェクトを指定します。インスタンス化が必要な場合は、先にインスタンス化してから指定しましょう。上のコードでは、applicationDidFinishLaunching:が呼ばれたオブジェクトでサービスのメソッドも呼ばれることになります。

3. info.plistを書く

あとは、info.plistに次のように追記するだけです。

<key>NSServices</key>
<array>
	<dict>
		<key>NSMenuItem</key>
		<dict>
			<key>default</key>
			<string>nDownloader/Download Comment</string>
		</dict>
		<key>NSMessage</key>

		<string>downloadComment</string>
		<key>NSPortName</key>
		<string>nDownloader</string>
		<key>NSSendTypes</key>
		<array>
				<string>NSStringPboardType</string>
		</array>
	</dict>
</array>

上の画像と見比べて察していただければ幸いでありますが、一応説明します。

<key>NSServices</key>
<array>
	<dict>
		<key>NSMenuItem</key>
		<dict>
			<key>default</key>
			<string>アプリケーション名/メニューの名前</string>
		</dict>
		<key>NSMessage</key>

		<string>呼び出すメソッド名</string>
		<key>NSPortName</key>
		<string>アプリケーション名</string>
		<key>NSSendTypes</key>
		<array>
				<string>対応するデータタイプ</string>
		</array>
	</dict>
</array>

手書きが面倒な人は、Property List Editorが付属してるはずなので、そちらで編集してみてください。

Property list editor

こんな感じになれば大丈夫です。

4. 実行する

Mac OS Xは、起動時にアプリケーションフォルダ、またはサービスフォルダから、アプリケーションのinfo.plistを読み取り、サービスメニューを構築しています。従って、サービスメニューを実際に試すには、ビルドしてアプリケーションフォルダに入れる必要があります。また通常、OSの起動時にサービスメニューが作られるので、一度ログアウトしなければサービスメニューが更新されません。ログアウトが面倒な方は、以下のメソッドを呼び出すことで強制的にメニューを再構築することができます。

NSUpdateDynamicServices()

サービスへの登録がうまく完了すると、サービスメニューに表示され、実行すると指定したメソッドが実行されるはずです。メニューからサービスを実行する際、アプリケーションが起動している必要はありません。起動していない場合、勝手に起動されます。

というわけで今日は、なんとなくドキュメントが少なかったシステムサービスについて書いてみました。

ADSL月額4000円の人は光を考えよう

うちは3年ほど前にYahoo! BBの50Mに契約し、初めてインターネット入れたのですが、そのままの契約で現在も月額4000円くらい支払っています。あとから、本当は50Mも必要なかったかもしれないと思い、契約を変更しようと思った事もありますが、それほど料金が変わらないようなので、今に至っている訳です。

これまで特に不満のなかったADSLでしたが、ここ1ヶ月くらい回線速度が約50kb/s、以前の約1/10しかでなくなってしまい、モデムの故障ではないかと疑いつつも、光導入を視野に入れて考えてみる事にしました。

どこの光サービスもだいたい料金は一緒で、一戸建ての場合、光電話込みで月額7000円くらいになります。これを見るとADSLより料金が高くなってしまっているように見えますが、実は、固定電話を光電話に引き継ぐ事で、固定電話が不要になり、NTTに支払っている固定電話料金2000円くらいが不要になります。結果的に、光の料金はADSLより、たった1000円くらい高いということになります。

ADSLの回線速度が元に戻ってくれれば一番いいのですが、元々うちは基地局から遠い場所にあるので、今まで安定していただけでも良い方だったのかもしれません。それに今まで、近所には前に家が一軒建っているだけだったのですが、最近周りに十軒以上たったので、速度低下の原因としては一番怪しいところです。

ということで、光の月額料金は、固定電話の料金も一緒に考えて検討してみましょう。

NNDouga.m 修正案

NNDouga.m – 動画を表すクラスの記事がなぜかブックマークされているので、NNDougaクラスのあまりよろしくないところをあげていこうと思います。

DougaとVideoの表記のぶれ

DougaとVideoが名前に使われていますが、どちらかわからなくなるので、どちらかに表記を統一すべきでしょう。

キー値コーディングで取ってくる値

NNVideoFLVURLKeyとか、どう見てもNSURLで返ってきそうなのに、手抜きしてすべてNSStringで返しているのはいただけないところ。名前の通り素直なクラスで返すべきです。

キーアクセスの仕方

NNDougaクラスに直接キー値コーディングして値を取ってくるようになっていましたが、attributesForKey:メソッドとか作った方がよかったかもしれない。

コンストラクタで取得

公開するために、完全なモデルのクラスじゃ地味すぎると思って、コンストラクタでデータを取得するようにしたのは間違いでした。やはりモデルクラスはモデルであるべきで、データの取得は外部に任せるべきです。

追加として

ファイルの書き込みとかNSCodingに対応してオブジェクトのシリアライズに対応したい。

まあ、こんなところなんですが、本当はFLV,コメントデータ,動画情報を入れた.nVideoのパッケージ形式のファイルを作ろうと思っているところです。後々必要になってくるので…

フォルダをパッケージにしたり、ファイルタイプを登録したりするところをまだ調べてる途中なので、しばし時間がかかりそうです。ファイルタイプの登録は、Cocoa以下のローレベルAPIを使わなければならなそうです。

Spacesの使い方

Spacesとは、Leopardの新機能の仮想デスクトップ機能の名称ですが、人によってさまざまな使い方をしていると思うので、今日は自分の使い方を紹介しようと思います。

Spaces

Spacesは最大16までのデスクトップを作る事ができますが、私は見ての通り9面のデスクトップを使っています。使っていないところもありますが…

ホームポジション的な位置は、左の真ん中で主にブラウザなどを置いているところです。メッセンジャーもよく使うのでここに置いています。その上のデスクトップにはメールソフトを置いています。左の一番下はiTunesです。だいたいいつも左の列はこのようになっています。

次に中心のデスクトップですが、これがメインの作業場となります。なぜデスクトップを9面にしているかというと、この中心のデスクトップが、4面のデスクトップと隣接するからです。別にまったく違う場所のデスクトップへ飛べない訳ではないですが、気分的には周りにあった方がいいです。

例えば上の画像は、プログラミングをするXcodeを立ち上げている様子ですが、真ん中のデスクトップでコーディング、下にUIをデザインするInterface Builder、右にリファレンス、左はブラウザ、といった感じで配置しています。

大変実用的なSpacesですが、ひとつ気になる不具合があり、たまに二つのデスクトップを行ったり来たり、連続的に勝手に移動してしまうことがあります。これはなるべ早くに修正してもらいたと思っているのですが、自分でもどういう時に起こるのかがよくわからないので、そう早くは修正されないかもしれません。対処法としては、連続的に切り替わってしまうときに、ウインドウをつかむ事によって、ウインドウを一カ所のデスクトップに移動することができます。そうするとSpacesの切り替えが止まってくれます。そういう現象に陥ったときはぜひためしてみてください。

NNDouga.m – 動画を表すクラス

ニコニコ動画の動画の情報を表すクラスを適当に作ってみたので、使いたい人は自由に使ってください。需要はよくわかりませんが…

ファイル

NNDouga.h
NNDouga.m

簡単な説明

動画ID(sm*****とか)を引数に初期化して使います。このとき中では、簡易的にネットワークから情報を取得するメソッド(initWithContentOfURL:とか)を使って、動画の情報を取得しています。ダウンロードしてる間は制御が戻りません。初期化が終了すると様々な動画情報を取得する事ができます。

使い方

#import "NNDouga.h"

NNDouga *douga = [NNDouga dougaWithIdentify:@"sm*******"];
NSLog( [[douga valueForKey:NNTitleKey] description] );
NSLog( [[douga valueForKey:NNVideoDateKey] description] );

動画のID(sm*****とか)を指定して初期化すれば準備完了です。あとはキー値コーディングでいろいろな情報が手に入ります。いわゆるサムネイル情報と、GetFLVの情報を手に入れる事ができます。ヘッダで定義されているキーを見てみましょう。

ニコニコ動画情報について

ニコニコ動画のサムネイル情報と、GetFLVについてはこちらから
ニコニコ動画のサムネイル情報を取得する(WebProgを極めて居酒屋を開発する)
getflv解析(OKの日記)

まとまった情報はこちら
ニコニコ動画のAPIのメモ(picasの日記)

Cocoaの話

NSScannerとかNSXMLDocumentとかちょっと使ってるので、何かの参考になるかもしれません。

応用例

自分はこのクラスを継承またはカテゴリ追加のどちらかをして、非同期で情報を取得できるようにして使うつもりです。

まったく関係ない話

Pathクラスが地味すぎて作り直す気がなかなか起きない。やっぱり動いて嬉しいFile関係を先に…

WPFとCore Animation

WPFとは、Windows Presentation Foundationの略であり、.NET Frameworkの一部であるが、文字通りインターフェースを担当するフレームワークとなっているようだ。この前からたびたび話に出てきている.NETのデータバインディングも、ここに含まれているようであるが、実はLeopardで大々的に披露されたCore Animationとほぼ同様のものも備えているらしい。

その前に、もう少しWPFについて触れておくと、WPFはMac OS XでいうCocoaのAppKitと同じようなもので、ユーザーインターフェースを担当しているものである。AppKitとの大きな違いは、WPFはXAML(ザムル)と呼ばれるXMLで記述する事ができる点だ。このことは、インターフェースを設計する開発ツールを容易に作成できる事を意味する。

そして、WPFのアニメーションもまた、LeopardのCore Animationとほとんど同じなのである。次のリンク先を見てもらえばわかるだろうと思うが、基本的にプロパティをアニメーションさせているあたり、まるで同じである。

第 4 回 私のアプリが動き出す! ~インターフェイスにダイナミックな表現を~(WPF)
プロパティベースのアニメーション(Core Animation)

こうして見るとWPFのアニメーションとCore Animationには違いが無いように見える。少なくとも今日調べた段階では、WPFは動画とアニメーションを同期する事ができるが、Core Animationはできない。Core Animationは何百枚ものレイヤーを同時にアニメーションさせることができるが、WPFはどれほどのパフォーマンスなのかわからない。といったところだろうか。

今日はこの本を立ち読みしてXAMLについて勉強してみました。

XAMLプログラミング WPFアプリケーションの概要と開発 XAMLプログラミング WPFアプリケーションの概要と開発
高橋 忍 川西 裕幸

ソフトバンク クリエイティブ 2007-04-07

Amazonで詳しく見る by G-Tools

バインディングの遅延効果

最近バインディグの仕組みを知った私は、積極的にアプリケーションに取り入れていましたが、今日はバインディングを使った事によって生じた後発のメリットを体験する事ができました。

下のようなNSTableViewを使ったアプリケーションを作っていたのですが、プログレスインディケータ(プログレスバー)を簡単にセル内に表示できない事がわかり、どうしようかと思っていたところでした。ここで、そういえばLeopardから新しく追加されたNSCollectionViewがあったな、と思ってちょっと使い方を調べると、バインディングの設定だけでなんとかなりそうだったので、NSCollectionViewにインターフェースを変更しようと考えました。

NSTableView

驚くべきというか、バインディングを使っていたので当たり前なのかもしれませんが、コードにいっさい手を加えずに、Interface BuilderだけでインターフェースをNSTableViewからNSCollectionViewに変更する事ができました。バインディングは少し取っ付きにくいのですが、慣れれば凄まじく便利なものです。

NSTableView

NSCollectionViewはどんなビューなのかというと、自分でレイアウトしたNSViewを、指定した行と列で列挙してくれるものです。例えば、上の列挙してるViewは下のようにレイアウトしています。

NSCollectionViewItem

あとでまた別の記事でNSCollectionViewについては詳しく触れたいと思っていますが、参考にしたサイトを下に記しておきます。

なにかつくるか。 » NSCollectionView。
基本的にここだけ見ればNSCollectionViewの使い方はわかります。

NSCollectionView内の個々のViewを選択状態にするには
NSCollectionViewのアイテムを選択したときに、どうやってアイテムを選択状態にするかのヒントが書かれています。私もとりあえずこれに習ってやっていますが、バインディングの際、transparentのFormatterでnegativeBooleanを指定するといい感じだと思います。このformatterを指定すると、バインディングした値とは逆のbool値が設定されます。