KVC、KVO、RunTime、RunLoop

KVC-->Key-Value Coding: 键值编码 (KVC)

原理:KVC运用了一个isa-swizzling技术. isa-swizzling就是类型混合指针机制, 将2个对象的isa指针互相调换, 就是俗称的黑魔法.

KVC主要通过isa-swizzling, 来实现其内部查找定位的. 默认的实现方法由NSOject提供

  isa指针, 如其名称所指,(就是is a kind of的意思), 指向分发表对象的类. 该分发表实际上包含了指向实现类中的方法的指针, 和其它数据。

KVC是一种非正式的Protocol,提供一种机制来间接访问对象的属性

获取值方式:

1.valueForKey: 传入NSString属性的名字。
2.valueForKeyPath: 属性的路径,xx.xx
3.valueForUndefinedKey 默认实现是抛出异常,可重写这个函数做错误处理

修改值方式:

1.setValue:forKey:
2.setValue:forKeyPath:
3.setValue:forUnderfinedKey:
4.setNilValueForKey: 对非类对象属性设置nil时调用,默认抛出异常。

举例:Person对象有2个属性  name(NSSting),age(NSInteger) KVC赋值代码如下

    Person *person = [[Person alloc]init];

    //KVC进行赋值

    [person setValue:[NSNumber numberWithInteger:18] forKey:@"age"];

    [person setValue:@"Jany" forKey:@"name"];

KVC字典转模型赋值

//KVC 字典转模型 字典要和model中的属性一一对应  为防止闪退报错重写model中setValue: forUndefinedKey:   方法

    NSDictionary *dict = @{@"name":@"Jenny",@"age":@26};

    [person setValuesForKeysWithDictionary:dict];

 

 

KVO--> Key-Value Observing 键值观察(KVO), KVO是观察者模式的一种应用

原理:通过对某个对象的某个属性添加观察者,若该属性值改变,就会调用observeValueForKeyPath:方法

给上面的Person对象person 添加观察者  观察name属性变化 代码如下

//KVO 添加观察者  观察person对象中属性name的值改变

[person addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:nil];

观察者监听到值改变了回调方法

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {

    NSLog(@"keyPath: %@", keyPath);

    NSLog(@"object: %@", object);

    NSLog(@"change: %@", change);

    NSLog(@"context: %@", context);

}

全代码如下

#import "Person.h"

#import <objc/message.h>

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {

    [super viewDidLoad];

  

    Person *person = [[Person alloc]init];

    //KVO 添加观察者  观察person对象中属性name的值改变

    [person addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:nil];

    //KVC进行赋值

    [person setValue:[NSNumber numberWithInteger:18] forKey:@"age"];

    [person setValue:@"Jany" forKey:@"name"];

    

  

    //KVC 字典转模型 字典要和model中的属性一一对应  为防止闪退报错重写model中setValue:(id)value forUndefinedKey:   方法

    NSDictionary *dict = @{@"name":@"Jenny",@"age":@26};

    [person setValuesForKeysWithDictionary:dict];

    

    NSLog(@"%zd",person.age);

}

 

// 观察者监听到之后回调方法

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {

    NSLog(@"keyPath: %@", keyPath);

    NSLog(@"object: %@", object);

    NSLog(@"change: %@", change);

    NSLog(@"context: %@", context);

}

KVO使用场景:

1、下拉刷新、下拉加载监听UIScrollerView的ContentOffset方法

2、webView混排监听contentsize

3、监听模型属性实时更新UI

4、监听控制器frame改变,实现抽屉效果

 

 

RunTime

简介:RunTime简称运行时。OC就是运行时机制,也就是在运行时候的一些机制,其中最主要的是消息机制。

作用:

1.发送消息。 objc_msgSend只有对象才能发送消息,因此以objc开头。使用消息机制前提必须导入#import <objc/message.h>

举例:Person中声明两个方法-(void)eat;  +(void)eat;

调用对象方法 

[person eat];

本质让对象发送消息  objc_msgSend(person,@selector(eat));

 

调用类方法:两种方式

1.[Person eat]; 

2.[[Person class] eat];

本质让类对象发送消息 objc_msgSend([Person class],@selector(eat));

 

2.交换方法

开发使用场景:系统自带的方法功能不够,给系统自带的方法扩展一些功能,并保持原有的功能。

方式:

1.集成系统的类,重写方法

2.使用runtime,交换方法

举例:创建UIImage类别Image  

.m中实现方法交换代码

#import <objc/message.h>

@implementation UIImage (Image)

 

+ (void)load{

    //获取imageWithName方法地址

    Method imageWithName = class_getClassMethod(self, @selector(imageWithName:));

    

    //获取imageName方法地址

    Method imageName = class_getClassMethod(self, @selector(imageNamed:));

    

    method_exchangeImplementations(imageWithName, imageName);

    

}

// 不能在分类中重写系统方法imageNamed,因为会把系统的功能给覆盖掉,而且分类中不能调用super.

 

// 既能加载图片又能打印

+ (instancetype)imageWithName:(NSString *)name

{

    // 这里调用imageWithName,相当于调用imageName

    UIImage *image = [self imageWithName:name];

    if (image == nil) {

        NSLog(@"加载空的图片");

    }

    return image;

    

}

实现:

 [UIImage imageNamed:@"123"];  //方法交换imageNamed-->imageWithName

 

 

3.给分类添加属性

