runtime應(yīng)用場(chǎng)景-歸檔/KVO內(nèi)部實(shí)現(xiàn)原理

新建一個(gè)OSX命令行項(xiàng)目,是一個(gè)然后看代碼加命令行,顯示結(jié)果

main.m文件

#import <Foundation/Foundation.h>
#import "TTSleep.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // OC的代碼
        TTSleep *tt  = [[TTSleep alloc] init];
        
        /** 底層c的代碼
         TTSleep *tt = ((TTSleep *(*)(id, SEL))(void *)objc_msgSend)((id)((TTSleep *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("TTSleep"), sel_registerName("alloc")), sel_registerName("init"));
         */
    }
    return 0;
}
命令行編譯OC語(yǔ)言

將OC編譯成c語(yǔ)言的東西:cd到此目錄下,然后:clang -rewrite-objc main.m
你就可以看到


文件名

runtime的兩個(gè)必要常識(shí):

1.Method :成員方法
2.Ivar:成員變量
快捷鍵:cmd+shift+0 :打開(kāi)官方文檔

runtime的函數(shù):

1.class_copyIvarList 拷貝出成員變量列表
2.class_copyMethodList 成員方法

message的函數(shù):

1.objc_msgSend:給某一個(gè)對(duì)象發(fā)送消息
2.objc_msgSendSuper:給對(duì)象父類(lèi)發(fā)送消息

兩者開(kāi)頭不同

Ivar的函數(shù)

ivar_getName:(<#Ivar v#>) :給我一個(gè)ivar給你一個(gè)名稱(chēng)

runtime的應(yīng)用場(chǎng)景:

1.歸檔

在Controllers中寫(xiě)一些方法含義和變量含義

#import "ViewController.h"
#import <objc/runtime.h>
#import <objc/message.h>
#import "TTPerson.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    unsigned int count = 0;
    /**
     在controller中runtime實(shí)現(xiàn)
     runtime是底層C的庫(kù),所以C用的最多的就是指針指向首地址
     第一個(gè):class 第二個(gè):conut的指針地址(可以在內(nèi)部改變值)
     */
    Ivar *ivars = class_copyIvarList([TTPerson class], &count);
    NSLog(@"%d",count); /// 此時(shí)的count就是你這個(gè)類(lèi)里面的屬性列表
    for (int i =0; i<count; i++) {
        // 取出屬性
        Ivar ivar = ivars[i]; ///C語(yǔ)言的指針就像OC中的數(shù)組,根據(jù)角標(biāo)獲取值
        // 查看變量名稱(chēng)
      const char *name = ivar_getName(ivar);
        NSLog(@"%s",name); // 這里%s就是c里面的字符占位符
    }
}

在自定義類(lèi)中實(shí)現(xiàn)歸檔

//  Copyright ? 2016年 糖糖. All rights reserved.
/// 歸檔

#import "TTPerson.h"
#import <objc/runtime.h>
#import <objc/message.h>

@interface TTPerson ()<NSCoding> // 實(shí)現(xiàn)歸檔協(xié)議

@end

