前言
Runtime基本是用C和匯編寫的,可見蘋果為了動態(tài)系統(tǒng)的高效而作出的努力。你可以在這里下到蘋果維護(hù)的開源代碼。蘋果和GNU各自維護(hù)一個開源的runtime版本自阱,這兩個版本之間都在努力的保持一致。Objective-C 從三種不同的層級上與 Runtime 系統(tǒng)進(jìn)行交互米酬,分別是通過 Objective-C 源代碼沛豌,通過 Foundation 框架的NSObject類定義的方法,通過對 runtime 函數(shù)的直接調(diào)用赃额。大部分情況下你就只管寫你的Objc代碼就行加派,runtime 系統(tǒng)自動在幕后辛勤勞作著。
1跳芳、概念
RunTime簡稱運(yùn)行時,就是系統(tǒng)在運(yùn)行的時候的一些機(jī)制芍锦,其中最主要的是消息機(jī)制。
??飞盆、ios進(jìn)階之傳遞消息
??
2娄琉、作用
1.動態(tài)交換兩個方法的實現(xiàn)
2.為類別添加屬性(我們知道類別是不能擴(kuò)展屬性的次乓,只能擴(kuò)展方法,但可以運(yùn)行時實現(xiàn)孽水,通過為類增加屬性)
3.獲取某個類的所有成員變量和成員方法
4.實現(xiàn)NSCoding的自動歸檔和自動解檔
5.動態(tài)添加對象的成本變量和成員方法
作用:當(dāng)硬件內(nèi)存過小的時候票腰,如果我們將每個方法都直接加到內(nèi)存當(dāng)中去,但是幾百年不用一次女气,這樣就造成了浪費(fèi)杏慰,所有采取動態(tài)添加
6.實現(xiàn)字典和模型的自動轉(zhuǎn)換
3、詳解:
一主卫、動態(tài)交換方法
1.在自定義類DWExchangeTwoMethod.m中
- (instancetype)init {
if (self = [super init]) {
Method workMethod = class_getInstanceMethod([DWExchangeTwoMethod class], @selector(work));
Method playMethod = class_getClassMethod([DWExchangeTwoMethod class], @selector(play));
method_exchangeImplementations(workMethod, playMethod);
}
return self;
}
- (void)play {
NSLog(@"玩...");
}
+ (void)work {
NSLog(@"工作...");
}
拋磚引玉:可以交換方法逃默,防止數(shù)組越界導(dǎo)致的崩潰
2鹃愤、然后在其他類調(diào)用
DWExchangeTwoMethod *exchangeMethod = [DWExchangeTwoMethod new];
[exchangeMethod play];
由上可以看出簇搅,調(diào)用的是play
方法
但打印結(jié)果為 【玩...】,證明已經(jīng)動態(tài)交換成功
二软吐、為分類添加屬性
一般情況下瘩将,分類不可以添加屬性,但用runtime卻可以實現(xiàn)
- (NSString *)height {
return objc_getAssociatedObject(self, @"height");
}
- (void)setHeight:(NSString *)height {
objc_setAssociatedObject(self, @"height", height, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
注:
objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
id object :表示關(guān)聯(lián)者凹耙,是一個對象姿现,變量名理所當(dāng)然也是object
const void *key :獲取被關(guān)聯(lián)者的索引key
id value :被關(guān)聯(lián)者,這里是一個block
objc_AssociationPolicy policy : 關(guān)聯(lián)時采用的協(xié)議肖抱,有assign备典,retain,copy等協(xié)議意述,一般使用OBJC_ASSOCIATION_RETAIN_NONATOMIC
objc_getAssociatedObject(id object, const void *key)
參數(shù)解析同上
三提佣、獲取實例變量列表
unsigned int outCount = 0;
Ivar *ivars = class_copyIvarList([DWExchangeTwoMethod class], &outCount);
for (int i = 0; i < outCount; i++) {
Ivar ivar = ivars[i];
const char* name = ivar_getName(ivar);
const char* type = ivar_getTypeEncoding(ivar);
NSLog(@"變量名%s :%s", name, type);
}
// 此時要注意, 盡管在ARC模式下, 取出變量后要依然手動釋放內(nèi)存, 利用free()方法即可:
free(ivars);
注:
class_copyPropertyList與class_copyIvarList
class_copyPropertyList
返回的僅僅是對象類的屬性(@property申明的屬性),而class_copyIvarList
返回類的所有屬性和變量(包括在@interface大括號中聲明的變量)
四荤崇、實現(xiàn)NSCoding的自動歸檔和自動解檔拌屏;
(不用對每個屬性edcode和decode了,如果幾十個屬性一個個的encode和decode真的很麻煩啊,使用運(yùn)行時可以遍歷出每個對象的屬性,數(shù)組的方式遍歷eccode,decode)
#import "DWPerson.h"
#define kname @"name"
#define kidCard @"idCard"
#define kage @"age"
#define kothers @"others"
@implementation DWPerson
- (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeObject:_name forKey:kname];
[aCoder encodeObject:_idCard forKey:kidCard];
[aCoder encodeInt:_age forKey:kage];
[aCoder encodeObject:_others forKey:kothers];
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
if (self = [super init]) {
_name = [aDecoder decodeObjectForKey:kname];
_age = [aDecoder decodeIntForKey:kage];
_idCard = [aDecoder decodeObjectForKey:kidCard];
_others = [aDecoder decodeObjectForKey:kothers];
}
return self;
}
@end
上面每個類都要寫一次,所以我們添加個分類會更加簡化
#import "NSObject+ArchiveExtension.h"
#import <objc/runtime.h>
@implementation NSObject (ArchiveExtension)
// 先對當(dāng)前類進(jìn)行編碼术荤,然后對父類進(jìn)行編碼倚喂,如果父類是NSObject就結(jié)束編碼
- (void)encode:(NSCoder *)aCoder {
// 一層層父類往上查找,對父類的屬性執(zhí)行歸解檔方法
Class c = self.class;
while (c && c != [NSObject class]) {
unsigned int outCount = 0;
Ivar *ivars = class_copyIvarList(c, &outCount);
for (int i = 0; i < outCount; i++) {
Ivar ivar = ivars[i];
NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
id value = [self valueForKey:key];
[aCoder encodeObject:value forKey:key];
}
free(ivars);
c = [c superclass];
}
}
- (void)decode:(NSCoder *)aDecoder {
Class c = self.class;
while (c && c != [NSObject class]) {
unsigned int outCount = 0;
Ivar *ivars = class_copyIvarList(c, &outCount);
for (int i = 0; i < outCount; i++) {
Ivar ivar = ivars[i];
NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
//注意與歸檔的順序不同
id value = [aDecoder decodeObjectForKey:key];
[self setValue:value forKey:key];
}
free(ivars);
c = [c superclass];
}
}
@end
這時瓣戚,可以在DWPerson.m中取代之前的代碼
- (void)encodeWithCoder:(NSCoder *)aCoder {
[self encode:aCoder];
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
if (self = [super init]) {
[self decode:aDecoder];
}
return self;
}
當(dāng)然還可以更加簡潔端圈,把NSCoding的定義成宏
#ifndef Archive_h
#define Archive_h
#import "NSObject+ArchiveExtension.h"
#define ArchiveImplemention \
\
- (instancetype)initWithCoder:(NSCoder *)aDecoder {\
if (self = [super init]) {\
[self decode:aDecoder];\
}\
return self;\
}\
\
- (void)encodeWithCoder:(NSCoder *)aCoder {\
[self encode:aCoder];\
} //最后這句不用斜線
#endif /* Archive_h */
注:
如果有空行,需要加斜線補(bǔ)充
使用:
//宏定義
#import "Archive.h"
@implementation DWPerson
ArchiveImplemention
@end
五子库、動態(tài)添加方法
在viewController使用
- (IBAction)clickBtn:(id)sender {
[self performSelector:@selector(play)];
}
void play(id self, SEL _cmd) {
NSLog(@"動態(tài)添加成功");
UIViewController *vc = [UIViewController new];
vc.view.backgroundColor = [UIColor redColor];
[self presentViewController:vc animated:YES completion:nil];
}
+ (BOOL)resolveInstanceMethod:(SEL)aSEL
{
if (aSEL == NSSelectorFromString(@"play")) {
class_addMethod(self, aSEL, (IMP) play, "v@:");
return YES;
}
return [super resolveInstanceMethod:aSEL];
}
六舱权、實現(xiàn)字典和模型的自動轉(zhuǎn)換
獲取屬性的列表的方法是字典轉(zhuǎn)模型的比較核心的方法