web-dev-qa-db-ja.com

SwiftのperformSelectorの代替?

メソッドのperformSelectorファミリー Swiftでは使用できません 。それでは、@objcオブジェクトのメソッドをどのように呼び出すことができますか。呼び出されるメソッドは実行時に選択され、コンパイル時には不明です。 NSInvocationはSwiftでも利用できないようです。

Swiftでは、Objective-CのAnyObjectと同様に、id型に(@objcメソッド宣言が表示されている)任意のメソッドを送信できることを知っています。ただし、それでもコンパイル時にメソッド名をハードコーディングする必要があります。実行時に動的に選択する方法はありますか?

45
user102008

クロージャーを使用する

class A {
    var selectorClosure: (() -> Void)?

    func invoke() {
        self.selectorClosure?()
    }
}

var a = A()
a.selectorClosure = { println("Selector called") }
a.invoke()

これは新しいものではないことに注意してください。Obj-Cでも、新しいAPIはperformSelectorよりもブロックの使用を好みます(respondsToSelector:performSelector:を使用してデリゲートメソッドを呼び出すUIAlertViewと比較新しいUIAlertController)。

performSelector:を使用することは常に安全ではなく、ARCとうまく機能しません(したがって、performSelector:に対するARCの警告)。

18
Sulthan

Xcode 7では、performSelectorOnMainThread()performSelectorInBackground()を含むperformSelectorメソッドの完全なファミリがSwiftで利用可能です。楽しい!

15
FizzBuzz

アプローチA

つかいます NSThread.detachNewThreadSelector、このアプローチの良い点は、メッセージにオブジェクトを添付できることです。 ViewControllerのサンプルコード:

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.

    let delay = 2.0 * Double(NSEC_PER_SEC)
    var time = dispatch_time(DISPATCH_TIME_NOW, Int64(delay))
    dispatch_after(time, dispatch_get_main_queue(), {
        NSThread.detachNewThreadSelector(Selector("greetings:"), toTarget:self, withObject: "sunshine")
        })
}

func greetings(object: AnyObject?) {
    println("greetings world")
    println("attached object: \(object)")
}

コンソールログ:

あいさつ世界

添付オブジェクト:サンシャイン

アプローチB

この代替案は以前に発見されており、デバイスとシミュレーターでもテストしました。アイデアは、次のメソッドを使用することですIControl

func sendAction(_ action: Selector, to target: AnyObject!, forEvent event: UIEvent!)

ViewControllerのサンプルコード:

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.

    var control: UIControl = UIControl()
    control.sendAction(Selector("greetings"), to: self, forEvent: nil) // Use dispatch_after to invoke this line as block if delay is intended 
}

func greetings() {
    println("greetings world")
}

コンソールログ:

あいさつ世界

アプローチC

NSTimer

class func scheduledTimerWithTimeInterval(_ seconds: NSTimeInterval,
                                      target target: AnyObject!,
                                 selector aSelector: Selector,
                                  userInfo userInfo: AnyObject!,
                                    repeats repeats: Bool) -> NSTimer!
12
vladof81

Swift

perform(#selector(someSelector), with: nil, afterDelay: 1.0, inModes: [.commonModes])

8
Magoo

@JTerryの回答「Swiftではセレクターは必要ありません」に従って、実際のメソッドを変数に割り当てることができます。私の解決策は次のとおりでした(メソッドに1つのパラメーターが必要でした)。

class SettingsMenuItem: NSObject {
    ...
    var tapFunction: ((sender: AnyObject?) -> ())?
}

そして、View Controllerで、この方法で関数を宣言、割り当て、実行します。

class SettingsViewController: UITableViewController {

    func editProfile(sender: AnyObject?) {
        ...
    }

    ...

    menuItem.tapFunction = editProfile

    ...

    if let tapFunction = menuItem.tapFunction {
        tapFunction(sender: self)
    }


}
7
Matej Ukmar

これはSwiftで使用できます

var timer = NSTimer.scheduledTimerWithTimeInterval(0.1, target: self, selector:  Selector("someSelector"), userInfo: nil, repeats: false)


func someSelector() {
// Something after a delay
}

これにより、Objective-CのperformSelectorによって実行されることを実行できます。

6
user3721424

私もこれに苦労していました。最終的に、ターゲットやセレクターを使用する必要がないことに気付きました。私にとっての解決策は、funcを変数に割り当て、その変数を呼び出すことでした。他のクラスから呼び出す場合でも機能します。以下に簡単な例を示します。

func Apple() ->Int
{
    let b = 45;
    return b;
}

func Orange()->Int
{
    let i = 5;
    return i;
}

func Peach()
{
    var a = Apple; // assign the var a the Apple function
    var b = Orange; // assisgn the var b to the Orange function

    let c = a(); // assign the return value of calling the 'a' or Apple function to c
    let d = b(); // assign the return value of calling the 'b' or Orange function d

    Pear(a, b)
}

func Pear(x:()->Int, y:()->Int)->Int
{
    let w = (x()+y()); // call the x function, then the y function and add the return values of each function.
    return w; // return the sum
}

Peach();
4
JTerry

ええと、目的のメソッドを公開するためにスウィズリングを使用できます!

このextensionを追加し、すべての呼び出しの前に????シンボル。

import Foundation

private var dispatchOnceToken: dispatch_once_t = 0

private var selectors: [Selector] = [
    "performSelector:",
    "performSelector:withObject:",
    "performSelector:withObject:withObject:",
    "performSelector:withObject:afterDelay:inModes:",
    "performSelector:withObject:afterDelay:",
]

private func swizzle() {
    dispatch_once(&dispatchOnceToken) {
        for selector: Selector in selectors {
            let ????selector = Selector("????\(selector)")
            let method = class_getInstanceMethod(NSObject.self, selector)

            class_replaceMethod(
                NSObject.self,
                ????selector,
                method_getImplementation(method),
                method_getTypeEncoding(method)
            )
        }
    }
}

extension NSObject {

    func ????performSelector(selector: Selector) -> AnyObject? {
        swizzle()
        return self.????performSelector(selector)
    }

    func ????performSelector(selector: Selector, withObject object: AnyObject?) -> AnyObject? {
        swizzle()
        return self.????performSelector(selector, withObject: object)
    }

    func ????performSelector(selector: Selector, withObject object1: AnyObject?, withObject object2: AnyObject?) -> AnyObject? {
        swizzle()
        return self.????performSelector(selector, withObject: object1, withObject: object2)
    }

    func ????performSelector(selector: Selector, withObject object: AnyObject?, afterDelay delay: NSTimeInterval, inModes modes: [AnyObject?]?) {
        swizzle()
        self.????performSelector(selector, withObject: object, afterDelay: delay, inModes: modes)
    }

    func ????performSelector(selector: Selector, withObject object: AnyObject?, afterDelay delay: NSTimeInterval) {
        swizzle()
        self.????performSelector(selector, withObject: object, afterDelay: delay)
    }

}
3

ディスパッチキューの実際の構文は次のとおりです。

dispatch_after(1, dispatch_get_main_queue()) { () -> Void in
        self.loadData() // call your method.
    }
2
Prakash Raj

いつからか正確にはわかりませんが、AppleはXcode 7.1.1でperformSelectorを復活させました(少なくとも私が使用しているバージョンです)。

現在作成しているアプリでは、CoreAnimator(素晴らしいアプリ、BTW)から生成されたUIViewで同様のfunctionNamesを使用してさまざまな関数を呼び出しているため、performSelectorが非常に役立ちます。使用方法は次のとおりです。

//defines the function name dynamically.  the variables "stepN" and "dir" are defined elsewhere. 
let AnimMethod = "addStep\(stepN)\(dir)Animation"

//prepares the selector with the function name above           
let selector: Selector = NSSelectorFromString(AnimMethod)

//calls the said function in UIView named "meter"            
meter.performSelector(selector)
2
LeftySwift

ときどき(特にtarget/actionパターンを使用している場合)-[UIApplication sendAction:to:from:forEvent:]メソッド(iOSの場合)を使用する必要がある場合があるため、Swiftでは次のようになります。

UIApplication.sharedApplication()
    .sendAction(someSelector, to: someObject, from: antotherObject, forEvent: someEvent)
2

そのトピックの別の入力です。

時々、関数/メソッドを「間接的に」呼び出さなければなりませんでした。例:特定のセルの個々の関数を呼び出す。 tabelViewの動作を定義するために、構造体の配列をよく使用します。

以前はPerformSelectorなどを使用していましたが、Swiftプログラムでは常に "奇妙"に見えるため、いくつかの研究を行った後、間接関数呼び出しを使用しました。

これは、構文と動作をテストするための私の遊び場の簡単な例です...(xCode 9.4.1)

// Test for indirect function calls

// ------------------------------------------------------------------------
// functions we want to call inderectly
func function1() {
    print("function1 active")
}

func function2() {
    print("function2 active")
}

func function3() {
    print("function3 active")
}

func function4(_ parameter: Int)  {

    print("function4 use the parameter: \(parameter)")
}


// ------------------------------------------------------------------------
// data structures

// a struct to build array items
struct functionCallTestStruct {

    // struct properties
    let what: String            // a string as an example for other variables
    let functionToCall : ()     // the function as an array element
    var functionWithParameter : (Int) -> () // the function as an array element
    let parameterForFunction : Int


    // Initializer
    init(_ what: String,
         _ functionToCall: (),
         _ functionWithParameter: @escaping (Int) -> (),
         _ parameterForFunction: Int) {

        self.what = what
        self.functionToCall = functionToCall
        self.functionWithParameter = functionWithParameter
        self.parameterForFunction = parameterForFunction
    }
}

// the array which holds the functions we want to call
let functionTestArray : [functionCallTestStruct] = [

    functionCallTestStruct("We will call the first function",  function1(), function4(_:), 10),
    functionCallTestStruct("We will call the second function", function2(), function4(_:), 11),
    functionCallTestStruct("We will call the third function",  function3(), function4(_:), 12),
]

// ------------------------------------------------------------------------
// Test program

// a loop over the array
for i in 0 ..< functionTestArray.count {

    // print explanation (be aware: print is quite lame, .. see the output ;-))
    print(functionTestArray[i].what)

    // and with this we indirectly call the functions
    functionTestArray[i].functionToCall
    let myParameter = functionTestArray[i].parameterForFunction
    functionTestArray[i].functionWithParameter(myParameter)
}

出力を与えます:

function1 active
function2 active
function3 active
We will call the first function
function4 use the parameter: 10
We will call the second function
function4 use the parameter: 11
We will call the third function
function4 use the parameter: 12

面白い事実:文字列の印刷(何)は、関数がprintで呼び出すよりも遅い...これも警告です:この戦術でシーケンスを信頼しないでください

0
Hardy_Germany

Swiftの "Matej Ukmar's"コメントから "J Terry's"回答への実世界の例:

class Button {
    var title:String = "The big button"
    var selector: ((sender: AnyObject?, type:String) -> ())?/*this holds any method assigned to it that has its type signature*/
    func click(){
        selector!(sender: self,type: "click")/*call the selector*/
    }
    func hover(){
        selector!(sender: self,type: "hover")/*call the selector*/
    }
}
class View {
    var button = Button()
    init(){
        button.selector = handleSelector/*assign a method that will receive a call from the selector*/
    }
    func handleSelector(sender: AnyObject?,type:String) {
        switch type{
            case "click": Swift.print("View.handleSelector() sender: " + String(sender!.dynamicType) + ", title: " + String((sender as! Button).title) + ", type: " + type)
            case "hover": Swift.print("View.handleSelector() sender: " + String(sender!.dynamicType) + ", title: " + String((sender as! Button).title) + ", type: " + type)
            default:break;
        }
    }
}
let view:View = View()
view.button.click()/*Simulating button click*/
view.button.hover()/*Simulating button hover*/
//Output: View.handleSelector() sender: Button, title: The big button, type: click
//Output: View.handleSelector() sender: Button, title: The big button, type: hover
0
eonist

セレクターがplistファイルからの文字列リテラルで構築される状況があります。 Swiftでセレクターを実行する最速の方法は次のコードで解決されました

var timer = NSTimer(timeInterval: 1000, target: self, selector: Selector(someString), userInfo: nil, repeats: false)
timer.fire()
timer.invalidate()
0
Modo Ltunzher

私は次のソリューションを使用しています:

// method will be called after delay
func method1() {

    ......    
}

// to replace performSelector
// delay 100 ms
let time : dispatch_time_t = dispatch_time(DISPATCH_TIME_NOW, Int64(NSEC_PER_MSEC/(USEC_PER_SEC*10)))
dispatch_after(time, dispatch_get_main_queue(), {
        self.method1()
})
0
Bagusflyer