home ホーム search 検索 -  login ログイン  | reload edit datainfo version cmd icon diff delete  | help ヘルプ

C言語系/Objective-C/ObjectとNSObjectの違い

C言語系/Objective-C/ObjectとNSObjectの違い

C言語系 / Objective-C / ObjectとNSObjectの違い
id: 1049 所有者: msakamoto-sf    作成日: 2012-01-07 17:19:03
カテゴリ: Objective-C 

Objective-Cの勉強を始めようと思って、適当にネットで漁ったHelloWorldをgccでコンパイルしたら上手く動かない。
で、調べ始めたらObjective-C/NeXT/OpenStep/MacOSXの歴史とかUniversalBinaryとかにまで広がってしまいましたが、ようやく収拾がついたのでまとめます。

環境:

Mac OS X (Lion), 10.7.2
Intel Core i5
64 ビットカーネルと拡張機能:有効
Xcode 4.2

$ gcc --version
i686-apple-darwin11-llvm-gcc-4.2 (GCC) 4.2.1 ¥
(Based on Apple Inc. build 5658) (LLVM build 2336.1.00)

helloworld.m:

#import <objc/Object.h>
#import <stdio.h>

@interface HelloWorld : Object
{
}
-(void) hello;
@end

@implementation HelloWorld
-(void) hello
{
  printf("Hello, Objective-C!\n");
}
@end

int main(int argc, char *argv[])
{
  HelloWorld *o = [[HelloWorld alloc] init];
  [o hello];
  [o free];
  return 0;
}

コンパイル:

[msakamoto:ex01]$ gcc -o helloworld helloworld.m -lobjc
helloworld.m: In function ‘main’:
helloworld.m:19: warning: ‘HelloWorld’ may not respond to ‘+alloc’
helloworld.m:19: warning: (Messages without a matching method signature
helloworld.m:19: warning: will be assumed to return ‘id’ and accept
helloworld.m:19: warning: ‘...’ as arguments.)
helloworld.m:19: warning: no ‘-init’ method found
helloworld.m:21: warning: ‘HelloWorld’ may not respond to ‘-free’

結論

64bit化に伴い、レガシーなObjectクラスの実装については64bit用のバイナリでは提供されなくなったようです(Appleからの明確なアナウンス情報が見つからなかった)。このため、"objc/Object.h"で定義されている"Object"クラスのallocメソッドなどが見つからないため、上記のようなwarningになっているようです。エラーではなくwarningというのも面白いけど・・・。
gccはデフォルトではホストマシンのアーキテクチャでバイナリを生成します。今回は64bitカーネルで動作しているため、64bit用のライブラリとリンクするようにビルドされた結果、上述のような状況に陥ったようです。

解決策その1:素直にNSObjectを使う。

素直にNSObjectを使いましょう。仮にMac以外のプラットフォームでのコンパイルを考慮したとしても、最近であればlibFoundationと組み合わせればNSObjectなども普通に使える・・・らしい、です。
helloworld2.m:

#import <Foundation/NSObject.h>
#import <stdio.h>

@interface HelloWorld : NSObject
{
}
-(void) hello;
@end

@implementation HelloWorld
-(void) hello
{
  printf("Hello, Objective-C!\n");
}
@end

int main(int argc, char *argv[])
{
  HelloWorld *o = [[HelloWorld alloc] init];
  [o hello];
  [o release];
  return 0;
}

コンパイル&実行:

$ gcc -o helloworld2 helloworld2.m -framework Foundation
$ ./helloworld2 
Hello, Objective-C!

解決策その2:"-arch i386"でコンパイル

レガシーなObjectクラスの実体は、/usr/lib/libobjc.A.dylib に含まれています。今回はIntel版のMacOSXということもあり、i386とx86_64の2つのバイナリが含まれたUniversalBinaryになっていました。

$ file libobjc.A.dylib 
libobjc.A.dylib: Mach-O universal binary with 2 architectures
libobjc.A.dylib (for architecture x86_64):	Mach-O 64-bit dynamically linked shared library x86_64
libobjc.A.dylib (for architecture i386):	Mach-O dynamically linked shared library i386

このうち、i386版の方にはObjectのallocなど各種実装が含まれていました。
i386版:

[msakamoto:lib]$ nm -arch i386 libobjc.A.dylib | fgrep "[Object"
00017950 t +[Object allocFromZone:]
0001791c t +[Object alloc]
000179f0 t +[Object class]
00017f63 t +[Object conformsTo:]
00018092 t +[Object descriptionForInstanceMethod:]
000179d7 t +[Object free]
0001789b t +[Object initialize]
0001823c t +[Object instanceMethodDescFor:]
00017beb t +[Object instanceMethodFor:]
00017b1c t +[Object instancesRespondTo:]
0001799e t +[Object name]
000178c8 t +[Object new]
...

