SwiftでのperformSelectorの完全な使い方10選

Swift言語のperformSelectorメソッドのイラスト図解Swift
この記事は約24分で読めます。

 

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

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

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

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

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

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

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

はじめに

SwiftはAppleが開発したプログラミング言語であり、iOS、macOS、watchOS、tvOSなどのアプリ開発に広く利用されています。

この記事では、Swiftの中でも特にperformSelectorメソッドに焦点を当てて、その使用方法や注意点、さらに応用例までを詳しく解説します。

このメソッドを使用することで、動的にメソッドを呼び出すことができるようになるため、柔軟なプログラミングが可能となります。

しかし、正確に使いこなすためにはその特性を理解することが重要です。

そこで、本記事では初心者から上級者まで、SwiftでのperformSelectorの使い方を、実用的なサンプルコードを通じて、徹底的に解説します。

今回のガイドでは、Swiftの基礎からperformSelectorの詳細、実用的なサンプルコードを交えた実際の使用例、注意点、カスタマイズ方法に至るまでを網羅しています。

●SwiftとperformSelectorの概要

Swiftとは何か、その特長やperformSelectorの起源と基本的な知識を押さえておきましょう。

これを理解することで、後述する具体的な使用方法や応用例がより理解しやすくなります。

○Swift言語の基礎

Swiftは2014年にAppleによって発表されたプログラミング言語です。

Objective-Cに代わる新しい言語として、より安全性を高め、また書きやすさを追求して設計されました。

静的型付け、強力なエラーハンドリング、高速な実行速度などの特長を持つ一方で、現代のプログラミング言語の良いところを取り入れています。

例えば、オプショナル型やクロージャ、ジェネリクスなどの機能が用意されていることから、多くの開発者に支持されています。

○performSelectorとは

performSelectorは、Swiftだけでなく、元々Objective-Cに存在していたメソッドです。

このメソッドを使用することで、文字列として渡されたセレクタ名を元に、動的にメソッドを呼び出すことができます。

これにより、実行時にメソッドを決定するというような柔軟な動作をプログラムに持たせることが可能となります。

しかし、Swiftは静的型付けの言語であるため、performSelectorを直接利用することは推奨されていません。

その代わり、Swiftでは#selectorという構文を用いて、コンパイル時にセレクタの存在を確認できるようになっています。

この機構を利用することで、performSelectorを安全にSwiftで利用することが可能となります。

●performSelectorの使い方

Swiftでは、Objective-Cの動的な特性を持ち込むための方法としてperformSelectorのようなメソッドが提供されています。

performSelectorを使用することで、動的にメソッドを呼び出すことが可能となります。

しかし、その使用方法や注意点はSwiftの静的な性質と合わせて理解する必要があります。

○サンプルコード1:基本的なperformSelectorの使用法

Swiftにおいて、performSelectorの直接的な使用は推奨されていません。

しかし、Objective-Cのクラスを継承することで、このメソッドを使用することができます。

下記のコードでは、Objective-CのNSObjectクラスを継承したクラス内で、performSelectorを使用してメソッドを動的に呼び出しています。

この例では、greetというメソッドをperformSelectorを使って呼び出しています。

import Foundation

class Greeting: NSObject {
    func greet() {
        print("こんにちは、Swift!")
    }

    func dynamicGreet() {
        self.performSelector(inBackground: #selector(greet), with: nil)
    }
}

let greeting = Greeting()
greeting.dynamicGreet()

このコードでは、GreetingというNSObjectを継承したクラスを作成しています。

その中に、メソッドとしてgreetdynamicGreetを定義しています。

dynamicGreetの中で、performSelector(inBackground:with:)を用いて、動的にgreetメソッドを呼び出しています。

この例では、greetメソッドをバックグラウンドで実行する方法を示しています。

このサンプルコードを実行すると、コンソールに「こんにちは、Swift!」と表示されます。

ただし、この表示は別のスレッドで行われるため、主スレッドの処理が早く終了する場合、表示が遅延する可能性があります。

○サンプルコード2:遅延実行を行うperformSelector

performSelectorには、指定した時間後にメソッドを実行する機能もあります。

この特性を利用することで、遅延実行やタイマーのような機能を実装することができます。

下記のコードは、performSelectorを使って3秒後にメソッドを実行する例を表しています。

import Foundation

class DelayedGreeting: NSObject {
    func greet() {
        print("3秒後の挨拶: こんにちは、Swift!")
    }

