对于一门编程语言而言,当初学者掌握了其基本语法和标准库的使用以后,如果能够继续了解该语言的核心思想与底层实现,则会渐入佳境,窥探语言实现的本质技术,能够为以后的性能优化以及规避技术陷阱等复杂工作提供思路。
了解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这门语言能够给予程序员非常大的自由度去享受编程的乐趣。