最終更新:2014-03-26 (水) 01:00:27 (3685d)  

Objective-C
Top / Objective-C

マジ変態。

概要

Cocoa

開発ツール

Appleのドキュメント (ADC)

特徴とか

NSObject

  • すべてのオブジェクトはNSObjectの派生クラス。
    • NS~というのはNeXTSTEPに由来。

オブジェクト識別子 id

  • すべてのオブジェクトはid型(NSObjectへのポインタ)で表すことができる。
    id hogeObject;
    HogeObject* hogeObject;
  • のどちらでも宣言できる。
  • (Objective-C ではオブジェクトはすべてポインタとして扱う。従ってオブジェクトの変数の宣言や引数の型としてオブジェクトを宣言するときには常に * が付くが、オブジェクトなら種類を問わず何でも格納できる id という型があり、この型には * は付かない。)
  • Objective-Cは原理的に、id型のオブジェクトを実行時に判定する、動的型付けの言語である。

オブジェクトメッセージング

  • 何か処理を呼ぶ時は関数呼び出しという形ではなく、メッセージという形でオブジェクト同士のやり取りを行う。
     [receiver message]
  • でターゲットにメッセージを送る。
  • ターゲットにmessageメソッドが実装されていればそのメソッドが呼び出される。
     NSString* string;
     // NSStringのインスタンスを確保して代入
     string = [[NSString alloc] init];
  • alloc や init というのがメッセージ。関数呼び出しとはちょっと違う
  • CocoaのCの関数は普通にNSMakeRange(2,3);のように呼び出す。

nil

  • キーワードnilは、NULLオブジェクト、すなわち値が0のid
  • Objective-Cでは、nilへのメッセージ送信が可能(何もおこらない)
  • メソッドがオブジェクトを返す場合は、nilに送信されたメッセージは0(nil)を返します
  • オブジェクトを指すポインタの初期値を指定しなければ、コンパイラが自動的に、nilに初期化

メモリ管理

  • メモリ管理のための3つの仕組みが提供されている

ARC (Automatic Reference Counting) - 自動参照カウント?

MRC (Manual Reference Counting?) - 非自動参照カウント?

GC

動的バインディング

  • 呼び出すメソッドを実行時に決定する事

関数呼び出しとメッセージの大きな違いは、関数とその引数コンパイル時にコードの中で結合されるのに対して、メッセージと受信側のオブジェクトはプログラムを実行してメッセージが送信されるまで結合されないこと。したがって、メッセージに応答するために呼び出される実際のメソッドは、コードのコンパイル時ではなく、実行時にのみ知ることができる。

Cocoaバインディング

基本的な文法

C言語と共通の部分

コメント

  • // 一行コメント
    /*
       複数行コメント
    */

データ型

  • int n;
    unsigned int n;
    char n;
    unsigned char n;
    long n;
    float n;
    double n;

変数

  • int i;//変数宣言
    int i = 0;//宣言&代入

四則演算とか

  • i = 1 + 1;
    i = 1 - 1;
    i = 1 * 2;
    i = 1 / 2;
    
    mod = 4 % 2;
    
    i++;
    ++i;
    i--;
    --i;

if文

  • if ( 条件 ) {
    } else if ( 条件 ) {
    }
  • オブジェクトの中身を比較する場合はisEqual?を使う。

while文

  • int i = 0;
    while (i < 5) {
    	// なんか処理
      i++;
    }

for文

  • for (int i = 0; i < 5; i++) {
      //処理
    }

Objective-C/ディレクティブ

関数の定義

  • Cで書くと
     void setWidthAndHeight(int width,int height);
  • なのが
     -(void) setWidth:(int)width andHeight:(int)height;
  • となる。
  • setWidth:がメソッド名で、andHeight:の部分はラベルという扱い。
    //objectのsetWidth:andHeight:を呼び出す
    [object setWidth:width andHeight:height];
  • このへんの命名規則がどうも慣れない。

変数の宣言

  • //型 変数名
    id varName;
    NSString* str;

真偽値

  • BOOL - YESかNO
    typedef signed char     BOOL; 
    #define YES             (BOOL)1
    #define NO              (BOOL)0

