Objective-C:言語の本質とiOS開発におけるその位置づけ

著者: IT Sectr 公開日: 2026-02-09 読了時間: 8 分

Objective-Cは、1980年代にブラッド・コックスによって作られた、動的メッセージディスパッチを備えたプログラミング言語です。AppleはNeXTSTEPの主要言語として、その後iOS SDKの主要言語としてObjective-Cを採用しました。Programming With Objective-C — メッセージ構文とメモリ管理の入門。

重要ポイント

  • Objective-C — 角括弧によるメッセージ構文と動的ランタイムを備えたCの厳密なスーパーセット
  • Message passing — [object selector:argument]によるメソッド呼び出しとobjc_msgSendによる動的実装検索
  • Categories — 継承やソースコードへのアクセスなしに既存クラスにメソッドを追加する仕組み
  • Blocks — クロージャ付き無名関数、Swiftクロージャの前身
  • ARC vs MRC — メモリ管理のための自動および手動参照カウント

Objective-Cとは?

Objective-Cは、Smalltalkスタイルのオブジェクト指向プログラミング機能でCを拡張するプログラミング言語です。Objective-CコードはLLVMまたはGCCを介してネイティブマシンコードにコンパイルされ、Cとの完全な後方互換性を維持します — あらゆるCコードはObjective-Cで有効です。

Appleは1997年にNeXT(Objective-Cとともに)を買収しました。この言語はmacOSとiOSのフレームワークであるCocoaとCocoa Touchの基盤となりました。2014年以降、AppleはSwiftを代替言語として推進していますが、Objective-Cはレガシープロジェクトや一部のシステムフレームワークにとって依然として極めて重要です。Apple(WWDC 2024)によると、App Storeのアプリの約35%が今もObjective-Cコードを含んでいます。

主要な特徴は動的ランタイムです。メソッド呼び出しがコンパイル時に解決されるSwiftとは異なり、Objective-Cはobjc_msgSend関数を介して実行時にメッセージを送信します。これにより、メソッドの実行時オーバーライド(method swizzling)、クラスの動的追加、存在しないセレクターに対するforward invocationの使用が可能になります。

構文:C + オブジェクト指向拡張

あらゆるCコードはObjective-Cで有効です。オブジェクト指向拡張は、クラス(@interface/@implementation)、カテゴリ、プロトコル(@protocol)、動的型付け(id)を追加します。ファイルの拡張子は.m(実装)と.h(ヘッダー)です。

メッセージ構文と動的ディスパッチ

メッセージ構文がObjective-CとC系言語の主な違いです。object.method(argument)の代わりに[object method:argument]を使用します。各メッセージはobjc_msgSendを通過し、実行時に動的にメソッドの実装を検索します。

objective-c
// 名前付きパラメータを持つメッセージ構文
NSString *greeting = [NSString stringWithFormat:@"Hello, %@", name];

// ネストされたメッセージ
NSArray *sortedArray = [[array sortedArrayUsingSelector:@selector(compare:)] copy];

// イントロスペクションによる型チェック
if ([object isKindOfClass:[UIView class]]) {
    UIView *view = (UIView *)object;
    view.backgroundColor = [UIColor redColor];
}

動的ディスパッチにより、実行時のメソッドオーバーライド(method swizzling)が可能です — 強力だが危険な技法です。例えば、AFNetworkingやAspectsなどのフレームワークはURLSession呼び出しをインターセプトするためにswizzlingを使用します。Appleは警告しています:誤った使用でswizzlingはシステムフレームワークを壊す可能性があります。

カテゴリとクラス拡張

カテゴリは、継承やソースコードへのアクセスなしに既存のクラス(NSStringやUIViewなどのシステムクラスを含む)にメソッドを追加できるObjective-Cのユニークな機能です。カテゴリは@interface ClassName (CategoryName)を使用して宣言します。

objective-c
// UIColor+Hex.hファイル — UIColorのカテゴリ
@interface UIColor (Hex)
+ (instancetype)colorWithHex:NSUIntegerhex;
@end

