/********************************************************************************************
* author:conowen@大钟
* E-mail:conowen@hotmail.com
* http://blog.csdn.net/conowen
********************************************************************************************/
1、前言
上一篇博文简单聊了一下runtime的基本概念,能干嘛,通过一个小demo来简单说了一下runtime的用处,以下还是以这个demo来聊聊。
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSString *str0 = [NSString stringWithFormat:@"%@",@"conowen"];
NSLog(@"length = %lu",[str0 length]);
NSString *str = ((NSString *(*)(id, SEL, NSString *, ...))(void *)objc_msgSend)((id)objc_getClass("NSString"), sel_registerName("stringWithFormat:"),@"%@", @"conowen");
NSLog(@"length = %lu",((NSUInteger (*)(id, SEL))(void *)objc_msgSend)((id)str, sel_registerName("length")));
}
2、objc_msgSend 函数详解
objc_msgSend 函数原形为
OBJC_EXPORT id objc_msgSend(id self, SEL op, ...)//后面省略号表示若干个参数,可有可无
同样的还有以下:
objc_msgSend_stret当发送的消息要返回的是结构体类型时
objc_msgSend_fpret当发送的消息要返回的是浮点数时
objc_msgSendSuper 如果要给超类(父类)发消息,就用这个函数处理
同理,还有objc_msgSendSuper_stret和objc_msgSendSuper_fpret。通常来说,编译器会根据实际情况,动态地调用这个几个方法中的一个,还有需要明确一点的是,objc_msgSend这个函数并没有返回数据,而是调用相对应的方法所返回来的数据。
3、objc_msgSend流程
3.1、id
// A pointer to an instance of a class.
typedef struct objc_object *id;
// An opaque type that represents a method selector.
typedef struct objc_selector *SEL;
然后再查看objc_object的定义如下,表示这是一个类的对象,这个结构体中只包含一个Class类型的isa指针(isa的缩写是 is a,可以理解为:This object "objectA"is a object of Class A,这个对象是一个A类的对象。),所以根据这个isa指针就能找到这个对象所属的类。
//Represents an instance of a class.
struct objc_object {
Class isa OBJC_ISA_AVAILABILITY;
};
而Class的定义如下,之所以说isa是指针,是因为Class指向的是objc_class类型的指针。
// An opaque type that represents an Objective-C class.
typedef struct objc_class *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;
/* Use `Class` instead of `struct objc_class *` */
虽然上面的定义包含了非Object c 2.0,但是我们还是可以详细说说,这个结构体包含的这些个玩意到底是啥。
3.2、category 增加成员变量
/**
* Sets an associated value for a given object using a given key and association policy.
*
* @param object The source object for the association.
* @param key The key for the association.
* @param value The value to associate with the key key for object. Pass nil to clear an existing association.
* @param policy The policy for the association. For possible values, see “Associative Object Behaviors.”
*
* @see objc_setAssociatedObject
* @see objc_removeAssociatedObjects
*/
OBJC_EXPORT void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
__OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_1);
/**
* Returns the value associated with a given object for a given key.
*
* @param object The source object for the association.
* @param key The key for the association.
*
* @return The value associated with the key \e key for \e object.
*
* @see objc_setAssociatedObject
*/
OBJC_EXPORT id objc_getAssociatedObject(id object, const void *key)
__OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_1);
/**
* Removes all associations for a given object.
*
* @param object An object that maintains associated objects.
*
* @note The main purpose of this function is to make it easy to return an object
* to a "pristine state”. You should not use this function for general removal of
* associations from objects, since it also removes associations that other clients
* may have added to the object. Typically you should use \c objc_setAssociatedObject
* with a nil value to clear an association.
*
* @see objc_setAssociatedObject
* @see objc_getAssociatedObject
*/
OBJC_EXPORT void objc_removeAssociatedObjects(id object)
__OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_1);
3.3、category 增加成员方法
struct objc_ivar {
char *ivar_name OBJC2_UNAVAILABLE;
char *ivar_type OBJC2_UNAVAILABLE;
int ivar_offset OBJC2_UNAVAILABLE;
#ifdef __LP64__
int space OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;
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;
struct objc_method {
SEL method_name OBJC2_UNAVAILABLE;
char *method_types OBJC2_UNAVAILABLE;
IMP method_imp OBJC2_UNAVAILABLE;
} OBJC2_UNAVAILABLE;
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;
}
3.4、IMP是什么
/// A pointer to the function of a method implementation.
#if !OBJC_OLD_DISPATCH_PROTOTYPES
typedef void (*IMP)(void /* id, SEL, ... */ );
#else
typedef id (*IMP)(id, SEL, ...);
#endif
简单来说,IMP(implement)是一个函数指针,指向了方法的具体实现。实际的应用场景是这样的,当你写了Object c代码,如[NSString alloc]的时候,在运行时转换为objc_mesgSend函数形式,最终这个alloc消息最终会执行什么代码,是有IMP来决定的,IMP直接指向了方法的实现。这样就会很有趣了,应该可以直接操作方法的入口,那不就是可以更改替换系统方法,这种技术被称为“方法调配”(method swizzling)。原理就是让IMP指针本来指向改到其他方法实现那里去。
3.5、指针函数与函数指针
a、指针函数
//类型标识符 *函数名(参数表)
int *fun(x,y);
b、函数指针
类型标识符 (*函数名)(参数表)
int (*fun) (int x,y); //函数名前面有一个星号,然后用小括号包起来
fun=funTest; /* 将funTest函数的首地址赋给指针
3.6、objc_class的cache是什么
struct objc_cache {
unsigned int mask /* total = mask + 1 */ OBJC2_UNAVAILABLE;
unsigned int occupied OBJC2_UNAVAILABLE;
Method buckets[1] OBJC2_UNAVAILABLE;
};
如果知道CPU的L1 cache和L2 cache,就大概知道这玩意设计有什么作用了。摘自某度百科的解说
4、objc_mesgSend的调用流程
1、先在cache里面查找这个消息对应IMP指针(找到就直接跳到IMP指针指向的方法,然后执行)
2、若是在cache找不到就会到该类的方法列表里面找。