事件循环NSRunLoop

1、run loop概念

NSRunLoop类封装了线程进入事件循环的过程,一个runloop实例就表示了一个线程的事件循环。更具体的说,在iOS开发框架中,线程每次执行完成程序员自定义的代码之后,都会检查当前线程对应的run loop中是否还有其他事件源,如果还有,那么线程就不会终止。

处于事件循环的线程接收的事件源有两种:input source 和 timer source。线程调用便利函数 [NSTimer scheduledTimerWithTimeInterval: target: selector: userInfo: repeats:] 在创建一个NSTimer实例的同时,以默认模式Default mode在当前线程的run loop中注册了一个timer source,并且把新创建的timer添加到run loop中,作为事件的观察者。

不过每个线程在创建定时器的时候不立刻把它添加到run loop中,只需要调用 [NSTimer timerWithTimeInterval: target: selector: userInfo: repeats:],或者可以调用 [[NSTimer alloc]  initWithFireDate:interval:target:selector:userInfo:repeats: ],两种方法等效。然后再使用[NSRunloop currentRunLoop]获取对应的事件循环对象,再调用 [runloop  addTimer: forMode:] 方法,那么就会注册一个定时事件,在这个定时器失效之前,当前线程就回去检查事件源,不会直接终止,而是会处于等待事件发生的状态。

以主线程为例,当线程执行完成程序员定义的代码之后,就会检查run loop中的事件,而不会终止。不过需要注意,如果主线程请求了同步信号量,也会阻塞,可以实现同步网络请求,防止main thread和web thread以外的线程更新界面,导致crash。

2、run loop mode类型

在不同run loop mode下运行的线程,运行过程有所不同,线程只会检查当前mode下的事件源。例如:

- (void)viewDidLoad
{
[super viewDidLoad]; NSTimer * timer = [NSTimer scheduledTimerWithTimeInterval:
target:self
selector:@selector(printMessage:)
userInfo:nil
repeats:YES];
}

这个时候如果我们在界面上滚动一个scrollview,那么我们会发现在停止滚动前,控制台不会有任何输出,就好像scrollView在滚动的时候将timer暂停了一样,这其实就是应为run loop处于不同的mode。

添加一个NSTimer到当前的runloop中的同时,还必须要设定事件源的run loop mode,而当scrollView滚动的时候,当前的MainRunLoop对象是处于UITrackingRunLoopMode的模式下,在这个模式下,并不会处理NSDefaultRunLoopMode的消息(因为线程所处的run loop mode和事件源的run loop mode不匹配),要想在scrollView滚动的同时也接受其它runloop的消息,我们需要改变两者之间的run loop mode.

