Objective-Cのselectorの完全解説10選

Objective-Cのselectorの詳しい使い方を図解Objctive-C
この記事は約23分で読めます。

 

【サイト内のコードはご自由に個人利用・商用利用いただけます】

この記事では、プログラムの基礎知識を前提に話を進めています。

説明のためのコードや、サンプルコードもありますので、もちろん初心者でも理解できるように表現してあります。

基本的な知識があればカスタムコードを使って機能追加、目的を達成できるように作ってあります。

※この記事は、一般的にプロフェッショナルの指標とされる『実務経験10,000時間以上』を凌駕する現役のプログラマチームによって監修されています。

サイト内のコードを共有する場合は、参照元として引用して下さいますと幸いです

※Japanシーモアは、常に解説内容のわかりやすさや記事の品質に注力しております。不具合、分かりにくい説明や不適切な表現、動かないコードなど気になることがございましたら、記事の品質向上の為にお問い合わせフォームにてご共有いただけますと幸いです。
(送信された情報は、プライバシーポリシーのもと、厳正に取扱い、処分させていただきます。)

はじめに

Objective-Cは、C言語をベースにしたオブジェクト指向プログラミング言語であり、特にAppleのiOSやmacOSのアプリケーション開発に広く利用されています。

この言語の中で、非常に重要な概念として「selector」が存在します。

selectorは、Objective-Cの中でメソッドの識別や呼び出しを行うための仕組みです。

他のプログラミング言語では、関数やメソッドの名前を直接使用して呼び出しますが、Objective-Cでは少し異なるアプローチを取ります。

それが、selectorの活用です。

本記事では、Objective-Cにおけるselectorの基本的な使い方から、応用例、注意点、そしてカスタマイズ方法に至るまで、初心者向けに徹底的に解説していきます。

この記事を読むことで、selectorの活用法を10通り以上学ぶことができるでしょう。

●Objective-Cとselectorの基本

Objective-Cとその中核をなすselectorに関して、基本的な情報から詳細な特性、そしてその重要性について説明します。

○Objective-Cとは

Objective-Cは、1980年代初頭にBrad CoxとTom Loveによって開発されたプログラミング言語です。

C言語の構文をベースに、Smalltalkのオブジェクト指向のコンセプトを取り入れて作られました。

そのため、C言語のプログラムとオブジェクト指向プログラムが共存する独特の構造を持っています。

AppleがNeXTを買収したことを契機に、Objective-CはmacOSやiOSの開発における主要な言語として採用されることとなり、現在に至っています。

Objective-Cのメソッドの呼び出しや識別に使われる「selector」について、その基本を次に詳しく述べます。

○selectorの基本

Objective-Cの世界で、メソッドを呼び出す際には、通常のメソッド名ではなく「selector」という特殊な識別子を用います。

selectorは、実際のメソッド名を表現するための文字列であり、メソッドの動的な呼び出しや識別に使用されます。

例えば、あるオブジェクトがprintMessageというメソッドを持っている場合、このメソッドを呼び出すためのselectorは@selector(printMessage)のように記述します。

Objective-Cでは、このようにselectorを使うことで、実行時に動的にメソッドを呼び出すことができるのです。

これにより、プログラムの柔軟性や拡張性が向上します。

●Objective-Cのselectorの使い方

Objective-Cの中でselectorを使うことで、非常に柔軟なプログラミングが可能となります。

ここでは、selectorの具体的な使い方を2つのサンプルコードを通して解説します。

○サンプルコード1:シンプルなselectorの使い方

Objective-Cでは、メソッドを呼び出す際には、@selectorを使ってメソッドを識別します。

下記のコードでは、helloWorldというメソッドを持つSampleClassクラスのインスタンスを作成し、そのメソッドをselectorを用いて呼び出しています。

#import <Foundation/Foundation.h>

@interface SampleClass : NSObject
- (void)helloWorld;
@end

