OC底层的初步理解
- OC是是一门运行时运行会在运行时动态的获去属性和方法
调用方法编译时会被转化为消息发送
/* OC调用 */
[obj test];
/* 编译时运行时会将上边的代码转化为下面的消息发送C函数 */
obj_msgSend(obj,@selector(test))
Class的结构(其实就是isa指针指向结构体)
OC类室友Class类型来表示的他实际是一个指向objc_class的结构体的指针.
// 1. 结构体
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; // 类的版本信息,默认为0
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;
- isa指向metaclass(元类).
- super_class指向父类.
objc继承原理:
- obj通过class生成,但是class本身也是obj
- 如上本质是结构体(写一个不继承NSObject的类,一个指向结构体的指针)
- objc的class保存了方法列表还有指向父类的指针.isa指向metaclass(元类).保存了所有实现的方法列表,以及父类的metaclass(元类)
![Uploading Snip20161107_6_028079.png . . .]
objc方法原理:
消息是如何映射方法的?方法又是如何执行的??
首先对象属性保存在对象中,方法在类中.class的方法列表其实相当于一个字典.key:selector,IMPs:value.并且注意key和value是在运行时才确定的.而不是在编译时.引出IMPs代表什么呢??
IMPs可以理解为指向方法的指针.
// Method
- (id)doSomethingWithInt:(int)aInt{}
/*
IMP
参数1:self
参数2:_cmd
参数3:方法的参数
*/
id doSomethingWithInt(id self, SEL _cmd, int aInt){}
# pragma mark - 运行时里面是一个强大的runtime库,用c和汇编写的,具有一些很强大的功能.以后再去研究.建议可以阅读南峰子的博客
class_addIvar, // 增加成员变量
class_addMethod, // 增加方法
class_addProperty // 增加属性
class_addProtocol // 增加协议
class_copyIvarList, // 复制成员变量列表
class_copyMethodList, // 复制方法列表
class_copyProtocolList // 复制协议列表
class_copyPropertyList // 复制属性列表
作用:
- 获取一个类的属性列表和成员变量
// 字典转模型中获取一个类的属性列表,遍历得到属性名数组,根据属性名赋值属性,实现setValueUndefined方法避免报错,完成字典转模型(简单实现思路)
class_copyPropertyList // 复制属性列表
- 交换方法的实现(消息发送机制,指针可以改变).
+ (void)load {
Method addobject = class_getInstanceMethod(self, @selector(addObject:));
Method logAddobject = class_getInstanceMethod(self, @selector(logAddObject:));
method_exchangeImplementations(addObject, logAddObject);
}
- (void)logAddObject:(id)aobject { [self logAddObject:aObject]; NSLog(@"Added object %@ to array %@", aObject, self);}
- 关联对象.用于给分类添加属性的set,get方法的实现.因为分为能过写属性但是不会生成set和get方法,需要我们手动去实现这个功能.
- (NSString *)downloadingURLString {
// return _downloadingURLString;
// id object: 获取哪个对象上面的值
// const void *key:
return objc_getAssociatedObject(self, key);
}
- (void)setDownloadingURLString:(NSString *)downloadingURLString {
// _downloadingURLString = downloadingURLString;
// 关联对象: 设置一个值到一个对象上面
// id object: 值保存到哪个对象上面
// const void *key: 名称�
// id value: 要保存的内容
// objc_AssociationPolicy policy: 策略
objc_setAssociatedObject(self, key, downloadingURLString, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- KVO监听原理,Key Value Observing。当你开始observing an object时,Cocoa会创建这个object的class的subclass,然后将这个object的isa指向新创建的subclass(父类)。
object_setClass(myObject, [MySubclass class]);
消息动态处理流程
/* 1. 时机处理之一,在这个方法中我们可以利用runtime的特性动态添加方法来处理 */
+ (BOOL)resolveInstanceMethod:(SEL)sel;
/* 2. 时机处理之二,在这个方法中看代理能不能处理,如果代理对象能处理,则转接给代理对象 */
- (id)forwardingTargetForSelector:(SEL)aSelector;
/* 3. 消息转发之一,该方法返回方法签名,如果返回nil,则转发流程终止,抛出异常 */
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector;
/* 4. 消息转发之二,在该方法中我们可以对调用方法进行重定向 */
- (void)forwardInvocation:(NSInvocation *)anInvocation;
消息转发:
如果resolve method:解决方法,返回一个NO,再去找代理对象有没有实现这个方法,运行时就会进入消息转发
有两种常见的方法
- 将消息转发到另一个可以处理消息的obj.(代理)
- 将多个消息转发到同一个方法.
1. forwardingTargetForSelector:发送给另一个obj
2. forwordInvacation:运行时将消息打包成NSInvacation返回给你处理的,处理完后调用invokeWithTarget
Cacoa有哪里用到了消息转发呢?第一个代理,第二个响应者链条.
NSInvacation方法对象
消息的对象.
NSProxy
NSProxy是一个轻量级的class,它的作用就是转发消息到另一个object。
NSUndoManager
NSUndoManager也有用到,不过是截取消息,之后再执行,而不是转发到其他的地方。