iOS核心笔记——RunLoop-基础

1、RunLoop介绍:

?了解:RunLoop从字面意思看就是运行循环跑圈,通常情况下,一个线程一次只能执行一个任务;任务执行完毕后线程就会进入消亡状态随之退出。有时候我们希望线程执行完任务之后还能随时处理事件且不退出,所以,iOS提供了RunLoop。


1-1、什么是RunLoop?

?重要:RunLoop实际上就是一个对象,RunLoop对象管理其需要处理的事件和消息;RunLoop能够让线程在没有处理消息时进入休眠状态以避免占用资源、在监听到事件源发送的消息时立刻唤醒线程。RunLoop提供了一个入口函数来实现运行循环,当线程执行该函数后,便会处于接收消息->等待处理->处理的循环中,直到运行循环结束、RunLoop退出;RunLoop对象也随之销毁。



RunLoop循环逻辑:

?重要:在iOS程序中,mian函数中的UIApplicationMain()函数内部启动了一个与主线程相关联的RunLoop;所以,UIApplicationMain()函数一直没有返回;保持程序持续运行。



RunLoop实现原理:


1-2、RunLoop对象:

?了解:苹果不允许我们直接通过alloc、init方式创建线程中的RunLoop对象,只有在第一次访问线程中的RunLoop对象时,RunLoop对象将以懒加载的形式创建,所以,iOS中提供了2套API来访问和使用RunLoop。


1、Foundation框架:
  1. NSRunLoop:在Foundation框架中,一个NSRunLoop对象就代表了一个线程中的运行循环。
  2. 获取方式
类型 方式 备注
当前线程 通过NSRunLoop的currentRunLoop类方法便能获取到当前线程中的RunLoop对象 当前RunLoop对象可以是主线程中的RunLoop对象,也可以是子线程中的RunLoop对象,如果当前为子线程,则懒加载RunLoop对象之后需要手动调用run方法开启运行循环
主线程 通过NSRunLoop的mainRunLoop类方法能够获取到主线程中的RunLoop对象 当当前线程为主线程时,mainRunLoop与currentRunLoop获取到的是同一个对象

2、Core Foundation框架:
  1. CFRunLoopRef:Core Foundation框架中,一个CFRunLoopRef对象就代表线程中的运行循环。
  2. 获取方式
类型 方式 备注
主线程 CFRunLoopGetMain()函数 获取主线程中的RunLoop对象,程序运行时,自动创建并开启
当前线程 CFRunLoopGetCurrent()函数 获取当前线程中的RunLoop对象,如果当前为子线程,则懒加载RunLoop对象之后需要手动调用run方法开启运行循环

1-3、RunLoop与线程:
1.    /// 全局的Dictionary,key 是 pthread_t, value 是 CFRunLoopRef  2.    static CFMutableDictionaryRef loopsDic;  3.    /// 访问 loopsDic 时的锁  4.    static CFSpinLock_t loopsLock;  5.6.    /// 获取一个 pthread 对应的 RunLoop。  7.    CFRunLoopRef_CFRunLoopGet(pthread_t thread){  8.        OSSpinLock Lock(&loopsLock);  9.10.        if(!loopsDic){  11.            // 第一次进入时,初始化全局Dic,并先为主线程创建一个 RunLoop。  12.            loopsDic = CFDictionaryCreateMutable();  13.            CFRunLoopRef mainLoop = _CFRunLoopCreate();  14.            CFDictionarySetValue(loopsDic, pthread_main_thread_np(), mainLoop);  15.        }  16.17.        /// 直接从 Dictionary 里获取。  18.        CFRunLoopRef loop = CFDictionaryGetValue(loopsDic, thread);  19.20.        if(!loop){  21.            /// 取不到时,创建一个  22.            loop = _CFRunLoopCreate();  23.            CFDictionarySetValue(loopsDic, thread, loop);  24.            /// 注册一个回调,当线程销毁时,顺便也销毁其对应的 RunLoop。  25.            _CFSetTSD(..., thread, loop, __CFFinalizeRunLoop);  26.        }  27.28.        OSSpinLockUnLock(&loopsLock);  29.        returnloop;  30.    }  31.32.    CFRunLoopRef CFRunLoopGetMain(){  33.        return _CFRunLoopGet(pthread_main_thread_np());  34.    }  35.36.    CFRunLoopRef CFRunLoopGetCurrent(){  37.        return _CFRunLoopGet(pthread_self());  38.    }  

