标签:
Objective-C提供了编译运行时,只要有可能,它都可以动态地运行,这意味着不仅需要编译器,还需要运行时系统执行编译的代码,运行时系统充当Objective-C语言的操作系统,有了它才能运行。
运行时系统所提供功能是非常强大的,在实际开发中是经常使用到的。比如,苹果不允许我们给Category追加扩展属性,是因为它不会自动生成成员变量,那么我们通过运行时就可以很好的解决这个问题。另外,常见的模型转字典或者字典转模型,对象归档等。
1.通过Objective-C原代码,编译器党编译包含Objective-C类和方法的代码时,编译器会创建实现语言动态特性的数据结构和函数调用。
2.通过foundation库中定义的NSObject提供的方法,NSObject提供了一些动态监测方法
3.直接通过调用runtime方法,直接调用#import <objc/runtime.h> 和#import <objc/objc.h>头文件里的函数
typedef struct objc_class *Class; struct objc_class { Class isa; // 指向metaclass Class super_class ; // 指向其父类 const char *name ; // 类名 long version ; // 类的版本信息,初始化默认为0,可以通过runtime函数class_setVersion和class_getVersion进行修改、读取 long info; // 一些标识信息,如CLS_CLASS (0x1L) 表示该类为普通 class ,其中包含对象方法和成员变量;CLS_META (0x2L) 表示该类为 metaclass,其中包含类方法; long instance_size ; // 该类的实例变量大小(包括从父类继承下来的实例变量); struct objc_ivar_list *ivars; // 用于存储每个成员变量的地址 struct objc_method_list **methodLists ; // 与 info 的一些标志位有关,如CLS_CLASS (0x1L),则存储对象方法,如CLS_META (0x2L),则存储类方法; struct objc_cache *cache; // 指向最近使用的方法的指针,用于提升效率; struct objc_protocol_list *protocols; // 存储该类遵守的协议 }
我们可以看到,对于一个Class类中,存在很多东西,下面我来一一解释下:
Class isa:指向metaclass,也就是静态的Class。一般一个Obj对象中的isa会指向普通的Class,这个Class中存储普通的成员变量和对象方法(-开头的方法),普通的Class中的isa指针指向静态的Class,静态的Class中存储static类型的成员变量和类方法(+开头的方法)
Class super_class指向父类,如果这个类是根类,则为NULL。
下面一张图片很好的描述了类和对象的继承关系:
在开发中经常需要给已有的类扩展添加方法和属性,但是Objective-C是不允许给已有类扩展属性的,因为类扩展是不会自动生成成员变量的,但是,苹果提供了runtime,我们可以通过runtime使用。
/** * 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);
/* Associative References */ /** * Policies related to associative references. * These are options to objc_setAssociatedObject() */ typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) { OBJC_ASSOCIATION_ASSIGN = 0, /**< Specifies a weak reference to the associated object. */ OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, /**< Specifies a strong reference to the associated object. * The association is not made atomically. */ OBJC_ASSOCIATION_COPY_NONATOMIC = 3, /**< Specifies that the associated object is copied. * The association is not made atomically. */ OBJC_ASSOCIATION_RETAIN = 01401, /**< Specifies a strong reference to the associated object. * The association is made atomically. */ OBJC_ASSOCIATION_COPY = 01403 /**< Specifies that the associated object is copied. * The association is made atomically. */ };
objc_property_t *properties = class_copyPropertyList objc_property_t property = properties[i]; const void *propertyName = property_getName(property); NSString *key = [NSString stringWithUTF8String:propertyName];
unsigned int outCount = 0; Ivar *ivars = class_copyIvarList([self class], &outCount); // 获取成员变量名 const void *name = ivar_getName(ivar); NSString *ivarName = [NSString stringWithUTF8String:name]; // 去掉成员变量的下划线 ivarName = [ivarName substringFromIndex:1];
其流程是这样的: 第一步:+ (BOOL)resolveInstanceMethod:(SEL)sel实现方法,指定是否动态添加方法。若返回NO,则进入下一步,若返回YES,则通过class_addMethod函数动态地添加方法,消息得到处理,此流程完毕。 第二步:在第一步返回的是NO时,就会进入- (id)forwardingTargetForSelector:(SEL)aSelector方法,这是运行时给我们的第二次机会,用于指定哪个对象响应这个selector。不能指定为self。若返回nil,表示没有响应者,则会进入第三步。若返回某个对象,则会调用该对象的方法。 第三步:若第二步返回的是nil,则我们首先要通过- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector指定方法签名,若返回nil,则表示不处理。若返回方法签名,则会进入下一步。 第四步:当第三步返回方法方法签名后,就会调用- (void)forwardInvocation:(NSInvocation *)anInvocation方法,我们可以通过anInvocation对象做很多处理,比如修改实现方法,修改响应对象等 第五步:若没有实现- (void)forwardInvocation:(NSInvocation *)anInvocation方法,那么会进入- (void)doesNotRecognizeSelector:(SEL)aSelector方法。若我们没有实现这个方法,那么就会crash,然后提示打不到响应的方法。到此,动态解析的流程就结束了。
objc_msgSend:其它普通的消息都会通过该函数来发送 objc_msgSend_stret:消息中需要有数据结构作为返回值时,会通过该函数来发送消息并接收返回值 objc_msgSendSuper:与objc_msgSend函数类似,只是它把消息发送给父类实例 objc_msgSendSuper_stret:与objc_msgSend_stret函数类似,只是它把消息发送给父类实例并接收数组结构作为返回值
创建并初始化NSObject 创建对象 NSObject *obj = (NSObject *(*)(id,SEL)objc_msgSend)((id)[NSObject class],@selector(alloc)); 初始化对象 obj = (NSObject *(*)(id,SEL)objc_msgSend)((id)msg,@selector(init));
Method类型是一个objc_method结构体指针,而结构体objc_method有三个成员:
/// An opaque type that represents a method in a class definition. typedef struct objc_method *Method; struct objc_method { SEL method_name; // 方法名称 char *method_typesE; // 参数和返回类型的描述字串 IMP method_imp; // 方法的具体的实现的指针 }
// 函数调用,但是不接收返回值类型为结构体 method_invoke // 函数调用,但是接收返回值类型为结构体 method_invoke_stret // 获取函数名 method_getName // 获取函数实现IMP method_getImplementation // 获取函数type encoding method_getTypeEncoding // 复制返回值类型 method_copyReturnType // 复制参数类型 method_copyArgumentType // 获取返回值类型 method_getReturnType // 获取参数个数 method_getNumberOfArguments // 获取函数参数类型 method_getArgumentType // 获取函数描述 method_getDescription // 设置函数实现IMP method_setImplementation // 交换函数的实现IMP method_exchangeImplementations
编码值 含意 c 代表char类型 i 代表int类型 s 代表short类型 l 代表long类型,在64位处理器上也是按照32位处理 q 代表long long类型 C 代表unsigned char类型 I 代表unsigned int类型 S 代表unsigned short类型 L 代表unsigned long类型 Q 代表unsigned long long类型 f 代表float类型 d 代表double类型 B 代表C++中的bool或者C99中的_Bool v 代表void类型 * 代表char *类型 @ 代表对象类型 # 代表类对象 (Class) : 代表方法selector (SEL) [array type] 代表array {name=type…} 代表结构体 (name=type…) 代表union bnum A bit field of num bits ^type A pointer to type ? An unknown type (among other things, this code is used for function pointers)
苹果的源码是闭源的,我们只有类名和类属性,方法等声明,却看不到实现,这时候我们若想改变其中一个方法的实现,就有三种方案:
1.通过继承。
2.通过类别重写。
3.swizzling。
标签:
原文地址:http://blog.csdn.net/liupinghui/article/details/51718576