我想知道为什么id
是一个弱引用指针,它如何能够处理任何class
类型的指针,并在运行时如何检测分配给id
的类指针的类型。
我想知道为什么id
是一个弱引用指针,它如何能够处理任何class
类型的指针,并在运行时如何检测分配给id
的类指针的类型。
为什么id是一个弱引用指针?
id
在ARC所有权意义上并不是一个弱引用指针。一个指向对象的id
类型引用是否是弱引用取决于该引用是否被声明为__weak
(以及其它变体)以及对象的类是否实际支持弱引用。
然而,你可以说id
提供了弱类型,尽管我认为动态/鸭子类型是一个更准确的描述。由于id
类型的引用不包含编译时的类类型信息,编译器无法判断底层对象是否能够响应给定的选择器,这可能导致运行时错误。
它如何处理任何类类型的指针?
这是Objective-C语言的定义的一部分。编译器将id
识别为每个Objective-C类的超类型,并对其进行不同的处理。另请参阅下面的答案。
在苹果的Objective-C运行时中,分配给一个对象的内存的前几个字节必须指向该对象的类。你可能会在其他地方看到这被称为在运行时,我们如何检测将哪种类型的类指针分配给id?
isa
指针,这就是苹果运行时找出每个对象的类的方法。id
类型也定义了这些信息。事实上,它唯一的属性就是isa
指针,这意味着所有的Objective-C对象都符合这个定义。id
引用并想发现所引用对象的类,你可以发送-class
消息:id someObject;
// Assign something to someObject
// Log the corresponding class
Class c = [someObject class];
NSLog(@"class = %@", c);
// Test whether the object is of type NSString (or a subclass of NSString)
if ([someObject isKindOfClass:[NSString class]]) {
NSLog(@"it's a string");
}
1标记指针是这种结构的一个显著偏差,正因为它们(部分原因),我们不应该直接访问isa
指针。
拥有通用对象类型是很好的,这样您可以定义可以容纳任何类型对象的集合类型,并且其他通用服务可以处理任何类型的对象而不知道它是什么类型的对象。
没有什么诀窍可以让id起作用。在二进制级别上,所有指针都是可互换的。它们只表示内存地址作为数值。为了使id接受任何类型的指针,只需要禁用编译器通常要求指针类型匹配的规则。
您可以通过以下方式了解id类型变量的类信息:
id theObject = // ... something
Class theClass = [theObject class];
NSString *className = NSStringFromClass(theClass);
NSClassDescription *classDescription = [NSClassDescription classDescriptionForClass:theClass];
但是在代码中很少有必要做那些事情。更常见的情况是,你想测试你的 id 变量是否是某个特定类的实例,如果是,则将其转换为该类并开始将其视为该类型。
if ([theObject isKindOfClass:[MySpecializedClass class]]) {
MySpecializedClass *specialObject = (MySpecializedClass *)theObject;
[specialObject doSomethingSpecial];
}
-class
查找类,但返回的是您不了解的类,则无论如何都不能根据其类处理对象。因此,除非您打算对这些类执行特殊处理,否则没有理由做任何事情,只需检查它是否与您知道的类匹配即可。isMemberOfClass
代替isKindOfClass
。这取决于您是要精确匹配还是包括子类。值得一看的是头文件objc/objc.h
,以查找id
的内部实现。
typedef struct objc_class *Class;
typedef struct objc_object {
Class isa;
} *id;
typedef struct objc_selector *SEL;
typedef id (*IMP)(id, SEL, ...);