?重要: CFRunLoop是基于pthread来管理的,线程和RunLoop之间是一一对应的,其关系是保存在一个全局的Dictionary里。线程刚创建时并没有RunLoop,如果你不主动获取,那它一直都不会有。RunLoop 的创建是发生在第一次获取时,RunLoop 的销毁是发生在线程结束时。只能在一个线程的内部获取其 RunLoop(主线程除外)。


1-4、主线程与子线程:

?重要:在子线程中使用RunLoop时,最好在子线程中的任务中包一个自动释放池(即:在子线程中的任务中手动添加@autoreleasepool,将任务包裹起来,避免内存泄露)。


1-5、RunLoop注意事项:

?了解:1、每条线程都有唯一一个与之对应的RunLoop对象;

?了解:2、主线程的RunLoop对象在程序启动时已经自动创建完毕,并自动开启运行循环;子线程的RunLoop对象需要主动访问其RunLoop对象以懒加载的形式创建,并且,子线程的RunLoop对象需要调用start方法手动开启;

?了解:3、RunLoop在第一次获取时以懒加载的形式创建,在线程结束时销毁。

?了解:4、RunLoop对象必须依赖线程,即:有线程才有RunLoop对象,没有线程就没有RunLoop对象;不能有RunLoop对象而没有线程。


1-6、RunLoop学习:
1、苹果官方文档:

RunLoop官方文档介绍

2、CFRunLoopRef源码:

CFRunLoop源码


2、RunLoop对外接口:

?重要:在 CoreFoundation 里面关于 RunLoop 有5个类:

CFRunLoopRef :RunLoop对象

CFRunLoopModeRef :RunLoop模式

CFRunLoopSourceRef :RunLoop事件源

CFRunLoopTimerRef :RunLoop定时器

CFRunLoopObserverRef:RunLoop观察者

其中,CFRunLoopModeRef 类并没有对外暴露,只是通过 CFRunLoopRef 的接口进行了封装。它们之间的关系如下图所示:


2-1、CFRunLoopRef:

?重要:1、一个 RunLoop 包含若干个 Mode,每个 Mode 又包含若干个 Source/Timer/Observer。每次调用 RunLoop 的主函数时,只能指定其中一个 Mode,这个Mode被称作 CurrentMode。如果需要切换 Mode,只能退出 Loop,再重新指定一个 Mode 进入。这样做主要是为了分隔开不同组的 Source/Timer/Observer,让其互不影响。

?重要:2、RunLoop中的Source/Timer/Observer被统称为mode item,一个 item 可以被同时加入多个mode。但一个 item被重复加入同一个mode时是不会有效果的。如果一个 mode 中一个item都没有,则RunLoop会直接退出,不进入循环。

?了解:3、


2-2、CFRunLoopSourceRef:

?重要:CFRunLoopSourceRef是事件产生的地方,Source有两个版本:Source0 和 Source1。

· Source0 :非基于Post,只包含了一个回调(函数指针),它并不能主动触发事件。使用时,你需要先调用 CFRunLoopSourceSignal(source),将这个 Source 标记为待处理,然后手动调用 CFRunLoopWakeUp(runloop) 来唤醒 RunLoop,让其处理这个事件。

· Source1 :基于Port,包含了一个 mach_port 和一个回调(函数指针),被用于通过内核和其他线程相互发送消息。这种 Source 能主动唤醒 RunLoop 的线程,其原理在下面会讲到。

