use*_*008 46 performselector swift
Swift中没有这类performSelector方法.那么如何在一个对象上调用一个方法,在运行时选择要调用的方法,而在编译时不知道?显然在Swift中也没有.@objcNSInvocation
我知道在Swift中,你可以向类型发送任何方法(@objc方法声明可见)AnyObject,类似于idObjective-C.但是,这仍然需要您在编译时对方法名称进行硬编码.有没有办法在运行时动态选择它?
Sul*_*han 18
使用闭包
class A {
var selectorClosure: (() -> Void)?
func invoke() {
self.selectorClosure?()
}
}
var a = A()
a.selectorClosure = { println("Selector called") }
a.invoke()
Run Code Online (Sandbox Code Playgroud)
请注意,这并不是什么新鲜事,即使在Obj-C中,新的API更喜欢使用块performSelector(比较UIAlertView使用respondsToSelector:和performSelector:调用委托方法,使用新的UIAlertController).
使用performSelector:总是不安全,并且不能很好地与ARC一起使用(因此ARC警告performSelector:).
Fiz*_*uzz 15
从Xcode 7开始,完整的performSelector方法系列在Swift中可用,包括performSelectorOnMainThread()和performSelectorInBackground().请享用!
vla*_*f81 12
使用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)")
}
Run Code Online (Sandbox Code Playgroud)
控制台日志:
问候世界
附着物:阳光
之前发现了这个替代方案,我也在设备和模拟器上进行了测试.我的想法是使用以下UIControl方法:
func sendAction(_ action: Selector, to target: AnyObject!, forEvent event: UIEvent!)
Run Code Online (Sandbox Code Playgroud)
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")
}
Run Code Online (Sandbox Code Playgroud)
控制台日志:
问候世界
的NSTimer
class func scheduledTimerWithTimeInterval(_ seconds: NSTimeInterval,
target target: AnyObject!,
selector aSelector: Selector,
userInfo userInfo: AnyObject!,
repeats repeats: Bool) -> NSTimer!
Run Code Online (Sandbox Code Playgroud)
斯威夫特3
perform(#selector(someSelector), with: nil, afterDelay: 1.0, inModes: [.commonModes])
根据@JTerry的回答"你不需要Swift中的选择器",你可以为变量分配实际的方法.我的解决方案如下(我在方法中需要一个参数):
class SettingsMenuItem: NSObject {
...
var tapFunction: ((sender: AnyObject?) -> ())?
}
Run Code Online (Sandbox Code Playgroud)
然后在视图控制器中我以这种方式声明,分配和运行该函数:
class SettingsViewController: UITableViewController {
func editProfile(sender: AnyObject?) {
...
}
...
menuItem.tapFunction = editProfile
...
if let tapFunction = menuItem.tapFunction {
tapFunction(sender: self)
}
}
Run Code Online (Sandbox Code Playgroud)
小智 6
你可以在Swift中使用它
var timer = NSTimer.scheduledTimerWithTimeInterval(0.1, target: self, selector: Selector("someSelector"), userInfo: nil, repeats: false)
func someSelector() {
// Something after a delay
}
Run Code Online (Sandbox Code Playgroud)
通过这个,您可以执行Objective-C中的performSelector执行的操作
Swift 3.1
对于标准的Swift项目,闭包是Sulthan的答案中已经涵盖的优雅解决方案。如果选择器字符串名称取决于旧的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)
Run Code Online (Sandbox Code Playgroud)
还具有主线程变体:
instance.performSelector(onMainThread: NSSelectorFromString("selectorArg:"), with: arg, waitUntilDone: false)
Run Code Online (Sandbox Code Playgroud)
如 iOS_MIB 在 /sf/answers/3405098511/中所述, 这不等同于
DispatchQueue.main.async {
//perform selector
}
Run Code Online (Sandbox Code Playgroud)
和后台线程变体:
instance.performSelector(inBackground: NSSelectorFromString("selectorArg:"), with: arg)
Run Code Online (Sandbox Code Playgroud)
但是有一些限制:
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)
Run Code Online (Sandbox Code Playgroud)
这些是静态等效项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)
Run Code Online (Sandbox Code Playgroud)
局限性:
获取运行时静态方法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
Run Code Online (Sandbox Code Playgroud)
没有实际的理由,但是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)
Run Code Online (Sandbox Code Playgroud)
相同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)
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
48531 次 |
| 最近记录: |