当前位置: 首页 > news >正文

网站域名 文件夹制作个人网站教程

网站域名 文件夹,制作个人网站教程,福建两学一做网站,公司做网站的 oa办公系统目录 概念 RunLoop与线程的关系 Runloop对外的接口 CFRunLoopSourceRef Source0 Source1 CFRunLoopTimer CFRunLoopObserver RunLoop的Mode 应用场景 Runloop的内部逻辑 Runloop应用 tableView延迟加载图片,保证流畅 Timer不被ScrollView的滑动影响 A…

目录

概念

RunLoop与线程的关系

Runloop对外的接口

CFRunLoopSourceRef

Source0

Source1

CFRunLoopTimer

CFRunLoopObserver

RunLoop的Mode

应用场景

Runloop的内部逻辑

Runloop应用

tableView延迟加载图片,保证流畅

Timer不被ScrollView的滑动影响

AFNetworking

​编辑

PerformSelecter


概念

一般来讲线程一次只能执行一个任务,执行完后就会退出,我们现在想实现一个功能:线程一直在处理事件并且不会退出,这就是我们Runloop的作用。其实很像runloop的名字所表示的,绕着一个圈圈一直跑。

要实现runloop这种模型一个关键点就是怎么样去管理事件/消息,让线程在没有处理消息时休眠以避免资源占用、在有消息到来时立刻被唤醒

runloop就是一个可以管理需要处理的消息与事件的一个对象,CFRunLoopRef 可以理解为在 CoreFoundation 框架内的NSRunLoop,它提供了纯 C 函数的 API,但是它的API都是线程安全的,而NSRunLoop却不是线程安全的

RunLoop与线程的关系

基本上所有的线程操作的底层都是对pthread_t的封装

而关于RunLoop和线程,苹果不允许直接创建Runloop,它只提供了两个自动获取的函数:CFRunLoopGetMain() 和 CFRunLoopGetCurrent()

CFRunLoopGetMain的实现如下:

这当中用到了函数_CFRunLoopGet0,其实现如下:

CF_EXPORT CFRunLoopRef _CFRunLoopGet0(pthread_t t) {//如果t不存在,则标记为主线程(即默认情况,默认是主线程)if (pthread_equal(t, kNilPthreadT)) {t = pthread_main_thread_np();}__CFLock(&loopsLock);if (!__CFRunLoops) {__CFUnlock(&loopsLock);//创建全局字典,标记为kCFAllocatorSystemDefaultCFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks);//通过主线程 创建主运行循环CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np());//利用dict,进行key-value绑定操作,即可以说明,线程和runloop是一一对应的// dict : key valueCFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop);if (!OSAtomicCompareAndSwapPtrBarrier(NULL, dict, (void * volatile *)&__CFRunLoops)) {CFRelease(dict);}CFRelease(mainLoop);__CFLock(&loopsLock);}//通过其他线程获取runloopCFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));__CFUnlock(&loopsLock);if (!loop) {//如果没有获取到,则新建一个运行循环CFRunLoopRef newLoop = __CFRunLoopCreate(t);__CFLock(&loopsLock);loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));if (!loop) {//将新建的runloop 与 线程进行key-value绑定CFDictionarySetValue(__CFRunLoops, pthreadPointer(t), newLoop);loop = newLoop;}// don't release run loops inside the loopsLock, because CFRunLoopDeallocate may end up taking it__CFUnlock(&loopsLock);CFRelease(newLoop);}if (pthread_equal(t, pthread_self())) {_CFSetTSD(__CFTSDKeyRunLoop, (void *)loop, NULL);if (0 == _CFGetTSD(__CFTSDKeyRunLoopCntr)) {_CFSetTSD(__CFTSDKeyRunLoopCntr, (void *)(PTHREAD_DESTRUCTOR_ITERATIONS-1), (void (*)(void *))__CFFinalizeRunLoop);}}return loop;
}

从上面的代码可以看出,线程和 RunLoop 之间是一一对应的,其关系是保存在一个全局的 Dictionary 里 线程刚创建时并没有runloop,我们需要主动获取,系统才会自动帮我们创建runloop并加到字典中

Runloop对外的接口

通过前两个函数我们可以分别获得当前线程的CFRunLoop对象和主线程的CFRunLoop对象,但是只是获得了,如果想要让Runloop运行起来,就还需要一些别的操作。首先先看一下runloop的一些具体结构

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

  • CFRunLoopRef

  • CFRunLoopModeRef

  • CFRunLoopSourceRef

  • CFRunLoopTimerRef

  • CFRunLoopObserverRef

