继承自NSObject的不常用又很有用的函数

初始化阶段 —— load 和 initialize
load函数
原型:
  1. +(void)load 
当类被引用进程序的时候会执行这个函数。
在一个程序开始运行之前(在main函数开始执行之前),在库开始被程序加载,load函数就会开始被执行。
我们开发的程序都可以认为是一个库,但是库又不会独立存在(我们的程序还会引用其他库,也可能被其他函数引用),所以库的初始化顺序可以如下:
1. 初始化我们引用的库
2. 执行我们自己库的Objective-C的load函数
3. 执行C++和C的static初始化变量
4. 初始化引用我们库的其他库
在我们的编写的库中,会有很多类重写load函数,他们之间的执行顺序是不确定的。
当父类和子类都实现load函数时,父类的load函数会被先执行。load函数是系统自动加载的,因此 不需要调用父类的load函数,否则父类的load函数会多次执行
在Category中写load函数是不会替换原始类中的load函数的, 原始类和Category中的load函数都会被执行,原始类的load会先被执行,再执行Category中的load函数。当有多个Category都实现了load函数,这几个load函数执行顺序不确定。
Initialize函数
原型:
  1. + (void)initialize 
当类第一次被执行到的时候这个函数会被执行。
如果类包含继承关系,父类的initialize函数会比子类先执行。由于是系统自动调用, 也不需要显式的调用父类的initialize,否则父类的initialize会被多次执行
假如这个类放到代码中,而这段代码并没有被执行,这个函数是不会被执行的。
Load or Initialize
这两个函数没有交集,也没有执行的先后顺序,他们各自遵循着各自的调用原则。因此 在写逻辑的时候,不能有逻辑依赖load函数比initialize函数先行调用
来看下面这种情况:
  1. @interface NoneClass : NSObject 
  2. @end 
  3. @implementation NoneClass  
  4. +(void)load 
  5.     NSLog(@"NoneClass _cmd: %@", NSStringFromSelector(_cmd)); 
  6.     MyTestObject *test = [[MyTestObject alloc] init]; 
  7.   
  8. @end 
  9.   
  10. @interface MyTestObject 
  11. @end 
  12. @implementation MyTestObject 
  13. + (void) load 
  14.    NSLog(@"MyTestObject _cmd: %@", NSStringFromSelector(_cmd)); 
  15.   
  16.   
  17. + (void)initialize 
  18.    NSLog(@"MyTestObject _cmd: %@", NSStringFromSelector(_cmd)); 
  19.   
  20. @end 
输出的结果
  1. 2014-04-11 12:45:44.297 ObjectTest[1617:60b] NoneClass _cmd: load 
  2. 2014-04-11 12:45:44.300 ObjectTest[1617:60b] MyTestObject _cmd: initialize  
  3. 2014-04-11 12:45:44.301 ObjectTest[1617:60b] MyTestObject _cmd: load 
结果分析
由于在执行NonoClass的load函数中调用了MyTestObject的构造函数,这样会触发MyTestObject的initialize函数调用。而此时MyTestObject的load函数还没有被调用。
使用场景
将针对于类修改放在intialize中,将针对Category的修改放在load中。
但是假如我们是修改系统的类,一般会通过添加Category来添加功能,但是如果修改initialize会导致原生的intialize不会执行,所以放在load中会比较妥当。
函数调用
Objective-C是一门动态语言,一个函数是由一个selector(SEL),和一个implement(IML)组成的。Selector相当于门牌号,而Implement才是真正的住户(函数实现)。
和现实生活一样,门牌可以随便发(@selector(XXX)),但是不一定都找得到住户,如果找不到系统会给程序几次机会来程序正常运行,实在没出路了才会抛出异常。下图是objc_msgSend调用时,查找SEL的IML的过程。咱们以这个流程为例看看其中涉及的很有用的函数。
图:运行时查找函数的流程
resolveInstanceMethod函数
原型:
  1. + (BOOL)resolveInstanceMethod:(SEL)name 
 这个函数在运行时(runtime),没有找到SEL的IML时就会执行。 这个函数是给类利用class_addMethod添加函数的机会
根据文档,如果实现了添加函数代码则返回YES,未实现返回NO。
实现的例子:
  1. //全局函数 
  2. void dynamicMethodIMP(id self, SEL _cmd) 
  3.     // implementation .... 
  4.   
  5. @implementation MyTestObject 
  6. //… 
  7. //类函数 
  8. + (BOOL) resolveInstanceMethod:(SEL)aSEL 
  9.     if (aSEL == @selector(resolveThisMethodDynamically)) 
  10.     { 
  11.           class_addMethod([self class], aSEL, (IMP) dynamicMethodIMP, "v@:"); 
  12.           return YES; 
  13.     } 
  14.     return [super resolveInstanceMethod:aSel]; 
  15. //… 
  16. @end 
注意事项:
根据Demo实验,这个函数返回的BOOL值系统实现的objc_msgSend函数并没有参考,无论返回什么系统都会尝试再次用SEL找IML,如果找到函数实现则执行函数。如果找不到继续其他查找流程。
forwardingTargetForSelector:
原型:
  1. - (id)forwardingTargetForSelector:(SEL)aSelector 