说明:可以为已有的类添加方法,但是却不能直接添加属性,因为即使你添加了@property,它既不会生成实例变量,也不会生成setter、getter方法,即使你添加了也无法使用。所以我们首先需要自己去添加setter、getter方法,这个好办,直接在.m文件里加就可以了,但是要真正添加可以使用的属性,还需要利用Runtime来关联对象

原理:给一个类声明属性,其实本质就是给这个类添加关联,并不是直接把这个值的内存空间添加到类存空间。

实现:创建一个NSObject类别  名为Property,添加属性为name   @property (nonatomic,strong)NSString *name;

@interface NSObject (Property)

@property (nonatomic,strong)NSString *name;

@end

 

#import <objc/message.h>

 

// 定义关联的key

static const char *key = "name";

@implementation NSObject (Property)

- (NSString *)name{

    // 根据关联的key,获取关联的值。

    return objc_getAssociatedObject(self, key);

}

 

- (void)setName:(NSString *)name

{

    // 第一个参数:给哪个对象添加关联

    // 第二个参数:关联的key,通过这个key获取

    // 第三个参数:关联的value

    // 第四个参数:关联的策略

    objc_setAssociatedObject(self, key, name, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

}

调用:

//runtime给NSObject对象添加属性name(通过类别property)

    NSObject *objc = [[NSObject alloc]init];

    objc.name = @"Jany";

    NSLog(@"%@",objc.name);

RunTime还有些其他的功能

4.动态添加方法

5.字典转模型 

 

 

RunLoop  

说明:

一般来讲,一个线程一次只能执行一个任务,执行完成后线程就会退出。如果我们需要一个机制,让线程能随时处理事件但并不退出.

基本作用:

1.保持程序的持续运行

2.处理App中的各种事件(比如触摸事件、定时器事件、Selector事件)

3.节约CPU资源,提高程序性能:该做事时做事,该休息时休息

. . . . . . 

main函数中的RunLoop

int main(int argc, char * argv[]) {

    @autoreleasepool {

        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));

    }

}

UIApplicationMain函数内部就启动了一个RunLoop,所以UIApplicationMain函数一直没有返回,保持了程序的持续运行,这个默认启动的RunLoop是跟主线程相关联的

RunLoop与线程

1.每条线程都有唯一的一个与之对应的RunLoop对象

2.主线程的RunLoop已经自动创建好了,子线程的RunLoop需要主动去创建

3.RunLoop在第一次获取时创建,在线程结束时销毁

 

获得RunLoop对象

1.Foundation

NSRunLoop *mainRunLoop = [NSRunLoop mainRunLoop];//获得主线程的RunLoop对象

NSRunLoop *currentRunLoop = [NSRunLoop currentRunLoop];//获得当前线程的RunLoop对象

2.Core Foundation

CFRunLoopGetMain()

CFRunLoopGetCurrent()

 

NSRunLoop使用须知

1.NSLog(@"%@",[NSRunLoop currentRunLoop]);打印当前线程的RunLoop,懒加载模式,一条线程对应一个RunLoop对象,有返回,没有创建,主线程的RunLoop默认创建,子线程的RunLoop需要手动创建,[NSRunLoop currentRunLoop],同一个线程中若是创建多个RunLoop,则返回的都是同一个RunLoop对象,一个RunLoop里会有多个mode运行模式(系统提供了5个),但运行时只能指定一个RunLoop,若是切换RunLoop,则需要退出当前的RunLoop

2.定时器NSTimer问题:1:若是创建定时器用timerWithTimeInterval,则需要手动将定时器添加到NSRunLoop中,指定的运行模式为default,但是如果有滚动事件的时候,定时器就会停止工作。解决办法:更改NSRunLoop的运行模式,UITrackingRunLoopMode界面追踪,此模式是当只有发生滚动事件的时候才会开启定时器。若是任何时候都会开启定时器: NSRunLoopCommonModes,

 NSRunLoopCommonModes = NSDefaultRunLoopMode + UITrackingRunLoopMode

 占用,标签,凡是添加到NSRunLoopCommonModes中的事件爱你都会被同时添加到打上commmon标签的运行模式上

3. 1:scheduledTimerWithTimeInterval此方法创建的定时器默认加到了NSRunLoop中,并且设置运行模式为默认。 2:若是想在子线程开启NSRunLoop:需要手动开启:NSRunLoop *currentRunloop = [NSRunLoop currentRunLoop];等到线程销毁的时候currentRunloop对象也随即销毁。2:在子线程的定时器,需要手动加入到runloop:不要忘记调用run方法

 

代码案例:

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{

    [self timer1];

//    [self timer2];

}

 

//在runloop中有多个运行模式,但是runloop只能选择一种模式运行

//mode里面至少要有一个timer或者是source

-(void)run

{

    

    NSLog(@"子线程开启Runloop中添加timer事件,让线程持续存");

    

}

//[NSTimer scheduledTimerWithTimeInter..]此方法创建的定时器默认加到了NSRunLoop中,并且设置运行模式为默认

-(void)timer2

{

    NSRunLoop *currentRunloop = [NSRunLoop currentRunLoop];

    //该方法内部自动添加到runloop中,并且设置运行模式为默认

    [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];

    //开启runloop

    [currentRunloop run];

}

 

//timerWithTimeInterval,需要手动将定时器添加到NSRunLoop中,指定的运行模式为default

- (void)timer1{

    NSRunLoop *currentRunloop = [NSRunLoop currentRunLoop];

    [currentRunloop addTimer:[NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES] forMode:NSDefaultRunLoopMode];

    //控制循环时间

//    [currentRunloop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:10]];

}

 

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了小程序应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值