    func delayGreet() {
        self.perform(#selector(greet), with: nil, afterDelay: 3.0)
    }
}

let delayedGreeting = DelayedGreeting()
delayedGreeting.delayGreet()

このコードでは、DelayedGreetingクラス内で、greetというメソッドを3秒後に呼び出すdelayGreetメソッドを定義しています。

delayGreetメソッド内で、perform(_:with:afterDelay:)メソッドを用いて、3秒後にgreetメソッドを呼び出しています。

サンプルコードを実行すると、コンソールに「3秒後の挨拶: こんにちは、Swift!」というメッセージが3秒後に表示されます。

○サンプルコード3:引数を伴うperformSelectorの利用法

SwiftでのperformSelectorメソッドは、Objective-C由来のメソッドで、動的なメソッドの呼び出しを行うことができます。

Swiftの型安全性のため、直接performSelectorを使用することは推奨されていませんが、Objective-Cのコードとの互換性や、特定の動的な操作が必要な場面での利用が考えられます。

このコードでは、引数を伴うperformSelectorの使い方を表しています。

この例では、withObjectパラメータを使用して、動的にメソッドを呼び出し、そのメソッドに引数を渡して実行します。

import Foundation

class SampleClass: NSObject {
    @objc func showMessage(message: String) {
        print("受け取ったメッセージ: \(message)")
    }
}

let instance = SampleClass()
let selector = #selector(SampleClass.showMessage(message:))
instance.perform(selector, with: "Hello, Swift!")

このコードでは、SampleClassというクラス内に、showMessageというメソッドを定義しています。

このメソッドは、String型の引数messageを受け取り、それをプリントします。

performSelectorを使用する際、指定するセレクタは#selectorで取得し、その後performメソッドで動的に呼び出します。withパラメータには、メソッドの引数として渡す値を指定します。

このコードを実行すると、”受け取ったメッセージ: Hello, Swift!”という出力が得られるでしょう。

○サンプルコード4:戻り値を受け取るperformSelectorの例

動的なメソッド呼び出しでは、戻り値も取得することが考えられます。

ここでは、performSelectorを使用して、戻り値を取得する方法を紹介します。

import Foundation

class ReturnValueClass: NSObject {
    @objc func fetchNumber() -> Int {
        return 42
    }
}

let instance = ReturnValueClass()
let selector = #selector(ReturnValueClass.fetchNumber)
if let result = instance.perform(selector)?.takeRetainedValue() as? Int {
    print("取得した数値: \(result)")
}

このコードでは、ReturnValueClass内にfetchNumberメソッドを定義しています。

このメソッドは、Int型の数値42を返すシンプルなものです。

メソッドを動的に呼び出した後の戻り値は、takeRetainedValueメソッドを使用して取得します。

得られた結果は、適切な型にキャストして使用します。

このコードを実行すると、”取得した数値: 42″という出力が得られるでしょう。

○サンプルコード5:performSelectorを用いた例外処理

Swiftでは、メソッド呼び出しの際に、指定したセレクタが存在しない場合や、メソッドのシグネチャが一致しない場合に、ランタイムエラーが発生します。

これを避けるために、performSelectorを使用した例外処理の方法を見ていきます。

import Foundation

class ExceptionClass: NSObject {
    @objc func existingMethod() {
        print("このメソッドは存在します。")
    }
}

let instance = ExceptionClass()
let existingSelector = #selector(ExceptionClass.existingMethod)
let nonExistingSelector = NSSelectorFromString("nonExistingMethod")

if instance.responds(to: existingSelector) {
    instance.perform(existingSelector)
} else {
    print("指定したメソッドは存在しません。")
}

if instance.responds(to: nonExistingSelector) {
    instance.perform(nonExistingSelector)
} else {
    print("指定したメソッドは存在しません。")
}

このコードでは、ExceptionClassクラス内にexistingMethodというメソッドを定義しています。

一方で、nonExistingMethodはこのクラス内には定義されていません。

メソッドの存在確認には、responds(to:)メソッドを使用しています。

このメソッドは、指定したセレクタが対象のインスタンスで利用可能かどうかを確認します。

このコードを実行すると、”このメソッドは存在します。”と”指定したメソッドは存在しません。”の2つの出力が得られるでしょう。

●performSelectorの応用例

SwiftのperformSelectorメソッドは、動的にメソッドを呼び出すための手段として用いられます。

ここではその応用例をいくつか挙げ、具体的なサンプルコードを交えて解説していきます。

○サンプルコード6:動的なメソッド呼び出し

このコードではperformSelectorを使って、文字列から動的にメソッド名を生成し、そのメソッドを呼び出すという一連の操作を行っています。

この例では、動的に”printHello”というメソッド名を生成して、該当するメソッドを実行しています。

import Foundation

class DynamicClass {
    @objc func printHello() {
        print("Hello from DynamicClass!")
    }
}

let dynamicInstance = DynamicClass()
let methodName = "printHello"

if dynamicInstance.responds(to: Selector(methodName)) {
    dynamicInstance.perform(Selector(methodName))
}

このコードを実行すると、”Hello from DynamicClass!”というメッセージがコンソールに表示されることとなります。

○サンプルコード7:performSelectorを用いたUIの操作

このコードでは、performSelectorを用いて、UIButtonのクリックイベントをプログラム的に実行する方法を表しています。

この例では、ボタンがクリックされたときに”Button was clicked!”というメッセージを表示します。

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        let button = UIButton(frame: CGRect(x: 100, y: 100, width: 200, height: 50))
        button.setTitle("Click Me", for: .normal)
        button.addTarget(self, action: #selector(buttonClicked), for: .touchUpInside)
        self.view.addSubview(button)

        // ボタンをプログラム的にクリック
        button.perform(#selector(buttonClicked))
    }

    @objc func buttonClicked() {
        print("Button was clicked!")
    }
}

このコードを実行し、アプリが起動された時点で、自動的に”Button was clicked!”というメッセージがコンソールに表示されます。

○サンプルコード8:外部ライブラリやフレームワークとの連携

外部のライブラリやフレームワークに含まれるメソッドを、performSelectorを利用して動的に呼び出す方法を考えます。

ここでは、仮のライブラリ「ExternalLib」を使った例を考えます。

import ExternalLib

class ExternalLibWrapper {
    let externalInstance = ExternalClass()

