2013/06/28

iOS7 beta2をiPad miniにインストールした

すごくいらいらしているので、更新する(自分にいらいら)。

 

昨日、iPad miniでも使えるiOS7 beta2がリリースされたので、インストールした。
初めてなのでいろいろわからんかったが、

  • ダウンロードは、Mac PCにする
  • iTunesを使うと、iPad miniがいろいろ触れる
  • iTunes経由でiPad miniのOSアップデートができる
  • iCloudってのでバックアップができる
  • iTunesからoptionキーを押しながら「アップデート」ってすると、ファイル選択できる

などなど。
うちのMac miniは、XcodeとChromeくらいしか入ってないので、iTunesとかあるなんて知らなかった。
音楽の再生ソフトとばかり思ってたけど、そうじゃないんだねぇ。

 

検索してiOS7をインストールした人の手順を見ながら、やると、特に迷わずできた。

うーん、アイコン、平べったい。
でも、なんかゆらゆらして見える。
「今日は疲れているのか・・・」と思ったら、壁紙がiPad miniの角度によって微妙に動くようだ。

「動く壁紙なのか?」と思ったら、そうではない。
でも、動く壁紙というのも増えてた。
これは、傾けると重力でボールみたいなのが動いた。
静止画のやつも、傾けると微妙に動いた。
個人的には、目にきついのでやめてほしいのだが、浮き上がってみせるための効果なのかねぇ。

 

そう思って時間表示を見ると、タイムゾーンの設定がうまく行っていないのか、時間がずれてた。
では、と設定画面から「日付と時刻」を選択し、時間帯を設定すると・・・固まった。

ここで慌てて再起動方法を調べ、「ホームボタンと電源ボタンの長押し」という技を覚えた。


そしてだな・・・それからうちのiPad miniは起動しなくなった。
いや、正確にはそうじゃない。
ホームボタンをトリプルクリックすると白黒反転するんだけど、そうすると真っ白になった。
つまり、起動するけど、何も表示されないままになってしまったのだ。


あれこれやったが、iTunesでiOS6に戻した。
そしてiOS7をもう一度焼いたが・・・だめ。

またiOS6に戻し、設定画面から初期化っぽい動作をさせ、もう一度iOS7を焼く。
ようやく起動するようになった。

 

そんな感じで、なんとなく使えるようになってきた。
まあ、まだよく基本アプリが固まるけどね、設定アプリとか。
壁紙が動くことさえ目をつぶれば、あまり気にならないかもしれない。

2013/06/17

[obc]@propertyはセッター/ゲッターを作ってくれる

@propertyは自動的にセッター(setter)/ゲッター(getter)を作ってくれるそうだ。
入門書だけ読んでたら、あやうくスルーするところだった。

基本的には、こうらしい。

  • @property 型 propertyName;
  • ゲッター : propertyName
  • セッター : setPropertyName:(型)
  • メンバ変数 : _propertyName

プロパティ宣言した名前とゲッターが同じになるけれど、メンバ変数は違うんだ。
だから、selfをつけないとうまくアクセスできなかったのだ。

 

それはいいとして、気になっているのは@synthesizeだ。
ネットでなんとなく読んでいる限りでは、@synthesizeしないとセッターやゲッターを作ってくれない、という風に読んでいたのだ。
しかしAppleのドキュメントを読むと、@synthesizeを使うのは、

  • プロパティのメンバ変数名を規定の名前以外にしたい場合
  • readwriteのプロパティに対して自前でセッター/ゲッターを実装したが、メンバ変数がほしい場合
  • readonlyのプロパティに対して自前でゲッターを実装したが、メンバ変数が欲しい場合

に使うものだそうだ。

 

あー、今日は時間切れ。
おやすみなさい。

2013/06/16

[obc]@protocolはpure virtualみたいなものか?

ちょっと説明を読んだだけなのだが、@protocolはC++でいうところのpure virtualなメンバ関数みたいなものじゃなかろうか、と思った。
「継承したらこいつらは必ず実装しろよー」というやつだ。