文字列

  • 文字列はNSStringで扱う。
  • @を使うとリテラルがNSStringになる(UTF-8)
     NSString* str = @"Hello, Hoge";
  • 文字列はstringWithString?とかで初期化。
    NSString* str;
    str = @"hoge";//リテラル
    str = [NSString stringWithString:@"日本語"];//NSStringで初期化
    str = [NSString stringWithCString:"hoge"];//C文字列で初期化
  • 格納する内容によって、さまざまなNSStringのサブクラスが用意してあり、そのサブクラスは自動的に選択される。(このようなクラスはクラスクラスタと呼ばれる)
  • 普通はNSCFString?とかになる(CFはCore Foundationの略)が、NSStringに対してパスの文字列操作([NSString stringByAppendingPathComponent?:]とか)を行うと、内部的にはNSPathStore2?になったりする。
     NSString* str;
     str = [NSString alloc];//allocされた時点では未決定なのでNSPlaceholderString
     ・・・
     //どのNSStringのサブクラスが選択されているか出力
     NSLog(@"str1 is %@", NSStringFromClass([str1 class])); 

リテラル

コレクション

  • Mutable~と付いて無いやつは内容の変更が不可。

配列

辞書

集合

  • 順序関係無いならNSSetNSMutableSet?

例外処理

  • ターゲットの設定で、「Objective-C例外処理を有効にする」をオンにしないと警告が出る
    @try{
      //ここでなんらかエラーを発生させる
      エラーの起きるような処理
    
      NSLog(@"hoge");//ここは実行されない
    }
    @catch(NSException* ex){
      NSLog(@"name %@,reason %@",[ex name],[ex reason]);
    }

クラスの書き方

クラスの宣言(hoge.h)

  • @interfaceでクラスを定義する
    • -がインスタンスメソッド
    • +がクラスメソッド
    @interface HogeObject:NSObject
    // @interface クラス名:親クラス名
    {
        //独自のインスタンス変数を宣言
        id _hogeValue;   //インスタンス変数は _から始めると区別しやすい(推奨)
        NSString *huga;
    }
    //プロパティの宣言
    //メソッドの宣言
    
    //-がインスタンスメソッドで、
    //+がクラスメソッド。
    -(id) init;//イニシャライザ
    //アクセッサ
    -(void) setHoge:(id)hoge;
    -(id) hoge;
    @end
  • 公開しない関数はヘッダに書かなくても良い
  • Objective-C にはコンストラクタは存在しない。初期化はイニシャライザ(init)で行う
  • 暗黙の変数
    • self
    • super
  • オブジェクトを指すポインタの初期値を指定しなければ、コンパイラが自動的に、nilに初期化

クラスの実装(hoge.m)

  • @implementation でクラスを実装する
     #import "hoge.h"
     @implementation HogeObject
    
     //イニシャライザ
     - (id)init {
      [super init];
      //ここで初期化
      return self;
     }
    
     //アクセッサ
     -(void) setHoge:(id)hoge
     {
       _hogeValue = hoge; 
     }
    
     -(id) hoge{
       return _hogeValue;
     }
     @end

アクセッサ

  • Objective-Cでは、すべてのメンバ変数はprivate(参照できるけど・・)。メンバ変数にアクセスする場合はアクセッサ(getter、setter)を使う。
    • setterはsetXXX
    • getterはXXX(get~にしない)
  • とする。
  • 下記のプロパティを使って合成(自動実装)することがほとんど。

