cv.cm

コーディング · プログラミング言語

Objective-C入門

約12分で読めます

結論

Objective-Cは、C言語にSmalltalk風のオブジェクト指向メッセージングを加えた言語です。メソッドは角括弧のメッセージ送信 [object doSomething:arg] で呼び出し、クラスは @interface と @implementation のブロックで定義し、文字列・配列・辞書にはAppleのFoundationフレームワークを使います。Swift登場以前はmacOSとiOSアプリの主力言語であり、今も多くの既存Appleコード資産で使われています。Macでは clang -fobjc-arc -framework Foundation hello.m でコンパイルします。

Objective-Cとは何か、誰が使っているのか

Objective-Cは1980年代前半にBrad CoxとTom Loveが考案し、NeXTがNeXTSTEPに採用し、さらにAppleが引き継ぎました。そのNeXTSTEPがmacOSとiOSの土台になっています。Cの厳密なスーパーセットで、あらゆるCコードがそのままObjective-Cとして有効であり、両者を自由に混在させられます。

現在、Appleは新規アプリにSwiftを推奨していますが、Objective-Cも引き続き完全にサポートされています。古いiOS・macOSアプリ、多くのシステムフレームワークやサードパーティライブラリ、CやC++とAppleのAPIを組み合わせるプロジェクトで見かけます。SwiftとObjective-Cは同じプロジェクト内で相互運用できるので、Appleプラットフォームの開発者は両方を知っておくと役立ちます。

インストールとHello, world

macOSでは、Xcode、またはコマンドラインツールのみ(xcode-select --install)をインストールします。どちらにもclangコンパイラとFoundationフレームワークが含まれています。ソースファイルの拡張子は .m、ヘッダーは .h です。

// hello.m
#import <Foundation/Foundation.h>

int main(void) {
    @autoreleasepool {
        NSLog(@"Hello, world!");
    }
    return 0;
}
clang -fobjc-arc -framework Foundation hello.m -o hello
./hello
# 2026-10-31 10:00:00.000 hello[1234:5678] Hello, world!

NSLog は標準エラー出力に書き込み、タイムスタンプとプロセス名を付加します。@"..." はC文字列ではなく NSString オブジェクトを作ります。-fobjc-arc は標準的なメモリ管理であるARC(自動参照カウント)を有効にします。Linuxでは、GNUstepプロジェクトが互換のFoundationライブラリを提供しており、ビルドコマンドは異なります。

型、オブジェクト、メッセージ

Objective-CはCなので、C言語の型はすべて使えます。オブジェクトは常にポインタ経由で扱います(NSString *name)。メソッドの呼び出しは「メッセージの送信」と呼ばれ、引数はメソッド名の各パーツの間に挟まれて並びます。

型例備考
C言語の数値型int, double, NSIntegerNSInteger は64ビットプラットフォームでは64ビット
BOOLBOOL ok = YES;YES / NO
NSString *@"hello"不変の文字列オブジェクト
NSNumber *@42, @3.14, @(x + 1)数値をボックス化し、コレクションに入れられるようにする
idid thing = @"x";任意のオブジェクトへのポインタ。静的な型なし
nilnilヌルのオブジェクトポインタ
NSString *name = @"Ana";
NSInteger age = 30;
NSString *line = [NSString stringWithFormat:@"%@ is %ld", name, (long)age];
NSLog(@"%@", line);                          // Ana is 30
NSLog(@"%lu", (unsigned long)[name length]); // 3
NSString *upper = [name uppercaseString];    // @"ANA"
BOOL same = [name isEqualToString:@"Ana"];   // YES
NSString *nothing = nil;
NSLog(@"%lu", (unsigned long)[nothing length]); // 0: messages to nil return 0

書式文字列では、%@ がオブジェクトを出力します(description メソッドが呼ばれます)。nil へのメッセージ送信は許可されており、nil または0を返します。これで多くのクラッシュを避けられますが、バグが隠れることもあります。

制御構文

制御構文はまったくCと同じで、if、else、switch、for、while、do ... while を使います。Objective-Cは、コレクション向けに for ... in による高速列挙を追加しています。

NSInteger score = 85;
if (score >= 90) {
    NSLog(@"A");
} else if (score >= 80) {
    NSLog(@"B");
} else {
    NSLog(@"C");
}

for (int i = 1; i <= 3; i++) {
    NSLog(@"%d squared is %d", i, i * i);
}

for (NSString *fruit in @[@"apple", @"pear"]) {
    NSLog(@"%@", fruit);
}

クラス、プロパティ、メソッド

クラスには、公開インターフェイス(通常は .h ファイル)と実装(.m ファイル)があります。インスタンスメソッドは - で、クラスメソッドは + で始めます。@property はプロパティを宣言し、そのゲッター、セッター、そしてアンダースコア付きの名前の内部変数を生成します。

@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic) NSInteger age;
- (instancetype)initWithName:(NSString *)name age:(NSInteger)age;
- (NSString *)greeting;
@end

@implementation Person
- (instancetype)initWithName:(NSString *)name age:(NSInteger)age {
    self = [super init];
    if (self) {
        _name = [name copy];
        _age = age;
    }
    return self;
}

- (NSString *)greeting {
    return [NSString stringWithFormat:@"Hi, I am %@, %ld", self.name, (long)self.age];
}
@end

// Usage inside main:
Person *ana = [[Person alloc] initWithName:@"Ana" age:30];
NSLog(@"%@", [ana greeting]);   // Hi, I am Ana, 30
ana.age = 31;                   // dot syntax calls the setter