一方、x86_64版にはObjectクラスの実装がほとんど含まれていません。

$ nm -arch x86_64 libobjc.A.dylib | fgrep "[Object"
000000000001d3d7 t +[Object class]
000000000001d3ce t +[Object initialize]
(これだけ。)

以下の記事より、推測ですが32bitバイナリは"__OBJC2__"マクロ無しでビルドされ、64bitバイナリでは"__OBJC2__"マクロ有りでビルドされているのかも・・・いやそうとも限らないか・・・真相は調べきれませんでした。

長くなりましたが、i386版のライブラリにObjectの実装があるならi386版でビルドすればよさそうです。

$ gcc -arch i386 -o helloworld helloworld.m -lobjc
$ ./helloworld
Hello, Objective-C!

Object, NSObjectの歴史的な経緯

  1. Objective-C自体はSmalltakを参考に開発されたC言語ベースのOOP(1980s)
  2. NeXTSTEPでの開発言語に採用された。この時点ではまだ"Object"がルートクラスだった。
  3. Solarisに移植するため、NeXTSTEPからOS以外の部分をAPIとして切り出した。
    1. この時初めて"NS"が頭につくようになった。・・・と思われる。
    2. この辺り(ちょっとあいまい)でAppKitとFoundationの二種類にAPIが大別されたが、両方共ごっそりと現在のCocoaに引き継がれ、包含されている。
    3. FoundatinにNSObjectが含まれる。恐らくNSObjectの誕生はこの辺り。
  4. 1996年、AppleによりNeXTが吸収され、OpenStepの後継としてコードネームRhapsodyというOSの開発が始まる。
    1. OpenStepベースのライブラリサポートは"YellowBox"と呼ばれる。これが現在のCocoaになる。
    2. RhapsodyではMacOS8までのバイナリを動作させる"BlueBox"という技術も搭載されていた。

・・・というわけで、OpenStep以降独自に発展していったOpenStep -> YellowBox -> Cocoaの血脈にNSObjectは含まれている次第。
ただしサポート自体はMacOSX 10.5(Leopard)までは続いていたようで、似たような状況「<objc/Object.h>を使いたいけどうまくいかない・・・」というのは10.6以降に発生しているみたいです。この辺りは丁度OS全体の64bit化への切り替えとも重なっており、それによりObjectの実装に関連した問題が表面化したのかもしれません。なんとも分かりませんが・・・。ただ、似たような現象について取り上げられたスレッドや記事の殆どで、「Mac OS 10.5とかの昔のバージョンでは動いていたのに、10.6とか最新のOSXだと動かない!」というような内容が書かれています。

参考:NSObjectの実体はどこ?

NSObjectのコードを格納しているライブラリを探してみます。まず、helloworld2がロードするライブラリを確認します。

$ otool -L helloworld2
helloworld2:
	/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation ¥
(compatibility version 300.0.0, current version 833.20.0)
	/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 159.1.0)
	/usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 228.0.0)
	/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation ¥
(compatibility version 150.0.0, current version 635.15.0)

Foundationファイルを調べてみます。

$ nm /System/Library/Frameworks/Foundation.framework/Versions/C/Foundation
...
0000000000116df4 t +[NSObject(NSObject) implementsSelector:]
0000000000002b89 t +[NSObject(NSObject) initialize]
0000000000116d85 t +[NSObject(NSObject) instancesImplementSelector:]
00000000000944f2 t +[NSObject(NSObject) load]
0000000000116e6b t +[NSObject(NSObject) poseAsClass:]
000000000002ce54 t +[NSObject(NSObject) setVersion:]
000000000002ca96 t +[NSObject(NSObject) version]
...

CoreFoundationについても確認してみます。

$ nm /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation | less
...
0000000000049320 t +[NSObject alloc]
000000000012fbc0 t +[NSObject allowsWeakReference]
000000000012fbe0 t +[NSObject autorelease]
0000000000055fd0 t +[NSObject class]
000000000009b4b0 t +[NSObject conformsToProtocol:]
000000000009b2f0 t +[NSObject copyWithZone:]
000000000012fc00 t +[NSObject copy]
...

Foundationは"NSObject(NSObject)"という表記になりましたが、CoreFoundationは"NSObject"のままです。
細い差異が多少あるものの、この辺のライブラリにNSObjectなどのCocoa Foundationの実体が収められているようです。

参考資料

他:

各種Wikipediaへのショートカット



プレーンテキスト形式でダウンロード
現在のバージョン : 1
更新者: msakamoto-sf
更新日: 2012-01-07 17:36:59
md5:d7332a3b5b0f885d6810837d4656a143
sha1:60ec7db0a7f90a50f429865c4898f83de7d4c88b
コメント
コメントを投稿するにはログインして下さい。