[Objective-C] のメモ @ iPhoneプログラミング

Objective-CはC言語にsmalltalkを足したような言語だそうだけど、smalltalk を全く知らないためものすごい抵抗感が…頭が痛いぞ。

だらだらと羅列

  • クラススコープの機能がないので名前がかぶらないように接頭語をつけたりする
  • #include ではなく #import を使う。#import は一度しか読まないため二回読み込む心配はない。
  • Objective-C 2.0 ではがベージコレクションが使えるけども iPhone では使えない。だから iPhone ではファイナライザは使えない。
  • id クラス型は全てのクラスをあらわせる汎用的な型
  • null なオブジェクトのメソッドをコールしてもなにも例外は投げられず実行される。この場合、返り値があるメソッドは 0 を返す。
  • 型に存在しないメソッドをコールすると例外が発生する。エラーではない。

インスタンスメソッドの定義と実行

プロジェクトを作成すると、以下のようにヘッダーファイル .h と 実装ファイル .m に分けられている。でも別に絶対に分ける必要はないらしくiPhoneシュミレータでは纏めてしまっても普通に動く。

/* .h ファイル */

// @interface は Javaのインターフェイスとは異なりクラスの宣言を行うもの
// @interface指定子 と @end指定子 の間にメンバの宣言を行う

// コロンは → 向きの継承を実行するもので この場合は NSObject を継承している
// NSObject は すべてのオブジェクトが継承すべきクラスでメモリ管理に関するメソッドなどを持つ。
@interface TestClass : NSObject

// インスタンスメソッドの宣言
// { 波括弧 } は付けない(インスタンス変数の定義では使用する)
-(void)methodName;

@end
/* .m ファイル */
// @interfece以下で宣言したものの中身を
// @implementation以下で定義していく
@implementation TestClass

-(void)methodName {
  if( 0 ) {
    // 角括弧 でメソッドを呼び出し
    [self methodName];
  }
  return;
}

@end  

また、Objective-C においてメソッドは常にパブリックな可視性を持つ。完璧な隠蔽は仕様として無理。

プロトコルの定義

プロトコルはJavaのインタフェスに当たるもの。

// @protocol ・@end で定義するゥ
// 山括弧ないの2つのプロトコルを多重継承している
@protocol Foo<Bar1, Bar2>

// @required に準拠しなければコンパイルエラーとなる
@required

-(void) hoge;

// @optional の準拠は任意
// @required と @optional を省略した場合はすべて @optional 扱いとなる
@optional

-(void) huge;

@end

プロトコルへの準拠

@interface TestClass : NSObject <Foo>

-(void) hoge {
  return;
}

@end

デリゲートプロトコル

delegate に移譲先クラスを指定することにより、処理を委譲できる。 で、移譲先のクラスがメソッドに実装させるためにプロトコルを記述する。 Javaのリスナーっぽい感じ。

#import <UIKit/UIKit.h>

@interface ViewBased001ViewController : UIViewController <UITextViewDelegate> {
  UITextView *textView;
}

@end

@implementation ViewBased001ViewController

- (void)btnMethod {
  [textView resignFirstResponder];
}

- (void)viewDidLoad {
    [super viewDidLoad];

  // ボタン
  UIButton *btn = [UIButton buttonWithType:UIButtonTypeRoundedRect];
  btn.frame = CGRectMake(0,100,200,30);
  [btn setTitle:@"キーボードを閉じる" forState:UIControlStateNormal];
  [btn addTarget:self action:@selector(btnMethod)
   forControlEvents:UIControlEventTouchUpInside];
  [self.view addSubview: btn];

  // テキストフィールド
  textView = [[[UITextView alloc]
         initWithFrame:CGRectMake(0, 0, 200, 60)] autorelease];
  // UITextViewDelegateプロトコルのメソッドの実行を
  // このViewControllerサブクラスのインスタンスに委譲
  textView.delegate = self;
  textView.text = @"クリック";
  [self.view addSubview: textView];
}

// UITextViewのデリゲートメソッド
// テキスト編集開始時にコールされる
-(void)textViewDidBeginEditing:(UITextView *)view{
  textView.text = @"キーボードが表示されたよ";
}

// UITextViewのデリゲートメソッド
// テキスト編集終了時にコールされる
-(void)textViewDidEndEditing:(UITextView *)view{
  textView.text = @"キーボードが閉じたよ";
}

- (void)didReceiveMemoryWarning {[super didReceiveMemoryWarning];}
- (void)viewDidUnload {}
- (void)dealloc {[super dealloc];}

@end

継承とインスタンス変数の定義

// .h
@interface Foo : Bar {
  @public
    int private_n;
  @protected
    int protected_n;
  @public
    int public_n;
}
@end
  • この場合は Foo が派生クラスで、Bar が基底クラス
  • { 波括弧 } 内でインスタンス変数を定義できる。
  • 可視性はデフォルトでは @private
  • 可視性は@private @protected @public で指定する(メソッドには可視性がないので注意。これらの修飾子はインスタンス変数にのみ使える)
  • クラス変数というものは明確にはないけれど、実装ファイルに static変数 を書けば同様の機能の変数を作れるらしい。

オブジェクトの生成

Foo *foo1 = [[Foo alloc] init];

// ポインタで扱う
Foo *foo1 =
  [
    //インスタンスの生成
    [Foo alloc]
  //実行する初期メソッド
  init];

Javaでいうところのコンストラクタを初期メソッドと呼ぶ。オーバーロード(多重定義)はできない…けどもJavaのコンストラクタとは違ってただのメソッドなので違う名前で複数用意すれば問題ない。初期メソッド名も自由(initClassというような名前が妥当)

NSObjectのメソッドによる参照カウンタの増減

  • 増加 : alloc, init でインスタンス化、-(id)retain
  • 減少 : -(oneway void)release
  • 参照カウンタ値の取得 : -(NSUInteger)retainCount;
  • 参照カウンタが0になるとコールされるメソッド : -(void)dealloc
  • 一時的に生成したインスタンスを自動破棄 : -(id)autorelease

例外

#import <UIKit/UIKit.h>

@interface ViewBased001ViewController : UIViewController
@end

@implementation ViewBased001ViewController

- (void)viewDidLoad {
  [super viewDidLoad];

  UILabel *text = [[[UILabel alloc] initWithFrame:CGRectMake(0, 0, 300, 200)] autorelease];
  [self.view addSubview:text];

  @try {
    @throw [NSException exceptionWithName:@"Exception Test" reason:@"Throw Test" userInfo:nil];
    // 下でも同じ
    // [NSException raise:@"Exception Test" format:@"Throw Test"];
  }
  @catch (NSException * e) {
    [text setText: [NSString stringWithFormat:@"%@, %@", [e name], [e reason]]];
  }
}

- (void)didReceiveMemoryWarning {[super didReceiveMemoryWarning];}
- (void)viewDidUnload {}
- (void)dealloc {[super dealloc];}

@end
Share
関連記事