//[0.h]
#import <Foundation/Foundation.h>
@protocol AddrBook
- (NSString *)name;
- (NSInteger)age;
@end
--------------------
//[1.h]
#import "0.h"
@interface HellBook : NSObject <AddrBook>
@end
--------------------
//[1.m]
#import <stdio.h>
#import "1.h"
@interface HellBook() {
	NSString *myName;
	NSInteger myAge;
}
@end
@implementation HellBook
- (id)init
{
	myName = @"Black Dog";
	myAge = 3000;
	return self;
}
- (NSString *)name
{
	return myName;
}
- (NSInteger)age
{
	return myAge;
}
@end
------------------------
//[main.m]
#import <Foundation/Foundation.h>
#import "1.h"
int main(void)
{
	HellBook *hb = [[HellBook alloc] init];
	NSLog(@"%@ - %ld\n", [hb name], [hb age]);
	
	return 0;
}

$ clang -o tst 1.m main.m -lobjc -framework Cocoa
$ ./tst
2013-06-16 17:40:09.364 tst[395:707] Black Dog - 3000

うーん・・・。
なんだかよくわからない・・・。

pure virtualなclassは、しょせんclassなので実装も書くことができる。
インスタンスは作れないけど、ポインタ変数としては使うことができる。
それに対して、@protocolはポインタ変数も作ることができない。

AddrBook *hb;

こういうことをしたいなら、こうやる。

id<AddrBook> hb;

と思ったのだが・・・、これはこれでうまくいくけど、<AddrBook>がなくてもうまくいく。
しかも、id型じゃなくてid*型にしてもうまくいってしまった(warning多数だが)。

<AddrBook>みたいなのを付けるメリットとしては、id型だと何でも受け入れてしまうけど、id<プロトコル>とすることで、該当するプロトコルを持ったインスタンスしか代入できなくなる、というところか。
間違えの防止に役立つんですかね。

でも、id型の変数を代入する処理にしたが、これは怒られない。
ってことは、動的エラーになる可能性があると言うことか(NSInvalidArgumentException例外が発生するみたい)。

void func(id val)
{
    id<AddrBook> hb = val;
}

 

うーん、なんか本質をつかんでないなあ。
もう少し資料を読まねばならぬようだ。

[obc]やっぱり名前に縛りはあるようだ

Appleの「ProgrammingWithObjectiveC.pdf」を読んでいると、後ろの方にこんな記述があった。

Objective-Cにはさまざまなメモリ管理規則があり、コンパイラはこれをもとに、必要な間はオブジェクトが確実に生きているようにしています。通常はそれほど意識しなくても構いませんが、コンパイラは生成メソッドの名前に基づいて、従うべき規則を判別します。

なんですってー!
コンパイラが名前を意識してるんだ。。。
本に書かれていたのは、ここを指していたのですな。

逆らうつもりも特になかったのだけど、根拠が見つかって安心した。

[obc]参照カウントを見てみる

さて、allocなんかも使ってみたことだし、噂の参照カウント値を見てみよう。

#import <Foundation/Foundation.h>
#import "1.h"
int main(void)
{
	Hell *hell = [[Hell alloc] init];
	NSLog(@"count = %ld\n", [hell retainCount]);
	[hell print];
	NSLog(@"count = %ld\n", [hell retainCount]);
	[hell release];
	NSLog(@"count = %ld\n", [hell retainCount]);
	
	return 0;
}

printfでもいいけど、そろそろNSLogってのにも慣れておかねば。

$ clang -o tst 1.m main.m -lobjc -framework Cocoa
$ ./tst
+[Hell initialize]
hell init!
2013-06-16 13:30:40.503 tst[791:707] count = 1
hell print : 10
2013-06-16 13:30:40.505 tst[791:707] count = 1
2013-06-16 13:30:40.505 tst[791:707] count = 1

うーん・・・。
releaseしようとすまいと、1だな。。。
gccでもそうだったのでclangにしてみたのだが、それでも同じだ。
(-framework Cocoa、がついたのは、NSLogがリンクエラーになるため。)

ratainすると2になり、次にreleaseすると1になる。
でも、今みたいにretainしなくても、releaseしたら1のままだ。
続けてreleaseすると、落ちる。
うーむ・・・・・・・。

そもそも、デフォルトの場合はARCが有効なのだろうか? 無効なのだろうか?


コンパイルオプションに「-fobjc-arc」をつけるとARC有効、「-fno-objc-arc」をつけるとARC無効。

