Obejctive-C 3分クッキング
この記事では C++ や Java、C♯、Rubyなどのオブジェクト指向言語の使用経験がある方のために Objective-C の特徴がさくっと簡単にわかるようにまとめてみました。
Objective-C ってどんな言語?
オブジェクト指向言語は大きく「クラスベース動的型付け」「クラスベース静的型付け」「プロトタイプベース*1動的型付け」の3つにわけることが出来ます。Objective-C は「クラスベース動的型付け」に分類されるオブジェクト指向言語です*2。Objective-C を他のオブジェクト指向言語と比較して分類してみると以下の図のようになります。
クラスベース動的型付けオブジェクト指向言語の元祖といえるのが Smalltalk です。Objective-C は C言語をベースに Smalltalk 型のオブジェクト指向機能を持たせた言語です。
必要な前提知識
Objective-C はC言語がベースになっていますのでC言語の基本構文(if 文や for 文、変数宣言など)を理解している必要があります。またこの記事ではオブジェクト指向の概念や用語の説明を省いているので、オブジェクト指向についてある程度理解していることが前提となります。
クラスやライブラリのインポート
ルートクラス
- Java の Object クラスと同じように Objective-C にもルートクラスが存在する。
- ルートクラスは明示的に継承すること(自動で継承されることはない)。
- NSObject(NS は NEXTSTEP の略と言われている)
- オブジェクトの生成で使用する alloc メソッドは NSObject クラスのクラスメソッド。
- NSObject クラスを継承しないとオブジェクト生成ができない。
クラスの定義
- .hファイルに書くこと
- 定義方法
@interface クラス名 : 親クラス名 プロパティ宣言 メソッド宣言 @end
- インスタンス変数は実装ファイルに宣言する(後述)
- クラス変数という概念がないので注意
メソッド宣言
引数が1つある場合
- 引数の前に「:」を付ける
- (戻り値の型)メソッド名:(引数の型)引数;
引数が2つ以上ある場合
コンストラクタ
- Objective-C にはコンストラクタのための特別な機能がないため init ではじまるメソッドを定義して使用するのが慣例となっている(戻り値は必ず id 型にすること)。Objective-C では init ではじまるメソッドのことを便宜的にコンストラクタメソッドと呼んでいる。
- デフォルトコンストラクタ
NSObject クラスに定義されている init メソッドを使う - 引数ありコンストラクタ
initWithXX: (XXは任意の名前) のような名前をつける。@interface Point : NSObject - (id)initWithX:(int)x withY(int)y; @end
プロパティ
- インスタンス変数を外部に公開するための仕組み
- @property を使ってプロパティを宣言する
@interface Hoge : NSObject @property (strong, nonatomic) NSString *value; @end
- インスタンス変数 + アクセサメソッド(getter, setter)を自動生成してくれる。インスタンス変数の名前はプロパティ名に接頭辞として「_」をつけたものが自動生成される
- @property に指定する項目の説明
- オブジェクトを強い参照で保持する場合は strong、弱い参照で保持する場合は weak を指定する。デフォルトは strong
- プロパティをスレッドセープにする場合は atomic、スレッドセーフにしない場合は nonatomic を指定する。デフォルトは atomic
- プロパティを読み取り専用にする(setter メソッドを生成しない)場合は readonly を指定する
クラス定義の例
- プロパティとメソッドの宣言を含むクラス定義の例
@interface Point : NSObject // プロパティ宣言 @property (nonatmic) int value; // クラスメソッド宣言 + (id)pointWithX:(int)x withY(int)y; // コンストラクタ宣言 - (id)initWithX:(int)x withY(int)y; // メソッド宣言 - (void)touchMove:(Touch *)touch withEvents:(Event *)event; @end
クラス型変数の宣言
- クラス名を明示的に指定する
// ポインタ変数として宣言する NSString *hoge; NSArray *fuga;
- クラス型変数を使う代わりにクラス名を明示しない id 型を使って変数の宣言をすることが出来る
id hoge; id fuga;
- id 型はオブジェクトへのポインタ。以下のように宣言されている
typedef struct objc_object *id;
文字列
- NSStringクラスのオブジェクト
- @""を使う
NSString *hoge = @"Hello World!";
メソッドの呼び方
プロパティへのアクセス
- 「.」演算子を使う
オブジェクト.プロパティ
self
super
オブジェクト(インスタンス)の生成
クラスの実装
- .mファイルに書く
@implementation クラス名 { // インスタンス変数の宣言 } // メソッドの実装をここに書く @end
メソッドの実装
基本形
- 「{」と「}」の間に実装を書く
@implementation Hoge - (戻り値の型)メソッド名:(引数の型)引数1 ラベル:(引数の型)引数2 { …処理 return 戻り値; } @end
- 実装例
@implementation Hoge - (NSString *)fugaWithString:(NSString *)str withInt:(int)num { …処理 return @"hogehoge"; } @end : // メソッド呼び出しイメージ [hoge fugaWithString:@"ABC" withInt:1000]; :
コンストラクタの実装
インスタンス変数の宣言
- インスタンス変数はヘッダファイルに宣言することも出来るが最近は実装ファイルに定義するのが主流。
- インスタンス変数には接頭辞として「_」をつけるのが慣例になっている
- 実装例
@implementation クラス名 { // インスタンス変数の宣言 NSString *_hoge; } @end
- インスタンス変数の初期化はコンストラクタで行う
@implementation クラス名 { NSString *_hoge; // 下記はコンパイルエラーになる // NSString *_hoge = @"test"; } - (id)init { self = [super init]; if (self) { // インスタンス変数の初期化 _hoge = @"test"; } return self; } @end
- インスタンス変数のスコープ は @public,@protected,@private の3種類。無指定は protected。public は原則使用禁止。代わりにプロパティを使うこと
@implementation クラス名 { int x; //protected @public int a; //public 原則使用禁止。プロパティを使うこと int b; //public 原則使用禁止。プロパティを使うこと @protected int c; //protected int d; //protected @private int e; //private int f; //private } @end
プロトコル
- Java のインターフェースに相当するもの
- @optinal以下のメソッドは任意実装
- プロトコルの宣言
@protocol プロトコル名 メソッド宣言1 @optional メソッド宣言2 メソッド宣言3 @end
- プロトコルの導入
@interface クラス名:親クラス名 <プロトコル名1,プロトコル名2...> @end
- 実装例
@protocol Comparable - (int)compareTo:(id)o; // 実装必須メソッド @optional - (void)hoge; // 実装任意メソッド @end @interface Hoge:NSObject <Comparable> //ここでプロトコルのメソッドを宣言する必要はない @end @implementation Hoge - (int)compareTo:(id)o { …処理 } @end
例外
実行時例外
検査例外の代わりに使う NSError
- Java の検査例外のような仕組みは存在しない(C♯やC++と同じく実行事例外のみサポート)
- NSError オブジェクトの参照を渡す方法で Java の検査例外と同じようなことができる
- 使用例
AVAudioSession *audioSession = [AVAudioSession sharedInstance]; NSError *error = nil; // NSError オブジェクトの参照を渡す [audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error:&error]; // エラーがあれば if(error){ NSLog(@"audioSession: %@ %d %@", [error domain], [error code], [[error userInfo] description]); }
- 「NSError**」のようにポインタ2つでオブジェクトの参照渡しを宣言する。エラーが発生したらNSError オブジェクトにエラーの内容を書き込む。
- (BOOL)setCategory:(NSString*)theCategory error:(NSError**)outError { : 処理 if (エラー発生) { *error = [NSError errorWithDomain:@"ドメイン" code:-1 userInfo:nil]; return NO; } return YES; }
上記のパターンはObjective-Cのいろんなフレームワークで使われている。
カテゴリ
- クラスに動的にメソッドを追加することができる
- Javaにはない概念
- C♯のパーシャルクラスに近い
- Rubyのクラス拡張と同じようなことができる。既存クラスの拡張で使うことが多い。
- 実装例(NSStringクラスを独自拡張)
// Hogeはカテゴリ名 @interface NSString (Hoge) - (BOOL)isNilOrEmpty; @end @implementation NSString (Hoge) - (BOOL)isNilOrEmpty { : 処理 } @end
- 詳細はカテゴリ - 動的なメソッドの追加によるクラスの拡張
を見ること
セレクタ
- C言語の関数ポインタやC♯のデリゲートのような仕組み
- Javaにはない概念
- @selectorディレクティブを使う
- イベントドリブンなプログラムをするときに頻繁に使う
- 実装例(UIButtonのオブジェクトにボタン押下時の処理をセレクタで指定する)
- (void)viewDidLoad { : UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect]; [button addTarget:self action:@selector(buttonClick) forControlEvents:UIControlEventTouchDown]; : } - (void)buttonClick { // ボタンが押された時の処理 }
- 詳細はセレクタ
を見ること
コレクション
- 配列を扱うための NSArray クラス、辞書データ(Java の Map)を扱うための NSDictionary クラス、セットを扱うための NSSet クラスがある
- NSArray の例
// 文字列配列 NSArray *array = @[ @"Apple", @"Banana", @"Orange" ]; // 要素へのアクセス NSString *apple = array[0]; NSString *banana = array[1]; NSString *orange = array[2];
- NSDictionary の例
NSDictionary *dict = @{ @"Key1" : @"Value1", @"Key2" : @"Value2", @"Key3" : @"Value3" }; // 要素へのアクセス NSString *value1 = dict[@"Key1"]; NSString *value2 = dict[@"Key2"]; NSString *value3 = dict[@"Key3"];
- NSSet の例
NSSet *set = [NSSet setWithObjects:@"Apple", @"Banana", @"Orange", nil]; // 要素へのアクセス NSString *value = [set anyObject];
- コレクションのネスト
NSArray *array = @[ @{ @"Key1-1" : @"Value1-1", @"Key1-2" : @"Value1-2" }, @{ @"Key2-1" : @"Value2-1", @"Key2-2" : @"Value2-2" }, @{ @"Key3-1" : @"Value3-1", @"Key3-2" : @"Value3-2" } ]; // 要素へのアクセス NSString *value11 = array[0][@"Key1-1"]; NSString *value22 = array[1][@"Key2-2"];
- 上記の NSArray, NSDictionary, NSSet クラスは不変オブジェクト(値の追加削除が出来ないオブジェクト)しか作成できないので可変オブジェクトを作成する時はそれぞれ NSMutableArray クラス、NSMutableDictionary クラス、NSMutableSet クラスを使用すること
- NSMutableArray の例
// 不変オブジェクト NSArray *array = @[ @"Apple", @"Banana", @"Orange" ]; // 可変オブジェクト NSMutableArray *mutableArray = [array mutableCopy];
- NSMutableDictionary の例
NSMutableDictionary *dict = @{ @"Key1" : @"Value1", @"Key2" : @"Value2", @"Key3" : @"Value3" }; // 可変オブジェクト NSMutableDictionary *mutableDict = [dict mutableCopy];
- NSMutableSet の例
NSMutableSet *mutableSet = [NSMutableSet setWithObjects:@"Apple", @"Banana", @"Orange", nil];
数値オブジェクト
- NSNumber クラスを使って int や long、floate、double、BOOL 型などの数値をオブジェクトとして扱うことができる
- 数値の前に「@」を付加することでオブジェクトとして扱える
// 整数 NSNumber *num = @9; // [NSNumber numberWithInt:9] と同じ NSNumber *numUnsigned = @9U; // [NSNumber numberWithUnsignedInt:9U] NSNumber *numLong = @9L; // [NSNumber numberWithLong:9L] NSNumber *numLongLong = @9LL; // [NSNumber numberWithLongLong:9LL] // 小数 NSNumber *numFloat = @9.123456789F; // [NSNumber numberWithFloat:9.123456789F] NSNumber *numDouble = @9.1234567891; // [NSNumber numberWithDouble:3.1415926535] // BOOL型 NSNumber *numYes = @YES; // [NSNumber numberWithBool:YES] NSNumber *numNo = @NO; // [NSNumber numberWithBool:NO] // 計算式 NSNumber *answer = @(100 + 300); int a = 100; int b = 300; NSNumber *answer = @(a + b); // メソッド呼び出し NSString *str = [@9 stringValue];
- コレクション要素に数値オブジェクトを設定することも可能
NSArray *array = @[ @100, @200, @300 ]; NSDictionary *dict = @{ @"Key1" : @3, @"Key2" : @5, @"Key3" : @8 };
名前空間は存在しない
プログラムのエントリーポイント
メモリ管理
- ガベージコレクションありとなしの実行環境が存在する
- Mac OS の実行環境にはガベージコレクションが存在する
- iOS(iPhone, iPad)の実行環境にはガベージコレクションが存在しない
iPhoneアプリ開発時のメモリ管理で気をつけること
改訂履歴
2012年9月19日 | 記事内容を全面的に見直し修正。Modan Objective-C Syntax に対応 |
もっと詳しく知りたい人はこちらの本がおススメです。
![]() |
詳解 Objective-C 2.0 第3版 荻原 剛志 |