@implementation SampleClass
- (void)helloWorld {
    NSLog(@"Hello, World!");
}
@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        SampleClass *sample = [[SampleClass alloc] init];
        [sample performSelector:@selector(helloWorld)];
    }
    return 0;
}

このコードでは、SampleClassクラスを定義し、その中にhelloWorldメソッドを実装しています。

メイン関数の中で、このクラスのインスタンスを作成し、performSelector:メソッドを使ってhelloWorldメソッドを呼び出しています。

この例では、helloWorldメソッドが実行されることで、コンソールに「Hello, World!」と表示されます。

○サンプルコード2:メソッドを呼び出すselectorの活用

次に、引数を持つメソッドの呼び出し方について説明します。

下記のコードでは、greetWithName:というメソッドを持つGreetingClassクラスのインスタンスを作成し、selectorを用いてそのメソッドを呼び出しています。

#import <Foundation/Foundation.h>

@interface GreetingClass : NSObject
- (void)greetWithName:(NSString *)name;
@end

@implementation GreetingClass
- (void)greetWithName:(NSString *)name {
    NSLog(@"Hello, %@!", name);
}
@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        GreetingClass *greeting = [[GreetingClass alloc] init];
        [greeting performSelector:@selector(greetWithName:) withObject:@"Taro"];
    }
    return 0;
}

このコードでは、GreetingClassクラスを定義し、その中にgreetWithName:メソッドを実装しています。

このメソッドは、引数として名前を受け取り、コンソールにその名前とともに挨拶のメッセージを表示します。

メイン関数の中で、このクラスのインスタンスを作成し、performSelector:withObject:メソッドを使ってgreetWithName:メソッドを呼び出しています。

この例では、greetWithName:メソッドが実行されることで、コンソールに「Hello, Taro!」と表示されます。

○サンプルコード3:条件を持つselectorの使い方

Objective-Cにおけるselectorは非常に柔軟性が高く、条件をもとに動作を制御することが可能です。例えば、特定の条件下でのみメソッドを呼び出したい場合や、特定の条件を満たすオブジェクトだけにメッセージを送りたい場合などに役立ちます。

このコードでは、ある条件を満たす場合にのみ特定のメソッドを呼び出す機能を表しています。この例では、数字が偶数である場合のみevenNumberDetected:メソッドを呼び出しています。

#import <Foundation/Foundation.h>

@interface MyClass : NSObject
- (void)evenNumberDetected:(NSNumber *)number;
@end

@implementation MyClass
- (void)evenNumberDetected:(NSNumber *)number {
    NSLog(@"偶数を検出しました: %@", number);
}
@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        MyClass *myObject = [[MyClass alloc] init];
        NSArray *numbers = @[@1, @2, @3, @4, @5];

        for (NSNumber *number in numbers) {
            if ([number intValue] % 2 == 0) {
                [myObject performSelector:@selector(evenNumberDetected:) withObject:number];
            }
        }
    }
    return 0;
}

この例での出力は、”偶数を検出しました: 2″ および “偶数を検出しました: 4” という2つのメッセージが表示されます。

○サンプルコード4:複数の引数を取るselectorの使用例

Objective-Cのselectorは、複数の引数を取るメソッドを呼び出すこともできます。

引数が複数存在する場合、それらの引数はコロン(:)を使用して区切られます。

このコードでは、printName:age:というメソッドを使用して、名前と年齢を引数として取ることを表しています。

この例では、名前と年齢を指定してprintName:age:メソッドを呼び出しています。

#import <Foundation/Foundation.h>

@interface MyClass : NSObject
- (void)printName:(NSString *)name age:(NSNumber *)age;
@end

@implementation MyClass
- (void)printName:(NSString *)name age:(NSNumber *)age {
    NSLog(@"名前: %@, 年齢: %@", name, age);
}
@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        MyClass *myObject = [[MyClass alloc] init];
        [myObject performSelector:@selector(printName:age:) withObject:@"田中" withObject:@25];
    }
    return 0;
}

この例での出力は、”名前: 田中, 年齢: 25″ というメッセージが表示されます。

○サンプルコード5:動的なselectorの生成

Objective-Cのselectorは動的に生成することも可能です。

これにより、実行時にメソッド名を動的に指定して呼び出すことができます。

このコードでは、NSSelectorFromString関数を使用して、文字列から動的にselectorを生成する方法を表しています。

この例では、動的にprintHelloメソッドを呼び出しています。

#import <Foundation/Foundation.h>

@interface MyClass : NSObject
- (void)printHello;
@end

@implementation MyClass
- (void)printHello {
    NSLog(@"Hello!");
}
@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        MyClass *myObject = [[MyClass alloc] init];
        SEL dynamicSelector = NSSelectorFromString(@"printHello");
        [myObject performSelector:dynamicSelector];
    }
    return 0;
}

この例での出力は、”Hello!” というメッセージが表示されます。

●Objective-Cのselectorの応用例

Objective-Cにおけるselectorは非常に多機能で、さまざまなシーンで活用することができます。

ここでは、selectorを用いた応用的な例をいくつか紹介します。

○サンプルコード6:selectorを用いたイベントハンドリング

Objective-Cでのイベントハンドリングには、しばしばselectorが活用されます。

UIButtonのクリックイベントなどを監視する際に、特定のメソッドを呼び出すための指定にselectorを利用するケースが多いのです。

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController
- (void)buttonClicked:(UIButton *)sender;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem];
    [button setTitle:@"Click Me!" forState:UIControlStateNormal];
    [button addTarget:self action:@selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:button];
}

- (void)buttonClicked:(UIButton *)sender {
    NSLog(@"Button was clicked!");
}
@end

このコードでは、UIButtonのインスタンスを作成し、ボタンがクリックされたときにbuttonClicked:メソッドを呼び出すように指定しています。

この例では@selector(buttonClicked:)を使って、ボタンのクリックイベントとメソッドを関連付けています。

このサンプルコードを実行すると、ボタンをクリックするとコンソールに「Button was clicked!」と表示されることが確認できます。

○サンプルコード7:selectorとタイマーの連携

タイマーとselectorは非常に相性が良いです。

NSTimerクラスを使用して、一定の間隔で特定のメソッドを呼び出す場面において、selectorは中心的な役割を果たします。

#import <Foundation/Foundation.h>

@interface TimerExample : NSObject
- (void)startTimer;
- (void)timerFired;
@end

@implementation TimerExample

- (void)startTimer {
    [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerFired) userInfo:nil repeats:YES];
}

- (void)timerFired {
    NSLog(@"Timer fired!");
}

@end

このコードでは、NSTimerを用いて1秒ごとにtimerFiredメソッドを呼び出しています。

この例では、@selector(timerFired)を使って、タイマーの時間経過イベントとメソッドを関連付けています。

このサンプルコードを実行すると、1秒ごとにコンソールに「Timer fired!」と表示されることが確認できます。

これにより、一定の間隔で特定の処理を実行する場面などでselectorを利用することができます。

○サンプルコード8:非同期処理でのselectorの活用

Objective-Cでは、非同期処理を行う際にNSOperationQueuedispatch_asyncを使用することができます。

これらの非同期処理の中でselectorを利用して、特定のメソッドを呼び出すことが可能です。

selectorは非同期処理の完了後や特定の条件下でメソッドを実行する場面で非常に役立ちます。

ここでは、非同期処理を実行し、完了後に特定のメソッドを呼び出すサンプルコードを紹介します。

#import <Foundation/Foundation.h>

@interface Sample : NSObject
- (void)processData;
- (void)completeProcess;
@end

@implementation Sample