まずは-fno-objc-arcをつけてみる。。。変化無し。
では、-fobjc-arcをつけると・・・コンパイルエラーだ!

つまり、デフォルトではARC無効でコンパイルされるようだ。

 

エラーの内容は「ARCが有効だとretainCountは参照できんよ」というもの。
あと、retainとreleaseもだめだって。

では、ちょっとソースを変えて試していこう。

//[1.h]
#import <Foundation/Foundation.h>
@interface Hell : NSObject
- (void)print;
@end
--------------------
//[1.m]
#import <stdio.h>
#import "1.h"
static int gValue;
@implementation Hell
//
+ (void)initialize
{
	printf("%s\n", __FUNCTION__);
	if (self == [Hell class]) {
		printf("hell init!\n");
		gValue = 10;
	}
}
- (id)init
{
	printf("%s\n", __FUNCTION__);
	return self;
}
- (void)dealloc
{
	printf("%s\n", __FUNCTION__);
}
//
- (void)print
{
	printf("hell print : %d\n", gValue++);
}
@end
--------------------
//[main.m]
#import <Foundation/Foundation.h>
#import "1.h"
void func(Hell *hell)
{
	[hell print];
	NSLog(@"%s end\n", __FUNCTION__);
}
int main(void)
{
	Hell *hell = [[Hell alloc] init];
	func(hell);
	
	NSLog(@"%s end\n", __FUNCTION__);
	return 0;
}

$ clang -o tst 1.m main.m -lobjc -framework Cocoa -fobjc-arc
$ ./tst
+[Hell initialize]
hell init!
-[Hell init]
hell print : 10
2013-06-16 14:00:20.938 tst[858:707] func end
2013-06-16 14:00:20.940 tst[858:707] main end
-[Hell dealloc]

mainで確保したhellが解放されるのは、mainを抜けたときだ。
では、ちょっと差し替えよう。

//[main.m]
#import <Foundation/Foundation.h>
#import "1.h"
void func(void)
{
	Hell *hell = [[Hell alloc] init];
	[hell print];
	NSLog(@"%s end\n", __FUNCTION__);
}
int main(void)
{
	func();
	
	NSLog(@"%s end\n", __FUNCTION__);
	return 0;
}

$ clang -o tst 1.m main.m -lobjc -framework Cocoa -fobjc-arc
$ ./tst
+[Hell initialize]
hell init!
-[Hell init]
hell print : 10
2013-06-16 14:03:34.217 tst[864:707] func end
-[Hell dealloc]
2013-06-16 14:03:34.218 tst[864:707] main end

funcで確保したhellは、funcを抜けた後で解放される。
じゃあ、これならどうだ?

#import <Foundation/Foundation.h>
#import "1.h"
static Hell *hell;
void func(void)
{
	hell = [[Hell alloc] init];
	[hell print];
	NSLog(@"%s end\n", __FUNCTION__);
}
int main(void)
{
	func();
	
	NSLog(@"%s end\n", __FUNCTION__);
	return 0;
}

$ clang -o tst 1.m main.m -lobjc -framework Cocoa -fobjc-arc
$ ./tst
+[Hell initialize]
hell init!
-[Hell init]
hell print : 10
2013-06-16 14:05:07.479 tst[872:707] func end
2013-06-16 14:05:07.480 tst[872:707] main end

確保はfunc()でやってるけど、変数スコープがグローバルなので解放するタイミングがない。
まあ、これはmain抜けたらプロセスごと終わるからいいんだろうけど、強制的に解放したいときがあるかもしれない。

#import <Foundation/Foundation.h>
#import "1.h"
static Hell *hell;
void func(void)
{
	hell = [[Hell alloc] init];
	[hell print];
	NSLog(@"%s end\n", __FUNCTION__);
}
int main(void)
{
	func();
	
	NSLog(@"%s end\n", __FUNCTION__);
	hell = nil;
	return 0;
}

$ clang -o tst 1.m main.m -lobjc -framework Cocoa -fobjc-arc
$ ./tst
+[Hell initialize]
hell init!
-[Hell init]
hell print : 10
2013-06-16 14:07:28.059 tst[880:707] func end
2013-06-16 14:07:28.060 tst[880:707] main end
-[Hell dealloc]