// UIColor+Hex.mファイル — 実装
@implementation UIColor (Hex)
+ (instancetype)colorWithHex:NSUIntegerhex {
    CGFloat r = ((hex >> 16) & 0xFF) / 255.0;
    CGFloat g = ((hex >> 8) & 0xFF) / 255.0;
    CGFloat b = (hex & 0xFF) / 255.0;
    return [self colorWithRed:r green:g blue:b alpha:1.0];
}
@end

拡張(Extensions)(Class Extension)は、名前なしで.mファイルに宣言される特別な種類のカテゴリです:@interface ClassName ()。カテゴリとは異なり、拡張はメソッドだけでなくivar(インスタンス変数)やプロパティも追加できます。拡張は外部モジュールから内部APIを隠すために使用されます。

ブロック(Blocks)— クロージャ付き無名関数

ブロックは、外部スコープから変数をキャプチャするObjective-Cの無名関数です。ブロック構文:^(int x) { return x * 2; }。ブロックはUIKitのコールバックハンドラ、Grand Central Dispatchの非同期タスク、NSArray/NSDictionaryコレクションの関数型操作で使用されます。

objective-c
// ブロック型宣言
typedef void (^CompletionBlock)(BOOL success, NSError *error);

// パラメータとしてのブロック
- (void)fetchDataWithCompletion:(CompletionBlock)completion {
    __weak typeof(self) weakSelf = self;
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        BOOL result = [weakSelf processData];
        if (completion) {
            completion(result, nil);
        }
    });
}

ブロックのメモリ管理は極めて重要です。ブロックはselfを強参照でキャプチャし、直接使用するとretain cycleを引き起こします。解決策は__weak typeof(self) weakSelf = self、続いてブロック内でのチェックです。この問題はSwiftではcapture lists [weak self]によって完全に解決されています。

ARCとMRC:メモリ管理

Objective-CはMRC(Manual Reference Counting)からARC(Automatic Reference Counting)へと進化しました。MRCでは、開発者は手動でretain(カウンター増加)、release(減少)、autorelease(遅延解放)を呼び出していました。これらの呼び出しのエラーはメモリリークやダングリングポインタによるクラッシュを引き起こしていました。

操作MRCARC
オブジェクト作成[[Object alloc] init][[Object alloc] init]
参照保持[object retain]自動
解放[object release]自動
自動解放[object autorelease]自動(不要)
弱参照__weakは存在しなかった__weak(自動nil)

ARCはXcode 4.2とLLVM 3.0(2011)で導入されました。コンパイラはオブジェクトのライフタイムを分析し、コンパイル時に自動的にretain/releaseを挿入します。ARCはガベージコレクションではありません — 自動挿入を伴う静的解析です。Objective-C ARCはSwift ARCと互換性があります:どちらもランタイムレベルで同じ参照カウントシステムを使用します。

Bridging HeaderによるSwiftとの相互運用性

1つのプロジェクトでObjective-CとSwiftを一緒に使用することは、レガシープロジェクトでは一般的なプラクティスです。AppleはBridging Headerを提供しています — SwiftがObjective-Cクラスを認識し、Objective-CがNSObjectを継承するSwiftクラスを認識するための自動生成ファイルです。

objective-c
// ProjectName-Bridging-Header.h
// Swiftはこれらのヘッダーを自動的に認識します
#import "LegacyManager.h"
#import "NetworkClient.h"
#import "DataStore.h"

Objective-CからSwiftへの移行は段階的なプロセスです。新しいファイルはSwiftで書き、古いファイルは徐々にリファクタリングします。Bridging Headerは、Objective-Cプロジェクトに最初のSwiftファイルを追加したときに自動生成されます。逆方向の可視性(ObjC → Swift)のために、Xcodeは@objcでマークされたSwiftクラスの@interface宣言を含む<ProjectName>-Swift.hファイルを生成します。

swift
// Objective-Cから見えるSwiftクラス
@objc class SwiftRouter: NSObject {
    @objc func navigateToProfile(userId: Int) {
        // 実装
    }
}

制限事項:Swiftの値型(struct、enum)はObjective-Cから直接見えません — @objc付きのクラスでラップする必要があります。SwiftのジェネリクスはObjective-Cからのアクセスが制限されています。推奨されるアプローチは、新しいコードをSwiftで書き、既存のObjective-Cコードは対応する機能を変更する場合にのみリファクタリングすることです。

