コーディング · プログラミング言語
Objective-C入門
結論
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, NSInteger | NSInteger は64ビットプラットフォームでは64ビット |
| BOOL | BOOL ok = YES; | YES / NO |
| NSString * | @"hello" | 不変の文字列オブジェクト |
| NSNumber * | @42, @3.14, @(x + 1) | 数値をボックス化し、コレクションに入れられるようにする |
| id | id thing = @"x"; | 任意のオブジェクトへのポインタ。静的な型なし |
| nil | nil | ヌルのオブジェクトポインタ |
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によって非推奨となり、現在は利用できません。