hellにnilを突っ込むことで結合が切れ、mainを抜けたタイミングで解放される処理が呼ばれた、というところか?
いや、そうではないようだ。
nilを代入する時点で解放が呼び出されていた。
mainのNSLog()前で代入すればそこでdeallocが呼ばれたし、func()で代入すればそこで呼ばれた。
そうなっているんだ、というくらいしか言えることはありませんな。

 

あとは、プロパティとかプロトコルみたいな基本を押さえて、今日は終わりにしますかな。

[obc]クラスオブジェクト

クラスにひっついている変数を、クラスオブジェクト、と呼ぶそうだ。
今私が読んでいるところでは、クラスオブジェクトといいつつも、クラスの中に入っているわけではない。
staticにした変数は同じファイルの中でしか扱えないから、それを使おう、というしくみみたい。
調べればクラス内に定義した変数でも同じことができるのかもしれんが、まあ後のお楽しみだ。

クラス変数は、+initializeというメソッドで初期化しましょう、ともある。
面倒だから試さないが、きっとこのinitializeもNSObjectを継承しないと使えないのだろう。
では、試しに書いてみる。

//[1.h]
#import <Foundation/Foundation.h>
@interface Hell : NSObject
+ (void)hello;
- (void)print;
@end
------------------------------------------
//[1.m]
#import <stdio.h>
#import "1.h"
static int gValue;
@implementation Hell
//
+ (void)initialize
{
	printf("%s\n", __FUNCTION__);
	if (self == [Hell class]) {
		printf("hell init!\n");
		gValue = 10;
	}
}
//
+ (void)hello
{
	printf("hell hello : %d\n", gValue++);
}
//
- (void)print
{
	printf("hell print : %d\n", gValue++);
}
@end
------------------------------------------
//[main.m]
#import <Foundation/Foundation.h>
#import "1.h"
int main(void)
{
	[Hell hello];
	Hell *hell = [[Hell alloc] init];
	[hell print];
	return 0;
}

今回から、ヘッダファイルも追加したし、mainも別にした。
そうせんとわからんからね。

yebisu:objc hirokuma$ gcc -o tst 1.m main.m -lobjc
yebisu:objc hirokuma$ ./tst
+[Hell initialize]
hell init!
hell hello : 10
hell print : 11

mainではインスタンス生成前に+helloを呼び出しているのだが、それより前に+initializeが呼び出されていることがわかる。
+initializeだが、継承しても呼ばれるとか何とかで、自分のクラスかどうかを判定せにゃならんらしい。

 

では、まず+initializeが1回しか呼ばれないことを確認しよう。

#import <Foundation/Foundation.h>
#import "1.h"
int main(void)
{
	[Hell hello];
	Hell *hell = [[Hell alloc] init];
	Hell *hell2 = [[Hell alloc] init];
	[hell print];
	[hell2 print];
	
	return 0;
}

yebisu:objc hirokuma$ ./tst
+[Hell initialize]
hell init!
hell hello : 10
hell print : 11
hell print : 12

ふむ、よかろう。
では、継承させてみよう。

//[2.h]
#import "1.h"
@interface Hell2 : Hell
+ (void)hello;
- (void)print;
@end
--------------------------------------
//[2.m]
#import <stdio.h>
#import "2.h"
static int gValue;
@implementation Hell2
//
+ (void)initialize
{
	printf("%s\n", __FUNCTION__);
	if (self == [Hell2 class]) {
		printf("hell init!\n");
		gValue = 20;
	}
}
//
+ (void)hello
{
	printf("hell hello : %d\n", gValue++);
}
//
- (void)print
{
	printf("hell print : %d\n", gValue++);
}
@end
----------------------------
//[main.m]
#import <Foundation/Foundation.h>
#import "1.h"
#import "2.h"
int main(void)
{
	[Hell2 hello];
	[Hell hello];
	Hell *hell = [[Hell alloc] init];
	Hell2 *hell2 = [[Hell2 alloc] init];
	[hell print];
	[hell2 print];
	
	return 0;
}

Hellを継承したHell2を作り、あとは全部オーバーライド。

$ gcc -o tst 1.m 2.m main.m -lobjc
$ ./tst
+[Hell initialize]
hell init!
+[Hell2 initialize]
hell init!
hell hello : 20
hell hello : 10
hell print : 11
hell print : 21