@implementation TTPerson
/// 歸檔
-(void)encodeWithCoder:(NSCoder *)aCoder{
    
    [aCoder encodeObject:self.name forKey:@"name"];
    /* 所有屬性寫(xiě)完:如果有N多個(gè)屬性,歸檔會(huì)寫(xiě)很多冗余的代碼*/
    
    
    /** runtime實(shí)現(xiàn)歸檔
     count:裝載成員屬性個(gè)數(shù),可以在runtime內(nèi)部改變
     
     */
    unsigned int count = 0;
    //取出成員屬性列表
    Ivar *ivars = class_copyIvarList([TTPerson class], &count);
  
    for (int i =0; i<count; i++) {
        // 取出屬性
        Ivar ivar = ivars[i]; ///C語(yǔ)言的指針就像OC中的數(shù)組,根據(jù)角標(biāo)獲取值
        
        // 查看變量名稱(chēng)
        const char *name = ivar_getName(ivar);
        // 根據(jù)key-value獲取屬性
        NSString *key = [NSString stringWithUTF8String:name];
        // 歸檔
        [aCoder encodeObject:[self valueForKey:key] forKey:key];
    }
    // 在C語(yǔ)言中  只要用到了 copy/new/creat 就一定要釋放(不然會(huì)造成內(nèi)存泄露)
    free(ivars);
    
}
/// 解檔
- (instancetype)initWithCoder:(NSCoder *)coder
{
    if (self = [super init]) {
        unsigned int count = 0;
        //取出成員屬性列表
        Ivar *ivars = class_copyIvarList([TTPerson class], &count);
        
        for (int i =0; i<count; i++) {
            // 取出屬性
            Ivar ivar = ivars[i]; ///C語(yǔ)言的指針就像OC中的數(shù)組,根據(jù)角標(biāo)獲取值
            
            // 查看變量名稱(chēng)
            const char *name = ivar_getName(ivar);
            // 根據(jù)key-value獲取屬性
            NSString *key = [NSString stringWithUTF8String:name];
            // 解檔
            id value = [coder decodeObjectForKey:key];
            //設(shè)置到成員變量上面(KVC設(shè)置)
            [self setValue:value forKey:key];
        }
        // 在C語(yǔ)言中  只要用到了 copy/new/creat 就一定要釋放(不然會(huì)造成內(nèi)存泄露)
        free(ivars);
    }
    return self;
}

@end

2.KVO內(nèi)部實(shí)現(xiàn)原理:
東西有點(diǎn)多涉及到了三個(gè)類(lèi)

KVO原理

控制器

##控制器中
//  Copyright ? 2016年 糖糖. All rights reserved.
//

#import "ViewController.h"
#import <objc/runtime.h>
#import <objc/message.h>
#import "TTPerson.h"
#import "TTDog.h"

@interface ViewController ()
// 強(qiáng)引用
@property (nonatomic,strong)TTPerson *person;
@property (nonatomic,strong)TTDog *dog;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.person = [[TTPerson alloc] init];
    self.dog = [[TTDog alloc] init];
    /**注冊(cè)監(jiān)聽(tīng)
     NSKeyValueObservingOptionNew:傳遞新值
     KVO內(nèi)部實(shí)現(xiàn)原理:通過(guò)runtime動(dòng)態(tài)的傳遞了一個(gè)對(duì)象。
     在運(yùn)行的時(shí)候給TTDog動(dòng)態(tài)的創(chuàng)建并重寫(xiě)了一個(gè) [NSKVONotifying_TTDog setAge]
     */
    [self.dog addObserver:self.person forKeyPath:@"age" options:NSKeyValueObservingOptionNew context:nil];
}

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    self.dog.age = 10;
}

Person類(lèi):只貼寫(xiě)了代碼的地方

//  Copyright ? 2016年 糖糖. All rights reserved.

#import "TTPerson.h"

@interface TTPerson ()

@end

@implementation TTPerson
/// 監(jiān)聽(tīng)到object的keyPath屬性變化為change
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context{
    NSLog(@"監(jiān)聽(tīng)到%@的%@屬性變化為%@",object,keyPath,change);
}
@end

TTDog類(lèi)

//  Copyright ? 2016年 糖糖. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface TTDog : NSObject
@property (nonatomic,assign)NSInteger age;
@end

創(chuàng)建一個(gè)dog的子類(lèi)解釋KVO原理

//  Copyright ? 2016年 糖糖. All rights reserved.
//

#import "NSKVONotifying_TTDog.h"

@implementation NSKVONotifying_TTDog
-(void)setAge:(NSInteger)age{
    [super setAge:age];
    // 在子類(lèi)中調(diào)用兩個(gè)方法:內(nèi)部都會(huì)調(diào)用TTPerson的observeValueForKeyPath 方法
    [self willChangeValueForKey:@"age"]; //即將改變的時(shí)候獲取舊值
    [self didChangeValueForKey:@"age"]; // 完成改變時(shí)獲取新值
}
輸出的日志信息

今天就先到這里吧,要七夕了,我和代碼有個(gè)約會(huì),晚安了,各位男神女神們。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容