RunLoop的学习总结.doc_第1页
RunLoop的学习总结.doc_第2页
RunLoop的学习总结.doc_第3页
RunLoop的学习总结.doc_第4页
RunLoop的学习总结.doc_第5页
已阅读5页,还剩11页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

RunLoop的学习总结一. RunLoop相关概念1. 什么是RunLoopRunLoop与线程相关且是基础框架的一部分。一个RunLoop就代表一个事件处理循环,它可以不停的调度工作以及处理输入事件。使用RunLoop的目的是有效的控制线程的执行和休眠,让线程在有工作的时候忙于工作,而在没工作的时候处于休眠状态。如果不使用RunLoop类似的循环机制,线程执行完当前任务队列中的任务就结束了,程序不能持续运行。也可以把RunLoop理解成一个高级的死循环,这个死循环可以让程序持续运行,且可以时刻监听和处理各种事件。每一个线程都有唯一对应的RunLoop,主线程的RunLoop是默认开启的;子线程的RunLoop要显示开启且至少添加一个事件源source。我们不需要显示的创建RunLoop,因为RunLoop是懒加载的,在 Cocoa 和 Core Fundation 中都提供了关于RunLoop对象的API来帮助配置和管理线程对应的RunLoop。2. RunLoop的简单剖析下图是RunLoop与Source的联系图,图中的左边方框代表一个线程,线程的开始Start到结束End之间有一个RunLoop,当这个RunLoop一直存在的时候,线程就不会销毁。方框中黄色的圈圈代表一个RunLoop,圈圈左边代表当有事件源时,RunLoop就会被唤醒(线程也随之被唤醒),RunLoop会先检测定时事件源,再检测关于performSelector:onThread:的事件源,再检测自定义事件源,最后检测基于端口的事件源。圈圈右边代表当没有事件源时,RunLoop和线程都处于睡眠状态。当然RunLoop也可以通过runUntilDate:方法设定过期时间来退出,当时间到的时候,RunLoop退出,线程也随之销毁。图中的右边代表事件源的类型,它们分别是:基于端口的事件源、自定义事件源、关于performSelector:onThread:的事件源、定时事件源,前三种又统称为输入事件源。只有当RunLoop存在时,才能保证这些事件源能被处理;如果RunLoop不存在,当前线程运行到End时,线程就会被销毁了,之后如果再有事件源尝试在这个线程中处理事件,系统就会崩溃报错。RunLoop与Source的联系示意图:提示:输入事件源传递异步事件,通常消息来自于其它线程或程序;定时事件源传递同步事件,事件发生在特定时间或者重复的时间间隔。RunLoop会在处理事件之前发出通知,但要监听这些通知,必须注册一个观察者observer添加到RunLoop中才可监听。3. RunLoop的运行模式一个RunLoop要能运行,必须要有一个运行模式。RunLoop的一个运行模式是所有要监听的输入源、定时源、观察者的集合。当要运行一个RunLoop时,必须指定(无论显示还是隐式指定)一个运行模式。在RunLoop的运行过程中,只有和模式相关的输入源和定时源才会被处理,只有和模式相关的观察者才会被激活。和其它运行模式相关的输入源、定时源、观察者,只有在其相关的模式下才能被运行,否则处于暂停状态。通过指定RunLoop的运行模式可以使得RunLoop在某一阶段过滤来源于源的事件。大多数时候,RunLoop都是运行在系统定义的默认模式上。CFRunLoopModeRef对象代表RunLoop的一个运行模式,一个Runloop对象可以有多个运行模式,但至少有一个运行模式,每个运行模式内又包含若干个Source/Timer/Observer,运行模式内的Source/Timer/Observer可以没有,但是如果没有,RunLoop对象运行时就直接退出了。当要切换运行模式时,必须先停止当前的运行模式,才能启动新的运行模式,这样做主要是为了分隔开不同组的Source/Timer/Observer,让其互不影响。系统默认注册的5个运行模式:NSDefaultRunLoopMode:App的默认Mode,通常主线程是在这个Mode下运行UITrackingRunLoopMode:界面跟踪Mode,用于ScrollView追踪触摸滑动,保证界面滑动时不受其他Mode的影响UIInitializationRunLoopMode: 刚启动App时进入的第一个Mode,启动完成后就不再使用,开发中一般不用GSEventReceiveRunLoopMode: 接受系统事件的内部Mode,通常用不到提示: 在Core Foundation的底层有一个mutable set类型的集合Common Modes,集合中保存着NSDefaultRunLoopMode和UITrackingRunLoopMode,NSRunLoopCommonModes是用来标记它们的,只要使用了NSRunLoopCommonModes,就相当于同时使用NSDefaultRunLoopMode和UITrackingRunLoopMode4. Source的分类按照官方文档:a. 基于端口的输入事件源 b. 自定义输入事件源 c. Cocoa关于performSelector的事件源 d. 定时事件源按照函数调用栈:a. Source0:非基于Port的源 b. Source1:基于Port的源,通过内核和其他线程通信,能接收和分发系统的事件 c. 定时事件源5. 输入事件源输入源异步的发送消息给你的线程。事件来源可以分为两种:基于端口的输入源和自定义输入源。基于端口的输入源监听程序相应的端口。自定义输入源则监听自定义的事件源。RunLoop不关心输入源的是基于端口还是自定义的。两类输入源的区别在于是谁发送: 基于端口的输入源由内核自动发送,而自定义的输入源则需要人工从其他线程发送。创建好了事件源,还需要把事件源分配给RunLoop的一个或多个运行模式,当RunLoop运行在被添加到的运行模式时,事件源才会被监听到。基于端口的输入事件源 Cocoa和Core Foundation支持与端口相关的对象和函数,用它们来创建的基于端口的源。在Cocoa中不需要直接创建输入源,只要简单地创建端口对象,并使用NSPort的方法把该端口添加到RunLoop中,端口对象会自己创建和配置输入源,基于端口的事件源处理完后不会自动从RunLoop中移除。在Core Foundation中,必须人工创建端口和它的事件源,可以使用端口相关的函数(CFMachPortRef,CFMessagePortRef,CFSocketRef) 来创建相关对象。 void createPortSource() / 创建消息端口 CFMessagePortRef messagePort = CFMessagePortCreateLocal(kCFAllocatorDefault, CFSTR(com.someport),myCallbackFunc, NULL, NULL); / 创建自定义基于端口的源 CFRunLoopSourceRef sourcePort = CFMessagePortCreateRunLoopSource(kCFAllocatorDefault, messagePort, 0); / 把自定义基于端口的源加入RunLoop CFRunLoopAddSource(CFRunLoopGetCurrent(), sourcePort, kCFRunLoopCommonModes); / 运行RunLoop CFRunLoopRun(); / RunLoop退出后移除自定义基于端口的源 CFRunLoopRemoveSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode); / 撤销引用 CFRelease(source);自定义输入事件源 要创建自定义输入源,只能使用Core Foundation里面与CFRunLoopSourceRef类型相关的函数来创建。可以使用回调函数来配置自定义输入源,当Core Fundation配置源时,会在多处地方调用回调函数,处理输入事件,当事件源被移除的时要清理它。除了定义自定义输入源的行为,还要定义消息传递机制;事件源的消息传递在线程里面实现,并负责在数据等待处理的时候传递数据给事件源并通知它处理数据;消息传递机制的定义逻辑取是随意的,但最好不要过于复杂。 void createCustomSource() / 定义自定义输入源的上下文 CFRunLoopSourceContext context = 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL; / 创建自定义输入源 CFRunLoopSourceRef source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context); / 把自定义输入源加入RunLoop CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode); / 运行RunLoop CFRunLoopRun(); / RunLoop退出后移除自定义输入源 CFRunLoopRemoveSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode); / 撤销引用 CFRelease(source);Cocoa关于performSelector的事件源 在Cocoa中,可以使用关于performSelector的方法自定义事件源,一个selector执行完后会自动从RunLoop里面移除。关于performSelector:onThread:的方法,目标线程要有一个活动的RunLoop,否则系统可能崩溃,因为目标线程可能已经被销毁。当目标线程是非主线程时,要显示开启RunLoop,才能保证程序正常执行。关于performSelector:afterDelay:的方法,也需要有一个活动的RunLoop,因为它会自动创建一个NSTimer对象加入当前的RunLoop,如果在子线程中使用,需要手动启动RunLoop,事件才会被处理。/ 在主线程中执行selectorperformSelectorOnMainThread:withObject:waitUntilDone:performSelectorOnMainThread:withObject:waitUntilDone:modes:/ 在指定线程中执行selector/ 目标线程必须要有一个活动的RunLoop,否则可能崩溃,因为目标线程可能已经被销毁performSelector:onThread:withObject:waitUntilDone:performSelector:onThread:withObject:waitUntilDone:modes:/ 在当前线程执行selector,可设置延迟时间/ 会自动创建一个NSTimer对象加入当前的RunLoop,如果在子线程中使用,需要手动启动RunLoop,事件才会被处理performSelector:withObject:afterDelay:performSelector:withObject:afterDelay:inModes:/ 在当前线程执行selectorperformSelector:performSelector:withObject:performSelector:withObject:withObject:/ 撤销消息cancelPreviousPerformRequestsWithTarget:cancelPreviousPerformRequestsWithTarget:selector:object:6. 定时事件源定时事件源在预设的时间点以同步方式传递消息。定时器可以通知线程在某一时间处理某件事,但定时器并不是实时机制。定时器可以配置成仅工作一次或重复工作,当它是仅工作一次时,处理完事件后定时器就会被自动移除出RunLoop;当是重复工作时,会一直存在于RunLoop中。如果定时器所在的运行模式不是当前RunLoop的运行模式,那么定时器将不会开始,只有RunLoop的运行模式是定时器所在的运行模式定时器才会被启动。类似的,如果定时器在运行期间,RunLoop的运行模式被切换了,定时器不在被切换的运行模式,定时器就会暂停。如果定时器在切换模式时,被延迟以至于它错过了一个或多个触发时间,那么被错过的触发事件会被忽略,定时器会在下一个最近的触发时间重新启动,后面的触发事件会正常的按照时间间隔执行。7. RunLoop的观察者事件源是在同步或异步事件发生时触发的,而RunLoop的观察者则是在RunLoop本身运行的特定情况下触发的。RunLoop的观察者是在事件即将被触发之前收到通知的,所以可以使用RunLoop的观察者来监听某一事件即将被触发,且可以在这些事件被触发前做一些准备工作。RunLoop的观察者可以仅用一次或循环使用,若仅用一次,那么在它被触发后,会把它自己从RunLoop里面移除,而循环使用的观察者则不会。要往RunLoop中添加观察者,只能使用Core Fundation的相关函数创建和添加观察者。观察者可以监听的状态为:typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) kCFRunLoopEntry = (1UL 0), / 即将进入 RunLoop,枚举成员对应的整数为1 kCFRunLoopBeforeTimers = (1UL 1), / 即将处理 Timer,枚举成员对应的整数为2 kCFRunLoopBeforeSources = (1UL 2), / 即将处理 Source,枚举成员对应的整数为4 kCFRunLoopBeforeWaiting = (1UL 5), / 即将进入休眠,枚举成员对应的整数为32 kCFRunLoopAfterWaiting = (1UL 6), / 刚从睡眠中唤醒,枚举成员对应的整数为64 kCFRunLoopExit = (1UL 7), / 即将退出 RunLoop,枚举成员对应的整数为128 kCFRunLoopAllActivities = 0x0FFFFFFFU / 监听所有状态;一般使用下面两个函数添加观察者,函数原型为: / allocator: 用于分配observer的内存空间 / activities: 用以设置observer要监听的状态 / repeats: 用于设置是否只监听一次 / order: 用于设置observer的优先级,一般为0 / block: 用于设置observer的回调代码 / observer: 当前的observer对象 / activity: 当前的状态 CFRunLoopObserverRef CFRunLoopObserverCreateWithHandler(CFAllocatorRef allocator, CFOptionFlags activities, Boolean repeats, CFIndex order, void (block) (CFRunLoopObserverRef observer, CFRunLoopActivity activity); / rl: observer要加入的RunLoop / observer: 要加入的观察者observer / mode: 要加入的运行模式 void CFRunLoopAddObserver(CFRunLoopRef rl, CFRunLoopObserverRef observer, CFStringRef mode);添加观察者举例: / 创建观察者observer CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, (CFRunLoopObserverRef observer, CFRunLoopActivity activity) / 回调,即将要切换到activity时调用 NSLog(-监听到RunLoop状态发生改变-%zd, activity); ); / 添加观察者,监听RunLoop在kCFRunLoopDefaultMode下的状态 CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode); / 释放Observer CFRelease(observer);提示:在使用Core Fundation中的函数时,凡是带有Create、Copy、Retain等字眼的函数,创建出来的对象,都需要在最后做一次release。release的方法:CFRelease(对象);8. RunLoop的事件队列处理逻辑每次运行RunLoop时,线程的RunLoop会自动处理之前未处理的消息,并通知相关的观察者。具体的顺序如下:判断当前的运行模式是否为空,为空直接退出RunLoop通知观察者RunLoop已经启动通知观察者任何即将要开始的定时器通知观察者任何即将要启动的非基于端口的源启动任何准备好的非基于端口的源如果基于端口的源准备好并处于等待状态,立即启动。并进入步骤9通知观察者线程进入休眠将线程置于休眠直到有下面的任一事件发生: a. 某一事件到达基于端口的源 b. 定时器启动 c. RunLoop设置的退出时间已到 d. RunLoop被显式唤醒通知观察者线程将被唤醒处理未处理的事件 a. 如果用户定义的定时器启动,处理定时器事件并重启RunLoop。进入步骤 3b. 如果输入源启动,传递相应的消息 c. 如果RunLoop被显式唤醒而且退出时间未到,重启RunLoop。进入步骤 3通知观察者RunLoop结束上面RunLoop的事件队列处理逻辑,可以通过分析Apple开源的Core Fundation代码中去理解,可以参考我的博客【关于RunLoop部分源码的注释】,也可以【在线浏览Core Fundation开源代码】和【下载Core Fundation开源代码】,还可以查看官方文档【Threading Programming Guide】里面的RunLoop部分的内容。补充:因为输入源和定时器的观察者是在相应的事件发生之前传递消息的,所以通知的时间和实际事件发生的时间之间可能存在误差。在运行RunLoop时,定时器和其它周期性事件经常需要被传递,当撤销RunLoop时,也会终止消息传递。二. RunLoop的使用场景仅当在子线程中才需要显式启动RunLoop,主线程是默认开启的。对于子线程,RunLoop的启动不是必须的,仅当在需要的时候才去配置并启动它。不需要在任何情况下都去配置和启动一个线程的RunLoop,因为当此线程在处理完所以任务时,它不能被自动释放,这就会占用一定的内存空间。比如,当使用子线程来处理一个预先定义的需要长时间运行的任务时,没必要启动RunLoop,因为这条线程所处理的任务会在特定的时间内会结束,线程运行期间不会自动销毁,只有在任务结束时才会被销毁,所以不需要用到RunLoop,用RunLoop的目的是在线程还需要处理任务时保证它不被销毁。RunLoop是在要和线程有更多的交互时才需要,比如以下情况:使用端口或自定义输入源来和其他线程通信使用线程的定时器在Cocoa中使用以performSelector开头的部分方法(上文已经解释)使线程周期性工作,即让线程常驻内存,等待其他线程发来消息,处理其他事件在特定的运行模式下执行特殊任务(如:图片延迟加载,默认模式不加载,滚动时才去加载图片)三. RunLoop对象在iOS中有2套API来访问和使用RunLoop,分别是Foundation和Core Foundation,它们都提供了配置输入源、定时器和RunLoop的观察者以及启动RunLoop的接口,NSRunLoop是基于CFRunLoopRef的一层OC包装。RunLoop在Cocoa中被称为NSRunLoop类的一个实例,而在Carbon或BSD程序中则是一个指向CFRunLoopRef类型的指针。每个线程都有唯一的与之关联的RunLoop对象。RunLoop对象不用人工显示创建,只要是第一次使用的时候系统会自动创建一个RunLoop对象,并会把它保存在一个字典中,当再次使用的时候直接从字典中取,这种机制也称为懒加载。可以通过官方开放源码进行分析。1. 获取RunLoop对象获取RunLoop对象有下面两种方式:在Cocoa程序中,使用NSRunLoop的currentRunLoop类方法来获取/访问NSRunLoop对象/ 获得当前线程的RunLoop对象NSRunLoop *runloop = NSRunLoop currentRunLoop;/ 获得主线程的RunLoop对象NSRunLoop *runloopMain = NSRunLoop mainRunLoop;使用Core Foundation的函数获取/访问RunLoop对象/ 获得当前线程的RunLoop对象CFRunLoopRef runloop = CFRunLoopGetCurrent(); / 获得主线程的RunLoop对象CFRunLoopRef runloopMain = CFRunLoopGetMain();虽然上面两种创建方式不一样,但是它们都指向同一个RunLoop,这两种获取方式可以在需要的时候混合使用。NSRunLoop类定义了一个getCFRunLoop方法,该方法可以返回一个CFRunLoopRef类型的RunLoop。CFRunLoopRef runloop = NSRunLoop currentRunLoop getCFRunLoop;2. 配置RunLoop在子线程中,要启动RunLoop,至少要在运行模式mode中添加一个输入源或定时器,因为RunLoop启动时先检查运行模式是否为空,如果为空就直接退出RunLoop。上文已经介绍过观察者的创建和添加,下面程序将用另外一个函数创建观察者,其中SJMThread类是继承自NSThread类,重写main方法,在RunLoop中添加定时器和观察者。#import SJMThread.himplementation SJMThread/* 观察者的回调函数 */void observerCallBack(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info) NSLog(%ld, activity);/* 线程的入口 */- (void)main / 1. 获取RunLoop NSRunLoop *myRunLoop = NSRunLoop currentRunLoop; / 2. 创建观察者 / 2.1 初始化一个观察者的上下文 CFRunLoopObserverContext context = 0, (_bridge void *)(self), NULL, NULL, NULL; / 2.2 设置观察者的回调函数 CFRunLoopObserverCallBack myRunLoopObserver = observerCallBack; / 2.3 创建观察者 CFRunLoopObserverRef observer = CFRunLoopObserverCreate(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, myRunLoopObserver, &context); / 2.4 如果观察者创建成功就添加到RunLoop if (observer) / 转换RunLoop的类型 CFRunLoopRef cfLoop = myRunLoop getCFRunLoop; CFRunLoopAddObserver(cfLoop, observer, kCFRunLoopDefaultMode); / 3. 创建一个定时器,scheduledTimerWithTimeInterval会把定时器自动添加到当前的RunLoop NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:selector(doFireTimer:) userInfo:nil repeats:YES; / 4. 连续启动10次RunLoop,是为了配合定时器的打印 / 4.1 循环次数 NSInteger loopCount = 10; do / 4.2 启动RunLoop,1秒后退出 myRunLoop runUntilDate:NSDate dateWithTimeIntervalSinceNow:1; loopCount -; while (loopCount);/* 定时器的定时调用方法 */- (void)doFireTimer:(NSTimer *)timer NSLog(-);end3. 启动RunLoop启动RunLoop只对程序的子线程才有意义,因为主线程是自动启动的不需要显示启动。一个RunLoop的运行模式mode至少要添加一个输入源或定时器,因为RunLoop启动时先检查运行模式是否为空,如果为空就直接退出RunLoop。需要注意的是,只添加观察者不添加输入源和定时器,RunLoop是启动不了的。有几种方式可以启动RunLoop,包括以下这些:无条件的启动RunLoop虽然无条件的启动RunLoop的方式很简单,但是RunLoop就不容易被控制,可能会使线程常驻内存。/ 方式1NSRunLoop currentRunLoop run;/ 方式2CFRunLoopRun();通过设置RunLoop的退出时间来启动RunLoop/ 默认是运行在NSDefaultRunLoopMode下,10秒后退出NSRunLoop currentRunLoop runUntilDate:NSDate dateWithTimeIntervalSinceNow:10;使用特定的运行模式启动RunLoop/ 在NSDefaultRunLoopMode下运行,10秒后退出NSRunLoop currentRunLoop runMode:NSDefaultRunLoopMode beforeDate:NSDate dateWithTIntervalSinceNow:10;/ 在kCFRunLoopDefaultMode下运行,10秒后退出,这个函数可以返回结束的原因resultCFRunLoopRunResult result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 10, YES);/ RunLoop的结束类型 typedef CF_ENUM(SInt32, CFRunLoopRunResult) kCFRunLoopRunFinished = 1, / 所有的Sources都被移除时退出 kCFRunLoopRunStopped = 2, / 手动调用CFRunLoopStop()退出 kCFRunLoopRunTimedOut = 3, / 时间超时退出 kCFRunLoopRunHandledSource = 4 / 与returnAfterSourceHandled配合使用,事件被分发处理后退出;4. 退出RunLoopRunLoop退出后,不意味着RunLoop马上被销毁,默认情况是在子线程被销毁的时候同时被销毁。退出RunLoop有两种方式:给RunLoop设置超时时间- (void)runUntilDate:(NSDate *)limitDate;- (BOOL)runMode:(NSString *)mode beforeDate:(NSDate *)limitDate;CFRunLoopRunResult CFRunLoopRunInMode(CFStringRef mode, CFTimeInterval seconds, Boolean returnAfterSourceHandled);通知RunLoop停止void CFRunLoopStop(CFRunLoopRef rl);通过移除RunLoop里面的所有输入源和定时器的方式退出RunLoop这种退出方式不靠谱,因为程序在运行过程中系统会添加一些输入源到RunLoop中,导致输入源和定时器的数量不确定,在移除输入源和定时器的时候,可能达不到预期效果。5. 线程安全和RunLoop对象线程是否安全取决于使用那些API来操作RunLoop。Core Foundation中的函数通常是线程安全的,可以被任意线程调用。但是如果修改了RunLoop的配置,然后需要执行某些操作,任何时候你最好还是在RunLoop所属的线程执行这些操 作。Cocoa的NSRunLoop类则不像Core Foundation具有与生俱来的线程安全性。如果想使用NSRunLoop类来操作RunLoop,也应该在RunLoop所属的线程里面完成这些操作。给属于不同线程的RunLoop添加输入源和定时器有可能导致你的代码崩溃或产生不可预知的行为。6. RunLoop对象和自动释放池在RunLoop中,系统通过Observer监听RunLoop的状态,一旦监听到RunLoop即将进入睡眠等待状态(kCFRunLoopBeforeWaiting),就释放旧的自动释放池,并在RunLoop被唤醒之前创建新的自动释放池。当我们在创建一个工程的时候,在main函数中会看到一个自动释放池,如int main(int argc, char * argv) autoreleasepool return UIApplicationMain(argc, argv, nil, NSStringFromClass(AppDelegate class); 在上面代码中,在主线程结束时,会给所有对象release一次。因为子线程是异步的,不能够使用主线程的自动释放池,所有当我们创建一个子线程时,应该在子线程的任务中创建一个自动释放池,本博文为了简单起见,很多地方都没有创建自动释放池,创建自动释放池的方式如下:#import ViewController.hinterface ViewController ()endimplementation ViewController- (void)viewDidLoad super viewDidLoad; self performSelectorInBackground:selector(test) withObject:nil;- (void)test / 自动释放池,加在最外面! autoreleasepool NSTimer *timer = NSTimer timerWithTimeInterval:3.0 target:self selector:selector(timerRun:) userInfo:nil repeats:NO; NSRunLoop currentRunLoop addTimer:timer forMode:NSDefaultRunLoopMode; NSRunLoop currentRunLoop runUntilDate:NSDate dateWithTimeIntervalSinceNow:4.0; - (void)timerRun:(NSTimer *)timer NSLog(%, timer);end四. RunLoop在开发中的使用1. 线程常驻内存#import ViewController.hinterface ViewController ()property (nonatomic, strong) NSThread *thread;endimplementation ViewController- (void)viewDidLoad super viewDidLoad; _thread = NSThread alloc initWithTarget:self selector:selector(threadRun) object:nil; _thread start;- (void)threadRun NSLog(%s,_func_); / 让线程常驻内存,只要有一个RunLoop在活动即可 / 像RunLoop添加输入源 NSRunLoop currentRunLoop addPort:NSPort port forMode:NSDefaultRunLoopMode; / 启动RunLoop NSRunLoop currentRunLoop run; /* 补充 * 与NSRunLoop currentRunLoop run;等价的代码有以下两种: * NSRunLoop currentRunLoop runMode:NSDefaultRunLoopMode beforeDate:NSDate distantFuture; * NSRunLoop currentRunLoop runUntilDate:NSDate distantFuture; */- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event self performSelector:selector(performRun) onThread:_thread withObject:nil waitUntilDone:YES;- (void)performRun NSLog(-);end有些开发者会使用死循环while (1) 驱动RunLoop,让线程常驻内存,使用那种方法也可实现常驻内存。但是线程在收到输入源之前会一直处于活动状态,因为只有RunLoop对象才能让线程进入睡眠状态。在下列代码中,在收到输入源之前,RunLoop会一直在启动和退出中循环交替进行,只有RunLoop收到输入源后,且处理完输入源后才让线程处于睡眠状态。RunLoop在交替执行启动和退出过程中,RunLoop不会被销毁,也不会多次创建,因为runloop是懒加载的。反观上面的代码,随

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论