Rol*_*nge 6 objective-c objective-c-runtime method-signature swift
Swift 编译器为带有块的 swift 方法生成的 Objective-C 方法编码似乎使用了任何地方都没有记录的语法。
例如方法:
func DebugLog2(message: String) async -> String
Run Code Online (Sandbox Code Playgroud)
有方法编码:
v32@0:8@"NSString"16@?<v@?@"NSString">24
并且文档中至少有三个字符("、<和)未涵盖:>
该类NSMethodSignature也不喜欢这种编码:
[NSMethodSignature signatureWithObjCTypes:"\"NSString\"16@?<v@?@\"NSString\">24"];
Run Code Online (Sandbox Code Playgroud)
结果是:
'+[NSMethodSignaturesignatureWithObjCTypes:]: '"NSString"16@?<v@?@"NSString">24'' 中不支持类型编码规范 '"'
我尝试查看 Swift 的代码,似乎有一种叫做“扩展方法类型编码”的东西:
但我在试图找出 Swift 代码库中方法类型编码生成的位置时迷失了方向。
不回答此问题的相关问题:
示例代码:
decodeProto()
@objc
class TestClass : NSObject {
@objc
func DebugLog(message: String) -> String {
print(message)
return message
}
@objc
func DebugLog2(message: String) async -> String {
do {
try await Task.sleep(nanoseconds: 1_000_000_000)
} catch {}
print(message);
return message;
}
}
func decodeProto() {
var methodCount : UInt32 = 1
let methodList = class_copyMethodList(TestClass.self, UnsafeMutablePointer<UInt32>(&methodCount))!
for i in 0..<Int(methodCount) {
let method = methodList[i];
let desc = method_getDescription (method);
let name = desc.pointee.name!
let types = String(validatingUTF8: desc.pointee.types!)!
print("Selector: \(name) Description: \(types)")
}
}
Run Code Online (Sandbox Code Playgroud)
印刷:
Selector: DebugLog2WithMessage:completionHandler: Description: v32@0:8@"NSString"16@?<v@?@"NSString">24
Selector: DebugLogWithMessage: Description: @24@0:8@16
Selector: init Description: @16@0:8
Run Code Online (Sandbox Code Playgroud)
但为什么我需要这个?
我需要计算堆栈帧的最大大小,为了做到这一点,我解析方法编码。我需要计算最小大小的原因是因为我在调用实际的 objc_msgSend 之前拦截对 objc_msgSend 的调用以添加 Objective-C 异常处理,所以它类似于:
Selector: DebugLog2WithMessage:completionHandler: Description: v32@0:8@"NSString"16@?<v@?@"NSString">24
Selector: DebugLogWithMessage: Description: @24@0:8@16
Selector: init Description: @16@0:8
Run Code Online (Sandbox Code Playgroud)
请注意,不可能在 C 中创建通用拦截方法,因此我在汇编代码中完成了它。通用拦截代码需要为原始参数(堆栈帧)分配空间并复制,为了做到这一点,我解析方法编码以找出每个参数有多大。
计数不必精确,只需要最大值,因此将所有参数所需的空间相加并分配那么多堆栈空间就可以了,无需减去寄存器中传递的任何参数的大小。
我不相信编码格式是公开记录的。您可以从ASTContext源中计算出大部分内容。Mattt 的类型编码博客文章总结得很好:
\n\n\n那么,我们从对 Objective-C 类型编码的新理解中获得了什么?老实说,没有那么多(除非你\xe2\x80\x99正在做任何疯狂的元编程)。
\n但正如我们从一开始就说过的,追求破译秘密信息是有智慧的。
\n
我同意,这是一个值得追求的目标。只是不要指望这在 Swift 中那么有用。
\n针对这个具体情况回答一下:
\n\n\nv32@0:8@"NSString"16@?<v@?@"NSString">24
\n
正如您从之前的研究中知道的那样,这些数字并没有多大意义,因此我们不会担心这些。
\n语法@"..."是一个对象,其类名用引号括起来。您可以在Type::ObjCObjectPointer:
8501 S += \'@\';\n 8502 if (OPT->getInterfaceDecl() &&\n 8503 (FD || Options.EncodingProperty() || Options.EncodeClassNames())) {\n 8504 S += \'"\';\n 8505 S += OPT->getInterfaceDecl()->getObjCRuntimeNameAsString();\n 8506 for (const auto *I : OPT->quals()) {\n 8507 S += \'<\';\n 8508 S += I->getObjCRuntimeNameAsString();\n 8509 S += \'>\';\n 8510 }\n 8511 S += \'"\';\n 8512 }\nRun Code Online (Sandbox Code Playgroud)\n语法@?<...>是块指针,详细信息请参见Type::BlockPointer:
8401 case Type::BlockPointer: {\n 8402 const auto *BT = T->castAs<BlockPointerType>();\n 8403 S += "@?"; // Unlike a pointer-to-function, which is "^?".\n 8404 if (Options.EncodeBlockParameters()) {\n 8405 const auto *FT = BT->getPointeeType()->castAs<FunctionType>();\n 8406 \n 8407 S += \'<\';\n 8408 // Block return type\n 8409 getObjCEncodingForTypeImpl(FT->getReturnType(), S,\n 8410 Options.forComponentType(), FD, NotEncodedT);\n 8411 // Block self\n 8412 S += "@?";\n 8413 // Block parameters\n 8414 if (const auto *FPT = dyn_cast<FunctionProtoType>(FT)) {\n 8415 for (const auto &I : FPT->param_types())\n 8416 getObjCEncodingForTypeImpl(I, S, Options.forComponentType(), FD,\n 8417 NotEncodedT);\n 8418 }\n 8419 S += \'>\';\n 8420 }\n 8421 return;\n 8422 }\nRun Code Online (Sandbox Code Playgroud)\n这是编码的内容如下:
\n- (void)m:(NSString *)s completion:(void (^)(NSString *))c;\nRun Code Online (Sandbox Code Playgroud)\n就是这样async方法被转换为 ObjC 的方式:添加一个接受返回值的完成处理程序。
为了好玩,您可以将其扩展为一个async throws函数:
func DebugLog2(message: String) async throws -> String\nRun Code Online (Sandbox Code Playgroud)\n结果将是:
\nv32@0:8@"NSString"16@?<v@?@"NSString"@"NSError">24\nRun Code Online (Sandbox Code Playgroud)\n这相当于:
\n- (void)m:(NSString *)s completion:(void (^)(NSString *, NSError *))c;\nRun Code Online (Sandbox Code Playgroud)\n(我应该清楚,当我在这里说“等效”时,我并不是说它实际上与调用method_getTypeEncoding这些方法相同。method_getTypeEncoding不返回扩展编码。您会得到v32@0:8@16@?24这两个: void 返回方法,接受一个对象 (@) 和一个块 (@?)。但这些是 Swift 编码所描述的方法。)
顺便说一句,NSMethodSignature 绝对可以解析结果。您刚刚将其更改为无效字符串(您删除了返回类型):
\nconst char *types = "v32@0:8@\\"NSString\\"16@?<v@?@\\"NSString\\">24";\nNSMethodSignature *signature = [NSMethodSignature signatureWithObjCTypes:types];\nprintf("%s\\n", [signature getArgumentTypeAtIndex:3]);\n\n// Outputs: @?<v@?@"NSString">\nRun Code Online (Sandbox Code Playgroud)\n
| 归档时间: |
|
| 查看次数: |
133 次 |
| 最近记录: |