+initializeの呼出順は、ベースから順番なのかな?
最初、2.mの+initializeのif文をそのままにしていたので、gValueが初期化されなかった。
この部分の書き換えは忘れやすそうだから、自動的に「今のクラス」を取ってきてくれないものだろうか。

いや、その前に、+initializeが2回呼ばれる、というふうになってない。
どうもそれは、継承先で+initializeをオーバーライドしてないときのようだ。
2.mの+initializeをまるまるコメントアウトすると、こうなった。

$ gcc -o tst 1.m 2.m main.m -lobjc
$ ./tst
+[Hell initialize]
hell init!
+[Hell initialize]
hell hello : 0
hell hello : 10
hell print : 11
hell print : 1

Hell2のときもHell::initializeが呼ばれるけど、そのときはクラス名が違うからif文の中には流れ込まない、と。

となると、やはり書き方をパターン化してしまいたいなあ。
ネットで見ると、初期化済みstatic変数を用意しているところもあったけど、最初の1回だけのためにメモリは使いたくないし・・・。

やりたいことは、@implementationに書いたクラス名を使いたい、ということだ。
そうなると、プリプロセスの段階か。
#defineでクラス名をつけてしまえばいいけど、それはそれでいやだなぁ。

まあ、+initializeを書くことはそう多くないだろうから、注意すればいいか。

[obc]第1引数だけラベルがないのが、性格に合わない

Objective-Cで、引数2つのメソッドを定義したとしよう。

- (void)methodName:(int)x yPos:(int)y
{
  ・・・
}

呼ぶときは、こうなる。

[obj methodName:20 yPos:30];

 

メソッド名はクラス内で別の名前を付けたいから、凝った名前というか、重複しないようわかりやすくつける。
でも、引数はメソッド名と直接関係があるとは限らない。
2つ以上あるんだったら、1つ目はこれ、2つ目はこれ、と意味が変わってくるだろう。

いや、言語仕様にたてついても仕方ないとわかってはいるのだ。
いるんだけど、一言言わずにはおれなかったのだ、すまん。


さて、明日は@interface、@implementation、それとプロトコルってやつまでは調べておきたいな。

[obc]やっぱり名前に縛りはないのか?

いろいろと考えたが、やっぱりObjective-Cの(あるいはFoundationの)命名ルールというものはあったとしても、名前によって動作が変わるような縛りはないんじゃなかろうか、と思った。

だって、やっぱり、予約語のように言語で確保しているわけでもないのに、動作が名前だけで縛られるってのはおかしいと思うからだ。
そういう動きをしたいなら、JavaのAnnotationsみたいにやってしまわないと、わかりづらい。

という推測だけじゃなくて、AppleのCocoa向け コーディングガイドライン[pdf]にも、そういうことが書かれてなかったのだ。
allocも「allocは略語として使ってよいよ(普通は略語は推奨せんのだがね)」みたいに書いてあるだけだ。


あとは気になるのが、「allocは標準なの?」だ。
isa変数を試すためにNSObjectを継承したのだが、そもそもallocは使えるのだろうか?

#import <stdio.h>
@interface Hell
- (void)print;
@end
@implementation Hell
- (void)print
{
	printf("self=%p\n", self);
}
@end
int main(void)
{
	Hell *hell = [Hell alloc];
	return 0;
}

yebisu:objc hirokuma$ gcc -o tst 1.m -lobjc
1.m: In function 'main':
1.m:16: warning: 'Hell' may not respond to '+alloc'
1.m:16: warning: (Messages without a matching method signature
1.m:16: warning: will be assumed to return 'id' and accept
1.m:16: warning: '...' as arguments.)

よし、わかった。

allocとかは、やっぱりNSObjectが実装しているメソッドなんだ(クラスメソッド、だっけ)。
そういうことからすると、Objective-Cにはコンストラクタやデストラクタのしくみはなく、それはFoundationだかCocoaだか知らないが、とにかくそういうフレームワーク上で実現している、ということか。

allocやnewは予約語ではなく、単なるメソッド名にすぎない。
ARC用の参照カウンタや、前回のisa変数なども、NSObjectがうまいこと処理しているだけということだ。

