performSelector
方法系列are not available in Swift。那么如何在@objc
对象上调用一个方法,在运行时选择要调用的方法,而在编译时不知道? NSInvocation
显然在Swift中也不可用。
我知道在Swift中,您可以将任何方法(对于@objc
方法声明可见)发送到类型AnyObject
,类似于Objective-C中的id
。但是,这仍然需要您在编译时对方法名称进行硬编码。有没有办法在运行时动态选择它?
答案 0 :(得分:17)
使用闭包
class A {
var selectorClosure: (() -> Void)?
func invoke() {
self.selectorClosure?()
}
}
var a = A()
a.selectorClosure = { println("Selector called") }
a.invoke()
请注意,这并不是什么新鲜事,即使在Obj-C中,新的API更喜欢使用performSelector
之上的块(比较UIAlertView
使用respondsToSelector:
和performSelector:
来调用委托方法,使用新的UIAlertController
)。
使用performSelector:
总是不安全,并且不能很好地使用ARC(因此performSelector:
的ARC警告)。
答案 1 :(得分:15)
从Xcode 7开始,swift中提供了完整的performSelector方法系列,包括performSelectorOnMainThread()
和performSelectorInBackground()
。享受!
答案 2 :(得分:11)
使用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)")
}
控制台日志:
问候世界
附属物:阳光
之前发现了这个替代方案,我也在设备和模拟器上进行了测试。我们的想法是使用以下 UIControl :
的方法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")
}
控制台日志:
问候世界
的NSTimer
class func scheduledTimerWithTimeInterval(_ seconds: NSTimeInterval,
target target: AnyObject!,
selector aSelector: Selector,
userInfo userInfo: AnyObject!,
repeats repeats: Bool) -> NSTimer!
答案 3 :(得分:8)
Swift 3
perform(#selector(someSelector), with: nil, afterDelay: 1.0, inModes: [.commonModes])
答案 4 :(得分:7)
根据@JTerry的回答“你不需要Swift中的选择器”,你可以为变量分配实际的方法。我的解决方案如下(我在方法中需要一个参数):
class SettingsMenuItem: NSObject {
...
var tapFunction: ((sender: AnyObject?) -> ())?
}
然后在视图控制器中我以这种方式声明,分配和运行该函数:
class SettingsViewController: UITableViewController {
func editProfile(sender: AnyObject?) {
...
}
...
menuItem.tapFunction = editProfile
...
if let tapFunction = menuItem.tapFunction {
tapFunction(sender: self)
}
}
答案 5 :(得分:6)
你可以在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 :(得分:4)
我也在努力解决这个问题。我终于意识到我不需要使用目标或选择器。对我来说,解决方案是将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();
答案 7 :(得分:4)
Swift 3.1
对于标准的Swift项目,闭包是Sulthan's answer中已涵盖的优雅解决方案。
如果一个依赖于传统的Objective-C代码/库或者想要调用私有API,那么使用选择器字符串名称动态调用方法是有意义的。
只有NSObject
个子类可以接收消息,尝试将其发送到纯Swift类会导致崩溃。
#selector(mySelectorName)
只能在其类源文件中解析类型化的选择器名称
通过牺牲类型检查,可以使用NSSelectorFromString(...)
来检索选择器
(它与 Selector("selectorName:arg:")
相比并不安全,它恰好不会产生警告。
调用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)
还有主线程变体:
instance.performSelector(onMainThread: NSSelectorFromString("selectorArg:"), with: arg, waitUntilDone: false)
正如 https://stackoverflow.com/a/48644264/5329717中 iOS_MIB 所述 这不等同到
DispatchQueue.main.async {
//perform selector
}
和后台线程变体:
instance.performSelector(inBackground: NSSelectorFromString("selectorArg:"), with: arg)
但是有一些限制:
Unmanaged<AnyObject>
因此,当不需要返回结果和值类型参数时,这种低效方法很方便。
获取NSObject
运行时方法IMP
允许使用适当的参数和返回类型进行类型化调用。
@convention(c)(types)->type
允许将IMP
结果转换为兼容的Swift闭包函数。
在@convention(c)
中,并非所有类型都被允许
根据定义,这是 不安全 ,如果操作不正确会导致崩溃和副作用。
C级别的每个Objective-C方法都包含两个符合objc_msgSend(id self, SEL op, ...)
的隐藏参数,这些参数需要包含在函数类型中@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)
这些是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)
限制:
获取运行时静态方法IMP
和处理类型,@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
没有实际的理由这样做,但objc_msgSend
可以动态使用。
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)
NSInvocation
相同(这只是有趣的练习,不要执行此操作)
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)
}
}
答案 8 :(得分:3)
只需添加此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)
}
}
答案 9 :(得分:2)
调度队列的实际语法如下。
dispatch_after(1, dispatch_get_main_queue()) { () -> Void in
self.loadData() // call your method.
}
答案 10 :(得分:2)
有时(特别是如果你使用的是target/action
模式)你可能不得不使用方法-[UIApplication sendAction:to:from:forEvent:]
(对于iOS),所以在Swift中它可能是这样的事情:
UIApplication.sharedApplication()
.sendAction(someSelector, to: someObject, from: antotherObject, forEvent: someEvent)
答案 11 :(得分:2)
我从来没有确切知道,但是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)
答案 12 :(得分:0)
我使用以下解决方案:
// 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()
})
答案 13 :(得分:0)
我有一种情况,其中selector使用来自plist文件的字符串文字构造。 因此,使用下一个代码
解决了在swift中执行某个选择器的最快方法var timer = NSTimer(timeInterval: 1000, target: self, selector: Selector(someString), userInfo: nil, repeats: false)
timer.fire()
timer.invalidate()
答案 14 :(得分:0)
Matej Ukmar&#34;中的一个真实世界的例子。评论&#34; J Terry&#34;回答:
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
答案 15 :(得分:0)
只是该主题的另一个输入。
我有时会不得不“间接”调用函数/方法。示例:为特定单元格调用单个函数。我经常使用结构数组来定义tabelView行为。
我以前使用过 PerformSelector 等,但是在快速程序中它看起来总是“奇怪”,所以我做了一些研究,从那时起,我就使用了间接函数调用。
这是我的操场上用来测试语法和行为的快速示例...(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
有趣的事实:String(输出)的打印速度比带有print的函数调用要慢...这也是一个警告:不要以这种策略相信序列