[英]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
的实例。 例如,这是NSObject
或id
别名的样子:
// "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.