swift performselector

¿Alternativa para realizarSelector en Swift?



performselector (16)

Enfoque A

Use NSThread.detachNewThreadSelector , lo bueno de este enfoque es que podemos adjuntar un objeto al mensaje. Código de ejemplo en 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)") }

Registro de consola:

saludos mundo

objeto adjunto: sol

Enfoque B

Esta alternativa fue descubierta anteriormente, también he probado en dispositivos y simuladores. La idea es usar el siguiente método de UIControl :

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

Código de ejemplo en 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") }

Registro de consola:

saludos mundo

Enfoque C

NSTimer

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

La familia de métodos performSelector no está disponible en Swift . Entonces, ¿cómo se puede llamar a un método en un objeto @objc , donde el método a ser llamado se elige en el tiempo de ejecución, y no se conoce en tiempo de compilación? NSInvocation aparentemente no está disponible en Swift.

Sé que en Swift, puede enviar cualquier método (para el cual exista una declaración del método @objc visible) al tipo AnyObject , similar a id en Objective-C. Sin embargo, eso todavía requiere que codifique el nombre del método en tiempo de compilación. ¿Hay alguna forma de elegirlo dinámicamente en tiempo de ejecución?


A partir de Xcode 7, la familia completa de métodos performSelector está disponible en Swift, incluyendo performSelectorOnMainThread() y performSelectorInBackground() . ¡Disfrutar!


A veces (especialmente si está utilizando target/action patrón de target/action ) puede que tenga que usar el método -[UIApplication sendAction:to:from:forEvent:] (para iOS), por lo que en Swift puede haber -[UIApplication sendAction:to:from:forEvent:] como esta:

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


Estoy usando la siguiente solución:

// 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() })


Huh, podemos usar Swizzling para revelar los métodos deseados.

Simplemente agregue esta extension y prefija todas las llamadas con el símbolo 🚀 .

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) } }


No sé exactamente desde cuándo, pero Apple trajo de vuelta performSelector en Xcode 7.1.1 (Al menos esa es la versión que estoy usando).

En mi aplicación que estoy construyendo actualmente, estoy llamando a varias funciones con funciones similares en una UIView generada desde CoreAnimator (gran aplicación, BTW), así que performSelector es muy útil. Así es como lo uso:

//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)


Puedes usar esto en Swift

