季節は、秋である。
僕はRetina MacBook Proを買って以来、iPhone 5を予約したり天国のスティーブジョブズ様に祈りを捧げたりと大忙しで、ブログの更新をサボりがちになっていた。
さて。
そんなAppleの先進的なプロダクトの中核をなす開発言語と言えば、Objective-Cである。
嘗てはC言語の陰に隠れてマイナーなポジションに在った言語であるが、昨今のiPhoe / iPadの普及に伴い、その知名度もようやく上がりつつある。
というわけで、僕もObjective-Cに再挑戦してみようと思い、荻原剛志著『詳解Objective-C 2.0 第3版』なんぞを買ってきて読んで今に至るわけである。
ここから本題であるが、Objective-Cを用いてプログラミングを行う上でネックなポイントが『メモリ管理』であった。この話は去年の3月頃にも一度書いた気がする。
あれから時代は流れ、Objective-Cではメモリ管理を自動化するための ARC (Automatic Reference Counting) という機能が使用できるようになった。もうretainやらreleaseやら手作業でリファレンスカウンタをいじらなくてもよいのだ。
しかし、例えば僕がiPhoneアプリを開発する事になり、UIまわりをObjective-Cで、ロジックをC++でそれぞれコーディングする事にしたとしよう。この場合、UIからロジックにアクセスするために、Objective-C側は何らかの方法でC++側のオブジェクトを参照しておく必要がある。
残念ながらARC は C++ の生ポインタを自動解放してはくれないため、このままではObjective-C側にC++のメモリ解放コードが混在する穢らわしいコードになってしまう。安全性も下がる。たちまちメモリリークが発生し、またたく間にアプリが落ち、やがてApp Storeのレビューには心ないユーザからの罵詈雑言が書き込まれる事だろう。
だが悲観するには早い。
2011年8月12日(僕の誕生日!)に、C++の最新規格であるC++11がISOに承認され、参照カウンタを自動制御できるスマートポインタという仕組みが正式に導入された。
Visual Studioはどうか知らんが、最新のXcode 4.5ではC++11をサポートしている。これでObjective-C、C++ともにメモリ周りにおける懸念がずいぶん軽減された事になる。
しかしながら、ARCはObjective-Cだけの機能であり、スマートポインタはC++だけの機能である。
ここで、一つの疑問が生じた(ほこ×たてっぽく)。
【Objective-CのARCとC++のスマートポインタは共存できるのか】
というわけで、いろいろ実験してみたよー。今日の記事は備忘録的側面が強いので、何言ってるのか分からんという方は読み飛ばして下さい。
1. まずはARCを使ってみる
#import <Foundation/Foundation.h>
#import <cstdio>
/* 宣言部 */
@interface ObjectiveTercel : NSObject
-(id) init;
-(void) dealloc;
@end
/* 実装部 */
@implementation ObjectiveTercel
-(id) init { // イニシャライザ
self = [super init];
printf("[こんにちは]\n");
return self;
}
-(void) dealloc { // ファイナライザ
printf("[さようなら]\n");
}
@end
/* main関数 */
int main(int argc, const char * argv[])
{
@autoreleasepool {
ObjectiveTercel* tercel = [[ObjectiveTercel alloc] init];
}
return 0;
}
これは適当なクラスを作り、それをインスタンス化するコードである。
main()関数の内部では、インスタンスをポインタ参照している。動的にメモリをアロケートしているため、従来は明示的な解放コードが必要であった。
しかし、上記のコードを実行してみると、@autorelease ブロックを抜けるタイミングで dealloc が呼ばれ、メモリ解放が行われる。実行結果は以下の通りだ。
[こんにちは]
[さようなら]
2. C++11のスマートポインタを使ってみる
//#import <Foundation/Foundation.h>
#import <iostream>
/* 宣言部 */
class Tercel {
public:
Tercel();
virtual ~Tercel();
};
/* 実装部 */
Tercel::Tercel() {
std::cout << "[Hello!]" << std::endl;
}
Tercel::~Tercel() {
std::cout << "[Bye!]" << std::endl;
}
/* main関数 */
int main(int argc, const char * argv[])
{
// @autoreleasepool {
auto tercel = std::shared_ptr<Tercel>(new Tercel());
// }
return 0;
}
これも、明示的に解放コードを書いてはいない。代わりに、std::shared_ptrでnewされたインスタンスに一枚皮をかぶせている。
実行すると、きちんとデストラクトされている事が判る。
[Hello!]
[Bye!]
3. Objective-Cのインスタンス変数に、C++側オブジェクトのスマートポインタを持つ場合
最後に、両者を共存させるコードを示そう。サンプルは、Objective-Cで記述されたクラスが、C++側のオブジェクトの参照を持つ場合を想定している(※ ソースコードは展開してご覧ください)。
#import <Foundation/Foundation.h>
#import <iostream>
#import <cstdio>
/* C++のコード */
class Tercel {
public:
Tercel() {
std::cout << "[Hello!]" << std::endl;
}
virtual ~Tercel() {
std::cout << "[Bye!]" << std::endl;
}
};
/* Objective-Cのコード */
@interface ObjectiveTercel : NSObject
-(id) init;
-(void) dealloc;
@end
@implementation ObjectiveTercel {
std::shared_ptr<Tercel> mHoge;
}
-(id) init {
self = [super init];
printf("[こんにちは]\n");
mHoge = std::shared_ptr<Tercel>(new Tercel());
return self;
}
-(void) dealloc {
printf("[さようなら]\n");
}
@end
int main(int argc, const char * argv[])
{
@autoreleasepool {
ObjectiveTercel* tercel = [[ObjectiveTercel alloc] init];
}
return 0;
}
これを実行すると、
[こんにちは]
[Hello!]
[さようなら]
[Bye!]
と表示され、正常にメモリが解放されている。
というわけで、前置きでさんざんぐだぐだ書いて疲れたので一気にまとめるけど、ARCとスマートポインタは共存できたよ良かったね。
0 件のコメント:
コメントを投稿
ひとことどうぞφ(・ω・,,)