言語仕様に隠されていない分、わかりやすいといえばわかりやすい。。。のか?
C++は言語仕様になっているがために、言語の仕様書が分厚くなった。
Objective-Cはフレームワークと一体化したところがあるため、組込みの人みたいに「言語+それ以外は調べる」という人にとっては、「ARCがなんで言語のバージョンじゃなくてOSのバージョンに依存するんだ?」というところがわかりづらいんじゃなかろうかね。
正確には、OSのバージョンじゃなくて、つかっているフレームワークのバージョン、ということになるのか。

まだまだ把握はできていないが、何がどこまでサポートしているのかを知っておかないといかんだろう。

2013/06/15

[nfc]RC-S380はチップが違うのでlibnfcは動かないと思う

iOSというか、Objective-Cを見てて疲れたので、息抜きだ。

久しぶりにこのブログの管理画面を見ると、リンク先から呼ばれた形跡があった。
(Bloggerは、どうやってるのか知らないけどそういうのが出てくる)

Web Application Programming Wiki*

おお、ありがたや。


libnfcでRC-S330とか370が動くのは、あれはRC-S956というチップがPN532などと有線コマンドが類似しているからだ。
コマンドIDは同じで違う機能のものもあるし、機能が割り振られていないIDなんかもあるけど、通常使うレベルではほぼ同じといっていいくらいの互換性がある。

しかし、RC-S380に載っているチップは、RC-S956ではない。
USBのVID/PIDが違うというレベルではなく、有線コマンドが違う。
コマンドというか、もしかするとフレームのフォーマットすら違うかもしれない。

なので、libnfcをRC-S380で使いたいのであれば、USBで流れているデータを解析するくらいのことをやらないと対応できないであろう。

 

私?
私は、RC-S380を解析するつもりはほとんどない。
やりたいのは、NFC R/Wを自由に動かすことができる環境を作る、だったので、それはRC-S956で達成できている(うまくいってないとこもあるけど)。

まあ、ソフト屋さんとしては、扱えることができるデバイスがあるなら扱えるようにしてしまいたいのだけど、ぐっとこらえるのも、またよし。

[obc]名前の付け方で動作が変わる?

メモリ管理用に買った本を読んでいて、「alloc、new、copy、mutableCopy、で始まるようなメソッド名はメモリを生成する」とあった。

ふんふん。

「allocator、とか、newer、みたいな名前はダメだから、キャメルで名前を付けよう」とも。

ええっ!
名前の付け方で動作が変わるの??

これは前回の回答のようなものだ。
ほへー。

こちらにも、そのようなことが書かれていた。
EZ-NETさん : http://program.station.ez-net.jp/special/handbook/objective-c/multithread/autoreleasepool.asp

 

いやあ、名前くらいは自由になると思っていたのだが、initとかはふっと使いそうなので怖いな。
まあ、中身がまったく違ったらうまいことやってくれるのかもしれんが、気をつけねば。

[obc]ARCは参照カウンタのことでよいらしい

こういうメモリ関係は、早めに把握しておいた方がよかろう。

自動参照カウント(Automatic Reference Counting)、とある。
「コンパイラがオブジェクトの存続期間を推論します」と、なんかすごそうなことが書かれている。
もう1つ、非自動参照カウント(Manual Reference Counting)もある。
そして、ガーベッジコレクションはiOSにはないとのこと。

参照カウントといえば、C++のshared_ptrを想像すると思う。
コンストラクタでカウントアップ、デストラクタでカウントダウン。
アドレス渡しするときにカウントアップ、だっけ。
カウントが0になったら誰からも参照されていないということでメモリ解放。

私のイメージとしては、教室の電気だ。
最初の人が電気をつけて、いろんな人が出たり入ったりする間は付けっぱなしで、誰もいなくなったら消す、と。
あ、これだと相手に渡すってのがないな・・・。


日本語のAppleドキュメントにはそれらしいファイルがなかったのだが、ちょうど「エキスパートObjective-Cプログラミング ― iOS/OS Xのメモリ管理とマルチスレッド」というPDF本が500円くらいだったので、買って読むことにした。

どうやら、ARCはコンパイラがやってくれるものらしい。
そして・・・やはり照明で参照カウンタの説明をしてる・・・。
みんなイメージは似たようなものらしい。ちょっと安心。