オブジェクトは2段階で作ります。alloc がメモリを確保し、init... メソッドが初期化します。上のメソッドの名前は initWithName:age: です(コロンも名前の一部です)。プロトコル(@protocol)はインターフェイスの役割を担い、カテゴリを使うと NSString のような既存クラスにもメソッドを追加できます。

Foundationのコレクションとブロック

Foundationフレームワークは、不変のコレクションと、その可変なサブクラスを提供します。リテラルを使うと短く書けます。コレクションに入れられるのはオブジェクトだけなので、数値は NSNumber にボックス化し、nil は格納できません(代わりに [NSNull null] を使います)。

クラスリテラルまたは作成方法可変版
NSArray@[@"Ana", @"Ben"]NSMutableArray
NSDictionary@{@"Ana": @30}NSMutableDictionary
NSSet[NSSet setWithArray:arr]NSMutableSet
NSString@"text"NSMutableString
NSArray<NSString *> *names = @[@"Ana", @"Ben"];
NSMutableArray<NSString *> *more = [names mutableCopy];
[more addObject:@"Cleo"];
NSDictionary<NSString *, NSNumber *> *ages = @{@"Ana": @30, @"Ben": @17};
NSLog(@"%lu names, Ana is %@", (unsigned long)more.count, ages[@"Ana"]);

NSInteger (^square)(NSInteger) = ^(NSInteger x) { return x * x; };
NSLog(@"%ld", (long)square(7));    // 49

NSArray<NSNumber *> *sorted = [@[@3, @1, @2]
    sortedArrayUsingComparator:^NSComparisonResult(NSNumber *a, NSNumber *b) {
        return [a compare:b];
    }];
NSLog(@"%@", [sorted componentsJoinedByString:@", "]);   // 1, 2, 3

ブロックは ^ で書くクロージャ、つまり周囲のスコープの変数を捕捉できる無名関数です。AppleのAPIでは、コールバック、ソート、Grand Central Dispatchによる非同期処理に使われます。<NSString *> の部分は、コンパイラとSwiftを助ける軽量なジェネリクスで、省略も可能です。

メモリ管理とエラー処理

ARCでは、参照カウントの呼び出しをコンパイラが挿入してくれます。強い参照が1つもなくなると、オブジェクトは解放されます。自分で対処する必要があるのは循環参照だけで、通常は片側を weak にします(例えばデリゲートプロパティ)。

回復可能なエラーには NSError パターンを使います。メソッドは失敗時に nil または NO を返し、参照渡しで受け取った NSError オブジェクトに内容を設定します。例外(@try、@catch、NSException)もありますが、Cocoaでは、範囲外の添字のようなプログラマーのミスを知らせるためのもので、通常の失敗には使いません。

NSError *error = nil;
NSString *text = [NSString stringWithContentsOfFile:@"/no/such/file"
                                           encoding:NSUTF8StringEncoding
                                              error:&error];
if (text == nil) {
    NSLog(@"Failed: %@", error.localizedDescription);
}

小さな完成プログラム:単語の出現頻度

#import <Foundation/Foundation.h>

int main(void) {
    @autoreleasepool {
        NSString *text = @"the cat and the hat and the bat";
        NSMutableDictionary<NSString *, NSNumber *> *counts = [NSMutableDictionary dictionary];
        for (NSString *word in [text componentsSeparatedByString:@" "]) {
            counts[word] = @(counts[word].integerValue + 1);
        }
        NSArray<NSString *> *sorted = [counts keysSortedByValueUsingComparator:
            ^NSComparisonResult(NSNumber *a, NSNumber *b) { return [b compare:a]; }];
        for (NSString *word in [sorted subarrayWithRange:NSMakeRange(0, 2)]) {
            printf("%s %ld\n", word.UTF8String, (long)counts[word].integerValue);
        }
    }
    return 0;
}
// the 3
// and 2

新しい単語の場合、counts[word] は nil で、nil.integerValue は0を返すため、最初のカウントは1になります。コンパレーターは順序を逆にして、カウントの多い順に並べます。printf は NSLog の接頭辞なしで出力します。

次のステップ

  • 言語の基礎は、ドキュメントアーカイブにあるAppleの「Programming with Objective-C」ガイドで。
  • Appleのデベロッパードキュメントにある、Foundationフレームワークのリファレンス。
  • 正確なメモリのルールは、ClangのAutomatic Reference Countingのドキュメントで。
  • 混在したコード資産を保守する場合は、Objective-CとSwiftの連携方法を学びましょう。

よくある質問

Objective-Cは今でも使われていますか?

はい。主に既存のiOS・macOSアプリやライブラリで使われています。Appleは新しいコードにSwiftを推奨していますが、XcodeやApple SDKでObjective-Cは引き続きサポートされています。

SwiftとObjective-Cはどちらを先に学ぶべきですか?

新規のApple向け開発なら、まずSwiftを学びましょう。Objective-Cは、古いコードを読んだり保守したりする必要があるとき、CやC++のライブラリと密接に連携させるとき、Appleのフレームワークが内部でどう動くかを理解したいときに学びます。

Objective-Cの角括弧は何を意味しますか?

メッセージの送信を表します。[receiver message] は、オブジェクト receiver のメソッド message を呼び出します。引数は、[dict setObject:value forKey:key] のようにコロンの後ろに続きます。

Objective-Cにはガベージコレクションがありますか?

現在のObjective-Cは自動参照カウント(ARC)を使い、コンパイラがretainとreleaseの呼び出しを追加します。macOS向けの古いガベージコレクタはAppleによって非推奨となり、現在は利用できません。

コーディング講座の一覧