    func callMethodDynamically(methodName: String) {
        if externalInstance.responds(to: Selector(methodName)) {
            externalInstance.perform(Selector(methodName))
        }
    }
}

このコードでは、ExternalLibからExternalClassを利用し、指定されたメソッド名で動的にメソッドを呼び出しています。

コードを実行すると、ExternalClass内の該当メソッドが実行される結果となります。

○サンプルコード9:マルチスレッド環境でのperformSelectorの使用

Swiftにおいて、マルチスレッドの環境でperformSelectorを使用する際には、performSelectorの異なるバージョンを用いることが求められます。

具体的には、スレッドやキューを指定して実行を行うためのメソッドが用意されています。

このコードでは、performSelectorを使用して別のスレッドで特定のメソッドを実行する例を表しています。

この例では、メインスレッドでの処理を遅延させずに、バックグラウンドでの非同期処理を行っています。

import Foundation

class MultiThreadedSample: NSObject {

    func backgroundMethod() {
        print("このメソッドはバックグラウンドスレッドで実行されます")
    }

    func executeInBackground() {
        self.performSelector(inBackground: #selector(backgroundMethod), with: nil)
    }
}

let sample = MultiThreadedSample()
sample.executeInBackground()

このコードを実行すると、backgroundMethodはバックグラウンドスレッドで実行されます。

このようにperformSelector(inBackground:with:)を用いることで、非同期にメソッドを実行することが可能となります。

このように、SwiftでのperformSelectorはマルチスレッド環境でも非常に便利であり、非同期処理を簡潔に行うことができます。

○サンプルコード10:高度なオブジェクト操作とperformSelector

Swiftでは、performSelectorを使って、動的にメソッドを呼び出すことが可能です。

これにより、高度なオブジェクト操作を行う際の柔軟性が向上します。

このコードでは、performSelectorを使用してオブジェクトのプロパティを動的に変更する例を表しています。

この例では、指定されたプロパティ名に対応するセッターメソッドを動的に呼び出して、オブジェクトのプロパティ値を変更しています。

import Foundation

class AdvancedObject: NSObject {
    var name: String = ""
}

extension AdvancedObject {