プロパティ

  • 変数ごとにいちいちアクセッサかいてたら面倒なので、@propertyという仕組みが用意されている。
    • Objective-C 2.0から@property@synthesizeが導入された。
    • プロパティを宣言すれば、アクセサメソッドとインスタンス変数が自動的に生成される
    • 特に指定しなければ、合成されるインスタンス変数の名前は、プロパティ名の先頭にアンダースコアを置いたものになる
  • @interface
  • @implementation
    • アクセッサをどうするか@synthesize@dynamicで指定
    • @synthesize指示子にプロパティ名を指定すると、アクセッサメソッドを自動的に「合成」してくれる
      • プロパティに対応するgetterメソッドとsetterメソッドのデフォルトの名前は、XXX,setXXX
      • 手動で実装するときは自分でXXX,setXXXを実装する
    • コンパイル時にはアクセッサメソッドが存在しないが、実行時に動的にそれらが追加される、というケースに対応するために、@dynamicという指示子も用意されている。
      • @dynamicとした場合は、@propertyで宣言しても自前で実装する(アクセッサが生成されない)
  • Xcode 4.4
    • @synthesizeを省略可能
    • @synthesize@dynamicも書かなかった場合には@synthesizeを付けたのと同じ扱いとなる
      // ViewController.h
      @property (strong) NSString* testString;
      
      // ViewController.m
      @synthesize testString;//@synthesize testString = _testString;
    • // ViewController.h
      @property NSString* testString;
    • デフォルトがstrongなので書かなくてよくなった
    • @synthesizeされる相手のインスタンス変数は、同名の変数ではなくてアンダーバーを最初に付けた物がデフォルト
  • ブール型(値がYESまたはNO)のプロパティには、ゲッタメソッドを「is」で始まる名前にする慣習がある
    @property (getter=isFinished) BOOL finished;

ドット構文?

  • アクセサメソッドの呼び出しを明示的に記述する代わりに、ドット構文を使ってもオブジェクトのプロパティにアクセスできる。
  • オブジェクトに関して使用される場合、ドット構文は「構文上の便宜」であり、コンパイラによってアクセサメソッドの呼び出しに変換されます
  • 下記は同じ意味。
    myInstance.value = 10;
    printf("myInstance value: %d", myInstance.value);
    [myInstance setValue:10];
    printf("myInstance value: %d", [myInstance value]);
  • 自分のクラスで定義しているプロパティはselfキーワードを使って参照する必要がある。hoge ではインスタンス変数として扱われるが、self.hoge ではプロパティとしてアクセスされる。

キー・バリュー・コーディング(キー値コーディング)

  • 変数名の文字列表記によって該当変数を読み書きするメソッド
    //変数の値を取得
    -(id)valueForKey:(NSString *)attrName
    //変数の値をセット
    -(void)takeValue:(id)newValue forKey:(NSString *) attrName

インスタンス化

  •  HogeObject* objHoge; //id objHogeでも良い
     objHoge = [[HogeObject alloc] init]; //メモリを確保してイニシャライザで初期化
    
  • C#で書くとこんな感じ?
     HogeObject objHoge;
     objHoge = new HogeObject();//コンストラクタ

メソッドの呼び出し方

  • setHoge,hogeというのはメッセージ。実装されていなければ何も起きない。
  • allocやinitはNSObjectで実装されているから呼び出すこと!(Cocoaの前提)
     //[オブジェクト名 メソッド名:引数1]
     HogeObject* objHoge;
     //インスタンス化して初期化(alloc と initはNSObjectで定義されている)
     objHoge = [[HogeObject alloc] init];
     [objHoge setHoge: @"hoge"];
     hoge = [objHoge hoge];
     [objHoge setHoge: [objHoge hoge]];
  • id は「任意のオブジェクトを参照するもの」として利用可能。ただし、コンパイル時にはクラスは確定していないので、クラスに存在するメソッドを利用しているかどうかのチェックは行われないことになる。
  • (後述するプロトコルを使った場合、コンパイル時にメソッド利用の可否を確認できる。id<hogeProtocol> のように記述する)

カテゴリ

  • サブクラス化を行わずに既存のクラスの機能を拡張できる強力な機能
  • クラス名の後に()を付けてカテゴリ名を指定する。
  • メソッドをカテゴリ分けすることもできる
    @interface クラス名 (カテゴリ名)
      //メソッドの宣言
    @end
    
    @implementation クラス名 (カテゴリ名)
      //メソッドの実装
    @end
  • NSStringなど、既存のクラスにカテゴリを追加して拡張することも可能。→サブクラス化よりお手軽
  • カテゴリ独自のインスタンス変数は宣言できない。

