C是如何支持OC运行的
objc是C的扩展,它的使用是由C语言的类库支持的。其中最根本的就是objc类库,从其头文件< objc/objc.h >和< objc/runtime.h >,可以了解OC和C是怎么联系起来的。查看< message.h >可以有助于理解OC的消息机制。
先看<objc/objc.h>
下面逐句来看看,这个头文件都定义了些什么。本文都是以先代码,后解释的顺序进行的。
一、先看头文件中定义的各种数据类型,这些类型与OC中面向对象的基本概念有着密切联系
1.OC的类
typedef struct objc_class *Class; `
//这句话给结构体类型struct objc_class命了别名为Class,其实它代表的就是OC的类,可见OC的类Class实际上是一个C语言的结构体。
2.OC的对象
struct objc_object {//定义了一个表示类的对象的结构体 struct objc_object
Class isa OBJC_ISA_AVAILABILITY;//有一个Class类型的变量isa,指的就是对象的metaclass,metaclass指明了这个对象是属于哪个类的。它的类型是Class,说明OC里面的各种类,都是类Class的对象。
};
3.OC的id
typedef struct objc_object *id;
//OC的id类型是C语言的struct objc_object类型的指针,也就是说id指向Class类的对象,可见id是一个通用的指针。
4.OC的方法选择器
typedef struct objc_selector *SEL;
//一个表示方法选择器的结构体objc_selector,OC的SEL是一个指向该结构体的指针
5.函数指针,对应OC的方法
typedef id (*IMP)(id, SEL, ...);
//IMP是一个函数指针,指向实现了的方法
6.OC的布尔类型
typedef bool BOOL;
//BOOl实际上就是C语言的布尔类型
7.
#if ! (defined(__OBJC_GC__) || __has_feature(objc_arc))
#define __strong /* empty */
#endif
#if !__has_feature(objc_arc)
#define __unsafe_unretained /* empty */
#define __autoreleasing /* empty */
#endif
这里可以看到,当不处于ACR的情况下,__strong、__unsafe_unretained、__autoreleasing都是没有意义的。
二、接着来看头文件中声明的方法(方法在前,解释在后)
OBJC_EXPORT const char *sel_getName(SEL sel)
__OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
以上定义了一个从mac10.0和iPhone2.0开始可以使用的C语言方法
const char *sel_getName(SEL sel)这个方法获取方法选择器sel返回的方法的方法名,方法名是个常量值const char。
OBJC_EXPORT SEL sel_registerName(const char *str)
__OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
这个方法将一个方法名和一个方法选择器联系起来,并返回这个方法选择器。或者说,为一个方法名为str的方法注册一个方法选择器。
OBJC_EXPORT const char *object_getClassName(id obj)
__OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
这个方法返回一个对象的类名
OBJC_EXPORT void *object_getIndexedIvars(id obj)
__OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
新建一个对象的时候会给对象分配内存空间,这个方法就是返回一个指向对象obj的内存空间的指针。
OBJC_EXPORT BOOL sel_isMapped(SEL sel)
__OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
判断一个方法选择器sel是否有效。只有注册了具体方法的选择器才是有效的。
OBJC_EXPORT SEL sel_getUid(const char *str)
__OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
这个方法和上面提到的SEL sel_registerName(const char *str)方法是一样的。当参数str指定的方法名找不到的时候返回NULL。
typedef const void* objc_objectptr_t;
#if __has_feature(objc_arc)
# define objc_retainedObject(o) ((__bridge_transfer id)(objc_objectptr_t)(o))
# define objc_unretainedObject(o) ((__bridge id)(objc_objectptr_t)(o))
# define objc_unretainedPointer(o) ((__bridge objc_objectptr_t)(id)(o))
#else
# define objc_retainedObject(o) ((id)(objc_objectptr_t)(o))
# define objc_unretainedObject(o) ((id)(objc_objectptr_t)(o))
# define objc_unretainedPointer(o) ((objc_objectptr_t)(id)(o))
#endif
以上这一段定义了废弃的ARC原则
这以后,还有一段代码,是为了兼容性,这里先不看了。
再看 <runtime.h>
这个头文件定义了OC的类、方法、实例变量、类目、属性、协议,还有许多与它们相关的方法。下面一一来看。
一、首先看看OC的类、方法、实例变量、类目、属性、协议等概念所对应的C语言实体。
1.方法
typedef struct objc_method *Method;
给结构体struct objc_method命了一个别名Method,表示OC类的一个方法。给
这个头文件中,还定义了一个结构体是用来描述方法的:
struct objc_method_description {
SEL name; /**< The name of the method */
char *types; /**< The types of the method arguments */
};
2.实例变量
typedef struct objc_ivar *Ivar;
OC类中的实例变量Ivar对应的是C语言的一个结构体指针struct objc_ivar。
3.类目
typedef struct objc_category *Category;
表示OC的类目
4.属性
typedef struct objc_property *objc_property_t;
表示OC的属性property
这个头文件中还定义了一个结构体来描述OC属性property的特性,包括特性的名称和值。
typedef struct {
const char *name; /**< The name of the attribute */
const char *value; /**< The value of the attribute (usually empty) */
} objc_property_attribute_t;
5.类
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;
这一段,定义了结构体struct objc_class
,在<objc/objc.h>
中,这个结构体起了个别称Class(typedef struct objc_class *Class;
),表示OC中对类的抽象——Class类。它包含了OC的一个类需要有的信息。
- isa表示本类的metaclass,它是Class类的一个对象。在前面讲到struct objc_object 结构体的时候有提到,metaclass确定一个对象是属于哪个类的。那么作为这个表示类的结构体本身,也有表示metaclass的变量isa,说明Class类本身也是一个对象。
- super_class表示本类的父类,父类也是Class类的一个对象。
- struct objc_ivar_list 是一个表示实例变量列表的结构体,ivars是结构体指针
- struct objc_method_list是一个表示方法列表的结构体,methodLists可以理解为结构体指针数组
- struct objc_cache 表示缓存的结构体,用来缓存常用的方法。cache是结构体指针。
- struct objc_protocol_list 表示协议列表的结构体,protocols是结构体指针。
上面提到的这几个结构体在本头文件中有定义,但是为了思路清晰,就不放在这里一一展示,在最后贴出来。
6.协议
typedef struct objc_object Protocol;
可见,这里把objc.h里面定义的表示一个类的对象的结构体命了别称为Protocol,说明Protocol本质上和类的对象是一样的。
从上面6点可以看到,OC的几个重要的概念,都对应着C语言中的一个结构体。
二、以下是<runtime.h>
中定义的方法,这些方法是我们自己也可以在编程中使用的。在开源项目中也经常会遇到。当然这些方法并不需要一一仔细阅读,但是浏览这些方法的用途,会发现,OC中每一步操作,都对应着C语言的一个函数。OC与C的联系就建立起来了。当然,其中也有一些方法,确实是可以为我们所用的,这些方法都是与OC的特性相关的,对于这些方法,有的非常有用的,将会在本节最后一点讲述。(方法在前,解释在后)
1.有关对象的方法
OBJC_EXPORT id object_copy(id obj, size_t size)
OBJC_ARC_UNAVAILABLE;
返回obj的一个副本,OBJC_ARC_UNAVAILABLE表示在ARC环境下该方法不可用。
OBJC_EXPORT id object_dispose(id obj)
OBJC_ARC_UNAVAILABLE;
释放对象obj占用的内存空间。
OBJC_EXPORT Class object_getClass(id obj)
返回对象obj的所属的类。
OBJC_EXPORT Class object_setClass(id obj, Class cls)
把对象obj转换所属对Class修改成另一个,即cls,并返回原来对Class。
OBJC_EXPORT BOOL object_isClass(id obj)
判断一个对象obj是不是一个类的对象。如果obj是一个OC类,或者一个metaclass,就返回真。
OBJC_EXPORT const char *object_getClassName(id obj)
返回对象obj所属的类的类名。
OBJC_EXPORT void *object_getIndexedIvars(id obj)
OBJC_ARC_UNAVAILABLE;
返回为对象obj分配的内存空间的指针。
OBJC_EXPORT id class_createInstance(Class cls, size_t extraBytes)
新建一个某类的对象,可以指定额外分配的内存空间。这些额外的空间可以用来存储额外添加的实例变量。
OBJC_EXPORT id objc_constructInstance(Class cls, void *bytes)
在新建某类的对象,存放在指定的一块内存空间中。
OBJC_EXPORT void *objc_destructInstance(id obj)
销毁一个对象,但是没有释放内存,也没有破坏key-value的关系。
2.有关类的方法
OBJC_EXPORT Class objc_getClass(const char *name)
返回类名为name的这个类的定义。
OBJC_EXPORT Class objc_getMetaClass(const char *name)
返回一个类名为name的类的metaclass。
OBJC_EXPORT Class objc_lookUpClass(const char *name)
这个方法也是返回类名为name的这个类的定义。和objc_getClass方法的区别在于,objc_getClass会在找不到类的时候调用class handler callback然后再找一遍,本方法不会调用callback方法。
OBJC_EXPORT Class objc_getRequiredClass(const char *name)
这个方法也是返回类名为name的这个类的定义。区别是它找不到的情况下会造成崩溃。
OBJC_EXPORT int objc_getClassList(Class *buffer, int bufferCount)
获取已注册的类的列表。buffer是一系列Class,每一个Class指向一个类的定义,bufferCount指定了要获取的Class的个数,如果传入NULL,就会获取到所有已注册的类。返回值是已注册的类的总数。
OC运行时会自动将源文件中定义过的类都进行注册。可以通过方法objc_addClass方法,在运行时定义新的类,并且进行注册。这个方法声明如下:
OBJC_EXPORT BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types)
OBJC_EXPORT Class *objc_copyClassList(unsigned int *outCount)
创建并返回一个数组的指针,该数组包含了所有已注册的类,以nil结尾,必须要用free()方法把这个指针释放掉。outCount存储了已注册的类的总数。
OBJC_EXPORT const char *class_getName(Class cls)
返回一个类的类名。
OBJC_EXPORT BOOL class_isMetaClass(Class cls)
判断一个类是否是metaclass。
OBJC_EXPORT Class class_getSuperclass(Class cls)
获取某类的父类。要获取父类应该用NSObject的superclass方法,不要用这个。
OBJC_EXPORT Class class_setSuperclass(Class cls, Class newSuper)
设置cls类的父类为newSuper类,返回值为旧的父类。这个方法不应该使用。
OBJC_EXPORT int class_getVersion(Class cls)
OBJC_EXPORT void class_setVersion(Class cls, int version)
获取和设置类的版本。
OBJC_EXPORT size_t class_getInstanceSize(Class cls)
获取类所占的内存空间
OBJC_EXPORT Class objc_allocateClassPair(Class superclass, const char *name, size_t extraBytes)
新建一个类class和一个metaclass。参数包括,新类的父类,类名,和为实例变量额外分配的内存空间大小,通常为0。
OBJC_EXPORT void objc_registerClassPair(Class cls)
为一个已经分配了内存空间的类进行注册。
OBJC_EXPORT void objc_disposeClassPair(Class cls)
销毁一个类及其metaclass。
3.有关实例变量的方法
OBJC_EXPORT id object_getIvar(id obj, Ivar ivar)
返回对象obj里面的实例变量ivar的值。
OBJC_EXPORT void object_setIvar(id obj, Ivar ivar, id value)
把值value赋给obj对象中的实例变量ivar。
OBJC_EXPORT Ivar object_setInstanceVariable(id obj, const char *name, void *value)
把值value赋给对象obj里面名称为name的实例变量。
OBJC_EXPORT Ivar object_getInstanceVariable(id obj, const char *name, void **outValue)
获取对象obj里面名称为name的实例变量的值。调用该方法后,返回值是这个实例变量,outValue指向了实例变量的值。如果已知实例变量Ivar,要获取实例变量的值的话,用上面的object_getIvar方法会更快。
OBJC_EXPORT Ivar class_getInstanceVariable(Class cls, const char *name)
OBJC_EXPORT Ivar class_getClassVariable(Class cls, const char *name)
获取cls类中名为name的实例变量的定义(不是值)。
OBJC_EXPORT Ivar *class_copyIvarList(Class cls, unsigned int *outCount)
返回一个数组的指针,这个数组包含类cls中定义的所有实例变量(Ivar),outCount是数组的长度。
OBJC_EXPORT BOOL class_addIvar(Class cls, const char *name, size_t size, uint8_t alignment, const char *types)
给类添加实例变量。
OBJC_EXPORT const char *ivar_getName(Ivar v)
OBJC_EXPORT const char *ivar_getTypeEncoding(Ivar v)
OBJC_EXPORT ptrdiff_t ivar_getOffset(Ivar v)
获取实例变量的名字、类型和偏移量(相对类的)。
4.与方法Method有关的方法
OBJC_EXPORT Method class_getInstanceMethod(Class cls, SEL name)
OBJC_EXPORT Method class_getClassMethod(Class cls, SEL name)
两个方法都是获取类cls的特定方法的定义。第二个方法在本类找不到该方法的时候会往上查找父类中是否实现该方法,第一个方法则不会。
OBJC_EXPORT IMP class_getMethodImplementation(Class cls, SEL name)
OBJC_EXPORT IMP class_getMethodImplementation_stret(Class cls, SEL name)
这个方法返回一个IMP,也就是一个函数指针。对于OC,通过一个类和一个方法选择器就可以确定一个具体的方法,C语言执行的时候往往需要把OC的方法转换为C语言函数来执行。class_getMethodImplementation就是实现了这个转化。
这个函数IMP在cls类的对象收到了对应的消息时就会被调用。
OBJC_EXPORT BOOL class_respondsToSelector(Class cls, SEL sel)
判断cls是否会响应方法选择器sel。要实现这个功能应该调用NSObject的5respondsToSelector:方法,不应该用这个。这个方法已过时。
OBJC_EXPORT Method *class_copyMethodList(Class cls, unsigned int *outCount)
与上面class_copyIvarList异曲同工,只是copy的对象是方法。
OBJC_EXPORT IMP class_replaceMethod(Class cls, SEL name, IMP imp, const char *types)
修改cls类和方法选择器name所确定的方法对应的C语言函数为imp,该函数的参数类型定义在字符串types中。返回值是以前的IMP。
OBJC_EXPORT SEL method_getName(Method m)
返回方法名。
OBJC_EXPORT IMP method_getImplementation(Method m)
获取方法的对应的C函数指针。
OBJC_EXPORT const char *method_getTypeEncoding(Method m)
返回一个对方法的参数和返回值的描述,字符串类型的。
OBJC_EXPORT unsigned int method_getNumberOfArguments(Method m)
获取参数个数。
OBJC_EXPORT char *method_copyReturnType(Method m)
获取返回值的描述。
OBJC_EXPORT char *method_copyArgumentType(Method m, unsigned int index)
返回方法m第index个参数的描述。
OBJC_EXPORT void method_getReturnType(Method m, char *dst, size_t dst_len)
OBJC_EXPORT void method_getArgumentType(Method m, unsigned int index, char *dst, size_t dst_len)
这两个方法和前面两个相同,不同的是获取描述是通过指针引用。
OBJC_EXPORT struct objc_method_description *method_getDescription(Method m)
获取对方法的描述的结构体。
OBJC_EXPORT IMP method_setImplementation(Method m, IMP imp)
设置方法对应的C语言函数,返回值是之前对应的C语言函数的指针。
OBJC_EXPORT void method_exchangeImplementations(Method m1, Method m2)
交换两个方法的IMP。
5.与协议有关的方法
OBJC_EXPORT BOOL class_conformsToProtocol(Class cls, Protocol *protocol)
用NSObject的conformsToProtocol:方法来验证一个类是否遵循每个协议,不要用这个方法。
OBJC_EXPORT Protocol * __unsafe_unretained *class_copyProtocolList(Class cls, unsigned int *outCount)
异曲同工,不再赘述。
OBJC_EXPORT BOOL class_addProtocol(Class cls, Protocol *protocol)
添加协议。
OBJC_EXPORT Protocol *objc_getProtocol(const char *name)
根据协议名称获取对应的协议。
OBJC_EXPORT Protocol * __unsafe_unretained *objc_copyProtocolList(unsigned int *outCount)
返回的是运行时所有可用的协议。
OBJC_EXPORT BOOL protocol_conformsToProtocol(Protocol *proto, Protocol *other)
判断一个协议是否遵循另外一个协议。
OBJC_EXPORT BOOL protocol_isEqual(Protocol *proto, Protocol *other)
判断两个协议是否相等。
OBJC_EXPORT const char *protocol_getName(Protocol *p)
获取协议的名称。
OBJC_EXPORT struct objc_method_description protocol_getMethodDescription(Protocol *p, SEL aSel, BOOL isRequiredMethod, BOOL isInstanceMethod)
获取协议中定义的某方法的描述。
OBJC_EXPORT struct objc_method_description *protocol_copyMethodDescriptionList(Protocol *p, BOOL isRequiredMethod, BOOL isInstanceMethod, unsigned int *outCount)
返回协议中定义的方法的描述的列表。
OBJC_EXPORT objc_property_t protocol_getProperty(Protocol *proto, const char *name, BOOL isRequiredProperty, BOOL isInstanceProperty)
返回协议中定义的名称为name的属性。
OBJC_EXPORT objc_property_t *protocol_copyPropertyList(Protocol *proto, unsigned int *outCount)
获取协议中定义的属性列表。
OBJC_EXPORT Protocol * __unsafe_unretained *protocol_copyProtocolList(Protocol *proto, unsigned int *outCount)
返回某协议所遵循的所有协议。
OBJC_EXPORT Protocol *objc_allocateProtocol(const char *name)
OBJC_EXPORT void objc_registerProtocol(Protocol *proto)
创建并注册协议。只有注册了的协议才可以在运行时使用。
OBJC_EXPORT void protocol_addMethodDescription(Protocol *proto, SEL name, const char *types, BOOL isRequiredMethod, BOOL isInstanceMethod)
为协议添加方法。
OBJC_EXPORT void protocol_addProtocol(Protocol *proto, Protocol *addition)
为协议添加一个合作性质的协议。
OBJC_EXPORT void protocol_addProperty(Protocol *proto, const char *name, const objc_property_attribute_t *attributes, unsigned int attributeCount, BOOL isRequiredProperty, BOOL isInstanceProperty)
为协议添加属性。
6.与属性相关的方法
OBJC_EXPORT objc_property_t class_getProperty(Class cls, const char *name)
OBJC_EXPORT objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount)
异曲同工,不再赘述。
OBJC_EXPORT BOOL class_addProperty(Class cls, const char *name, const objc_property_attribute_t *attributes, unsigned int attributeCount)
添加属性。
OBJC_EXPORT void class_replaceProperty(Class cls, const char *name, const objc_property_attribute_t *attributes, unsigned int attributeCount)
替换一个类的属性。
OBJC_EXPORT const char *property_getName(objc_property_t property)
OBJC_EXPORT const char *property_getAttributes(objc_property_t property)
获取属性的名称、描述。
OBJC_EXPORT objc_property_attribute_t *property_copyAttributeList(objc_property_t property, unsigned int *outCount)
应该可以推测出来,获取一个存储了属性的特征的数组。需要注意的地方和前面功能类似的方法一样。
OBJC_EXPORT char *property_copyAttributeValue(objc_property_t property, const char *attributeName)
获取的是属性的特征的值的数组。
7.有关库的方法
OBJC_EXPORT const char **objc_copyImageNames(unsigned int *outCount)
返回所有已加载的OC frameworks和动态库的名字。返回值是一个字符串数组,在调用以后必须对指针进行释放free()。
OBJC_EXPORT const char *class_getImageName(Class cls)
返回一个类所在的动态库。
OBJC_EXPORT const char **objc_copyClassNamesForImage(const char *image, unsigned int *outCount)
返回一个类库中所有的类名。
8.与方法选择器相关的方法
OBJC_EXPORT const char *sel_getName(SEL sel)
返回某个方法选择器对应的方法名。
OBJC_EXPORT SEL sel_getUid(const char *str)
OBJC_EXPORT SEL sel_registerName(const char *str)
注册一个方法选择器。第一个方法,在某方法名找不到对应方法的时候返回空。第二个方法,某方法名已经注册过的时候返回对应的方法选择器。
OBJC_EXPORT BOOL sel_isEqual(SEL lhs, SEL rhs)
比较两个方法选择器是否相等。
9.有关OC的特性的方法
重头戏来了!!!
(1)枚举
OBJC_EXPORT void objc_enumerationMutation(id obj)
这个方法在枚举中对象被修改的时候触发,enumerationMutationHandler会捕捉到它。如果没有设置enumerationMutationHandler就会造成严重错误。obj是被修改的对象。
OBJC_EXPORT void objc_setEnumerationMutationHandler(void (*handler)(id))
新建一个enumerationMutationHandler。参数是函数指针。
(2)消息
OBJC_EXPORT void objc_setForwardHandler(void *fwd, void *fwd_stret)
这个方法设置了在<message.h>
中声明的objc_msgForward方法会调用的函数。fwd是objc_msgForward方法会调用的函数。fwd_stret是objc_msgForward_stret方法会调用的函数。
(3)块
OBJC_EXPORT IMP imp_implementationWithBlock(id block)
返回一个函数指针,这个函数会调用block。这个指针必须要调用下面的imp_removeBlock方法来释放掉。
OBJC_EXPORT id imp_getBlock(IMP anImp)
返回与anlmp关联的block。
OBJC_EXPORT BOOL imp_removeBlock(IMP anImp)
释放掉由imp_implementationWithBlock方法产生的指针。返回结果表示成功与否。
(4)弱引用
OBJC_EXPORT id objc_loadWeak(id *location)
用弱引用指向一个存储在location的对象,返回这个引用。也就是在OC中使用__weak来修饰变量的时候就会调用这个函数。弱引用会在变量所指的对象释放之前一直保持alive,在对象释放之后自动把这个引用释放,并让变量指向nil。
OBJC_EXPORT id objc_storeWeak(id *location, id obj)
修改一个弱引用(用__weak修饰的)变量obj的值,也就是让这个变量指向一个新的地方location,返回的是存储在location的对象,也就是obj现在的值。
(5)KVC
typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {
OBJC_ASSOCIATION_ASSIGN = 0,
OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1,
OBJC_ASSOCIATION_COPY_NONATOMIC = 3,
OBJC_ASSOCIATION_RETAIN = 01401,
OBJC_ASSOCIATION_COPY = 01403
};
上面这个枚举类型,定义了建立key-value的模式。从上面的枚举类型涉及了ASSIGN、RETAIN_NONATOMIC、COPY_NONATOMIC、RETAIN、COPY,实际上所表示的意义就在字面上了。比如,第一个枚举类型OBJC_ASSOCIATION_ASSIGN,表示建立key对value的弱引用,而OBJC_ASSOCIATION_RETAIN就表示,建立key对value的强引用等等。
OBJC_EXPORT void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
这个方法,就是给对象object内部的属性值value和键key建立KVC关系,并且建立模式是policy。这个方法在使用category的方法扩展系统类的时候非常有用,因为在使用category扩展类的时候并不能给类添加新的实例变量,但是我们可以通过这个方法,实现给系统类添加新的实例变量。也就是说,我们可以在自定义的类目当中,新建一个变量value,再新建一个key,调用这个方法以后,就建立了value和key的关系,之后就可以通过key来访问到value了。先把下面的这个方法看完再上例子。
id objc_getAssociatedObject(id object, const void *key)
这个方法,就是根据键key来获取对象object中的实例变量。
例子:
在NSString+Custom.m中定义
id obj;//作为value的对象
const char*keyForObj;//作为key的字符串
objc_setAssociatedObject(self, &keyForObj, obj, OBJC_ASSOCIATION_ASSIGN);//建立键值对
id anObject=objc_getAssociatedObject(self, &keyForObj);//通过key来访问value
OBJC_EXPORT void objc_removeAssociatedObjects(id object)
这个方法,解除键对值的引用关系。
(6)OC中用到的符号
#define _C_ID ‘@'
平时经常用到@表示的就是取id的意思。
终于到<message.h>
的部分了
struct objc_super {
__unsafe_unretained id receiver;
#if !defined(__cplusplus) && !__OBJC2__
__unsafe_unretained Class class;
#else
__unsafe_unretained Class super_class;
#endif
};
以上结构体用来表示一个对象的父类。receiver就是对象(既然是消息机制,这里的对象指的就是接受消息的对象)。super_class指的就是这个对象的父类。父类是第一个搜索的类。
OBJC_EXPORT id objc_msgSend()
以上方法就执行了消息的发送。方法返回一个id。在OC中给对象发送消息,比如[self method];实际上会被翻译成objc_msgSend()这句话。
这个方法objc自动获取消息的接受者self,根据方法名method,来找到要实际执行的IMP。
实际上,这个方法实现了switch-case功能,根据调用的方法的要求不同,实际调用的方法也不同。一般情况下调用的就是objc_msgSend,要将消息发送给父类就会调用objc_msgSendSuper。如果要求返回结构体类型,就会相应调用objc_msgSend_stret和objc_msgSendSuper_stret。如果要求返回浮点数类型,就会调用与浮点数相关的函数,这个函数可能是objc_msgSend_fpret、objc_msgSend_fp2ret,具体哪个是由处理器的类型和位数决定的。
另外,要给父类发送消息指的是什么情况呢?就是在某个类中调用[super method]的情况。这时候就会调用objc_msgSendSuper,并把一个上面定义的struct objc_super类型的变量传给这个方法,这个方法根据这个类型中的super_class变量找到这个类的父类,然后将消息传给父类。值得注意的是,[super method]虽然看起来消息接受者是super,实际上消息接收者仍然是self,super只是一个编译器的符号,并不代表父类,这就是为什么它需要向objc_msgSendSuper方法传递一个struct objc_super类型的变量。
OBJC_EXPORT id method_invoke(id receiver, Method m, ...)
这个方法直接调用某方法。这个方法也实现了与上面类似的switch-case机制。
OBJC_EXPORT id _objc_msgForward( )
这个方法实现了消息的转发。在调用某个消息无响应的时候就会触发这个方法。
在<runtime.h>
中定义的objc_setForwardHandler
方法设定了这个消息转发方法会调用的函数的地址,这里暂时把这个函数称为method1。
也就是说,在某个地方执行了一句话,比如 [obj aMethod],但是obj并不包含方法aMethod,也就是所谓消息无响应,就会转而执行函数method1。
当然,这个方法能够正常执行的前提是,已经通过objc_setForwardHandler方法指定了method1方法。
与<runtime.h>
中定义的有关方法替换的方法不同,这个方法通过消息的转发,效果等同于可以修改任意类的方法,而<runtime.h>
中的方法直接修改方法对应的IMP,只能在本类中使用。