blog

Ruby開発者がObjective-Cを楽しむ方法

Objective-CはRubyと同じくらいSmalltalkの影響を受けていますし、Ruby開発者が好きな言語機能(動的メソッドルックアップ、ダック型、オープンクラスなど)の多くを備えています。...

Jul 6, 2025 · 9 min. read
シェア

一方は動的、もう一方は静的、一方はインタプリタ、もう一方はコンパイル、一方はすっきりした構文、もう一方はやや冗長な構文。エレガンスの観点からは、Rubyの方がより自由なプログラミング体験ができそうなので、多くの人がObjective-Cに見切りをつけています。

Objective-CはRubyと同じようにSmalltalkの影響を受けていますし、Rubyの開発者が好きな言語機能(動的メソッドルックアップ、ダックタイピング、オープンクラス、多くの場合、高度に可変なランタイムなど)をたくさん持っています!これらの機能はObjective-Cにもあり、あまり知られていないテクニックもあります。Objective-Cはこれらの機能のすべてをそのIDEとコンパイラに負っていますが、自由にコーディングできない理由にもなっています。

でも、どうしてObjective-Cが動的言語だと言えるのですか?C言語をベースにしているのでは?

Objective-CのコードにCやC++のコードを含めることはできますが、だからといってObjective-CがCやC++のコードに限定されるわけではありません。Objective-Cの興味深いクラス操作やオブジェクトのイントロスペクションはすべて、Objective-Cランタイムと呼ばれるものに由来します。Objective-CランタイムはRubyインタープリタに匹敵します。強力なメタプログラミングに必要なすべての重要な機能が含まれています。

property_getAttributesmethod_getImplementation実際、C言語でもRubyと同様にこれらの機能をサポートしており、サブクラス・ツリーを走査する前に、orメソッドを使ってセレクタを具体的な実装に対応させ、オブジェクトがそのセレクタに応答できるかどうかを判断します。多くのObjective-Cメソッドの中で最も重要なのは、アプリケーションで送信されるすべてのメッセージを駆動するobjc_msgSendメソッドです。

メッセージの配信

Smalltalkは、オブジェクト指向言語の本当の****種類であり、それは "オブジェクトから別のオブジェクトに情報を送信する "古い概念 "呼び出し関数 "に代わる新しい概念であり、後の言語開発に大きな影響を与えました。

Rubyでメッセージを送るには、次のように書きます:

receiver.the_message argument 

Objective-Cの実装はRubyとほとんど同じです:

[receiver theMessage:argument]; 

これらのメッセージはダック・タイピング・アプローチを実装しています。つまり、オブジェクト自体のタイプやクラスではなく、オブジェクトがメッセージに反応できるかどうかが重要なのです。

メッセージを送ることは本当に素晴らしいことですが、その価値がより実感できるのは、メッセージがデータを配信しているときだけです:

receiver.send(:the_message, argument) 

歌で応える

[receiver performSelector:@selector(theMessage:) 
withObject:argument]; 

Rubyのメソッドがシンボルのサポートを必要とするように、Objective-Cのセレクタは文字列のサポートを必要とします。これにより、メソッドを動的に使用することができます。NSSelectorFromStringメソッドで文字列を使ってセレクタを作成し、オブジェクトの中で実行することもできます。同様に、Rubyで文字列やシンボルを作成し、Object#sendメソッドに渡すこともできます。

もちろん、言語に関係なく、処理できないオブジェクトにメッセージを送ると、デフォルトで例外がスローされ、アプリケーションもクラッシュします。

あるメソッドを呼び出す前に、そのオブジェクトが実行可能かどうかを調べたい場合、Rubyのrespond_to?

if receiver.respond_to? :the_message 
  receiver.the_message argument 
end 

Objective-Cでもほとんど同じアプローチがあります:

if ([receiver respondsToSelector:@selector(theMessage:)]) { 
    [receiver theMessage:someThing]; 
} 

ますますダイナミックに

Objective-Cのカテゴリは、Rubyの「オープンクラス」のようなものです。

たとえば、Railsのto_sentenceメソッドをNSArrayクラスに追加したい場合は、NSArrayクラスを継承するだけです:

@interface NSArray (ToSentence) 
- (NSString *)toSentence; 
@end 
 
@implementation NSArray (ToSentence) 
- (NSString *)toSentence { 
    if (self.count == 0) return @""; 
    if (self.count == 1) return [self lastObject]; 
    NSArray *allButLastObject = [self subarrayWithRange:NSMakeRange(0, self.count-1)]; 
    NSString *result = [allButLastObject componentsJoinedByString:@", "]; 
    BOOL showComma = self.count > 2; 
    result = [result stringByAppendingFormat:@"%@ and ", showComma ? @"," : @""]; 
    result = [result stringByAppendingString:[self lastObject]]; 
    return result; 
} 
@end 

カテゴリーとは、コンパイル時にプログラムにメソッドを追加することです。

Railsの動的ファインダのように、データをネストできるメッセージもあります。rubyでは、メソッドmethod_missingとrespond_toをオーバーライドしてパターンに一致させてから、新しいメソッドの定義をそのオブジェクトに追加します。

Objective-Cでの処理もほぼ同じですが、doesNotRecogniseSelector:メソッドをオーバーライドする代わりに、Categoryによって追加されたメソッドをresolveClassMethod:メソッドに取り込みます。プロパティの名前と値を取得する+findWhere:equals:というクラスメソッドがあると仮定すると、プロパティの名前を見つける正規表現を実装し、そのセレクタをブロックに登録するのは簡単です。