- (void)processData {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // 何らかの非同期処理
        sleep(3);  // 擬似的に3秒の処理時間を想定

        // 非同期処理完了後にcompleteProcessメソッドを呼び出す
        [self performSelectorOnMainThread:@selector(completeProcess) withObject:nil waitUntilDone:NO];
    });
}

- (void)completeProcess {
    NSLog(@"非同期処理が完了しました。");
}

@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Sample *sample = [[Sample alloc] init];
        [sample processData];
        sleep(5);  // メインスレッドの処理が終わらないように5秒待機
    }
    return 0;
}

このコードでは、非同期でprocessDataメソッドを実行しており、その完了後にcompleteProcessメソッドを呼び出しています。

非同期処理が完了した後に必要な後処理を実行する場合や、UIの更新など、主要なスレッドでの実行が必要な処理を行いたい場合にこのような方法を取ることができます。

このコードを実行すると、3秒後に「非同期処理が完了しました。」というメッセージが表示されることが確認できます。

○サンプルコード9:selectorを利用したデータの取得

Objective-Cでは、selectorを利用して特定のメソッドからデータを取得することができます。

これは、インスタンス変数に直接アクセスせず、メソッド経由でデータを取得する場合に役立ちます。

ここでは、selectorを利用してデータを取得するサンプルコードを紹介します。

#import <Foundation/Foundation.h>

@interface Person : NSObject
@property (nonatomic, strong) NSString *name;
- (NSString *)getName;
@end

@implementation Person

- (instancetype)init {
    self = [super init];
    if (self) {
        _name = @"Tanaka";
    }
    return self;
}

- (NSString *)getName {
    return _name;
}

@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person *person = [[Person alloc] init];
        SEL selector = @selector(getName);
        NSString *name = [person performSelector:selector];
        NSLog(@"取得した名前: %@", name);
    }
    return 0;
}

このコードでは、PersonクラスにgetNameメソッドが定義されており、このメソッドをselectorを用いて呼び出しています。

この例では、「取得した名前: Tanaka」というメッセージが表示されることが確認できます。

○サンプルコード10:カスタムクラスでのselectorの実装

selectorはObjective-Cの組み込みクラスだけでなく、カスタムクラスでも実装可能です。

これにより、自分自身が定義したメソッドをselectorとして扱うことができます。

ここでは、カスタムクラスでselectorを実装するサンプルコードを紹介します。

#import <Foundation/Foundation.h>

@interface Calculator : NSObject
- (NSInteger)addNumber:(NSInteger)num1 toNumber:(NSInteger)num2;
@end

@implementation Calculator

- (NSInteger)addNumber:(NSInteger)num1 toNumber:(NSInteger)num2 {
    return num1 + num2;
}

@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Calculator *calculator = [[Calculator alloc] init];
        SEL selector = @selector(addNumber:toNumber:);
        NSInteger result = (NSInteger)[calculator performSelector:selector withObject:@(5) withObject:@(3)];
        NSLog(@"計算結果: %ld", (long)result);
    }
    return 0;
}

このコードでは、Calculatorクラス内にaddNumber:toNumber:メソッドを定義し、このメソッドをselectorとして呼び出しています。

この例では、「計算結果: 8」というメッセージが表示されることが確認できます。

●Objective-Cのselectorの注意点と対処法

Objective-Cのselectorを使う際には、注意点としていくつかの重要な要点があります。

ここでは、これらの注意点と、それに対する対処法を詳細に説明します。

○selectorの命名規則

Objective-Cにおけるselectorは、実際にはメソッドの名前そのものです。

そのため、命名の際には次の点に注意する必要があります。

  1. セレクタ名は一意である必要があります。
  2. メソッド名には英数字といくつかの特殊文字(例:コロン「:」)のみを使用することができます。

このコードではシンプルなselectorの命名を表しています。

この例ではdoSomethingというメソッド名をselectorとして利用しています。

// シンプルなselectorの命名例
SEL simpleSelector = @selector(doSomething);

○未定義のselectorのエラーとその対処法

