Swift 编译器为带有块的 swift 方法生成的 Objective-C 方法编码似乎使用了任何地方都没有记录的语法。
例如方法:
func DebugLog2(message: String) async -> String
有方法编码:
v32@0:8@"NSString"16@?
24
并且文档中至少有 3 个字符(
"
、<
和 >
)未涵盖:
NSMethodSignature
类也不喜欢这种编码:
[NSMethodSignature signatureWithObjCTypes:"\"NSString\"16@?<v@?@\"NSString\">24"];
结果:
'+[NSMethodSignaturesignatureWithObjCTypes:]:'"NSString"16@?
中不支持类型编码规范 '"'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)")
}
}
打印:
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
我不相信编码格式是公开记录的。您可以从 ASTContext 源中计算出大部分内容。 Mattt 的类型编码博客文章总结得很好:
那么我们从对 Objective-C 类型编码的新理解中获得了什么?老实说,没有那么多(除非你正在做任何疯狂的元编程)。
但正如我们从一开始就说过的,追求破译秘密信息是有智慧的。
我同意,这是一个值得追求的目标。只是不要指望这在 Swift 中那么有用。
回答这个具体情况:
v32@0:8@"NSString"16@?
24
正如您从之前的研究中知道的那样,这些数字意义不大,所以我们不会担心这些。
语法
@"..."
是一个对象,其类名用引号括起来。您可以在Type::ObjCObjectPointer
案例中发现:
8501 S += '@';
8502 if (OPT->getInterfaceDecl() &&
8503 (FD || Options.EncodingProperty() || Options.EncodeClassNames())) {
8504 S += '"';
8505 S += OPT->getInterfaceDecl()->getObjCRuntimeNameAsString();
8506 for (const auto *I : OPT->quals()) {
8507 S += '<';
8508 S += I->getObjCRuntimeNameAsString();
8509 S += '>';
8510 }
8511 S += '"';
8512 }
语法
@?<...>
是一个块指针,如Type::BlockPointer
案例中详细描述:
8401 case Type::BlockPointer: {
8402 const auto *BT = T->castAs<BlockPointerType>();
8403 S += "@?"; // Unlike a pointer-to-function, which is "^?".
8404 if (Options.EncodeBlockParameters()) {
8405 const auto *FT = BT->getPointeeType()->castAs<FunctionType>();
8406
8407 S += '<';
8408 // Block return type
8409 getObjCEncodingForTypeImpl(FT->getReturnType(), S,
8410 Options.forComponentType(), FD, NotEncodedT);
8411 // Block self
8412 S += "@?";
8413 // Block parameters
8414 if (const auto *FPT = dyn_cast<FunctionProtoType>(FT)) {
8415 for (const auto &I : FPT->param_types())
8416 getObjCEncodingForTypeImpl(I, S, Options.forComponentType(), FD,
8417 NotEncodedT);
8418 }
8419 S += '>';
8420 }
8421 return;
8422 }
这是编码如下:
void f(NSString *, void (^)(NSString *))
这就是
async
方法转换为 ObjC 的方式:添加一个接受返回值的完成处理程序。
为了好玩,您可以将其扩展为
async throws
函数:
func DebugLog2(message: String) async throws -> String
结果将是:
v32@0:8@"NSString"16@?<v@?@"NSString"@"NSError">24
相当于:
void f(NSString *, void (^)(NSString *, NSError *))
顺便说一下,NSMethodSignature 绝对可以解析结果。您刚刚将其更改为无效字符串(您删除了返回类型):
const char *types = "v32@0:8@\"NSString\"16@?<v@?@\"NSString\">24";
NSMethodSignature *signature = [NSMethodSignature signatureWithObjCTypes:types];
printf("%s\n", [signature getArgumentTypeAtIndex:3]);
// Outputs: @?<v@?@"NSString">