Objective-CのランタイムとMethod Swizzling

Swiftに対するObjective-Cの主な利点はランタイムへの完全なアクセスです。Method swizzling(実行時のメソッド実装の置き換え)により、継承なしでシステムメソッドをオーバーライドできます。AspectsやJRSwizzleなどのライブラリは、AOP(アスペクト指向プログラミング)— 監視、分析、ロギングにこの機能を使用します。

Forward invocationは別のランタイム機能です:オブジェクトがセレクターに応答しない場合、システムはforwardInvocation:を呼び出し、メッセージを別のオブジェクトに転送できます。これはObjective-C(NSProxy)のプロキシパターンの基礎であり、遅延初期化や分散オブジェクトに使用されます。

Appleは新しいコードでのランタイムトリックの使用を最小限に抑え、Swiftの静的型付けを優先することを推奨しています。ただし、レガシープロジェクトでは、objc_msgSend、method_exchangeImplementations、objc_getAssociatedObjectの知識が既存のコードベースのメンテナンスに必要です。

プロパティとKey-Value Observing

Objective-Cはgetter/setter付きのivarに対するプロパティをシンタックスシュガーとしてサポートしています。atomic/nonatomic、strong/weak/copy、readonly/readwrite、assign/retainの修飾子がメモリ動作とスレッドセーフティを決定します。Key-Value Observing(KVO)はランタイムに組み込まれたプロパティ変更監視メカニズムです:任意のオブジェクトがaddObserverを介して別のオブジェクトのプロパティ変更を購読できます。

よくある質問

Objective-Cのメッセージ構文とは?

メソッド呼び出しは[object selector:argument]のように見えます。メッセージはobjc_msgSendを通過し、ランタイムで動的に実装を見つけます。セレクターはメソッド名(@selector(methodName))であり、関数ポインタではありません。これによりswizzlingとforward invocationが可能になります。

カテゴリ(Categories)と拡張(Extensions)の違いは?

カテゴリ(@interface ClassName (Name))は継承なしで任意のクラス(システムクラスを含む)にメソッドを追加します。拡張(@interface ClassName ())は.mファイルで宣言され、プロパティとivarsを追加できます。カテゴリはivarsを追加できませんが、拡張は追加できます。

Objective-Cのブロック(Blocks)とは?

ブロックは^(パラメータ) { 本体 }構文の無名関数で、コンテキストから変数をキャプチャします。C++のラムダやSwiftのクロージャに類似しています。selfをキャプチャする際のretain cycleを防ぐために__weakが必要です。UIKit、GCD、Foundationで使用されます。

ARCとMRCの違いは?

MRCは手動参照カウント:プログラマーがretain、release、autoreleaseを呼び出します。ARCは自動:コンパイラが静的解析に基づいてretain/releaseを挿入します。ARCはGCではありません — カウンターがゼロになるとオブジェクトは即座に解放されます。ARCは__weakと__strong修飾子をサポートします。

同じプロジェクトでObjective-CとSwiftを使用できますか?

はい、Bridging Headerを介して可能です。Swiftはこのヘッダーを通じてObjective-Cを認識し、Objective-Cは<ProjectName>-Swift.hを通じてSwiftを認識します。SwiftクラスはNSObjectを継承し、@objcでマークする必要があります。SwiftのstructとenumはObjective-Cから直接見えません。

まとめ

  • Objective-C — Cocoa/Cocoa Touch向け動的ランタイムとメッセージ構文を備えたCの厳密なスーパーセット
  • Message passing — objc_msgSendによる動的ディスパッチとswizzling機能を備えた[object method]呼び出し
  • カテゴリ — Appleのシステムクラスを含む継承なしでの既存クラスへのメソッド追加
  • ブロック — retain cycle防止に__weakを必要とするクロージャ付き無名関数
  • ARC vs MRC — 手動retain/releaseからコンパイラによる自動参照カウントへの進化
  • Bridging Header — 段階的移行のための1つのプロジェクトでのSwiftとObjective-Cの連携メカニズム
  • レガシー重要性 — App Storeアプリの約35%がメンテナンスを必要とするObjective-Cコードを含む

ターンキー方式のモバイルアプリケーションを開発します

IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。

プロジェクトについて相談

こちらもお読みください