1.id在objc.h中定义
typedef struct objc_class *Class;
typedef struct objc_object {
Class isa;
} *id;
从上面可以看出,id的本质是struct objc_object结构体指针,可以指向任何OC对象。
注意:这里说的是指向OC对象,像int、NSInteger这些基本数据类型是不可以的,将id指向int会报错

image.png
OC中的基类除了NSObject之外,还有一个NSProxy虚类。因此,id相比NSObject *指向的对象范围要更广。
2.id是动态数据类型,而NSObject *是静态数据类型,默认情况下所有的数据类型都是静态。
id类型的实例在编译阶段不会做类型检查,会在运行时确定,而类NSObject的实例在编译期要做编译检查,保证指针指向是其NSObject类或其子类,当然,实例的具体类型也要在运行时才能确定,这也就是iOS三大特性之一的多肽。-
静态类型在编译时就知道变量的类型,编译时就知道变量的类型,在编译的时候就可以访问对象的属性和方法,如果访问了不属于静态类型的属性和方法,那么编译器就会报错,而动态数据类型在编译的时候并不知道变量的真实类型,只有在运行时的时候才知道它的真实类型,因此编译时候如果访问了不属于动态类型的属性和方法,编译器不会报错,导致运行时的错误,这也是动态数据类型的弊端。
举例如下:#import <Foundation/Foundation.h> NS_ASSUME_NONNULL_BEGIN @interface People : NSObject -(void)eatFood; @end NS_ASSUME_NONNULL_END #import "People.h" @implementation People -(void)eatFood { NSLog(@"eatFood"); } @end #import "ViewController.h" #import "People.h" @interface ViewController () @end @implementation ViewController -(void)drinkWater { NSLog(@"drinkWater"); } - (void)viewDidLoad { [super viewDidLoad]; id test = [People new]; [test drinkWater]; // Do any additional setup after loading the view. } @end
从上面代码我们不难看出,drinkWater方法并不是id指针指向对象的方法,调用了不是自己的方法,但是编译并没有报错,只有运行时才报错了。