一个Runloop可以包含多个Mode,CFRunLoopModeRef类没有对外暴露,只是通过CFRunLoopRef 的接口进行了封装,他们的关系如下:

可以看到每个model中包含了Source/Timer/Observer的集合,每次调用RunLoop的主函数时,只能指定其中一个Mode,这个Mode被称作CurrentMode,如果要切换Mode,必须退出Loop,再重新指定一个Mode进入,这样做可以分隔开不同组的Source/TImer/Observer,避免互相影响

接下来我们分别来看看Source/Timer/Observer这三种结构,首先是Source

CFRunLoopSourceRef

CFRunLoopSourceRef 是事件产生的地方。Source有两个版本:Source0 和 Source1。两个的区别主要是RunLoop事件源的不同:

  • Source0:处理非基于端口的事件(如程序内部自定义的事件)

  • Source1:处理基于端口的事件(如来自内核的Mach端口信息)

Source0 需要手动标记(CFRunLoopSourceSignal)并唤醒 RunLoop 才能触发,而 Source1 会自动唤醒 RunLoop。

需要明确一个概念,RunLoop主要用来处理异步事件,如用户输入、定时器触发、网络响应等这些事件通常被封装成事件源,然后由RunLoop在适当的时机调度和处理

Source0

Source0 只包含了一个回调(函数指针),它并不能主动触发事件

使用时,先调用CFRunLoopSourceSignal(source0)将 Source 标记为待处理,然后手动调用 CFRunLoopWakeUp(runloop) 来唤醒 RunLoop,让其处理这个事件

// 假设有一个方法,用于处理按钮点击
- (void)buttonClicked {// 手动触发RunLoop的Source0CFRunLoopSourceSignal(source0);CFRunLoopWakeUp(CFRunLoopGetCurrent()); // 唤醒RunLoop来处理事件
}
​
// 配置Source0
- (void)setupSource0 {CFRunLoopSourceContext context = {0, (__bridge void *)(self), NULL, NULL, NULL, NULL, NULL, NULL, NULL, &callout};source0 = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context);CFRunLoopAddSource(CFRunLoopGetCurrent(), source0, kCFRunLoopDefaultMode);
}
​
// Source0的回调函数
void callout(void *info) {NSLog(@"Source0 event triggered.");
}

Source1

Source1 包含了一个 mach_port 和一个回调(函数指针),被用于通过内核和其他线程相互发送消息。这种 Source 能主动唤醒 RunLoop 的线程。

// 配置Source1
- (void)setupSource1 {CFRunLoopSourceContext1 context = {0, (__bridge void *)(self), NULL, NULL, NULL, NULL, NULL, &perform, NULL};CFMessagePortRef localPort = CFMessagePortCreateLocal(kCFAllocatorDefault, CFSTR("com.example.app.port"), &callback, &context, false);CFRunLoopSourceRef source1 = CFMessagePortCreateRunLoopSource(kCFAllocatorDefault, localPort, 0);CFRunLoopAddSource(CFRunLoopGetCurrent(), source1, kCFRunLoopCommonModes);
}
​
// Source1的回调函数
CFDataRef callback(CFMessagePortRef local, SInt32 msgid, CFDataRef data, void *info) {NSLog(@"Received message: %d", msgid);return NULL;
}
​
// Source1的事件执行
void perform(void *info) {NSLog(@"Performing work in response to external event.");
}

使用场景: • 处理来自其他进程的数据或信号。 • 监听系统级事件或网络事件。

CFRunLoopTimer

这是一个基于时间的触发器,包含一个时间长度和一个回调,当其加入到 RunLoop 时,RunLoop会注册对应的时间点,当时间点到时,RunLoop会被唤醒以执行那个回调

CFRunLoopObserver

这是用来监视和相应RunLoop的特定活动的一种对象,通过 CFRunLoopObserver,开发者可以在 RunLoop 的不同阶段插入自定义的代码来执行特定的任务

可以观测的时间点有以下几个:

我们在上面讲的 Source/Timer/Observer 被统称为 mode item,一个item被重复添加到同一个mode时不会多次执行,但是如果一个mode中一个item都没有,runloop会自动退出,不会进出循环

RunLoop的Mode

刚才在上文讲了mode item,modeitem是被加到mode中的,我们现在讲一下Runloop的Mode

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

应用场景

比如说之前写项目时经常遇到的那个滑动时计数器停止计时的问题,用这个点就可以回答:

主线程的Runloop中有两个预置的Mode:

kCFRunLoopDefaultModeUITrackingRunLoopMode