プロトコル

  • オブジェクトの振る舞いを表すメソッドの集合を定義
  • @protocolという指示子で宣言。
  • クラスの宣言時にプロトコルを指定することにより、オブジェクトの振る舞いが分かりやすくなる
  • プロトコルが指定してあるのにメソッドが足りないとコンパイル時にエラーが出る
  • プロセス間通信(ドラッグ&ドロップとか)のときに便利
  • conformsToProtocol?:で、あるオブジェクトがプロトコルに適合しているかどうかを実行中に調べることもできる。
     //プロトコルの定義(どこかのファイルに書く)
     @protocol HogeProtocol
     - (void)encodeWithCoder:(NSCoder*)coder;
     - (id)initWithCoder:(NSCoder*)decoder;
     @end 
    HogeObject?HogeProtocol?に準拠(hoge.hに書く)
     //HogeObjectクラスの宣言にHogeProtocolを追加
     @interface HogeObject : NSObject <HogeProtocol>
     {
         ...
     }
     ...
     @end
    カテゴリのことを非形式プロトコル、@protocolのことを形式プロトコルと呼ぶこともある

クラスクラスタ

  • NSStringなどのクラスは、フレームワーク内ではさらにそのサブクラスが定義されていて実際にはそのクラスのインスタンスが作られる。こうした複数の隠れたクラスが生成されるようなクラスを「クラスクラスタ」と呼んでいる。

セレクタ

  • メソッドを示す変数。メソッドを指定するために使う。
  • オブジェクトはメッセージを受け取り、該当するセレクタが存在すればそのセレクタのメソッドを実行する。
  • メソッドを宣言すると、同名のセレクタが有効になる。
  • 型はSEL型。取得には@selector指示子を使う
     //nameというメソッドを指定
     SEL selector
     selector = @selector(name);
    
     // objectがメソッドnameを実装しているかどうかを確認
     if ([object respondsToSelectlr:@selector(name)]) {
       ...
     } 
    メソッドの呼び出しは直接行ってもいいし、セレクタを指定して呼び出すこともできる。
     // メソッドを呼び出す
     [object performSelector:@selector(name)];
  • IMP型(関数へのポインタ)を利用すると、セレクタを介さずに直接関数を呼び出すことができる。

設計

イベントの処理

イベントの処理方法にはたぶん3種類ある。

アウトレット(IBOutlet)とアクション(IBAction)で関連付け

  • ボタン押したときとかの処理
  • Interface BuilderでGUIをデザインして、Xcode/Assistant Editorを使ってコードと結びつける。
    @interface{
        IBOutlet NSTextField textField;//IBで接続したNSTextFieldのアウトレット
    }
    //ボタンを押したときのアクションにbuttonPushed:を追加し、このIBActionと接続する
    -(IBAction)buttonPushed:(id)sender;

アウトレット (IBOutlet)

  • クラスからIB上のGUI部品を指定するときに使う。
    IBOutlet NSTextfiled textField;
  • みたいな感じで宣言。textFiledをウィンドウ上のどの部品と結び付けるかはIBで右ドラッグ。(Crtl押しながらドラッグ)

アクション (IBAction)

  • アクション=イベントの通知
  • GUIによって呼び出されるメソッドのこと。
    -(IBAction)someAction:(id)sender;
  • と言う感じで宣言。

デリゲートでイベントを委譲するクラスを指定

  • オブジェクト指向では、ある既存クラスの機能をカスタマイズしたいとき(機能追加など)は、サブクラスを作成するが、Cocoaでは、デリゲート(委譲)を使用してクラスの機能をカスタマイズ?する
  • Xcodeからクラスを作成しIBに追加、File's Ownerのdelegateをその追加したクラスに設定する
  • File's Owner(デフォルトではNSApplication)で処理されなかったイベントの処理は上で設定したクラスに委譲される
    //デリゲートメソッド
    // アプリケーションが終了する前のイベント
    - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender{
    //なんか処理
    }
    // 最後のウィンドウが閉じた時のイベント
    - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender{
    //なんか処理
    }

Cocoa/デリゲート

  • ほとんどの表示アイテムはマウスとキーボードイベントに対応するデリゲートを持つ
  • IBでコントロールのデリゲートを設定すると、発生したイベントに応じてデリゲートへの問い合わせが起きる
    • デリゲートにはカテゴリ(非形式プロトコル)を使う
    • デリゲートは一つのオブジェクトしか登録できない