var timer = NSTimer.scheduledTimerWithTimeInterval(0.1, target: self, selector: Selector("someSelector"), userInfo: nil, repeats: false) func someSelector() { // Something after a delay }

con esto puedes hacer lo que realiza performSelector en Objective-C


Según la respuesta de @JTerry "No necesitas selectores en Swift", puedes asignar métodos reales a las variables. Mi solución fue la siguiente (necesitaba un parámetro en el método):

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

Y luego, en el controlador de vista, declaré, asigné y ejecuté la función de esta manera:

class SettingsViewController: UITableViewController { func editProfile(sender: AnyObject?) { ... } ... menuItem.tapFunction = editProfile ... if let tapFunction = menuItem.tapFunction { tapFunction(sender: self) } }


Tengo una situación, donde selector se construye con string literal que proviene de un archivo plist. Por lo tanto, la forma más rápida de realizar algún selector en forma rápida se resolvió con el siguiente código

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


Un ejemplo del mundo real a toda velocidad del comentario de "Matej Ukmar" a la respuesta de "J Terry":

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


Usando cierres

class A { var selectorClosure: (() -> Void)? func invoke() { self.selectorClosure?() } } var a = A() a.selectorClosure = { println("Selector called") } a.invoke()

Tenga en cuenta que esto no es nada nuevo, incluso en Obj-C, las nuevas API prefieren usar bloques sobre performSelector (compare UIAlertView que usa respondsToSelector: y performSelector: para llamar a métodos delegados, con el nuevo UIAlertController ).

Usar performSelector: siempre es inseguro y no funciona bien con ARC (de ahí las advertencias de ARC para performSelector: .


Yo estaba luchando con esto también. Finalmente me di cuenta de que no necesitaba usar objetivos o selectores. Para mí, la solución fue asignar el func a una variable y llamar a esa variable. Incluso funciona si lo llamas desde otras clases. Aquí hay un ejemplo rápido:

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();


la sintaxis real para la cola de despacho es la siguiente.

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


solo otra entrada para ese tema.

De vez en cuando tenía que llamar funciones / métodos "indirectamente". Ejemplo: llamar funciones individuales para celdas específicas. A menudo utilizo matrices de estructuras para definir el comportamiento de tabelView.

Utilicé PerformSelector, etc., pero eso siempre parece "extraño" en un programa rápido, así que investigué un poco y, desde entonces, utilicé las llamadas a funciones indirectas.

Este es un ejemplo rápido de mi patio de recreo para probar la sintaxis y el comportamiento ... (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) }

da la salida:

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

hecho curioso: la impresión de la cadena (qué) es más lenta que la llamada a la función con una impresión ... que también es una advertencia: no confíe en la secuencia con esta táctica


Swift 3.1
Para los proyectos estándar de Swift, los cierres son una solución elegante que ya está cubierta en la respuesta de Sulthan . Invocar métodos dinámicamente usando nombres de cadenas de selector tiene sentido si uno depende del código / bibliotecas Objective-C heredado.

Solo NSObject subclases de NSObject pueden recibir mensajes, e intentar enviar uno a una clase Swift pura dará como resultado un bloqueo.

#selector(mySelectorName) puede resolver los nombres de los selectores tipeados en su archivo fuente de clase solamente.
Al sacrificar la verificación de tipos, se puede recuperar un selector usando NSSelectorFromString(...)
( no es más seguro en comparación con Selector("selectorName:arg:") simplemente no genera una advertencia ).

Llamando NSObject método de instancia de la subclase NSObject

let instance : NSObject = fooReturningObjectInstance() as! NSObject instance.perform(#selector(NSSelectorFromString("selector")) instance.perform(#selector(NSSelectorFromString("selectorArg:"), with: arg) instance.perform(#selector(NSSelectorFromString("selectorArg:Arg2:"), with: arg, with: arg2)

también con la variante de hilo principal:

instance.performSelector(onMainThread: NSSelectorFromString("selectorArg:"), with: arg, waitUntilDone: false)

Como lo señala iOS_MIB en https://.com/a/48644264/5329717 esto no es equivalente a

DispatchQueue.main.async { //perform selector }

y variante de hilo de fondo:

instance.performSelector(inBackground: NSSelectorFromString("selectorArg:"), with: arg)

Sin embargo, hay algunas limitaciones:

  • Solo puede tomar 0-2 argumentos
  • los argumentos de tipo de valor como enteros y selectores no funcionan
  • no puede manejar tipos de valores devueltos
  • devuelve objetos como Unmanaged<AnyObject>

Por lo tanto, este enfoque de bajo esfuerzo es conveniente cuando los argumentos de resultado de devolución y tipo de valor no son necesarios.

NSObject método de tiempo de ejecución NSObject IMP permite realizar una llamada tipeada con los argumentos adecuados y el tipo de retorno. @convention(c)(types)->type permite convertir el resultado de IMP a la función de cierre de Swift compatible.

En @convention(c) no se permiten todos los tipos

  • Para las clases usa Any or AnyClass
  • Para objetos, use Cualquiera o el tipo de clase exacto si su símbolo está disponible
  • Para tipos de valor use el tipo relevante
  • Para void * use OpaquePointer

Esto es, por definición, inseguro y, si se hace incorrectamente, provocará bloqueos y efectos secundarios.

Cada método de Objective-C en el nivel C contiene dos argumentos ocultos para ajustarse a objc_msgSend(id self, SEL op, ...) que deben incluirse en el tipo de función como @convention(c)(Any?,Selector, ... )

let instance : NSObject = fooReturningObjectInstance() as! NSObject let selector : Selector = NSSelectorFromString("selectorArg:") let methodIMP : IMP! = instance.method(for: selector) unsafeBitCast(methodIMP,to:(@convention(c)(Any?,Selector,Any?)->Void).self)(instance,selector,arg)

Estos son equivalentes estáticos de perform(...)

NSObject.perform(NSSelectorFromString("selector")) NSObject.perform(NSSelectorFromString("selectorArg:"), with: arg) NSObject.perform(NSSelectorFromString("selectorArg:Arg2:"), with: arg, with: arg2) NSObject.performSelector(onMainThread: NSSelectorFromString("selectorArg:"), with: arg, waitUntilDone: false) NSObject.performSelector(inBackground: NSSelectorFromString("selectorArg:"), with: arg)

Limitaciones

  • Todos los problemas de tipo mencionados anteriormente
  • La clase de receptor debe tener un símbolo definido

Obtención del método estático de tiempo de ejecución IMP y tipos de manejo, se @convention(c)

let receiverClass = NSClassFromString("MyClass") let selector : Selector = NSSelectorFromString("selectorArg:") let methodIMP : IMP! = method_getImplementation(class_getClassMethod(receiverClass, selector)) let result : NSObject = unsafeBitCast(methodIMP,to:(@convention(c)(AnyClass?,Selector,Any?)->Any).self)(receiverClass,selector,arg) as! NSObject

No hay ninguna razón práctica para hacerlo, pero objc_msgSend se puede usar de forma dinámica.

let instance : NSObject = fooReturningObjectInstance() as! NSObject let handle : UnsafeMutableRawPointer! = dlopen("/usr/lib/libobjc.A.dylib", RTLD_NOW) let selector : Selector = NSSelectorFromString("selectorArg:") unsafeBitCast(dlsym(handle, "objc_msgSend"), to:(@convention(c)(Any?,Selector!,Any?)->Void).self)(instance,selector,arg) dlclose(handle)

Lo mismo para NSInvocation (esto es solo ejercicio divertido, no lo hagas)

class Test : NSObject { var name : String? { didSet { NSLog("didSetCalled") } } func invocationTest() { let invocation : NSObject = unsafeBitCast(method_getImplementation(class_getClassMethod(NSClassFromString("NSInvocation"), NSSelectorFromString("invocationWithMethodSignature:"))),to:(@convention(c)(AnyClass?,Selector,Any?)->Any).self)(NSClassFromString("NSInvocation"),NSSelectorFromString("invocationWithMethodSignature:"),unsafeBitCast(method(for: NSSelectorFromString("methodSignatureForSelector:"))!,to:(@convention(c)(Any?,Selector,Selector)->Any).self)(self,NSSelectorFromString("methodSignatureForSelector:"),#selector(setter:name))) as! NSObject unsafeBitCast(class_getMethodImplementation(NSClassFromString("NSInvocation"), NSSelectorFromString("setSelector:")),to:(@convention(c)(Any,Selector,Selector)->Void).self)(invocation,NSSelectorFromString("setSelector:"),#selector(setter:name)) var localName = name withUnsafePointer(to: &localName) { unsafeBitCast(class_getMethodImplementation(NSClassFromString("NSInvocation"), NSSelectorFromString("setArgument:atIndex:")),to:(@convention(c)(Any,Selector,OpaquePointer,NSInteger)->Void).self)(invocation,NSSelectorFromString("setArgument:atIndex:"), OpaquePointer($0),2) } invocation.perform(NSSelectorFromString("invokeWithTarget:"), with: self) } }


Swift 3

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