Objective-C 面向对象(一)——对象的本质
栏目: Objective-C · 发布时间: 5年前
内容简介:对于一门编程语言而言,当初学者掌握了其基本语法和标准库的使用以后,如果能够继续了解该语言的核心思想与底层实现,则会渐入佳境,窥探语言实现的本质技术,能够为以后的性能优化以及规避技术陷阱等复杂工作提供思路。 了解Objective-C语言的面向对象的本质,有益于程序员更加深刻理解该语言是如何实践OOP思想,并使得在构建面向对象程序的时候更加游刃有余。Objective-C是C语言的超集,也就是说,C语言的语法全部被Objective-C兼容,而面向对象的特性则是建立在C语言的基础之上,当熟悉过C语言的指针、
对于一门编程语言而言,当初学者掌握了其基本语法和标准库的使用以后,如果能够继续了解该语言的核心思想与底层实现,则会渐入佳境,窥探语言实现的本质技术,能够为以后的性能优化以及规避技术陷阱等复杂工作提供思路。 了解Objective-C语言的面向对象的本质,有益于 程序员 更加深刻理解该语言是如何实践OOP思想,并使得在构建面向对象程序的时候更加游刃有余。
背景知识
Objective-C是 C语言 的超集,也就是说,C语言的语法全部被Objective-C兼容,而面向对象的特性则是建立在C语言的基础之上,当熟悉过C语言的指针、内存管理、自定义数据-结构体等一系列知识以后,对于Objective-C的面向对象实现的理解,就容易多了,因为本质上,Objective-C的面向对象,就是使用这些东西构建出来的。 我们需要了解的是,对于C语言来说,除了语言本身定义的数据类型,程序员想要自定义数据类型以供编程使用,结构体是必然选择,基于这样的事实,那么理应能够猜到,Objective-C中的一切面向对象概念,诸如类、对象等,都是基于C语言的结构体之上构建的,而如何进行对象方法的调用、类方法调用等等,则通过Objective-C从smalltalk借鉴过来的消息调用思想而实现的Runtime思想,后者是消息调用思想的鼻祖。
什么是类和对象
C语言是没有面向对象概念的,只有基本数据类型、指针、结构体等等。那么如何通过这些概念构建面向对象的概念,要明白这个的前提是大体总结一下对象和类有什么特点。
类
类是描述一个对象规格的模板,即它说明了一个对象有什么样的属性,有什么样的方法。对象的构建,通过指定类,并且初始化,从而得到类的实例-对象,那么也就是说类是一种描述实例对象的数据结构。 在Objective-C中,标准库为Foundation,事实上几乎所有的类都继承与NSObject,那么类具体有如下表现
- 类具有方法和类方法的声明,描述对象实例有什么方法和类有什么方法
- 类具有属性的声明,描述对象实例有什么样的属性
- 类可以被集成或集成其他类,从而给他人提供或从父类获取对象描述的规格信息
对象
对象是一个根据类实例化出来的数据结构。具有实例方法,实例变量,对象没有继承概念,只有持有其他对象或被其他对象持有,具有以下特点。
- 对象具有实例方法
- 对象具有实例属性
- 对象可以被其他对象持有或持有其他对象
类和对象的实现
既然类和对象只不过是特点不同的自定义数据类型,那么类和对象必然要使用结构体实现,Objetcive-C也是这样设计的,我们看一下NSObject的定义:
*@interface* NSObject <NSObject> { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wobjc-interface-ivars" Class isa OBJC_ISA_AVAILABILITY; #pragma clang diagnostic pop } + (void)load; + (void)initialize; - (instancetype)init #if NS_ENFORCE_NSOBJECT_DESIGNATED_INITIALIZER NS_DESIGNATED_INITIALIZER #endif ; + (instancetype) new OBJC_SWIFT_UNAVAILABLE("use object initializers instead"); + (instancetype)allocWithZone:(struct _NSZone )zone OBJC_SWIFT_UNAVAILABLE("use object initializers instead"); *@end* 复制代码
NSObject的定义中,有类方法定义、属性定义、实例方法定义,如何使用C语言的结构体来表达和存储这样的自定义数据结构呢?NSObject是一个Class也就是一个类,在描述中有一个Class isa的变量,按图索骥查找到Class的数据结构:
typedef struct objc_class *Class; //class是一个objc_class结构体的指针 struct objc_class { Class isa OBJC_ISA_AVAILABILITY; #if !__OBJC2__ Class super_class OBJC2_UNAVAILABLE; const char *name OBJC2_UNAVAILABLE; long version OBJC2_UNAVAILABLE; long info OBJC2_UNAVAILABLE; long instance_size OBJC2_UNAVAILABLE; struct objc_ivar_list *ivars OBJC2_UNAVAILABLE; struct objc_method_list **methodLists OBJC2_UNAVAILABLE; struct objc_cache *cache OBJC2_UNAVAILABLE; struct objc_protocol_list *protocols OBJC2_UNAVAILABLE; #endif } OBJC2_UNAVAILABLE; //objc_class是一个结构体,描述了一个类的信息 复制代码
从上面定义可以看出来,一个类包含哪些信息,是通过objc_class 结构体来表示的,NSObject的定义中,有一个Class isa属性,而Class是一个指向objc_class结构体的指针,也就是说,NSObject通过isa指针来寻找到其类的信息所在的结构体。 该结构体中有几个比较重要的变量:
- Class isa OBJC_ISA_AVAILABILITY; 又是一个指向objc_class结构体的指针,指向另外一个类信息的结构体,那么一个类指向一个说明其规格的类结构体,其意义是来描述类的信息,一般称描述类的结构的数据类型称之为元类,即meta-class,以为着使用元类来描述类的规格,那么从对象与类的关系类比中,可以将类看作是元类的实例,也就说,元类是类对象的类。
- super_class 是该类父类的信息,使用super_class指针,找到父类信息的结构体。NSObject的实例对象的superclass为null
- long info OBJC2_UNAVAILABLE; 类信息,供运行期使用的一些位标识
- long instance_size OBJC2_UNAVAILABLE; /该类的实例变量大小
- struct objc_ivar_list *ivars OBJC2_UNAVAILABLE; 类的成员变量链表
- struct objc_method_list methodLists OBJC2_UNAVAILABLE; 方法定义的链表
- struct objc_cache *cache OBJC2_UNAVAILABLE; 方法缓存,对象接到一个消息会根据isa指针查找消息对象,这时会在methodLists中遍历,如果cache了,常用的方法调用时就能够提高调用的效率。
- struct objc_protocol_list *protocols OBJC2_UNAVAILABLE; 协议链表
类的实例->对象也是通过一个objc_class结构体描述其结构。如下:
struct objc_object { Class isa OBJC_ISA_AVAILABILITY; }; typedef struct objc_object *id; 复制代码
id 类型,即对象,其为一个指向objc_object结构体的指针,意味着任意一个Objective-C的对象,其本质是一个指向objc_object的结构体指针,而objc_object结构体中,有一个isa指针,指向objc_class结构体,来描述其属于哪个类,也就是上面的类信息结构体。 当定义一个类的时候,如下:
//main.m @interface ClassA : NSObject @property(nonatomic,copy)NSString * name; -(void)sayHello; +(void)SayHello; @end @implementation ClassA -(void)sayHello{ NSLog(@"object say hello"); } +(void)SayHello{ NSLog(@"class say hello"); } @end int main(int argc, const char * argv[]) { @autoreleasepool { NSLog(@"Hello, World!"); NSObject * obj1 = [[NSObject alloc]init]; ClassA * obj2 = [[ClassA alloc]init]; [obj2 sayHello]; [ClassA SayHello]; NSLog(@"%@", NSStringFromClass([obj1 superclass])); } return 0; } 复制代码
从上面的定义中,按照之前的说明,将该文件转换为C++代码,将在C++代码中得到确切的信息。
# 得到main.cpp文件 clang -rewrite-objc main.m 复制代码
对于NSObject这个类,可以得到:
#ifndef _REWRITER_typedef_NSObject #define _REWRITER_typedef_NSObject typedef struct objc_object NSObject; typedef struct {} _objc_exc_NSObject; #endif 复制代码
NSObject是一个objc_object结构体。 ClassA的结构如下:
typedef struct objc_object ClassA; 复制代码
ClassA是一个指向objc_object结构体,其相关的其他部分为:
#ifndef _REWRITER_typedef_ClassA #define _REWRITER_typedef_ClassA typedef struct objc_object ClassA; typedef struct {} _objc_exc_ClassA; #endif extern "C" unsigned long OBJC_IVAR_$_ClassA$_name; struct ClassA_IMPL { struct NSObject_IMPL NSObject_IVARS; NSString *_name; }; // @property(nonatomic,copy)NSString * name; // -(void)sayHello; // +(void)SayHello; /* @end */ // @implementation ClassA static void _I_ClassA_sayHello(ClassA * self, SEL _cmd) { NSLog((NSString *)&__NSConstantStringImpl__var_folders_dn_6w6g4h112csgf73k_bz07xpr0000gn_T_main_08dee3_mi_0); } static void _C_ClassA_SayHello(Class self, SEL _cmd) { NSLog((NSString *)&__NSConstantStringImpl__var_folders_dn_6w6g4h112csgf73k_bz07xpr0000gn_T_main_08dee3_mi_1); } static NSString * _I_ClassA_name(ClassA * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_ClassA$_name)); } extern "C" __declspec(dllimport) void objc_setProperty (id, SEL, long, id, bool, bool); static void _I_ClassA_setName_(ClassA * self, SEL _cmd, NSString *name) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct ClassA, _name), (id)name, 0, 1); } // @end 复制代码
这个结构中,清楚地描述出了ClassA类中的实例变量、类方法、实例方法的结构和实现。 ClassA是一个objc_object结构体,其类方法和静态方法在声明以后,被使用在如下:
extern "C" unsigned long int OBJC_IVAR_$_ClassA$_name __attribute__ ((used, section ("__DATA,__objc_ivar"))) = __OFFSETOFIVAR__(struct ClassA, _name); static struct /*_ivar_list_t*/ { unsigned int entsize; // sizeof(struct _prop_t) unsigned int count; struct _ivar_t ivar_list[1]; } _OBJC_$_INSTANCE_VARIABLES_ClassA __attribute__ ((used, section ("__DATA,__objc_const"))) = { sizeof(_ivar_t), 1, {{(unsigned long int *)&OBJC_IVAR_$_ClassA$_name, "_name", "@\"NSString\"", 3, 8}} }; static struct /*_method_list_t*/ { unsigned int entsize; // sizeof(struct _objc_method) unsigned int method_count; struct _objc_method method_list[3]; } _OBJC_$_INSTANCE_METHODS_ClassA __attribute__ ((used, section ("__DATA,__objc_const"))) = { sizeof(_objc_method), 3, {{(struct objc_selector *)"sayHello", "v16@0:8", (void *)_I_ClassA_sayHello}, {(struct objc_selector *)"name", "@16@0:8", (void *)_I_ClassA_name}, {(struct objc_selector *)"setName:", "v24@0:8@16", (void *)_I_ClassA_setName_}} }; static struct /*_method_list_t*/ { unsigned int entsize; // sizeof(struct _objc_method) unsigned int method_count; struct _objc_method method_list[1]; } _OBJC_$_CLASS_METHODS_ClassA __attribute__ ((used, section ("__DATA,__objc_const"))) = { sizeof(_objc_method), 1, {{(struct objc_selector *)"SayHello", "v16@0:8", (void *)_C_ClassA_SayHello}} }; static struct /*_prop_list_t*/ { unsigned int entsize; // sizeof(struct _prop_t) unsigned int count_of_properties; struct _prop_t prop_list[1]; } _OBJC_$_PROP_LIST_ClassA __attribute__ ((used, section ("__DATA,__objc_const"))) = { sizeof(_prop_t), 1, {{"name","T@\"NSString\",C,N,V_name"}} }; 复制代码
OBJC _INSTANCE_METHODS_ClassA , OBJC _PROP_LIST_ClassA为该类的类方法、属性、实例方法等定义的结构体,这些结构体的被用到在:
static struct _class_ro_t _OBJC_CLASS_RO_$_ClassA __attribute__ ((used, section ("__DATA,__objc_const"))) = { 0, __OFFSETOFIVAR__(struct ClassA, _name), sizeof(struct ClassA_IMPL), (unsigned int)0, 0, "ClassA", (const struct _method_list_t *)&_OBJC_$_INSTANCE_METHODS_ClassA, 0, (const struct _ivar_list_t *)&_OBJC_$_INSTANCE_VARIABLES_ClassA, 0, (const struct _prop_list_t *)&_OBJC_$_PROP_LIST_ClassA, }; 复制代码
OBJC_CLASS_RO $_ClassA 结构体变量将类方法、属性等结构体进行包装,其数据类型_class_ro_t如下:
struct _class_ro_t { unsigned int flags; unsigned int instanceStart; unsigned int instanceSize; unsigned int reserved; const unsigned char *ivarLayout; const char *name; const struct _method_list_t *baseMethods; const struct _objc_protocol_list *baseProtocols; const struct _ivar_list_t *ivars; const unsigned char *weakIvarLayout; const struct _prop_list_t *properties; }; 复制代码
该结构体其实就是objc_class结构体的变形,代表根类的结构体类型。 OBJC_CLASS_RO $_ClassA又被另外一个结构体进行包装:
extern "C" __declspec(dllexport) struct _class_t OBJC_CLASS_$_ClassA __attribute__ ((used, section ("__DATA,__objc_data"))) = { 0, // &OBJC_METACLASS_$_ClassA, 0, // &OBJC_CLASS_$_NSObject, 0, // (void *)&_objc_empty_cache, 0, // unused, was (void *)&_objc_empty_vtable, &_OBJC_CLASS_RO_$_ClassA, }; 复制代码
OBJC_CLASS_$_ClassA 则就是ClassA这个类的结构体,其结构体是_class_t。
struct _class_t { struct _class_t *isa; struct _class_t *superclass; void *cache; void *vtable; struct _class_ro_t *ro; }; 复制代码
OBJC_CLASS_ _ClassA两个结构体被用在:
static void OBJC_CLASS_SETUP_$_ClassA(void ) { OBJC_METACLASS_$_ClassA.isa = &OBJC_METACLASS_$_NSObject; OBJC_METACLASS_$_ClassA.superclass = &OBJC_METACLASS_$_NSObject; OBJC_METACLASS_$_ClassA.cache = &_objc_empty_cache; OBJC_CLASS_$_ClassA.isa = &OBJC_METACLASS_$_ClassA; OBJC_CLASS_$_ClassA.superclass = &OBJC_CLASS_$_NSObject; OBJC_CLASS_$_ClassA.cache = &_objc_empty_cache; } 复制代码
这是一个类初始化函数,从函数中得到明确的信息是:
- OBJC_METACLASS_ _NSObject结构体,其类型为_class_ro_t(objc_class)的结构体
- OBJC_METACLASS_ _NSObject结构体指针
- OBJC_CLASS_ _ClassA
- OBJC_CLASS_ _NSObject根类数据结构 至此,可以非常清晰得出以下结论: 如下图所示:
- ClassA的实例对象,是一个objc_object结构体指针,其isa指针指向ClassA类的objc_object结构体
- ClassA,是一个_class_t(即objc_class)的结构体,其isa指针指向MetaClassA结构体
- ClassA的父类,是NSObject,其superclass是一个指向NSObject类对象的isa指针
- ClassA的元类,是MetaClassA,OBJC_METACLASS_元类结构体表明,其isa指针指向MetaNSObject结构体指针
- 一个类所拥有的方法、属性,都会存储在类的元类中,当调用对象的方法的时候,也就是向对象发送消息,runtime会在这个对象所属的类方法列表中查找消息对应的方法,但向类发送消息的时候,runtime就会在这个类的meta class的方法列表中查找。 更加通用和清晰的关系图如下:
即:
- 对象的isa指针指向类对象
- 对象的superclass的指针指向父类类对象
- 类对象的isa指针指向元类
- 类对象的superclass的指针指向父类元类
- 元类的isa指针,指向根类(NSObject)元类
- 元类的superclass指针指向父类元类,直接继承根类的类的元类的superclass指向根类元类(NSObject)
- 根类(NSObject)的isa,指向根元类
- 根类(NSObject)的superclass为nil
- 根元类的superclass指向NSObject类
- 根元类的isa指针,指向自身
类与对象的相关信息
name
const char * class_getName(Class cls); 复制代码
super_class和meta-class
//获取父类 Class class_getSuperclass(Class cls); //判定类是否为一个meta class BOOL class_isMetaClass(Class cls); 复制代码
instance_size
size_t class_gerInstanceSize(Class cls); 复制代码
objc_ivar_list与objc_method
//objc_ivar_list结构体存储objc_ivar数组列表 struct objc_ivar_list { int ivar_count OBJC2_UNAVAILABLE; #ifdef __LP64__ int space OBJC2_UNAVAILABLE; #endif /* variable length structure */ struct objc_ivar ivar_list[1] OBJC2_UNAVAILABLE; } OBJC2_UNAVAILABLE; //objc_method_list结构体存储着objc_method的数组列表 struct objc_method_list { struct objc_method_list *obsolete OBJC2_UNAVAILABLE; int method_count OBJC2_UNAVAILABLE; #ifdef __LP64__ int space OBJC2_UNAVAILABLE; #endif /* variable length structure */ struct objc_method method_list[1] OBJC2_UNAVAILABLE; } 复制代码
objc_ivar_list 为成员变量单向链表,其结构体最后一个成员变量是一个objc_ivar结构体数组,该数组为变长,所以objc_ivar_list可以是一个变长结构体,objc_ivar标示一个成员变量:
struct objc_ivar { char * _Nullable ivar_name OBJC2_UNAVAILABLE;//变量名 char * _Nullable ivar_type OBJC2_UNAVAILABLE;//变量类型 int ivar_offset OBJC2_UNAVAILABLE; #ifdef __LP64__ int space OBJC2_UNAVAILABLE; #endif } OBJC2_UNAVAILABLE; 复制代码
objc_method_list为方法列表
struct objc_method_list { struct objc_method_list * _Nullable obsolete OBJC2_UNAVAILABLE; int method_count OBJC2_UNAVAILABLE; #ifdef __LP64__ int space OBJC2_UNAVAILABLE; #endif /* variable length structure */ struct objc_method method_list[1] OBJC2_UNAVAILABLE; } 复制代码
objc_method为方法结构体,如下:
struct objc_method { SEL _Nonnull method_name OBJC2_UNAVAILABLE; char * _Nullable method_types OBJC2_UNAVAILABLE; IMP _Nonnull method_imp OBJC2_UNAVAILABLE; } 复制代码
IMP与SEL
typedef struct *SEL; #if !OBJC_OLD_DISPATCH_PROTOTYPES typedef void (*IMP)(void /* id, SEL, ... */ ); #else typedef id _Nullable (*IMP)(id _Nonnull, SEL _Nonnull, ...); #endif 复制代码
IMP是一个void * ()的函数指针,实际上就是方法的实现,SEL为一个char * 字符串。 每一个objc_class结构体中都有objc_method_list列表,而objc_method_list列表中有objc_method结构体,该结构体为一个SEL对应一个IMP实现。 在runtime运行的时候,加载的每一个类对应有一个virtual table,用来缓存SEL与IMP的对应关系,从而能够通过SEL快速找到其对应实现。
成员变量(ivars)及其属性
//成员变量操作函数 // 获取类中指定名称实例成员变量的信息 Ivar class_getInstanceVariable ( Class cls, const char *name ); // 获取类成员变量的信息 Ivar class_getClassVariable ( Class cls, const char *name ); // 添加成员变量 BOOL class_addIvar ( Class cls, const char *name, size_t size, uint8_t alignment, const char *types ); //这个只能够向在runtime时创建的类添加成员变量 // 获取整个成员变量列表 Ivar * class_copyIvarList ( Class cls, unsigned int *outCount ); //必须使用free()来释放这个数组 //属性操作函数 // 获取类中指定名称实例成员变量的信息 Ivar class_getInstanceVariable ( Class cls, const char *name ); // 获取类成员变量的信息 Ivar class_getClassVariable ( Class cls, const char *name ); // 添加成员变量 BOOL class_addIvar ( Class cls, const char *name, size_t size, uint8_t alignment, const char *types ); // 获取整个成员变量列表 Ivar * class_copyIvarList ( Class cls, unsigned int *outCount ); 复制代码
methodLists
// 添加方法 BOOL class_addMethod ( Class cls, SEL name, IMP imp, const char *types ); //和成员变量不同的是可以为类动态添加方法。如果有同名会返回NO,修改的话需要使用method_setImplementation // 获取实例方法 Method class_getInstanceMethod ( Class cls, SEL name ); // 获取类方法 Method class_getClassMethod ( Class cls, SEL name ); // 获取所有方法的数组 Method * class_copyMethodList ( Class cls, unsigned int *outCount ); // 替代方法的实现 IMP class_replaceMethod ( Class cls, SEL name, IMP imp, const char *types ); // 返回方法的具体实现 IMP class_getMethodImplementation ( Class cls, SEL name ); IMP class_getMethodImplementation_stret ( Class cls, SEL name ); // 类实例是否响应指定的selector BOOL class_respondsToSelector ( Class cls, SEL sel ); 复制代码
objc_protocol_list与Protocol
objc_protocol_list:
struct objc_protocol_list { struct objc_protocol_list * _Nullable next; long count; __unsafe_unretained Protocol * _Nullable list[1]; }; 复制代码
Protocol的定义如下:
#ifdef __OBJC__ @class Protocol; #else typedef struct objc_object Protocol; #endif 复制代码
对protocol的操作为:
// 添加协议 BOOL class_addProtocol ( Class cls, Protocol *protocol ); // 返回类是否实现指定的协议 BOOL class_conformsToProtocol ( Class cls, Protocol *protocol ); // 返回类实现的协议列表 Protocol * class_copyProtocolList ( Class cls, unsigned int *outCount ); 复制代码
总结
Objective-C基于C语言的结构体定义面向对象的大部分概念,利用结构体指针与函数指针,来实现面向对象的类定义、类继承、实例化对象、对象成员变量和方法的存储与定义。 由此,Objective-C这本语言是一种在运行时发挥强大能力的语言,而这又归功于runtime这一消息分发系统,在运行时能够对类进行扫描、查找、调用、修改等等,这部分知识被称为rumtime核心技术,消息调用与动态类型的结合,使得Objective-C这门语言能够给予程序员非常大的自由度去享受编程的乐趣。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- OC对象的本质 实例对象,类对象,元类对象
- MJiOS底层笔记--OC对象本质
- IOS 底层原理 对象的本质--(1)
- 掌握面向对象编程本质,彻底掌握OOP
- 窥探iOS底层实现--OC对象的本质(二)
- 200行代码,7个对象——让你了解ASP.NET Core框架的本质
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。