本の中身を説明していっても仕方がないので、ここら辺にしておこう。


ここで、ふと疑問が。

Objective-Cはデフォルトでは基底クラスを持たないクラスを作ることができる、と思ってる。
じゃあ、allocとかは何なのだろう?
C++でいうところの3大自動生成「コンストラクタ、デストラクタ、コピーコンストラクタ」に当たるのだろうか?
あるいは、newみたいな予約語になるのだろうか?

細かいことではあるが、気になるんだよなあ。

[obc]isa変数はNSObjectが持っているようだ

Appleの日本語ドキュメントにあったObjC.pdfを見ながら勉強中だ。

どうやら、クラスのインスタンスにはisa変数なるものがあるらしい。
ポインタのようなので、printfで見てみよう。

#import <Foundation/Foundation.h>
#import <stdio.h>
@interface Hell : NSObject
- (void)print;
@end
@implementation Hell
- (void)print
{
	printf("isa=%p / self=%p\n", isa, self);
}
@end
int main(void)
{
	Hell *hell = [[Hell alloc] init];
	[hell print];
	return 0;
}

yebisu:objc hirokuma$ gcc -o tst 1.m -lobjc
yebisu:objc hirokuma$ ./tst
isa=0x10782f180 / self=0x7ffd59c037e0

動くようになるまでに、かなり時間がかかった。。。
主な理由は、isa変数は自動的に持っているわけではなく、NSObjectのメンバ変数であると言うことになかなか気付かなかったからだ。
アクセス方法が悪いのかと思っていたのだが、そうではなかったのだな。

つまり、Objective-Cのクラス定義には、JavaでいうところのObjectみたいなものはなく、基底クラスを作ることができるということだ。


isa変数がなんなのかわかっていないが、vtblと同じようなものだろうと思い、selfと一緒に値を出したのだ。
しかし、アドレス全然違う。
C++も忘れかけているので、認識が間違っていたか・・・。

 

「is-a」の関係と言うことで、これは元となったクラス情報へのアクセス用なのか?
pdfには「isaインスタンス変数はオブジェクトのクラスを識別します」とある。

#import <Foundation/Foundation.h>
#import <stdio.h>
@interface Hell : NSObject
- (void)print;
@end
@implementation Hell
- (void)print
{
	printf("isa=%p / self=%p\n", isa, self);
}
@end
int main(void)
{
	Hell *hell = [[Hell alloc] init];
	[hell print];
	Hell *hell2 = [[Hell alloc] init];
	[hell2 print];
	return 0;
}

yebisu:objc hirokuma$ gcc -o tst 1.m -lobjc
yebisu:objc hirokuma$ ./tst
isa=0x10b345180 / self=0x7fad69c037e0
isa=0x10b345180 / self=0x7fad69c000e0

isaはselfによらず同じなので、認識は間違ってなさそうだ。

#import <Foundation/Foundation.h>
#import <stdio.h>
@interface Hell : NSObject
- (void)print;
@end
@implementation Hell
- (void)print
{
	printf("isa=%p / self=%p\n", &isa, self);
}
@end
int main(void)
{
	Hell *hell = [[Hell alloc] init];
	[hell print];
	Hell *hell2 = [[Hell alloc] init];
	[hell2 print];
	return 0;
}

yebisu:objc hirokuma$ gcc -o tst 1.m -lobjc
yebisu:objc hirokuma$ ./tst
isa=0x7fec324037e0 / self=0x7fec324037e0
isa=0x7fec32403830 / self=0x7fec32403830

isa変数はNSObjectの先頭に定義されているので、selfと同じアドレスに配置されることになるんだな。
isa変数が指す先が変わると、意味しているClassが変わるので、メソッドの呼び方も変わるのだろう。
C++のvtableと同じって認識でいいのかな。

[obc]mainはC風でしか書けなさそうなので、Objective-C風に書いた関数を呼び出してみよう。


#import <stdio.h>

@interface Hell
+ (void)print;
@end

@implementation Hell
+ (void)print
{
 printf("Hell\n");
}
@end