Objective-Cでプログラムを書いていると、未定義のselectorを使用した場合にエラーが発生することがあります。

具体的には、unrecognized selector sent to instanceというエラーメッセージが表示されます。

このエラーは、存在しないメソッドを呼び出している場合に発生します。

対処法としては、次の手順を取ることが考えられます。

  1. メソッドのスペルミスやタイポを確認する。
  2. メソッドが正しく定義されているか確認する。
  3. メソッドが正しいクラスやカテゴリに存在するか確認する。

このコードでは、未定義のselectorを呼び出す例を表しています。

この例では、doSomethingUndefinedという存在しないメソッドを呼び出そうとしています。

// 未定義のselectorを呼び出す例
[self performSelector:@selector(doSomethingUndefined)];

このようなコードを実行すると、上記のunrecognized selector sent to instanceエラーが発生します。

エラーを回避するためには、doSomethingUndefinedというメソッドが正しく定義されていることを確認するか、適切なメソッド名に変更する必要があります。

●Objective-Cのselectorのカスタマイズ方法

Objective-Cのselectorは非常に強力なツールであり、多くの開発者がその基本的な使い方や応用例に慣れ親しんでいます。

しかし、プロジェクトの要求に合わせてselectorをカスタマイズする方法もあります。

ここでは、selectorのカスタマイズ方法について深く掘り下げていきます。

○selectorの拡張

Objective-Cのselectorは、メソッドシグニチャを表す文字列として機能しますが、それをカスタマイズする方法もあります。

例えば、特定のprefixやsuffixを持つメソッドだけを呼び出すためのカスタマイズされたselectorを作成することも可能です。

このコードでは、”custom_”というprefixを持つメソッドだけを呼び出すカスタムselectorを作成するコードを表しています。

この例では、”custom_”というprefixを持つメソッド名を動的に生成し、それをselectorとして利用しています。

#import <objc/runtime.h>

- (SEL)customSelectorWithName:(NSString *)name {
    NSString *customMethodName = [NSString stringWithFormat:@"custom_%@", name];
    return NSSelectorFromString(customMethodName);
}

この関数を使用すると、例えば”print”という文字列を引数として渡すと、”custom_print”というメソッド名を持つselectorが返されます。

このselectorを用いてメソッドを呼び出すことができます。

○外部ライブラリを使ったselectorの拡張

Objective-Cのコミュニティは活発であり、多くの外部ライブラリやフレームワークが提供されています。

その中には、selectorをさらに強力に、かつ柔軟に扱えるようにするものも存在します。

例として、特定のライブラリを使用して、実行時に動的にselectorの挙動を変更する方法を考えてみましょう。

このコードでは、外部ライブラリを用いてselectorの挙動をカスタマイズする方法を表しています。

この例では、特定のメソッドが呼び出される前にログを出力するようにカスタマイズしています。

#import "ExternalLibrary.h"

- (void)setupCustomSelectorBehavior {
    [ExternalLibrary interceptSelector:@selector(targetMethod) withBlock:^{
        NSLog(@"Before calling targetMethod");
    }];
}

- (void)targetMethod {
    // 実際の処理
}

上記のコードを実行すると、targetMethodが呼び出される前に、”Before calling targetMethod”というログがコンソールに出力されます。

まとめ

Objective-Cのselectorは、メソッドを参照するための強力なツールとして多くの開発者に利用されています。

基本的な使用方法から、カスタマイズ方法まで、selectorの活用の幅は非常に広いです。

本記事で紹介したカスタマイズ方法を駆使することで、より柔軟かつ効率的なコードの実装が可能となります。

特に、プロジェクトの要件に応じてselectorの動作をカスタマイズする方法や、外部ライブラリを活用することで、さらに高度な操作が行えることを理解しておくと有益です。

Objective-Cの深い部分を探求する中で、selectorのカスタマイズは避けて通れないテーマと言えるでしょう。