一野哭、消息驅(qū)動(dòng)機(jī)制
消息驅(qū)動(dòng)機(jī)制: 運(yùn)行的時(shí)候的一些機(jī)制味赃,最主要的是消息機(jī)制。
消息驅(qū)動(dòng)機(jī)制-動(dòng)態(tài)調(diào)用過程 : 對(duì)于C語言虐拓,函數(shù)的調(diào)用在編譯的時(shí)候會(huì)決定調(diào)用哪個(gè)函數(shù)。編譯完成之后直接順序執(zhí)行傲武,無任何二義性蓉驹。
OC的函數(shù)調(diào)用為消息發(fā)送城榛。屬于動(dòng)態(tài)調(diào)用過程。在編譯的時(shí)候并不能決定真正調(diào)用哪個(gè)函數(shù)态兴,只有在真正運(yùn)行的時(shí)候才會(huì)根據(jù)函數(shù)的名稱找到對(duì)應(yīng)的函數(shù)來調(diào)用狠持。
(事實(shí)證明,在編譯階段瞻润,OC可以調(diào)用任何函數(shù)喘垂,即使這個(gè)函數(shù)并未實(shí)現(xiàn),只要聲明過就不會(huì)報(bào)錯(cuò)绍撞。而C語言在編譯階段就會(huì)報(bào)錯(cuò)
Objective-C 語言不僅需要一個(gè)編譯器,同時(shí)也需要一個(gè)運(yùn)行時(shí)系統(tǒng)來執(zhí)行編譯好的代碼正勒。運(yùn)行時(shí)系統(tǒng)扮演的角色類似于 Objective-C 語言的操作系統(tǒng),Objective-C 基于該系統(tǒng)來工作。
在OC中的方法調(diào)用:
[object doSomeMethod];
其中object是一個(gè)對(duì)象傻铣,doSomeMethod是一個(gè)函數(shù)名稱章贞。對(duì)于這樣一個(gè)簡(jiǎn)單的調(diào)用。在編譯時(shí)RunTime會(huì)將上述代碼轉(zhuǎn)化成:
objc_msgSend(id object,@selector(doSomeMethod));
消息驅(qū)動(dòng)機(jī)制-核心
發(fā)送消息:
objc_msgSend(receiver, selector) 消息不含有參數(shù)
objc_msgSend(receiver, selector, arg1, arg2, ...)消息含有參數(shù)
二非洲、Runtime實(shí)現(xiàn)面向?qū)ο?/h4>
一個(gè)面向?qū)ο蟮恼Z言一定要實(shí)現(xiàn)類鸭限、屬性、方法两踏、繼承败京、擴(kuò)展。
一切事物皆對(duì)象梦染,通過面向?qū)ο蟮姆绞缴穆螅瑢F(xiàn)實(shí)世界的事物抽象成對(duì)象,現(xiàn)實(shí)世界中的關(guān)系抽象成類弓坞、繼承隧甚,幫助人們實(shí)現(xiàn)對(duì)現(xiàn)實(shí)世界的抽象與數(shù)字建模。
通過面向?qū)ο蟮姆椒ǘ啥常谟萌死斫獾姆绞綄?duì)復(fù)雜系統(tǒng)進(jìn)行分析戚扳、設(shè)計(jì)與編程。提高軟件的重用性族吻、靈活性和擴(kuò)展性帽借。
OC中的對(duì)象通過Runtime轉(zhuǎn)化為對(duì)應(yīng)的結(jié)構(gòu)體:
在Runtime中定義了:objc_class和objc_object結(jié)構(gòu)體 我們知道在OC代碼中NSObject幾乎是所有類的基類,它的核心就是結(jié)構(gòu)體 下面這個(gè)結(jié)構(gòu)體中包含一個(gè)實(shí)例變量鏈表 能夠找到類中所有的實(shí)例變量 還需要有一個(gè)方法鏈表超歌,找到這個(gè)類所有對(duì)應(yīng)的方法砍艾,還要能找到它的父類,在這里我們可以看到Class是objc_class結(jié)構(gòu)體指針的別名巍举,而id是objc_object指針的別名脆荷。在objc_object這個(gè)結(jié)構(gòu)體中包含了一個(gè)結(jié)構(gòu)體指針
在objc_class這個(gè)結(jié)構(gòu)體中主要包括以下:(它需要找到自己所有的實(shí)例變量,于是就有一個(gè)實(shí)例變量鏈表,需要找到自己所有的方法蜓谋,于是就有了一個(gè)方法鏈表等)
typedef struct objc_class *Class;
/// Represents an instance of a class.
struct objc_object {
Class isa OBJC_ISA_AVAILABILITY;
};
/// A pointer to an instance of a class.
typedef struct objc_object *id;
struct objc_class {
struct objc_class super_class; /*父類*/
const char *name; /*類名字*/
long version; /*版本信息*/
long info; /*類信息*/
long instance_size; /*實(shí)例大小*/
struct objc_ivar_list *ivars; /*實(shí)例參數(shù)鏈表*/
struct objc_method_list **methodLists; /*方法鏈表*/
struct objc_cache *cache; /*方法緩存*/
struct objc_protocol_list *protocols; /*協(xié)議鏈表*/
};
實(shí)例變量及實(shí)例變量表
struct objc_ivar { // 單個(gè)實(shí)例變量
char *ivar_name; // 實(shí)例變量名
char *ivar_type; // 實(shí)例變量的類型(屬于哪個(gè)類)
int ivar_offset; // 實(shí)例變量的偏移量(OC中訪問實(shí)例變量是通過偏移量獲取到實(shí)例變量的位置)
#ifdef __LP64__
int space; // 實(shí)例變量所占空間長(zhǎng)度
#endif
}
struct objc_ivar_list { // 實(shí)例變量列表結(jié)構(gòu)體(以鏈表的形式管理多個(gè)實(shí)例變量)
int ivar_count; // 實(shí)例變量個(gè)數(shù)
#ifdef __LP64__
int space; // 實(shí)例變量總空間數(shù)
#endif
/* variable length structure */
struct objc_ivar ivar_list[1]; // 實(shí)例變量數(shù)組
}
方法及方法表
struct objc_method { // 單個(gè)方法
SEL method_name; // 方法名
char *method_types; // 方法類型
IMP method_imp; // 指向方法實(shí)現(xiàn)代碼的地址
};
struct objc_method_list { // 方法鏈表
struct objc_method_list *obsolete;
int method_count;
#ifdef __LP64__
int space;
#endif
/* variable length structure */
struct objc_method method_list[1];
}
關(guān)于Runtime中的結(jié)構(gòu)體總結(jié)如下:
三梦皮、與Runtime系統(tǒng)交互
Objective-C 程序有三種途徑和運(yùn)行時(shí)系統(tǒng)交互:
通過 Objective-C 源代碼;
通過 Foundation 框架中類 NSObject 的方法;
通過直接調(diào)用運(yùn)行時(shí)系統(tǒng)的函數(shù)。
由于Runtime的作用就是講OC的源代碼轉(zhuǎn)化為底層C++的實(shí)現(xiàn)桃焕,所以前兩種交互方式是隱含在代碼內(nèi)部的剑肯,通過直接調(diào)用運(yùn)行時(shí)系統(tǒng)中的函數(shù),我們可以對(duì)運(yùn)行時(shí)系統(tǒng)中的函數(shù)進(jìn)行操作观堂,主要是讀取成員變量的值让网,獲取信息以及一些簡(jiǎn)單的設(shè)置,關(guān)于這些操作函數(shù)總結(jié)如下:
四师痕、動(dòng)態(tài)綁定
所謂的動(dòng)態(tài)綁定就是對(duì)一個(gè)對(duì)象發(fā)送消息溃睹,到運(yùn)行時(shí)系統(tǒng)找到這個(gè)消息所對(duì)應(yīng)的方法實(shí)現(xiàn)并調(diào)用的過程。
消息函數(shù)objc_megSend做了動(dòng)態(tài)綁定所需要的一切:
1七兜、它首先找到選標(biāo)所對(duì)應(yīng)的方法實(shí)現(xiàn)丸凭。因?yàn)椴煌念悓?duì)同一方法可能會(huì)有不同的實(shí)現(xiàn),所以找到的 方法實(shí)現(xiàn)依賴于消息接收者的類型。
2腕铸、然后將消息接收者對(duì)象(指向消息接收者對(duì)象的指針)以及方法中指定的參數(shù)傳給找到的方法實(shí)現(xiàn)惜犀。
3、最后,將方法實(shí)現(xiàn)的返回值作為該函數(shù)的返回值返回狠裹。
當(dāng)對(duì)象收到消息時(shí),消息函數(shù)首先根據(jù)該對(duì)象的isa指針找到該對(duì)象所對(duì)應(yīng)的類的方法表,并從表中尋找該消息對(duì)應(yīng)的方法選標(biāo)虽界。如果找不到,objc_msgSend 將繼續(xù)從父類中尋找,直到 NSObject 類。一旦找到了方法選標(biāo), objc_msgSend 則以消息接收者對(duì)象為參數(shù)調(diào)用,調(diào)用該選標(biāo)對(duì)應(yīng)的方法實(shí)現(xiàn)涛菠。
這就是在運(yùn)行時(shí)系統(tǒng)中選擇方法實(shí)現(xiàn)的方式莉御。在面向?qū)ο缶幊讨?一般稱作方法和消息動(dòng)態(tài)綁定的過程。
五俗冻、動(dòng)態(tài)方法解析
你可以通過實(shí)現(xiàn) resolveInstanceMethod:和 resolveClassMethod:來動(dòng)態(tài)地實(shí)現(xiàn)給定選標(biāo) 的對(duì)象方法或者類方法礁叔。
Objective-C 方法可以認(rèn)為是至少有兩個(gè)參數(shù)——self 和_cmd—— 的 C 函數(shù)。您可以通過 class_addMethod 方法將一個(gè)函數(shù)加入到類的方法中迄薄。例如,有如下的函數(shù):
void dynamicMethodIMP(id self, SEL _cmd) {
// implementation ....
}
我們可以做如下操作在項(xiàng)目中創(chuàng)建兩個(gè)類琅关,一個(gè)繼承自NSObject的Student 另一個(gè)繼承自Student的BDStudent
#import "BDStudent.h"
#import <objc/objc-runtime.h>
@implementation LOStudent
+(BOOL)resolveInstanceMethod:(SEL)sel{
//動(dòng)態(tài)解析 實(shí)例方法
//創(chuàng)建實(shí)例方法的方法實(shí)現(xiàn)
void(^resolveInstanceBlock)(id, SEL) = ^(id objc_self,SEL objc_cmd){
NSLog(@"如果實(shí)例方法未實(shí)現(xiàn)則會(huì)執(zhí)行此方法");
};
//為本類添加實(shí)例方法
class_addMethod([self class], sel, imp_implementationWithBlock(resolveInstanceBlock), "v@:"); //v表示返回值類型 @表示id :表示方法參數(shù)
printf("%s\n",sel_getName(sel));
return YES;
}
+(BOOL)resolveClassMethod:(SEL)sel{
//動(dòng)態(tài)解析 類方法
//首先創(chuàng)建類方法的實(shí)現(xiàn)
void(^resoveClassMethod)(id,SEL) = ^(id objc_self,SEL objc_cmd){
NSLog(@"未實(shí)現(xiàn)的類方法,在這里執(zhí)行");
};
//為本類添加類方法
class_addMethod(object_getClass([self class]), sel, imp_implementationWithBlock(resoveClassMethod), "v@:");
return YES;
}
@end
要是我們以上方法沒寫的話 就會(huì)得到如下結(jié)果 反之讥蔽,則不然涣易。
/*
//創(chuàng)建LOStudent的實(shí)例 調(diào)用不存在的方法
LOStudent *stu = [[LOStudent alloc]init];
[stu performSelector:@selector(haha)];
//會(huì)因?yàn)闊o法識(shí)別方法selector而拋出異常
*/
六、消息轉(zhuǎn)發(fā)
消息轉(zhuǎn)發(fā)很象繼承,并且可以用來在Objective-C程序中模擬多重繼承冶伞。如圖 5-1所示, 一個(gè)對(duì)象通過轉(zhuǎn)發(fā)來響應(yīng)消息,看起來就象該對(duì)象從別的類那借來了或者”繼承“了方法實(shí)現(xiàn)一樣新症。
消息轉(zhuǎn)發(fā)提供了多重繼承的很多特性。
然而,兩者有很大的不同:多重繼承是將不同的行為封裝到單個(gè)的對(duì)象中,有可能導(dǎo)致龐大的,復(fù)雜的對(duì)象响禽。
而消息轉(zhuǎn)發(fā)是將問題分解到更小的對(duì)象中,但是又以一種對(duì)消息發(fā)送對(duì)象來說完全透明的方式將這些對(duì)象聯(lián)系起來徒爹。
消息轉(zhuǎn)發(fā)只是將其他類引入消息鏈荚醒,而不是繼承鏈。所以 respondsToSelector:瀑焦、 isKindOfClass:返回值均為NO腌且,如果您使用的是協(xié)議類, conformsToProtocol:也為NO。若我們以轉(zhuǎn)發(fā)消息方式擴(kuò)展類榛瓮,那么有時(shí)需要重新實(shí)現(xiàn)這些方法,以達(dá)到對(duì)開發(fā)人員透明的效果巫击。
我們假設(shè)有兩個(gè)類都繼承與NSObject :Student和BeiDa 學(xué)生需要實(shí)現(xiàn)消息轉(zhuǎn)發(fā)禀晓,而學(xué)習(xí)作為一個(gè)方法是愛北大實(shí)現(xiàn)的:我們可以做如下處理
#import <Foundation/Foundation.h>
@interface BeiDa : NSObject
-(void)leart;
@end
#import "BeiDa.h"
@implementation BeiDa
//實(shí)現(xiàn)學(xué)習(xí)iOS開發(fā)的方法
-(void)leart{
NSLog(@"在北京大學(xué)軟件工程中心開發(fā)實(shí)踐");
}
@end
而作為學(xué)生
#import <Foundation/Foundation.h>
#import "Lanou.h"
@interface Student : NSObject
//接收轉(zhuǎn)發(fā)消息的對(duì)象
@property(nonatomic,strong)Lanou *otherObject;
@end
#import "Student.h"
@implementation Student
-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
if (aSelector == @selector(leart)) {
return [self.otherObject methodSignatureForSelector:aSelector];
}
return [super methodSignatureForSelector:aSelector];
}
//將消息當(dāng)做參數(shù)anInvocation傳到方法里來 通過invokeWithTarget將targrt切換為 otherObject 由otherObject來執(zhí)行l(wèi)eart這個(gè)方法
-(void)forwardInvocation:(NSInvocation *)anInvocation{
if (anInvocation.selector == @selector(leart)) {
[anInvocation invokeWithTarget:self.otherObject];
}
}
@end
對(duì)于以上我們?cè)趺蠢斫饽兀?br>
學(xué)生沒有實(shí)現(xiàn)學(xué)習(xí)的方法 真正的學(xué)習(xí)是在北大
實(shí)現(xiàn)轉(zhuǎn)發(fā)消息的方法
1 需要有一個(gè)對(duì)象 在消息進(jìn)行轉(zhuǎn)發(fā)之前需要從對(duì)象里面取對(duì)應(yīng)方法選標(biāo)的方法簽名 然后在forwardInvocation這個(gè)方法里面將這個(gè)消息轉(zhuǎn)發(fā)給那個(gè)對(duì)象
2 .h定義接收轉(zhuǎn)發(fā)消息的對(duì)象 OtherObject
3 這里去方法選標(biāo)就需要在取這個(gè)方法選標(biāo)在OtherObject這個(gè)類里面對(duì)應(yīng)的方法簽名
4 如果是學(xué)習(xí)的時(shí)候我們才會(huì)轉(zhuǎn)發(fā) 判斷之后 返回方法簽名
5 先判斷 在進(jìn)行消息轉(zhuǎn)發(fā) 將消息轉(zhuǎn)發(fā)給另一個(gè)對(duì)象
當(dāng)有l(wèi)earn這個(gè)方法選標(biāo)傳過來的時(shí)候首先會(huì)從otherObject里面去方法選標(biāo)所對(duì)應(yīng)的方法簽名如果取到則會(huì)執(zhí)行forwardInvocation:方法
七、Runtime的項(xiàng)目應(yīng)用
1坝锰、實(shí)例變量遍例
遍例類的所有實(shí)例變量粹懒,實(shí)現(xiàn)自動(dòng)歸檔-反歸檔
Ivar *ivars = class_copyIvarList([self class], &count);
// KVC中setValue中使用
// 我們知道在KVC中如果直接setValue如果對(duì)象沒有這個(gè)屬性或者是變量就會(huì)直接Crash,如:
SomeObj *obj = [[SomeObj alloc]init];
[obj setValue:@"value" forKey:@"objName"];
// SomeObj 沒有objName這個(gè)屬性
// 這段代碼會(huì)直接Crash顷级,使用runtime遍例實(shí)例變量避免
我們創(chuàng)建一個(gè)NSObject的類目
#import <Foundation/Foundation.h>
@interface NSObject (AutoEncode)<NSCoding>
-(instancetype)initWithCoder:(NSCoder *)aDecoder;
-(void)encodeWithCoder:(NSCoder *)aCoder;
@end
#import "NSObject+AutoEncode.h"
#import <objc/objc-runtime.h>
@implementation NSObject (AutoEncode)
-(void)encodeWithCoder:(NSCoder *)aCoder{
//獲取這個(gè)類的實(shí)例變量鏈表
//創(chuàng)建一個(gè)ivarCount保存實(shí)例變量個(gè)數(shù)
unsigned int ivarCount = 0;
Ivar *vars = class_copyIvarList([self class], &ivarCount);
//循環(huán)遍歷實(shí)例變量鏈表
for (int i = 0; i < ivarCount; i++) {
//獲得實(shí)例變量的名字
NSString *ivarName = [NSString stringWithUTF8String:ivar_getName(vars[i])];
//通過kvc獲得實(shí)例變量的值
id value = [self valueForKey:ivarName];
//對(duì)此值 以實(shí)例變量名作為key進(jìn)行歸檔
[aCoder encodeObject:value forKey:ivarName];
}
free(vars);//釋放鏈表
}
-(instancetype)initWithCoder:(NSCoder *)aDecoder{
//初始化
self = [self init];//根 NSObject
if (self) {
//獲取實(shí)例變量鏈表
unsigned int ivarCount = 0;
Ivar *ivars = class_copyIvarList([self class], &ivarCount);
//循環(huán)便利實(shí)例變量鏈表
for (int i = 0; i < ivarCount; i++) {
//獲取實(shí)例變量名字
NSString *varName = [NSString stringWithUTF8String:ivar_getName(ivars[i])];
//以實(shí)例變量的名字進(jìn)行反歸檔
id value= [aDecoder decodeObjectForKey:varName];
//通過KVC對(duì)此對(duì)象進(jìn)行賦值
[self setValue:value forKey:varName];
}
free(ivars);
}
return self;
}
@end
#import "ViewController.h"
#import "BeiDaStudent.h"
@interface ViewController ()
@end
#在此我們并沒有直接遵守NSCoding協(xié)議 但是能夠自動(dòng)進(jìn)行歸檔個(gè)反歸檔的操作
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//創(chuàng)建對(duì)象
BeiDaStudent *stu = [BeiDaStudent new];
stu.name = @"張三";
stu.age = 25;
//進(jìn)行歸檔
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:stu];
//進(jìn)行反歸檔并打印出數(shù)據(jù)
BeiDaStudent *stu1 = [NSKeyedUnarchiver unarchiveObjectWithData:data];
NSLog(@"北大的學(xué)生name = %@,age = %ld",stu1.name,stu1.age);
// Do any additional setup after loading the view, typically from a nib.
}
2凫乖、動(dòng)態(tài)關(guān)聯(lián)對(duì)象
使用Runtime來在一個(gè)已有對(duì)象上動(dòng)態(tài)的掛載另一個(gè)對(duì)象
如:如果你在對(duì)象傳遞(傳參)的時(shí)候需要用到某個(gè)屬性,按照以往的思路:我繼承這個(gè)類重新創(chuàng)建一個(gè)新類就完事了弓颈,這個(gè)思路沒有問題帽芽,但麻煩,要是有一個(gè)方法能直接將我想要的屬性掛載上去豈不是更好翔冀?代碼簡(jiǎn)單导街、易懂。
實(shí)際開發(fā)中纤子,一個(gè)UIViewController中多個(gè)UITableView(或UIAlertView)它們的代理相同搬瑰,動(dòng)態(tài)關(guān)聯(lián)后在代理方法中就可以分別不同對(duì)象進(jìn)行不同處理。
下面就來講解下如何使用Runtime來 在已有對(duì)象上動(dòng)態(tài)掛載另外一個(gè)對(duì)象控硼。
// 關(guān)聯(lián)對(duì)象
objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
// 獲取關(guān)聯(lián)對(duì)象
id objc_getAssociatedObject(id object, const void *key)
// 移除關(guān)聯(lián)對(duì)象
void objc_removeAssociatedObjects(id object)
我們假設(shè)BeiDaStudent作為BeiDa的代理去執(zhí)行學(xué)習(xí)iOS
#import <Foundation/Foundation.h>
@interface BeiDaStudent : NSObject
@property(nonatomic,strong)NSString *name;
@property(nonatomic,assign)NSInteger age;
//BeiDaStudent作為BeiDa的代理去執(zhí)行學(xué)習(xí)iOS
-(void)learniOSProgram;
@end
#import "BeiDaStudent.h"
@implementation BeiDaStudent
-(void)learniOSProgram{
NSLog(@"%@ 努力學(xué)習(xí)iOS編程",self.name);
}
@end
BeiDa類
#import <Foundation/Foundation.h>
@interface BeiDa : NSObject
-(void)addDelegate:(id)aDelegate;
-(void)removeDelegate:(id)aDelegate;
@end
#import "BeiDa.h"
#import "BeiDaStudent.h"
#import <objc/objc-runtime.h>
//動(dòng)態(tài)關(guān)聯(lián)對(duì)象的函數(shù)需要有一個(gè)key泽论,通過key進(jìn)行賦值取值
const char *kMultiDelegateKey = "kMultiDelegateKey";
@implementation BeiDa
-(void)addDelegate:(id)aDelegate{
// 關(guān)聯(lián)數(shù)組
NSMutableArray *delegateArray = objc_getAssociatedObject(self, kMultiDelegateKey);
if (!delegateArray) {
delegateArray = [NSMutableArray new];
//第一個(gè)參數(shù):當(dāng)前對(duì)象
//第二個(gè)參數(shù):關(guān)聯(lián)是所需要的key
//第三個(gè)參數(shù):就是與當(dāng)前對(duì)象進(jìn)行關(guān)聯(lián)的對(duì)象
//第四個(gè)參數(shù):關(guān)聯(lián)規(guī)則 枚舉類型
objc_setAssociatedObject(self, kMultiDelegateKey, delegateArray, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
if(aDelegate){
[delegateArray addObject:aDelegate];
}
}
-(void)removeDelegate:(id)aDelegate{
NSMutableArray *delegateArray = objc_getAssociatedObject([self class], kMultiDelegateKey);
[delegateArray removeObject:aDelegate];
}
//消息轉(zhuǎn)發(fā)
-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
NSMutableArray *delegateArray =objc_getAssociatedObject(self , kMultiDelegateKey);
if (delegateArray) {
for (id aDelegate in delegateArray) {
return [aDelegate methodSignatureForSelector:aSelector];
}
}
return [self methodSignatureForSelector:@selector(doNothing)];
}
//什么都不做
-(void)doNothing{
}
-(void)forwardInvocation:(NSInvocation *)anInvocation{
NSMutableArray *delegateArray = objc_getAssociatedObject(self, kMultiDelegateKey);
if (delegateArray) {
for (id aDelegate in delegateArray) {
dispatch_async(dispatch_get_main_queue(), ^{
[anInvocation invokeWithTarget:aDelegate];
});
}
}
}
@end
接下來我們?cè)赩iewController里面做:
BeiDa *beida = [BeiDa new];
//創(chuàng)建多個(gè)student作為多代理
BeiDaStudent *student1 = [[BeiDaStudent alloc]init];
student1.name =@"張三";
BeiDaStudent *student2 = [[BeiDaStudent alloc]init];
student2.name = @"李四";
BeiDaStudent *student3 = [[BeiDaStudent alloc]init];
student3.name = @"王五";
//將三個(gè)student對(duì)象添加為beida的代理
[beida addDelegate:student1];
[beida addDelegate:student2];
[beida addDelegate:student3];
//通過 performSelector:aSelector方法,調(diào)用代理方法
[beida performSelector:@selector(learniOSProgram)];
運(yùn)行結(jié)果是:
3、動(dòng)態(tài)關(guān)聯(lián)函數(shù)/方法
動(dòng)態(tài)關(guān)聯(lián)方法卡乾,在運(yùn)時(shí)期動(dòng)態(tài)增加翼悴、交換方法。
替換系統(tǒng)方法说订,自動(dòng)實(shí)現(xiàn)功能抄瓦。
遍例系統(tǒng)類的方法,查看新特性陶冷。
1钙姊、聲明并實(shí)現(xiàn)函數(shù)
void newFunction(id self, IMP _imp) {
// doSomething在這里實(shí)現(xiàn)功能
}
2、為類添加新方法
class_addMethod([self class], sel_registerName("someNewMethod"), (IMP)newFunction, “v@:");
3埂伦、調(diào)用此方法
objc_msgSend((id)self, sel_registerName("someNewMethod"));
接下來我們通過runtime事先觀察者KVO
#import <Foundation/Foundation.h>
@interface NSObject (CKKVO)
- (void)ck_addObserver:(id)observer ForKey:(NSString *)key WithBlock:(void(^)(id observedObject,NSString *key,id oldValue,id newValue))block;
- (void)ck_removeObserver:(id)observer ForKey:(NSString *)key;
@end
#import "NSObject+CKKVO.h"
#import <objc/objc-runtime.h>
NSString *const kCKKVOClassPrefix = @"CKKVOClassPrefix_";
NSString *const kCKObserversAssociatedKey = @"CKObserversAssociatedKey";
typedef void(^ObserverBlock)(id observedObject, NSString *key, id oldValue, id newValue);
// 創(chuàng)建一個(gè)用于存放觀察者info的類
@interface CKObserverInfo : NSObject
// 觀察者屬性
@property (nonatomic, weak) id observer;
// key屬性
@property (nonatomic, copy) NSString *key;
// 回調(diào)block
@property (nonatomic, copy) ObserverBlock block;
@end
@implementation CKObserverInfo
// 初始化方法
- (instancetype)initWithObserver:(id)observer ForKey:(NSString *)key WithBlock:(ObserverBlock)block {
self = [super init];
if (self) {
_observer = observer;
_key = key;
_block = block;
}
return self;
}
@end
@implementation NSObject (CKKVO)
- (void)ck_addObserver:(id)observer ForKey:(NSString *)key WithBlock:(void(^)(id,NSString *,id,id))block {
// 獲取 setterName
NSString *setName = setterName(key);
SEL setSelector = NSSelectorFromString(setName);
// 通過SEL獲得方法
Method setMethod = class_getInstanceMethod(object_getClass(self), setSelector);
if (!setMethod) {
@throw [NSException exceptionWithName:@"CKKVO Error" reason:@"若無setter方法煞额,無法KVO" userInfo:nil];
}
// 獲得當(dāng)前類
// 判斷是否已經(jīng)創(chuàng)建衍生類
Class thisClass = object_getClass(self);
NSString *thisClassName = NSStringFromClass(thisClass);
if (![thisClassName hasPrefix:kCKKVOClassPrefix]) {
thisClass = [self makeKVOClassWithOriginalClassName:thisClassName];
// 改變類的標(biāo)識(shí)
object_setClass(self, thisClass);
}
// 判斷衍生類中是否實(shí)現(xiàn)了setter方法
if(![self hasSelector:setSelector]) {
const char *setType = method_getTypeEncoding(setMethod);
class_addMethod(object_getClass(self), setSelector, (IMP)ck_setter, setType);
}
// 將observer添加到觀察者數(shù)組
NSMutableArray *observers = objc_getAssociatedObject(self, (__bridge const void *)(kCKObserversAssociatedKey));
if (!observers) {
observers = [NSMutableArray new];
objc_setAssociatedObject(self, (__bridge const void *)(kCKObserversAssociatedKey), observers, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
// 創(chuàng)建觀察者info類
CKObserverInfo *observerInfo = [[CKObserverInfo alloc]initWithObserver:observer ForKey:key WithBlock:block];
[observers addObject:observerInfo];
}
void ck_setter(id objc_self, SEL cmd_p, id newValue) {
// setterName轉(zhuǎn)為name
NSString *setName = NSStringFromSelector(cmd_p);
NSString *key = nameWithSetName(setName);
// 通過KVC獲取key對(duì)應(yīng)的value
id oldValue = [objc_self valueForKey:key];
// 將set消息轉(zhuǎn)發(fā)給父類
struct objc_super selfSuper = {
.receiver = objc_self,
.super_class = class_getSuperclass(object_getClass(objc_self))
};
objc_msgSendSuper(&selfSuper,cmd_p,newValue);
// 調(diào)用block
NSMutableArray *observers = objc_getAssociatedObject(objc_self, (__bridge const void *)kCKObserversAssociatedKey);
for (CKObserverInfo *info in observers) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
if ([info.key isEqualToString:key]) {
info.block(objc_self,key,oldValue,newValue);
}
});
}
}
// 從setterName轉(zhuǎn)為name
NSString *nameWithSetName(NSString *setName) {
if (setName.length <= 4 || ![setName hasPrefix:@"set"] || ![setName hasSuffix:@":"]) {
@throw [NSException exceptionWithName:@"CKKVO Error" reason:@"set方法not available" userInfo:nil];
}
NSString *Name = [setName substringWithRange:NSMakeRange(3, setName.length - 4)];
NSString *firstCharacter = [Name substringToIndex:1];
return [[firstCharacter lowercaseString] stringByAppendingString:[Name substringFromIndex:1]];
}
// 判斷set方法是否存在
- (BOOL)hasSelector:(SEL)aSelector {
unsigned int mCount = 0;
Method *methods = class_copyMethodList(object_getClass(self), &mCount);
for (int i = 0; i < mCount; i ++) {
Method method = methods[i];
SEL setSelector = method_getName(method);
if (setSelector == aSelector) {
free(methods);
return YES;
}
}
free(methods);
return NO;
}
// 通過runtime創(chuàng)建類
- (Class)makeKVOClassWithOriginalClassName:(NSString *)className {
NSString *kvoClassName = [kCKKVOClassPrefix stringByAppendingString:className];
Class kvoClass = NSClassFromString(kvoClassName);
if (kvoClass) {
return kvoClass;
}
// objc_allocateClassPair創(chuàng)建類
kvoClass = objc_allocateClassPair(object_getClass(self), kvoClassName.UTF8String, 0);
objc_registerClassPair(kvoClass);
return kvoClass;
}
// 通過key獲取對(duì)應(yīng)的setterName
NSString *setterName(NSString *key) {
if (key.length == 0) {
@throw [NSException exceptionWithName:@"CKKVO Error" reason:@"沒有對(duì)應(yīng)的key" userInfo:nil];
}
NSString *firstCharacter = [key substringToIndex:1];
NSString *Name = [[firstCharacter uppercaseString]stringByAppendingString:[key substringFromIndex:1]];
return [NSString stringWithFormat:@"set%@:",Name];
}
- (void)ck_removeObserver:(id)observer ForKey:(NSString *)key {
// 刪除觀察者
CKObserverInfo *removeInfo = nil;
NSMutableArray *observers = objc_getAssociatedObject(self, (__bridge const void *)(kCKObserversAssociatedKey));
for (CKObserverInfo *info in observers) {
if (info.observer == observer && [info.key isEqualToString:key]) {
removeInfo = info;
}
}
[observers removeObject:removeInfo];
}
@end
然后在viewDidLoad:里面
// 異步Block的KVO
[p1 ck_addObserver:self ForKey:@"name" WithBlock:^(id observedObject, NSString *key, id oldValue, id newValue) {
NSLog(@"oldValue = %@,%@",oldValue,[NSThread currentThread]);
NSLog(@"newValue = %@,%@",newValue,[NSThread currentThread]);
}];
p1.name = @"456";
p1.name = @"789";
[p1 ck_removeObserver:self ForKey:@"name"];
p1.name = @"hahahaha";