DefalutMode是App平时所处的状态,TrackingMode是当滑动时所处的状态,当我们创建NSTimer添加到DefalutMode中,Timer会得到重复回调,但是当我们滚动我们的TableView时,Runloop会切换Mode,由DefalutMode切换为TrackingMode,此时Timer会停止同时不会进行回调,也不会影响到滑动的操作

这时想让滑动时NSTimer可以继续运作的话,有两个方法:

  • 一种方法就是将Timer分别加入到两个Mode

  • 另一种方法就是将NSTimer加到最顶层的RunLoop 的 commonModeItems,加入后的ModeItems类型会被Runloop加到具有common属性的Mode中去,也就是直接将Timer同时加到defaultMode与TrackMode中去

iOS中有5种Mode:

苹果公开的三种有:

  • NSDefaultRunLoopMode(kCFRunloopDefaultMode):默认状态,app通常在这个mode下运行

  • UITrackingRunLoopMode:界面跟踪mode(例如滑动scrollview时不被其他mode影响)

  • NSRunLoopCommonModes(kCFRunLoopCommonModes):是 前两个mode的集合,可以把自定义mode用CFRunLoopAddCommonMode函数加入到集合中

还有两种只需了解:

  • GSEventReceiveRunLoopMode:接收系统内部mode,通常用不到

  • UIInitializationRunLoopMode:私有,只在app启动时使用,使用完就不在集合中了

Runloop的内部逻辑

Runloop的逻辑有一张非常经典的图:

Runloop应用

tableView延迟加载图片,保证流畅

在快速滑动tableView时,滑动过的图片会一直加载,但滑动过的图片都不是我们想要呈现的图片,如果加载就浪费CPU资源,用RunLoop就可以避免滑动时加载图片

给ImgaeView的加载图片的方法指定只有在DefalutMode下才能加载,滑动时不加载图片

[self.imageView performSelector:@selector(setImage:) withObject:[UIImage imageNamed:@"imgName.png"] afterDelay:0 inModes:@[NSDefaultRunLoopMode]];

Timer不被ScrollView的滑动影响

上面其实已经讲过这个问题了,除了刚才提到的两个方法,还有一种方法:

用GCD创建定时器,它不会受到RunLoop影响

AFNetworking

在多线程中,线程执行完任务就会退出,那么如果需要反复执行任务的话,就会频繁地创建与销毁线程,这样不仅效率低下,还增加了系统的开销,因此如果有一个常驻线程来处理这些任务就可以避免这种情况。

一个RunLoop中如果没有Observer/Timer/Source等items,Runloop会自动退出,因此我们创建一个空的port发送消息给Runloop,以至于Runloop不会退出而是一直常驻

PerformSelecter

当调用 NSObject 的 performSelecter:afterDelay: 后,其内部会自动创建一个Timer加到Runloop中,当时间到了执行回调,如果当前线程没有Runloop,此方法也会失效

http://www.dtcms.com/a/501427.html

相关文章:

  • 好看动漫网替代网站做那事的网站
  • 番禺网站开发公司电话运营小程序的成本有哪些
  • 开发个网站需要多少钱编程猫的网站是什么
  • Pod 控制器与配置资源管理
  • 怎么做汽车网站软件公司招聘信息
  • 网站制作需要哪些软件wordpress文章输出数
  • 手机影视素材网站大全厦门工商网站查询企业信息
  • Linux中互斥锁
  • 为网站网站做代理被判缓刑四秒网站建设
  • 青岛网站集约化管理平台成品网页大全下载
  • 电影网站怎么做流量东莞市企业招聘信息网
  • NVIDIA Omniverse-开发下一代基于物理的 AI 应用程序-简单体验与介绍
  • 酒泉建设厅网站网站流量一直做不起来
  • 网站数据库设计模板网站流量查询平台
  • 佛山网站建设技术托管百度怎么免费推广
  • 英文网站推广方法移动网站网上营业厅
  • 中国建设银行车主卡网站自建网站需要哪些技术
  • 嵌入式进阶:C语言内联汇编
  • 万网虚拟主机建网站买布做衣裳 在哪个网站买好
  • 网站1g空间多大济宁专业网站建设
  • 网站建设預算免费自动建站
  • 企业网站设计开发服务单位网站的建设
  • 微信商城和微网站a站是什么
  • 峰峰网站建设wordpress下载网站模板
  • wix建站是免费的吗logo制作在线
  • 东阳做网站的公司有经验的邵阳网站建设
  • 微信互动营销网站建设建设网站要做的工作
  • 网站开发培训流程邯郸网络营销平台建设
  • 郑州php网站开发培训设计大赛网
  • 网络建站招聘在北京做家教的网站