流程到了这里,系统给了个将这个SEL转给其他对象的机会。
返回参数是一个对象,如果这个对象非nil、非self的话,系统会将运行的消息转发给这个对象执行。否则,继续查找其他流程。
实现示例:
  1. //转发目标类 
  2. @interface NoneClass : NSObject 
  3. @end 
  4.   
  5. @implementation NoneClass 
  6. +(void)load 
  7.     NSLog(@"NoneClass _cmd: %@", NSStringFromSelector(_cmd)); 
  8.   
  9. - (void) noneClassMethod 
  10.     NSLog(@"_cmd: %@", NSStringFromSelector(_cmd)); 
  11. @end 
  12.   
  13. @implementation MyTestObject 
  14. //… 
  15. //将消息转出某对象 
  16. - (id)forwardingTargetForSelector:(SEL)aSelector 
  17.     NSLog(@"MyTestObject _cmd: %@", NSStringFromSelector(_cmd)); 
  18.   
  19.     NoneClass *none = [[NoneClass alloc] init]; 
  20.     if ([none respondsToSelector: aSelector]) { 
  21.         return none; 
  22.     } 
  23.      
  24.     return [super forwardingTargetForSelector: aSelector]; 
  25. //… 
  26. @end 
当执行MyTestObject对象执行[myTestObject nonClassMethod]函数时,消息会抛到NoneClass对象中执行。
methodSignatureForSelector:
原型:
  1. - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector 
这个函数和后面的forwardInvocation:是最后一个寻找IML的机会。这个函数让重载方有机会抛出一个函数的签名,再由后面的forwardInvocation:去执行。
forwardInvocation:
原型:
  1. - (void)forwardInvocation:(NSInvocation *)anInvocation 
真正执行从methodSignatureForSelector:返回的NSMethodSignature。在这个函数里可以将NSInvocation多次转发到多个对象中,这也是这种方式灵活的地方。(forwardingTargetForSelector只能以Selector的形式转向一个对象)
下面这个示例代码,诠释了这种实现优势:
  1. #import <Foundation/Foundation.h> 
  2.   
  3. @interface Book : NSObject 
  4.     NSMutableDictionary *data; 
  5. //声明了两个setter/getter 
  6. @property (retain) NSString *title;  
  7. @property (retain) NSString *author; 
  8. @end 
  9.   
  10. @implementation Book 
  11. @dynamic title, author; //不自动生成实现 
  12.   
  13. - (id)init 
  14.     if ((self = [super init])) { 
  15.         data = [[NSMutableDictionary alloc] init]; 
  16.         [data setObject:@"Tom Sawyer" forKey:@"title"]; 
  17.         [data setObject:@"Mark Twain" forKey:@"author"]; 
  18.     } 
  19.     return self; 
  20.   
  21. - (void)dealloc 
  22.     [data release]; 
  23.     [super dealloc]; 
  24.   
  25. - (NSMethodSignature *)methodSignatureForSelector:(SEL)selector 
  26.     NSString *sel = NSStringFromSelector(selector); 
  27.     if ([sel rangeOfString:@"set"].location == 0) { 
  28.         //动态造一个 setter函数 
  29.         return [NSMethodSignature signatureWithObjCTypes:"v@:@"]; 
  30.     } else { 
  31.         //动态造一个 getter函数 
  32.         return [NSMethodSignature signatureWithObjCTypes:"@@:"]; 
  33.     } 
  34.   
  35. - (void)forwardInvocation:(NSInvocation *)invocation 
  36.     //拿到函数名 
  37.     NSString *key = NSStringFromSelector([invocation selector]); 
  38.     if ([key rangeOfString:@"set"].location == 0) { 
  39.         //setter函数形如 setXXX: 拆掉 set和冒号  
  40.         key = [[key substringWithRange:NSMakeRange(3, [key length]-4)] lowercaseString]; 
  41.         NSString *obj; 
  42.         //从参数列表中找到值 
  43.         [invocation getArgument:&obj atIndex:2]; 
  44.         [data setObject:obj forKey:key]; 
  45.     } else { 
  46.         //getter函数就相对简单了,直接把函数名做 key就好了。 
  47.         NSString *obj = [data objectForKey:key]; 
  48.         [invocation setReturnValue:&obj]; 
  49.     } 
  50.   
  51. @end 
doesNotRecognizeSelector:
原型:
  1. - (void)doesNotRecognizeSelector:(SEL)aSelector 
作为找不到函数实现的最后一步,NSObject实现这个函数只有一个功能,就是抛出异常。
虽然理论上可以重载这个函数实现保证不抛出异常(不调用super实现),但是苹果文档着重提出“一定不能让这个函数就这么结束掉,必须抛出异常”。
使用场景
在一个函数找不到时,Objective-C提供了三种方式去补救:
1、调用resolveInstanceMethod给个机会让类添加这个实现这个函数
2、调用forwardingTargetForSelector让别的对象去执行这个函数
3、调用methodSignatureForSelector(函数符号制造器)和forwardInvocation(函数执行器)灵活的将目标函数以其他形式执行。
如果都不中,调用doesNotRecognizeSelector抛出异常。
respondsToSelector
原型:
  1. + (BOOL)respondsToSelector:(SEL)aSelector 
这个函数大家再熟悉不过了,用来检查对象是否实现了某函数。
此函数通常是不需要重载的,但是在动态实现了查找过程后,需要重载此函数让对外接口查找动态实现函数的时候返回YES,保证对外接口的行为统一。
示例代码(接forwardInvocation的例子):
  1. @implementation Book 
  2. //… 
  3. - (BOOL) respondsToSelector:(SEL)aSelector 
  4.     if (@selector(setTitle:) == aSelector || 
  5.         @selector(title) == aSelector || 
  6.         @selector(setAuthor:) == aSelector || 
  7.         @selector(author) == aSelector) 
  8.     { 
  9.         return YES; 
  10.     } 
  11.      
  12.     return [super respondsToSelector: aSelector]; 
  13.  //… 
  14. @end 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值