与 Objective-C 运行时交互时,选择器名称似乎允许包含“非法”字符。人们可以使用这种选择器来添加新方法并以代码中不可能的方式调用它们。
我举个例子:
final class Test: NSObject {
@objc func test() {
print("This is Sparta!")
}
}
// Xcode indeed shows a warning "String literal is not a valid Objective-C selector"
let selector = Selector("invalid? !@#$%^&*()")
// let's add a new method, having the same implementation as an existing one
let testMethod = class_getInstanceMethod(Test.self, #selector(Test.test))!
let added = class_addMethod(Test.self, selector, method_getImplementation(testMethod), "v@:")
print("class_addMethod result: \(added)")
// let's see if calling the method works
Test().perform(selector)
上面的代码创建了一个具有无效(?)名称的选择器,使用该选择器向测试类添加一个新方法,然后调用该方法。当然,不能通过点符号直接调用该方法,因为它们会得到编译器错误,但是,可以通过
performSelector
调用新添加的方法。
令我惊讶的是,上面的代码生成以下输出:
class_addMethod result: true
This is Sparta!
这意味着我们可以在运行时定义方法,其名称如果写在类声明中会产生编译错误。
我找不到任何支持此行为的文档,直观地说,因为选择器名称是裸字符串,动态方法查找应该适用于任何类型的字符串。
这是预期的行为吗?如果是,选择器名称的内容是否有任何限制(长度除外)?
在 Objetive-C 语法中
@selector
名称是 Identifiers
标识符 Objective-C 标识符是用于标识变量、函数或任何其他用户定义项的名称。标识符以字母 A 到 Z 或 a 到 z 或下划线 _ 开头,后跟零个或多个字母、下划线和数字(0 到 9)。
Objective-C 不允许在标识符中使用标点符号,例如 @、$ 和 %。 Objective-C 是一种区分大小写的编程语言。因此,Manpower 和 manpower 在 Objective-C 中是两个不同的标识符。以下是可接受的标识符的一些示例:
myname50 _temp j a23b9 retVal
https://www.tutorialspoint.com/objective_c/objective_c_basic_syntax.htm
这是预期的行为吗?如果是,选择器名称的内容是否有任何限制(长度除外)?
我认为这是预期的行为; Obj-c 运行时保留一个选择器的哈希表,任何可以在 NSString 中编码并进行哈希处理的内容都应该可以工作,因此有效标识符的检查是在编译时而不是运行时进行的。我不会依赖这种行为。