[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

3、定时器NSTimer

上文中其实已经讲解了定时器的某些用法,也就是可以使用NSTimer的实例在一个NSRunLoop实例中注册一个定时事件源,并且把这个timer实例注册为这个事件的观察者。

一个定时器和定时事件是绑定的,使用定时器中的fire方法和invalidate方法来控制一个timer的生命周期,无重复的定时器在fire后立即invalidate,对于不断重复的timer来说,就需要手动invalidate。或者可以更改一个定时器触发的日期,如果触发日期是distant past,那么就会立即触发。如

//关闭定时器 
[myTimer setFireDate:[NSDate distantFuture]];
//开启定时器 
[myTimer setFireDate:[NSDate distantPast]];

当一个作为监听者的NSTimer实例被触发的时候,线程将会调用这个NSTimer实例的动作回调方法,有一种方法可以方便的传递多个参数,那就是使用调用类NSInvocation类的实例。

#import <Foundation/Foundation.h>
#import "MyClass.h" int main (int argc, const char * argv[])
{
@autoreleasepool{
MyClass *myClass = [[MyClass alloc] init];
NSString *myString = @"My string"; //普通调用
NSString *normalInvokeString = [myClass appendMyString:myString];
NSLog(@"The normal invoke string is: %@", normalInvokeString); //NSInvocation调用
SEL mySelector = @selector(appendMyString:);
NSMethodSignature * sig = [[myClass class] instanceMethodSignatureForSelector: mySelector]; NSInvocation * myInvocation = [NSInvocation invocationWithMethodSignature: sig];
[myInvocation setTarget: myClass];
[myInvocation setSelector: mySelector]; [myInvocation setArgument: &myString atIndex: ]; NSString * result = nil;
[myInvocation retainArguments];
[myInvocation invoke];
[myInvocation getReturnValue: &result];
NSLog(@"The NSInvocation invoke string is: %@", result); return ;
}
}

前两个参数是隐藏参数self和_cmd,对应target和selector,所以自定义参数从索引2开始。

4、日期对象 NSDate, NSDateFormatter

NSDate的实例表示一个日期,线程可以借助于NSDateFormatter的实例实现NSDate对象和NSString对象的相互转换。

// date方法返回的就是当前时间(now)
NSDate *date = [NSDate date];
// now: 11:12:40
// date: 11:12:50
date = [NSDate dateWithTimeIntervalSinceNow:];//返回当前时间10秒后的时间
// 从1970-1-1 00:00:00开始
date = [NSDate dateWithTimeIntervalSince1970:];//返回1970-1-1 00:00:00时间10秒后的时间
// 随机返回一个比较遥远的未来时间
date = [NSDate distantFuture];
// 随机返回一个比较遥远的过去时间
date = [NSDate distantPast];
// 返回1970-1-1开始走过的毫秒数
NSTimeInterval interval = [date timeIntervalSince1970];
// 跟其他时间进行对比
NSDate *date2 = [NSDate date];
// 返回比较早的那个时间
[date earlierDate:date2];
// 返回比较晚的那个时间
[date laterDate:date2]; //获取两个时间的时间差
[date1 timeIntervalSinceDate date2]; NSDate *date = [NSDate date];
// 2015-04-07 11:14:45
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
// HH是24进制,hh是12进制
formatter.dateFormat = @"yyyy-MM-dd HH:mm:ss";
// formatter.locale = [[[NSLocale alloc] initWithLocaleIdentifier:@"zh_CN"] autorelease];
NSString *string = [formatter stringFromDate:date];
NSDate *date2 = [formatter dateFromString:@"2016-03-09 13:14:56"];

关于KVC和KVO

1、KVC 键值编码

在什么场景下需要KVC?最简单的一种应用场景,如果一个控件的属性被声明为@property(nonatomic,readonly)只读,那么就只能通过KVC去修改这个属性,比如当我们需要用自定义tabBar替换UITabBarController中的原始tabBar的时候。

2、KVO 键值监听

Cocoa开发框架实现了通知机制,程序员无需编写太多代码就可以创建观察者,可以实现数据改变后对每个观察者的通知,应用场景可以是数据模型被一个控制器改变后,通知其它控制器。

- (void)setFinished:(BOOL)finished2
{
[self willChangeValueForKey:@"isFinished"];
_finished = finished2;
[self didChangeValueForKey:@"isFinished"];
} - (void)setExecuting:(BOOL)executing2
{
[self willChangeValueForKey:@"isExecuting"];
_executing = executing2;
[self didChangeValueForKey:@"isExecuting"];
}

要实现KVO,一般的步骤为:

(1)假设PersonObject希望能够觉察到BankObject对象的accountBalance属性的任何变化。

(2)那么 PersonObject必须发送一个“addObserver:forKeyPath:options:context:”消息,注册成为 BankObject的accountBalance属性的观察者。“addObserver:forKeyPath:options:context:”方法在指定对象实例之间建立了一个连接。

(3)为了能够响应消息,观察者必须实现 “observeValueForKeyPath:ofObject:change:context:”方法。这个方法实现如何响应变化的消息。在这个方法里面我们可以跟自己的情况,去实现应对被观察对象属性变动的相应逻辑。

(4)如果遵循KVO规则的话,当被观察的属性改变时调用willChangeValueForKey和didChangeValueForKey,那么方法 “observeValueForKeyPath:ofObject:change:context:”会自动被调用。

关于ARC

众所周知iOS中的采用引用计数来实现垃圾回收,有两种情况不是ARC能够解决的:循环强引用和通过C代码分配堆内存,这就需要程序员的注意。

现在先忙别的,过几天再写。此外过几天再写与block相关的ARC问题,比如下面

1、循环引用

最常见的循环引用错误出现在block中:当block中捕获了self指针的时候,只要block存在,那么self表示的对象就永远不会被release。

@property(nonatomic, readwrite, copy) completionBlock completionBlock;

__weak typeof(self) weakSelf = self;
self.completionBlock = ^ {
if (weakSelf.success) {
weakSelf.success(weakSelf.responseData);
}
};

Cocoa Touch(六):App运行机制 NSRunLoop, KVC, KVO, Notification, ARC的更多相关文章

  1. Cocoa Touch的3种类的交流方式delegate/target/notification

    1. Delegate 在Cocoa Touch类对象运行的周期中,某一个时间点它会去调用一些指定类的指定函数来完成他自身所要完成的功能.这个”指定的类”,就称为这个类的委托类.”指定函数”则是一些在 ...

  2. 微信小程序-APP生命周期与运行机制

    QQ讨论群:785071190 开发微信小程序之前需要先了解微信小程序运行机制以及其生命周期,小程序APP生命周期需要先从app.js这个文件开始. 阅读过"微信小程序-代码构成" ...

  3. ARP协议格式、ARP运行机制入门学习

    相关学习资料 http://baike.baidu.com/view/149421.htm?fromtitle=ARP%E5%8D%8F%E8%AE%AE&fromid=1742212& ...

  4. NSTimer运行机制和线程问题

    A.首先要理解NSTimer运行机制和Runloop之间的关系: 1.IOS的Run Loops机制 Run Loops是线程的基础部份,任何线程,包括主结程,都包含了一个run loop对象,Coc ...

  5. Cocoa与Cocoa Touch的区别

    Cocoa是在Mac OS X系统上原生的一个编译环境.他包含两个框架,其实就是一系列的类库,Foundation和AppKit. 在你的iPhone等掌上设备上,使用的则是他的一个子类 - Coco ...

  6. Swift—Cocoa Touch设计模式-备

    目标(Target)与动作(Action)是iOS和OS X应用开发的中事件处理机制.   问题提出 如图所示是一个ButtonLabelSample案例设计原型图,其中包含一个标签和一个按钮,当点击 ...

  7. JVM内存结构,运行机制

    三月十号,白天出去有事情出去了一天,晚上刚到食堂就接到阿里电话, 紧张到不行,很多基础的问题都不知道从哪里说了orz: 其中关于JVM内存结构,运行机制,自己笔记里面有总结的,可当天还是一下子说不出来 ...

  8. [转载]JavaScript 运行机制详解:再谈Event Loop

    https://app.yinxiang.com/shard/s8/sh/b72fe246-a89d-434b-85f0-a36420849b84/59bad790bdcf6b0a66b8b93d5e ...

  9. 使用Xcode 5创建Cocoa Touch Static Library(静态库)

    转自:http://blog.csdn.net/jymn_chen/article/details/21036035 首先科普一下静态库的相关知识: 程序编译一般需经预处理.编译.汇编和链接几个步骤. ...

随机推荐

  1. Android Studio利用GitHub托管项目

    自定义View系列教程00–推翻自己和过往,重学自定义View 自定义View系列教程01–常用工具介绍 自定义View系列教程02–onMeasure源码详尽分析 自定义View系列教程03–onL ...

  2. Linux下MySQL的安装和启动(转载)

    原文链接:http://www.linuxidc.com/Linux/2016-07/133234.htm 一.MySQL各类安装方法的比较 在Linux系统下,MySQL有3种主要的安装方式,分别是 ...

  3. 第2课 C到C++的升级

    C与C++的关系: C++在C语言的基础上进行的升级最重要的是两个方面: 1.类型的加强,C++更称得上是高级语言,C++语言对于类型的检查更严格 2.面向对象的支持,天生支持面向对象软件开发方法学 ...

  4. AlertDialog中使用ListView绑定数据

    在实际工作过程中,单单使用AlertDialog的单选功能不一定能满足我们的需求,需要绑定数据到 listview 1. 自定义Layout LayoutInflater factory = Layo ...

  5. office 2013 图标编辑

    1,找图标,网站:https://www.iconfinder.com/ 2,找到图标后用qq截图,之后粘贴到Adobe Illustrator 里边. 3,选定图像后,点击对象->图像描摹-& ...

  6. wpf 客户端【JDAgent桌面助手】业余开发的终于完工了。。晒晒截图

    目录区域: 业余开发的wpf 客户端终于完工了..晒晒截图 wpf 客户端[JDAgent桌面助手]开发详解-开篇 wpf 客户端[JDAgent桌面助手]详解(一)主窗口 圆形菜单... wpf 客 ...

  7. ambassador 学习七 Mapping说明

    mapping 通过rest 资源与k8s 的service进行关联,ambassador 必须有一个或者多个提供访问servide 的mapping定义 mapping 可以包含的配置 rewrit ...

  8. 采用轻量ServiceMesh实现灰度发布的实践

    软件总会有缺陷的,解决问题的同时往往会引入新的问题,关键是看这些问题是否在我们的控制范围内,“灰度发布”就是让问题受控的方法之一. 前言 我们的 CTO 经常说:“研发团队最首要的任务是提供稳定的服务 ...

  9. JSP动作指令

    JSP动作指令 动作指令与编译指令不间,编译指令是通知 Servlet 引擎的处理消息,而动作指令只是运行时的脚本动作.编译指令在将JSP 编译成 Servlet 时起作用:处理指令通常可替换成 Ja ...

  10. FT5X06 如何应用在10寸电容屏

    硬件搭起来看现象,如下图: 红色区域是FT5406上报有效数据的范围(1280*600),以左上角为原点 ,X轴方向上报数据的最大值1280,Y轴方向上报的最大数据是600..但是我用的LG的10.1 ...