?了解:以前CFRunLoopSourceRef可分为:①Port-Based Sources;②Custom Input Sources;③Cocoa Perform Selector Sources



基于事件源的函数调用栈


2-3、CFRunLoopTimerRef:

?重要:CFRunLoopTimerRef 是基于时间的触发器,它和 NSTimer是toll-free bridged的,可以混用。其包含一个时间长度和一个回调(函数指针)。当其加入到 RunLoop 时,RunLoop会注册对应的时间点,当时间点到时,RunLoop会被唤醒以执行那个回调。


2-4、CFRunLoopObserverRef:

?重要:CFRunLoopObserverRef是观察者,每个Observer都包含了一个回调(函数指针),当RunLoop的状态发生变化时,观察者就能通过回调接受到这个变化。可以观测的时间点有以下几个:

1.typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {2.   kCFRunLoopEntry = (1UL << 0),  // 1 --- 即将进入RunLoop3.   kCFRunLoopBeforeTimers = (1UL << 1),  // 2 --- 即将处理定时器4.   kCFRunLoopBeforeSources = (1UL << 2),  // 4 --- 即将处理Source5.   kCFRunLoopBeforeWaiting = (1UL << 5),  // 32 --- 即将进入休眠状态6.   kCFRunLoopAfterWaiting = (1UL << 6),  // 64 --- 唤醒RunLoop7.   kCFRunLoopExit = (1UL << 7),  // 128 --- 即将退出RunLoop8.   kCFRunLoopAllActivities = 0x0FFFFFFFU // RunLoop所有状态9.};


?重要:给RunLoop对象设置观察者:

步骤:①创建观察者对象;②将观察者对象设置为指定RunLoop对象的观察者。


3、CFRunLoopModeRef:

?重要:CFRunLoopModeRef代表RunLoop的运行模式,一个 RunLoop 包含若干个 Mode,每个 Mode 又包含若干个 Source/Timer/Observer。每次调用 RunLoop 的主函数时,只能指定其中一个 Mode,这个Mode被称作 CurrentMode。如果需要切换 Mode,只能退出 Loop,再重新指定一个 Mode 进入。这样做主要是为了分隔开不同组的 Source/Timer/Observer,让其互不影响。


3-1、CFRunLoopMode和CFRunLoop的结构大致如下:

CFRunLoopMode结构

1.struct__CFRunLoopMode{  2.    CFStringRef_name;            // Mode Name, 例如 @"kCFRunLoopDefaultMode"  3.    CFMutableSetRef_sources0;    // Set<CFRunLoopSourceRef>  4.    CFMutableSetRef_sources1;    // Set<CFRunLoopSourceRef>  5.    CFMutableArrayRef_observers;// Array<CFRunLoopObserverRef>  6.    CFMutableArrayRef_timers;    // Array<CFRunLoopTimerRef>  7.        ...  8.};  


CFRunLoop结构

1.struct__CFRunLoop{  2.      CFMutableSetRef_commonModes;  // Set<CFStringRef>  3.      CFMutableSetRef_commonModeItems;  // Set<Source/Observer/Timer>  4.      CFRunLoopModeRef_currentMode;  // Current Runloop Mode  5.      CFMutableSetRef_modes;  // Set<CFRunLoopModeRef>  6.        ...  7.};  

3-2、RunLoop5种模式:


?重要: “CommonModes”:一个 Mode 可以将自己标记为”Common”属性(通过将其 ModeName 添加到 RunLoop 的 “commonModes” 中)。每当 RunLoop 的内容发生变化时,RunLoop 都会自动将 _commonModeItems 里的 Source/Observer/Timer 同步到具有 “Common” 标记的所有Mode里。

?了解:应用场景举例:主线程的 RunLoop 里有两个预置的 Mode:kCFRunLoopDefaultMode 和 UITrackingRunLoopMode。这两个 Mode 都已经被标记为”Common”属性。DefaultMode 是 App 平时所处的状态,TrackingRunLoopMode 是追踪 ScrollView 滑动时的状态。当你创建一个 Timer 并加到 DefaultMode 时,Timer 会得到重复回调,但此时滑动一个TableView时,RunLoop 会将 mode 切换为 TrackingRunLoopMode,这时 Timer 就不会被回调,并且也不会影响到滑动操作。有时你需要一个 Timer,在两个 Mode 中都能得到回调,一种办法就是将这个 Timer 分别加入这两个 Mode。还有一种方式,就是将 Timer 加入到顶层的 RunLoop 的 “commonModeItems” 中。”commonModeItems” 被 RunLoop 自动更新到所有具有”Common”属性的 Mode 里去。

CFRunLoop对外暴露的管理 Mode 接口只有下面2个:

1.  CFRunLoopAddCommonMode(CFRunLoopRef runloop, CFStringRef modeName);  2.  CFRunLoopRunInMode(CFStringRef modeName, ...);  3.


Mode 暴露的管理 mode item 的接口有下面几个:

1.  CFRunLoopAddSource(CFRunLoopRef rl, CFRunLoopSourceRef source, CFStringRef modeName);  2.3.  CFRunLoopAddObserver(CFRunLoopRef rl, CFRunLoopObserverRef observer, CFStringRef modeName);  4.5.  CFRunLoopAddTimer(CFRunLoopRef rl, CFRunLoopTimerRef timer, CFStringRef mode);  6.7.  CFRunLoopRemoveSource(CFRunLoopRef rl, CFRunLoopSourceRef source, CFStringRef modeName);  8.9.  CFRunLoopRemoveObserver(CFRunLoopRef rl, CFRunLoopObserverRef observer, CFStringRef modeName);  10.11.  CFRunLoopRemoveTimer(CFRunLoopRef rl, CFRunLoopTimerRef timer, CFStringRef mode);  

?重要:只能通过mode name来操作内部的mode,当传入一个新的mode name时,但是,RunLoop内部没有对应mode时,RunLoop会自动帮你创建对应的 CFRunLoopModeRef。对于一个RunLoop来说,其内部的mode只能增加不能删除。苹果公开提供的 Mode 有两个:kCFRunLoopDefaultMode (NSDefaultRunLoopMode)UITrackingRunLoopMode,可以用这两个Mode Name来操作其对应的Mode。同时苹果还提供了一个操作 Common 标记的字符串:kCFRunLoopCommonModes (NSRunLoopCommonModes),可以用这个字符串来操作 Common Items,或标记一个 Mode 为 “Common”。使用时注意区分这个字符串和其他 mode name。


3-3、RunLoop模式详细介绍:


时间: 2024-12-13 00:05:05

iOS核心笔记——RunLoop-基础的相关文章

iOS核心笔记——UICollectionView-布局对象

iOS核心笔记--UICollectionView-布局对象 1.UICollectionView布局对象: 1-1.UICollectionViewLayout: ?了解:1.UICollectionViewLayout没有itemSize属性,而collectionView的cell的大小不能为0或者小于0:所以,通常collectionView使用其子类UICollectionViewFlowLayout布局cell. ?了解:2.UICollectionViewLayout中声明了布局可

iOS核心笔记——UICollectionView-辅助视图

iOS核心笔记--UICollectionView-辅助视图 1.UICollectionView辅助视图: ?了解:UICollectionView中的头部视图.尾部视图都是使用注册机制 + 重用机制,提高程序的执行效率,其头尾部视图类型均为:UICollectionReusableView.注册辅助视图时使用字符串区分注册的视图为头部还是尾部视图,其区分字符串为:UICollectionElementKindSectionHeader(头部视图).UICollectionElementKin

IOS科研IOS开发笔记学习基础知识

这篇文章是我的IOS学习笔记,他们是知识的基础,在这里,根据记录的查询后的条款. 1,UIScrollView能完毕滚动的功能. 示比例如以下: UIScrollView *tableScrollView; tableScrollView=[[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 320, 480)];//窗体大小 tableScrollView.contentSize=CGSizeMake(640, 480);//设置内容视图的大

iOS核心笔记—CoreLocation框架-基础

1.CoreLocation框架简介: ?了解:在iOS开发中,要想使用地图和定位功能,必须基于2个框架进行开发.CoreLocation框架:主要用于地理定位:MapKit框架:主要用于地图展示. 1-1.CoreLocation框架功能: 功能 作用 地理定位 定位用户所在位置,获取对应的经纬度.海拔等位置信息 区域监听 事先在APP内部通过代码指定一个区域,当用户进入.或离开该区域的时候,可以通过对应的代理方法监听到用户的位置 地理编码 将详细的位置信息转换为对应的经纬度,即:位置信息 -

iOS核心笔记—MapKit框架-基础

1.MapKit框架简介: ?了解:MapKit框架使用须知:①.MapKit框架中所有的数据类型的前缀都是MK:②.需要导入#import <MapKit/MapKit.h>头文件:③.MapKit框架中有一个非常重要的UI控件:MKMapView,专门用于地图显示,例如:大头针.路线.覆盖层展示等(着重界面展示). 1-1.地图设置: 1-1-1.设置地图显示类型: 地图类型: 地图枚举 地图类型 MKMapTypeStandard 普通地图 MKMapTypeSatellite 卫星云图

iOS核心笔记——网络编程-HTTPS协议

1.HTTPS的基本使用: 1.HTTPS简单说明: ?了解:HTTPS(全称:Hyper Text Transfer Protocol over Secure Socket Layer),是以安全为目标的HTTP通道,简单讲是HTTP的安全版. ?重要:即HTTP下加入SSL层,HTTPS的安全基础是SSL,因此加密的详细内容就需要SSL. 它是一个URI scheme(抽象标识符体系),句法类同http:体系.用于安全的HTTP数据传输. ?重要:https:URL:表明它使用了HTTPS,

iOS核心笔记——iOS应用程序启动原理

1.iOS应用程序启动示例图: 2.应用程序启动原理: 2-1.不通过storyboard文件启动: 执行main函数: 执行main函数中的UIApplicationMain函数,创建UIApplication对象,创建ApplicationDelegate对象并将其设置为UIApplication对象的代理: 程序启动完毕,开启消息循环(Main RunLoop).监听事件并调用注册方法:接着调用代理的application:didFinishLaunchingWithOptions:方法:

iOS核心笔记—MapKit框架-导航

1.导航简介: ?了解:导航,简单来说,就是根据用户指定的位置,进行路线规划:然后根据用户在行走过程中,实时的给出指引提示. 1-1.iOS导航实现方案: 方案 详细说明 方案一 将需要导航的位置传递给系统的地图APP进行导航 方案二 发送网络请求到Apple服务器/公司服务器获取导航数据,然后,手动绘制导航路线 方案三 利用第三方SDK实现导航功能(百度地图) ?说明:通常如果需要手动绘制导航路线,都是向Apple服务器发送请求.获取导航信息:此处,只对方案一.方案二做详细介绍,方案三将单独说

iOS核心笔记——多线程-GCD

1.GCD简介: ?了解:GCD全称为"Grand Central Dispatch",纯C语言,GCD提供了非常多功能强大的函数:GCD中所有的函数都包含于Libdispatch库中. 1-1.使用GCD的优势: ?了解:1.GCD是苹果公司为多核的并行运算提出的解决方案: ?了解:2.GCD会自动利用更多的CPU内核(例如:双核.四核): ?了解:3.GCD会自动管理线程的生命周期(创建线程.调度任务.销毁线程). 1-2.GCD两大核心概念: 名称 作用: 任务 执行什么操作 队