Entries

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

C++から見たObjective-C - カテゴリ

タグ: C++ Objective-C

指定したクラスに、インターフェースやプロトコルに記されていないメソッドを好き勝手追加する機能。
これによって、「特定のメソッドを実装したインスタンスが要求される」場面で、既存のクラスのインスタンスを使えるようにできる。

void append10(id obj)
{
	for(int i = 0; i < 10; i++)
	{
		[obj append:i];
	}
}

@interface NSMutableString (Foo)
- (void)append:(int)n;
@end

@implementation NSMutableString (Foo)
- (void)append:(int)n
{
	[self appendFormat:@"%d", n];
}
@end

void hoge()
{
	NSMutableString* str = [NSMutableString stringWithCapacity:10];
	append10(str);
	NSLog(@"%@", str);
}

C++では、既存のクラスにメンバ関数を追加することなどできないので、「特定のメソッドが実装されていること」ではなく、「特定の関数(関数オブジェクト)の引数となれること」を要求する。

/// appendメンバ関数を持つクラス用
template <typename T>
void append(T& obj, int n)
{
	obj.append(n);
}

template <typename T>
void append10(T& obj)
{
	for(int i = 0; i < 10; i++)
	{
		append(obj, i);
	}
}

void append(std::string& obj, int n)
{
	obj += boost::lexical_cast<std::string>(n);
}

void hoge()
{
	std::string str;
	append10(str);
	std::cout << str << std::endl;
}

カテゴリは他にも、単にメソッドを判りやすく分割したり、外部に公開したくないメソッドをPrivateカテゴリに定義したり(公開していないだけで、呼べば応えるが)するためにも使われる(Objective-C 2.0には「無名カテゴリ」的な要素として「クラス拡張」なるものが存在している)。
色々面白い使い方が出来る機能だが、1つ不安な点がある。
それは、同じクラスに同じ名前のメソッドが複数定義されたとき、どれが呼ばれるかは分からないことだ。
だから、インターフェースだけ確認して「同じ名前のメソッドはないな」と追加したら、実はPrivateカテゴリのメソッドと名前がかぶっていて、どちらかが予期しない方を呼んで困ったことに?などといったことが起こり得る。

// Fooカテゴリと同名のメソッドを定義
// どちらが呼ばれるかは判らない
@interface NSMutableString (Bar)
- (void)append:(int)n;
@end

@implementation NSMutableString (Bar)
- (void)append:(int)n
{
	[self appendFormat:@"[%d]", n];
}

それを考えると、非公開カテゴリなんてものは、あまり作らないようにした方が良いのかもしれない。

スポンサーサイト

コメント

[C28] 呼び出される順番

カテゴリでメソッド名が重なってしまった場合、一応呼び出されるルールがあります。一番簡単に確認できる方法として、XcodeのTargets --> Comile Sources の並び順を見てください。この並び順の最初の、追加されたカテゴリファイルのメソッドが呼ばれます。

また、プライベートのカテゴリの場合は、@interfaceだけ宣言し、 @implementは分けないのが一般のように思います。そうすると、重複して宣言できませんし、外部からこのプライベートカテゴリを呼び出そうとしても、警告が出ますので避けることができますよ。

[C29] Re: 呼び出される順番

naokitsさん
> XcodeのTargets --> Comile Sources の並び順を見てください。この並び順の最初の、追加されたカテゴリファイルのメソッドが呼ばれます。
興味深い情報ありがとうございます。
ただ、公開されている資料
http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/ObjectiveC/Articles/ocCategories.html#//apple_ref/doc/uid/TP30001163-CH20-TPXREF141
には 'A category cannot reliably override methods declared in another category of the same class.' とあるので、今のところは「2つのカテゴリが同名のメソッドを持つ場合、どちらが採用されるかは未定義である」と考えた方が無難だと思います。

> また、プライベートのカテゴリの場合は、@interfaceだけ宣言し、 @implementは分けないのが一般のように思います。
これはクラス拡張のことでしょうか。
プライベートなメソッドを宣言するには、たしかにそちらの方が一般的ですね。

> そうすると、重複して宣言できませんし、外部からこのプライベートカテゴリを呼び出そうとしても、警告が出ますので避けることができますよ。
非公開メソッドの難点は、そのクラスのカテゴリやサブクラスが「そんなメソッドがあるとは知らずにオーバーライドしてしまう」可能性があることです。
これはプライベートカテゴリでもクラス拡張でも変わりません。
  • 2010-02-17 01:09
  • むい
  • URL
  • 編集

コメントの投稿

コメントの投稿
管理者にだけ表示を許可する

トラックバック

トラックバック URL
http://idlysphere.blog66.fc2.com/tb.php/115-1cbbb1c3
この記事にトラックバックする(FC2ブログユーザー)

Appendix

タグ

Blog内検索

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。