    func setProperty(named propertyName: String, value: Any) {
        let setterName = "set\(propertyName.capitalized):"
        if self.responds(to: Selector(setterName)) {
            self.perform(Selector(setterName), with: value)
        }
    }
}

let obj = AdvancedObject()
obj.setProperty(named: "name", value: "Swift")
print(obj.name)  // 出力: Swift

このコードを実行すると、setPropertyメソッドを通じてnameプロパティの値が"Swift"に変更されます。

●注意点と対処法

SwiftでのperformSelectorの使用においては、非常に便利なツールである一方、その使い方や応用において注意しなければならない点がいくつか存在します。

ここでは、performSelectorを安全に、そして効果的に使用するための注意点やその対凡法について解説します。

○Selectorが存在しない場合のエラーハンドリング

performSelectorメソッドを使用する際の一般的なエラーの原因の一つとして、指定したSelectorが実際に存在しない、あるいは誤っているというケースが挙げられます。

このコードでは、存在しないメソッドをSelectorとして指定して呼び出そうとしています。

class SampleClass {
    func existingMethod() {
        print("Method exists!")
    }
}

let instance = SampleClass()
instance.performSelector(Selector("nonExistingMethod"))

この例では、nonExistingMethodというメソッドはSampleClass内に定義されていません。

したがって、上記のコードを実行するとアプリケーションはクラッシュします。

対処法としては、実行前にrespondsToSelectorメソッドを使用して、オブジェクトが特定のSelectorに応答するかどうかを確認することが推奨されます。

if instance.responds(to: Selector("nonExistingMethod")) {
    instance.performSelector(Selector("nonExistingMethod"))
} else {
    print("The method does not exist.")
}

上記のコードは、nonExistingMethodが存在しないことを確認し、その場合にエラーメッセージを出力する形になっています。

○performSelectorのパフォーマンスに関する考慮点

performSelectorは動的にメソッドを実行するため、直接メソッドを呼び出す場合と比較して、パフォーマンスの面で多少のオーバーヘッドが発生する可能性があります。

特に大量のメソッド呼び出しを行うようなシチュエーションでは、このオーバーヘッドが累積し、アプリケーションのパフォーマンスに影響を及ぼす可能性が考えられます。

対処法として、performSelectorの使用は最小限に抑え、可能であれば直接的なメソッド呼び出しを利用することが望ましいです。

もちろん、動的なメソッドの実行が必要な場面では、performSelectorの利用は避けられませんが、その場合でもパフォーマンスのテストや最適化を行うことで、オーバーヘッドを軽減することができます。

○メモリリークを避けるためのテクニック

performSelectorの使用においては、特にメモリ管理に関する注意が必要です。

Objective-Cの時代から、performSelectorを使用する際にメモリリークが発生する可能性があったため、Swiftでも同様のリスクが考えられます。

このコードでは、performSelectorを使用してメソッドを呼び出す際に、引数としてオブジェクトを渡しています。

class SampleClass {
    func methodWithArgument(arg: AnyObject) {
        print(arg)
    }
}

let instance = SampleClass()
let argument = "Argument"
instance.performSelector(Selector("methodWithArgument:"), with: argument)

この例では、argumentという変数を引数としてmethodWithArgumentメソッドに渡しています。

しかし、このような方法でオブジェクトを渡すと、メモリリークのリスクが高まります。

対処法としては、弱参照(weak reference)を使用してオブジェクトを渡すか、またはperformSelectorの使用を避ける方法が考えられます。

弱参照を使用することで、オブジェクトが不要になった際に自動的に解放されるため、メモリリークのリスクを軽減することができます。

●カスタマイズ方法

SwiftでのperformSelectorは非常に柔軟であり、様々なカスタマイズが可能です。

特に、このメソッドをカスタマイズして独自の振る舞いを追加することや、外部ライブラリと組み合わせて更に拡張することが考えられます。

ここでは、これらのカスタマイズ方法について、具体的なサンプルコードを交えて詳細に説明します。

○performSelectorをカスタマイズして独自のメソッドを実装する

performSelectorは基本的には指定したセレクタを実行する機能を提供していますが、これをカスタマイズして独自の動作を加えることができます。

例えば、メソッド実行前後にログを出力するようなカスタマイズを考えてみましょう。

このコードでは、performSelectorWithLoggingという独自のメソッドを実装し、指定したセレクタの前後にログを出力する機能を持つコードを表しています。

この例では、printを用いてログを出力しています。

extension NSObject {
    func performSelectorWithLogging(_ aSelector: Selector) {
        if self.responds(to: aSelector) {
            print("メソッド実行前: \(aSelector)")
            self.perform(aSelector)
            print("メソッド実行後: \(aSelector)")
        }
    }
}

class SampleClass: NSObject {
    @objc func exampleMethod() {
        print("exampleMethodが実行されました。")
    }
}

let sample = SampleClass()
sample.performSelectorWithLogging(#selector(sample.exampleMethod))

上記のサンプルコードを実行すると、次のような出力が得られます。

メソッド実行前: exampleMethod
exampleMethodが実行されました。
メソッド実行後: exampleMethod

このように、performSelectorをベースにしたカスタマイズを行うことで、独自のニーズに合わせた実装が可能となります。

○外部ライブラリとの組み合わせによるカスタマイズ例

Swiftの豊富なライブラリエコシステムを活用することで、performSelectorの機能をさらに拡張することが考えられます。

ここでは、外部ライブラリと組み合わせたカスタマイズ例として、非同期処理ライブラリPromiseKitを利用した例を紹介します。

このコードでは、performSelectorを使用してメソッドを非同期に実行し、その結果をPromiseとして取得するカスタマイズを表しています。

この例では、PromiseKitPromiseを用いて非同期処理の結果を取り扱っています。

import PromiseKit

extension NSObject {
    func performSelectorAsync(_ aSelector: Selector) -> Promise<Void> {
        return Promise { seal in
            if self.responds(to: aSelector) {
                DispatchQueue.global().async {
                    self.perform(aSelector)
                    seal.fulfill(())
                }
            } else {
                seal.reject(NSError(domain: "SelectorError", code: 1, userInfo: nil))
            }
        }
    }
}

class SampleClassAsync: NSObject {
    @objc func asyncExampleMethod() {
        print("asyncExampleMethodが非同期に実行されました。")
    }
}

let sampleAsync = SampleClassAsync()
sampleAsync.performSelectorAsync(#selector(sampleAsync.asyncExampleMethod)).done {
    print("非同期メソッド実行完了")
}

上記のサンプルコードを実行すると、次のような出力が得られると考えられます。

asyncExampleMethodが非同期に実行されました。
非同期メソッド実行完了

このように、外部ライブラリとperformSelectorを組み合わせることで、さらなるカスタマイズが可能となります。

まとめ

SwiftにおけるperformSelectorは、動的なメソッドの実行をサポートする強力なツールです。

本記事では、performSelectorの基本的な使用法から、応用例、注意点、そしてカスタマイズ方法まで、幅広く詳細に解説しました。

初心者から上級者まで、どのレベルの開発者でも、このガイドを通じてperformSelectorのメソッドを完全にマスターすることができるでしょう。

実用的なサンプルコードを豊富に取り上げることで、具体的なシチュエーションでの適切な使用方法やカスタマイズのアイディアも得られることと思います。

特に、Swiftのライブラリエコシステムと組み合わせることで、performSelectorの機能をさらに拡張し、より柔軟なプログラムの作成が可能となります。

これからSwiftでの開発を進める際、performSelectorを効果的に活用し、高品質なアプリケーションやシステムの開発を目指しましょう。