Swift 块的 `objc_method_description.types` 的格式是什么?

问题描述 投票:0回答:1

Swift 编译器为带有块的 swift 方法生成的 Objective-C 方法编码似乎使用了任何地方都没有记录的语法。

例如方法:

func DebugLog2(message: String) async -> String

有方法编码:

v32@0:8@"NSString"16@?24

并且文档中至少有 3 个字符(

"
<
>
)未涵盖:

https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html

NSMethodSignature
类也不喜欢这种编码:

[NSMethodSignature signatureWithObjCTypes:"\"NSString\"16@?<v@?@\"NSString\">24"];

结果:

'+[NSMethodSignaturesignatureWithObjCTypes:]:'"NSString"16@?24''

中不支持类型编码规范 '"'

我尝试查看 Swift 的代码,似乎有一个叫做“扩展方法类型编码”的东西:

https://github.com/apple/swift/blob/c2fc7ee9e72e9a39854548e3202c667e4934dc65/test/IRGen/objc_methods.swift#L13-L14

但是我在试图找出方法类型编码在 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
swift objective-c objective-c-runtime method-signature
1个回答
1
投票

我不相信编码格式是公开记录的。您可以从 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">
© www.soinside.com 2019 - 2024. All rights reserved.