ノーティフィケーション

  • クラスをノーティフィケーションセンター(NSNotificationCenter)に登録するとイベントが通知される
  • 例えばNSApplicationにはNSApplicationDidBecomeActiveNotification?とかがあるのでそれらを受け取るように設定できる
    //セレクタで指定するメソッド
    -(void)someMethod:(NSNotification*)notification
    {
      NSLog(@"%@ is notified",[notification name]);
    }
    
    //起動時に通知の設定を行う
    -(void)awakeFromNib{
      NSNotificationCenter* center;
    
      //センターの取得
      center = [NSNotificationCenter defaultCenter];
      //セレクタに通知名を設定
      [center addObserver:self //通知先オブジェクト
        selector:@selector(someMethod:) //通知先メソッドのセレクタ
        name: TestNotification //通知の名前
        object:nil]
    
    }
    //コードのどこかでTestNotificationというノーティフィケーションを通知
    [[NotificationCenter defaultCenter]postNotificationName:@"TestNotification" object:self userInfo:nil];

XIB (旧称:NIB)ファイル

  • Interface Builderでいじくるファイル。
  • IBのインスタンスウィンドウに表示されるアイコン(クラス)は、アプリケーションが起動され"MainMenu?.nib"ファイルが読み込まれた時にインスタンスが生成される。
  • 起動時にNSApplicationによってawakeFromNib関数が呼び出される。VBとかで言うところのForm_Load?

レスポンダ

ダイアログの表示

クラスオブジェクト

  • すべてのオブジェクトは自分のクラスに関する情報をメタ情報として持っており、これをクラスオブジェクトという形で取得可能。
  • 型は、id型か、クラスオブジェクトのための型Classを使う
     Class cls;
     cls = [object class]; // インスタンスからクラスオブジェクトを取得
     cls = [NSString class]; // クラスからクラスオブジェクトを取得
     cls = NSClassFromString(@"anyClassName"); //クラス名からクラスオブジェクトを取得 
    
     // ランタイムAPIを使って、C文字列からClassを取得する
     cls = objc_getClass("anyClassName");
     cls = objc_lookUpClass("anyClassName");
    どのクラスに属するかの判定
     if ([object isMemberOfClass:[NSString class]]) {
       // NSStringクラスの場合
       ...
     } 
    
     if ([object isKindOfClass:[NSString class]]) {
       // NSStringクラスか、それを継承したクラスの場合
       ...
     } 
  • Objective-Cのクラス(objc_class)は、結局のところC言語の構造体。
     printf("class name %s\n", ((struct objc_class*)cls)->name);
    みたいにして、クラスのフィールドにアクセス可能。

インスタンスの比較

  • 同値:内容が一緒
  • 同等:同じインスタンス
    if(objA==objB){
      //同じインスタンス(同等)
    }
    
    if([objA isEqual:objB]){
      //内容が一緒(同値)
    }
     

ファイル入出力

Objective-C には

  • テキストデータをテキストのまま保存し、テキストのまま読み込みをする
  • テキストデータを plist という Cocoa で採用された新しい形式で保存し読み込みをする
  • テキストデータおよびバイナリデータをバイアリデータとして保存し読み込みをする

という3つのファイル入出力形式が用意されている。

Objective-C 2.0

AppleはMac OS X 10.5においてObjective-C 2.0という名称で言語仕様の変更を行った。

  • ガベージコレクションの導入
  • プロパティの導入
  • 高速列挙
  • プロトコルの強化
  • Class Extensions
  • ランタイム構造の変更

    クラスのposingは廃止された。その代わりにメソッドの交換(セレクタのマッピングを入れ替える)が公式に用意され、カテゴリと組み合わせることでほぼ同じ機能を実現できる。

新機能

Xcode 4.4

Xcode 4.2

Objective-C/ブロック構文

  • Mac OS X 10.6から対応
    戻り値の型 (^ブロック名)(引数);
    ブロック名 = ^(引数){};

参考

Java使いのための「Objective-Cではこう書く」

参考

 


参考図書