+ (BOOL)resolveClassMethod:(SEL)sel { 
    NSString *selectorName = NSStringFromSelector(sel); 
  
    NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"^findWhere(\\w+)Equals:$" options:0 error:nil]; 
    NSTextCheckingResult *result = [regex firstMatchInString:selectorName options:0 range:NSMakeRange(0, selectorName.length)]; 
    if (result) { 
        NSRange propertyNameRange = [result rangeAtIndex:1]; 
        NSString *propertyName = [selectorName substringWithRange:propertyNameRange]; 
  
        IMP implementation  = imp_implementationWithBlock((id) ^(id self, id arg1) { 
            return [self findWhere:propertyName equals:arg1]; 
        }); 
  
        Class metaClass = object_getClass(self); 
  
        class_addMethod(metaClass, sel, implementation, "@@:@@"); 
        return YES; 
    } 
  
    return [super resolveClassMethod:sel]; 
} 

このメソッドの利点は、respondsToSelector: をオーバーライドする必要がないことです。なぜなら、クラスに登録されたすべてのセレクタがこのメソッドを呼び出すからです。それでは、[RGSong findWhereTitleEquals:@"Mercy"]を呼び出してみましょう。findWhereTitleEquals:***が2回目に呼び出されたとき、ランタイムはこのメソッドについて知らないので、resolveClassMethod:を呼び出し、メソッド findWhereTitleEquals:@"Mercy "を動的に追加します。findWhereTitleEquals:が2回目に呼び出されたとき、resolveClassMethod:は呼び出されません。

動的メソッドのキャプチャを実装するには、他にもいくつかの方法があります。resolveClassMethod: メソッドや resolveInstanceMethod: メソッドをオーバーライドすることで、メッセージを別のオブジェクトに渡したり、"呼び出し "を引き継いでメッセージが渡されている間にやりたいことをやったりできます。特に -forwardInvocation: では、これらのメソッドを実行するためにオブジェクトをインスタンス化する必要があります。forwardInvocation:メソッドのdoesNotRecognizeSelectorメソッドのデフォルト呼び出しは、アプリケーションの頻繁な例外やクラッシュにつながります。

内向的

動的メソッド解決は、RubyやObjective-Cのような言語の技術的なサポートだけではありません。ランタイムにいることで、これらのオブジェクトを面白い方法で操作することもできます。

RubyでMyClass#instance_methodsを呼び出すように、Objective-Cでclass_copyMethodList([MyClass class], &numberOfMethods)を呼び出すと、オブジェクト内のメソッドのリストを取得できます。class_copyPropertyListメソッドでクラスのプロパティのリストを取得することもできます。例えば、このRap Geniusアプリでは、JSONの辞書をローカルオブジェクトにマッピングするためにこのメソッドが使用されています。

即実践

ダイナミックツールはすべて、ActiveRecordのような永続オブジェクトグラフであるCore Dataのようなものを作成するために使用することができます。Core Dataでは、リレーションシップは "欠陥 "であり、他のオブジェクトからアクセスされたときにのみロードされます。各プロパティのアクセサとミューテータは実行時にオーバーライドされます。まだロードされていないオブジェクトがアクセスされた場合、フレームワークは動的に永続ストレージからオブジェクトをロードし、それを返します。これにより、メモリ使用量を低く抑え、オブジェクトのいずれかがフェッチされたときにエンティティ・オブジェクト・グラフをメモリにロードしなければならない状況を回避します。

ミューテータが Core Data エンティティ上で呼び出されると、各プロパティのゲッターやセッターを書き換えなくても、そのオブジェクトをクリーンアップする必要があるとシステムが判断します。

それがメタ・プログラム、嫉妬です!

コンパイラとは何ですか?

明らかに、Objective-CとRubyは同じ言語ではありませんし、これまでのところ、Objective-Cがコンパイル言語であるという違いがあります。

これは、これらのテクニックで最も重要なことです。コンパイル時に、コンパイラはまず、アプリケーションで使われるすべてのセレクタがアプリケーションの中にあるかどうかを確認します。これは、宣言されていないセレクタがオブジェクトの中で呼び出されるのを防ぐためです。関連するコンパイルの警告をオフにするなど、この厄介な制限を回避する方法はいくつかあります。ここで、Objective-C***のメタプログラミングの練習をしてみましょう。

この型情報をオブジェクトから削除するには、セレクタの型を未知の型または ID として保存します。コンパイラはこの型を認識しないため、プログラムが送られたメッセージはすべて受け入れることができると仮定するしかありません。

親切なアドバイス:コンパイラのマークとオブジェクトのid型としての保存をオフにするのは非常に危険なことです!実は、Objective-Cの欠点のひとつはコンパイラにあります。型チェックをすることで、コードの記述やリファクタリングが速くなり、プログラミング時のミスも減ります。誰もその警告をオフにしないので、id型のコードを共有するのは難しいのです。ほとんどのObjective-C開発者は、メタプログラムよりも強力な型を使用することをまだ好んでいます。

Objective-Cはより制約が多いことが判明しましたが、コンパイラーはよりセキュリティとスピードを向上させるため、それを選択し、その結果に苦しまなければなりませんでした。

もう一度言いますが、これらの言語はほとんど同じであり、Rubyの開発者はObjective-Cを楽しむべきです。

Read next

PowerShell自動スクリプティングについての雑談:利点と課題

ビジネスの競争力を維持するために、ITは信頼性が高く、予測可能で、効率的なサービスを提供する必要があります。ワークフロー自動化ツールはある程度の助けになりますが、特に自動化スクリプトは、管理者により多くの機能を提供することができます。

Jul 6, 2025 · 3 min read