int main(void)
{
 [Hell print];
 return 0;
}
yebisu:objc hirokuma$ gcc -o tst 1.m
Undefined symbols for architecture x86_64:
  "__objc_empty_cache", referenced from:
      _OBJC_METACLASS_$_Hell in ccJImdLx.o
      _OBJC_CLASS_$_Hell in ccJImdLx.o
  "__objc_empty_vtable", referenced from:
      _OBJC_METACLASS_$_Hell in ccJImdLx.o
      _OBJC_CLASS_$_Hell in ccJImdLx.o
  "_objc_msgSend", referenced from:
      _main in ccJImdLx.o
ld: symbol(s) not found for architecture x86_64
collect2: ld returned 1 exit status

リンクエラーだ。
オブジェクトだけ作ってみよう。
yebisu:objc hirokuma$ gcc -c 1.m
yebisu:objc hirokuma$ ls
1.m     1.o
yebisu:objc hirokuma$ nm 1.o
0000000000000000 t +[Hell print]
00000000000001c0 s +[Hell print].eh
00000000000001a8 s EH_frame0
000000000000006f s L_.str
0000000000000082 s L_OBJC_CLASS_NAME_0
0000000000000074 s L_OBJC_METH_VAR_NAME_0
000000000000007a s L_OBJC_METH_VAR_TYPE_0
0000000000000170 S _OBJC_CLASS_$_Hell
0000000000000148 S _OBJC_METACLASS_$_Hell
                 U __objc_empty_cache
                 U __objc_empty_vtable
0000000000000030 T _main
00000000000001f0 S _main.eh
                 U _objc_msgSend
                 U _puts
0000000000000098 s l_OBJC_$_CLASS_METHODS_Hell
0000000000000100 s l_OBJC_CLASS_RO_$_Hell
00000000000000b8 s l_OBJC_METACLASS_RO_$_Hell

えーっと・・・、「U」が外部を参照しているとかだっけ。
putsは、引数無しのprintf()だから置き換えられてるのかな。
そこはgccがlibcをリンクするから解決するけど、objc部分はライブラリを指定せんといかんと言うことか。
うーん、Objective-Cのライブラリ名ってなんだろう・・・。
yebisu:objc hirokuma$ gcc -o tst 1.m -lobjc
yebisu:objc hirokuma$ ./tst
Hell

あ、通った。
yebisu:objc hirokuma$ nm tst
0000000100000e70 t +[Hell print]
0000000100001170 S _NXArgc
0000000100001178 S _NXArgv
0000000100001148 S _OBJC_CLASS_$_Hell
0000000100001120 S _OBJC_METACLASS_$_Hell
0000000100001188 S ___progname
0000000100000000 T __mh_execute_header
                 U __objc_empty_cache
                 U __objc_empty_vtable
0000000100001180 S _environ
                 U _exit
0000000100000ea0 T _main
                 U _objc_msgSend
                 U _puts
0000000100001000 s _pvars
                 U dyld_stub_binder
0000000100000e30 T start

よくわからんが、「Objective-Cはメソッド呼出じゃなくてメッセージ送信」というのだが、動作としてそう作ってるんだな。
もちろん、print()をC風にすると関数呼び出しになる(と思う)。
#import <stdio.h>

void print()
{
 printf("Hell\n");
}

int main(void)
{
 print();
 return 0;
}
yebisu:objc hirokuma$ nm tst
0000000100001050 S _NXArgc
0000000100001058 S _NXArgv
0000000100001068 S ___progname
0000000100000000 T __mh_execute_header
0000000100001060 S _environ
                 U _exit
0000000100000ed0 T _main
0000000100000eb0 T _print
                 U _puts
0000000100001000 s _pvars
                 U dyld_stub_binder
0000000100000e70 T start


拡張子を.mmにすると、C++もいけるらしい。
#import <stdio.h>

class Hell {
public:
 static void print();
};

void Hell::print()
{
 printf("Hell\n");
}

int main(void)
{
 Hell::print();
 return 0;
}
yebisu:objc hirokuma$ nm tst
0000000100001050 S _NXArgc
0000000100001058 S _NXArgv
0000000100000eb0 T __ZN4Hell5printEv
0000000100001068 S ___progname
0000000100000000 T __mh_execute_header
0000000100001060 S _environ
                 U _exit
0000000100000ed0 T _main
                 U _puts
0000000100001000 s _pvars
                 U dyld_stub_binder
0000000100000e70 T start

まあ、違いはprint()のマングリングした名前くらいか。