繁体   English   中英

如何将 Class object 转换为符合协议

[英]How to cast Class object to conformance witih protocol

我正在尝试将 Class object 转换为某个协议,该协议定义了 class 实现的 class 方法 (+)。

我知道如何使用 (id< protocol>) 执行此操作,如该问题中所述,但我似乎无法找出 Class 个对象的正确方法。

基本场景如下。

我有一个协议:

@protocol Protocol <NSObject>
+ (id)classMethod:(id)arg;
@end

然后我有一个 function 接受 Class object,它知道有时符合基于另一个参数的协议(这显然非常简化):

- (id)someMethodWithClass:(Class)cls andUseArg:(BOOL)arg
{
    id instance;
    if (arg != nil) {
        instance = [(Class<Protocol>)cls classMethod:arg];
    }
}

现在我没有收到任何警告,它看起来对我来说是正确的。 (我不会在任何情况下看到任何错误,因为我可以保证如果 arg.= nil 那么 class 符合。)

但是,我没有在 Xcode 中自动完成,这让我想知道这是否是正确的方法。 有什么想法吗? (请注意,我对实例是 id<Protocol> 不感兴趣。)

如果要确定cls是否符合特定协议(并假设classMethod:是该协议的必需类方法),您可以简单地:

- (id)someMethodWithClass:(Class)cls andUseArg:(BOOL)arg
{
    id instance;
    if ([cls conformsToProtocol:@protocol(Protocol)]) {
        instance = [cls classMethod:arg];
    }

    return instance;
}

或者,只需查看它是否响应特定的类方法选择器:

- (id)someMethodWithClass:(Class)cls andUseArg:(BOOL)arg
{
    id instance;
    if ([cls respondsToSelector:@selector(classMethod:)]) {
        instance = [cls classMethod:arg];
    }

    return instance;
}

这个问题已有 11 年历史, Rob 的回答没有任何问题,但我发现不幸的是它的核心部分(使用协议对Class object 进行类型转换是否是正确的语法)从未得到适当的关注。

首先 static 输入 Objective-C 是非常人为的事情,它的存在仅仅是为了编译器发出警告(甚至不是错误)。 让我们从Class对象真正是什么开始——如果你看一下文档,你会发现Class类型实际上是objc_class *类型的别名:

typedef struct objc_class *Class;

您可以在Apple 的 objc 运行时库的源代码中找到objc_class类型的定义:

// inherits objc_object with some adjustments
struct objc_class : objc_object { ... }

如您所见, objc_class只是objc_object的扩展。 任何 Objective-C class 实际上都是这个objc_object的实例。 例如,这是NSObjectid别名的样子:


// "translation" of an Objective-C class declaration
typedef struct objc_object NSObject;

// the same for `id` type but with the pointer type included
typedef struct objc_object *id;

这意味着 Objective-C 中不存在“静态类型”,实例的“类型”是通过对给定实例的内省(不同类型的元信息objc_object存储)发生的。 它使所有 Objective-C 类彼此兼容(首先 - 因为它是一个指针,其次 - 因为它是指向同一结构的指针)。 例如你可以这样写代码:

Class obj = [NSObject new];

..它会很高兴地编译。

然而,这种语言的纯粹动态特性使其非常容易出错,暴露出程序员可能犯的各种错误。 为了避免clang 实际上确实对指定类型进行编译时检查,但它纯粹依赖于程序员为实例类型提供正确的数据,如果从 Objective-C 的角度来看这些类型不兼容,则编译器可以发出一个警告你。 这适用于实例对象,但不幸的是,Objective-C 中没有语法来键入 class object,而不是Class别名。 这意味着对于编译器来说,所有这些对象在编译期间都是无法区分的。

所有这一切都适用于协议类型。 在这里我的意思是,当您将协议一致性标记添加到变量类型 ( id<TDWLoadable> var ) 时,您只需要求编译器检查分配给变量 object 的值是否符合给定的协议:

@protocol TDWLoadable

+ (void)classMethod;
- (void)instanceMethod;

@end

@interface TDWObject : NSObject
@end

// Initializing '__strong id<TDWLoadable>' with an expression of incompatible type 'TDWObject *'
id<TDWLoadable> loadable = [TDWObject new];

然而,对于 class object,相同的检查将被忽略,因为无法键入Class对象:

Class<TDWLoadable> loadable = [[TDWObject new] class];

此行为在 Objective-C 编程语言(强调我的)的协议部分的类型检查部分中进行了描述:

...声明

id <Formatting> anObject;

将所有符合 Formatting 协议的对象归为一个类型,而不管它们在 class 层次结构中的位置。 编译器可以确保只将符合协议的对象分配给该类型。

在每种情况下,类型都将相似的对象组合在一起——要么是因为它们共享一个公共的 inheritance,要么是因为它们集中在一组公共的方法上。

这两种类型可以组合在一个声明中:

 Formatter <Formatting> *anObject;

协议不能用于键入 class 对象 只有实例可以静态类型化为协议,就像只有实例可以静态类型化为 class 一样。(但是,在运行时,类和实例都会响应 conformsToProtocol conformsToProtocol:消息。)

另外,如果我们考虑到objc_class实际上只是objc_object的扩展,那么有两个 kind 表达式:

  • Class<TDWLoadable> classObj;
  • id<TDWLoadable> obj;

应该遵循相同的契约(即+ (void)classMethod必须引用classObj的元类, - (void)instanceMethod必须引用 class object 本身)。


话虽如此,由于语法基本上没有效果,只是被编译器忽略,您可以自由地为Class<Protocol>类型制定自己的约定。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM