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

大连龙采做网站行不行网站制作内容文案

大连龙采做网站行不行,网站制作内容文案,php网站怎么做,做海报的软件app免费Runloop 1. 概述 一般来说#xff0c;一个线程只能执行一个任务#xff0c;执行完就会退出#xff0c;如果我们需要一种机制#xff0c;让线程能随时处理时间但并不退出#xff0c;那么 RunLoop 就是这样的一个机制。Runloop是事件接收和分发机制的一个实现。 RunLoop实际…Runloop 1. 概述 一般来说一个线程只能执行一个任务执行完就会退出如果我们需要一种机制让线程能随时处理时间但并不退出那么 RunLoop 就是这样的一个机制。Runloop是事件接收和分发机制的一个实现。 RunLoop实际上是一个对象这个对象在循环中用来处理程序运行过程中出现的各种事件比如说触摸事件、UI刷新事件、定时器事件、Selector事件从而保持程序的持续运行而且在没有事件处理的时候会进入睡眠模式从而节省CPU资源提高程序性能。 简单的说run loop是事件驱动的一个大循环如下代码所示 int main(int argc, char * argv[]) {//程序一直运行状态while (AppIsRunning) {//睡眠状态等待唤醒事件id whoWakesMe SleepForWakingUp();//得到唤醒事件id event GetEvent(whoWakesMe);//开始处理事件HandleEvent(event);}return 0; } 复制代码2. Runloop 基本作用 2.1 保持程序持续运行 程序一启动就会开一个主线程主线程一开起来就会跑一个主线程对应的Runloop, Runloop保证主线程不会被销毁也就保证了程序的持续运行。不光iOS在其他的编程平台Android, Windows等都有一个类似Runloop的机制保证程序的持续运行。 2.2 处理App中的各类事件 系统级别 GCD, mach kernel, block, pthread 应用层 NSTimer, UIEvent, Autorelease, NSObject(NSDelayedPerforming), NSObject(NSThreadPerformAddition), CADisplayLink, CATransition, CAAnimation, dispatch_get_main_queue() (GCD 中dispatch到main queue的block会被dispatch到main Runloop中执行) NSPort, NSURLConnection, AFNetworking这个第三方网络请求框架使用在开启新线程中添加自己到Runloop监听事件 2.3 节省CPU资源提高程序性能 程序运行起来时当什么操作都没有做的时候Runloop告诉CPU, 现在没有事情做我要去休息 这时CPU就会将资源释放出来去做其他的事情当有事情做的时候Runloop就会立马起来去做事情。 3. Runloop 的开启 程序入口 iOS 程序的入口是 main 函数 int main(int argc, char * argv[]) {autoreleasepool {return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));} } 复制代码程序主线程一开起来就会跑一个和主线程对应的Runloop, 那么Runloop一定是在程序的入口main函数中开启。 在main thread 堆栈中所处的位置 堆栈最底层是start(dyld)往上依次是mainUIApplication(main.m) - GSEventRunModal(Graphic Services) - RunLoop(包含CFRunLoopRunSpecific__CFRunLoopRun__CFRunLoopDoSouces0CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION) - Handle Touch Event 4. Runloop 原理 CFRunLoop开源代码http://opensource.apple.com/source/CF/CF-855.17/ Runloop 源码 void CFRunLoopRun(void) { /* DOES CALLOUT */int32_t result;do {result CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);CHECK_FOR_FORK();} while (kCFRunLoopRunStopped ! result kCFRunLoopRunFinished ! result); } 复制代码我们发现RunLoop确实是do while通过判断result的值实现的。因此我们可以把RunLoop看成一个死循环。如果没有RunLoopUIApplicationMain函数执行完毕之后将直接返回也就没有程序持续运行一说了。 执行顺序的伪代码 int32_t __CFRunLoopRun() {// 通知即将进入runloop__CFRunLoopDoObservers(KCFRunLoopEntry);do{// 通知将要处理timer和source__CFRunLoopDoObservers(kCFRunLoopBeforeTimers);__CFRunLoopDoObservers(kCFRunLoopBeforeSources);// 处理非延迟的主线程调用__CFRunLoopDoBlocks();// 处理Source0事件__CFRunLoopDoSource0();if (sourceHandledThisLoop) {__CFRunLoopDoBlocks();}/// 如果有 Source1 (基于port) 处于 ready 状态直接处理这个 Source1 然后跳转去处理消息。if (__Source0DidDispatchPortLastTime) {Boolean hasMsg __CFRunLoopServiceMachPort();if (hasMsg) goto handle_msg;}/// 通知 Observers: RunLoop 的线程即将进入休眠(sleep)。if (!sourceHandledThisLoop) {__CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopBeforeWaiting);}// GCD dispatch main queueCheckIfExistMessagesInMainDispatchQueue();// 即将进入休眠__CFRunLoopDoObservers(kCFRunLoopBeforeWaiting);// 等待内核mach_msg事件mach_port_t wakeUpPort SleepAndWaitForWakingUpPorts();// 等待。。。// 从等待中醒来__CFRunLoopDoObservers(kCFRunLoopAfterWaiting);// 处理因timer的唤醒if (wakeUpPort timerPort)__CFRunLoopDoTimers();// 处理异步方法唤醒,如dispatch_asyncelse if (wakeUpPort mainDispatchQueuePort)__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__()// 处理Source1else__CFRunLoopDoSource1();// 再次确保是否有同步的方法需要调用__CFRunLoopDoBlocks();} while (!stop !timeout);// 通知即将退出runloop__CFRunLoopDoObservers(CFRunLoopExit); } 复制代码5. Runloop 对象 RunLoop对象包括Fundation中的NSRunLoop对象和CoreFoundation中的CFRunLoopRef对象。因为Fundation框架是基于CFRunLoopRef的封装因此我们学习RunLoop还是要研究CFRunLoopRef 源码。 获得Runloop 对象 //Foundation [NSRunLoop currentRunLoop]; // 获得当前线程的RunLoop对象 [NSRunLoop mainRunLoop]; // 获得主线程的RunLoop对象//Core Foundation CFRunLoopGetCurrent(); // 获得当前线程的RunLoop对象 CFRunLoopGetMain(); // 获得主线程的RunLoop对象 复制代码值的注意的是子线程中的runloop不是默认开启的需要手动开启当调用 [NSRunLoop currentRunLoop] 时若已存在当前线程的runloop返回若不存在创建一个新的runloop对象再返回。 6. Runloop 和 线程 6.1 Runloop 和 线程 之间的关系 每条线程都有唯一的一个与之对应的Runloop 对象主线程的Runloop已经自动创建好了子线程的Runloop需要手动创建Runloop在第一次获取时创建在线程结束时销毁Thread 包含一个CFRunloop, 一个CFRunloop 包含一种CFRunloopMode, model 包含 CFRunloopSource, CFRunloopTimer, CFRunloopObserver. 6.2 主线程想关联的Runloop创建 CFRunloopRef 源码 // 创建字典CFMutableDictionaryRef dict CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, kCFTypeDictionaryValueCallBacks);// 创建主线程 根据传入的主线程创建主线程对应的RunLoopCFRunLoopRef mainLoop __CFRunLoopCreate(pthread_main_thread_np());// 保存主线程 将主线程-key和RunLoop-Value保存到字典中CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop); 复制代码6.3 创建与子线程想关联的Runloop Apple 不允许直接创建Runloop, 它只提供了两个自动获取的函数 CFRunLoopGetMain() 和 CFRunLoopGetCurrent()。 CFRunLoopRef源码: /// 用DefaultMode启动 void CFRunLoopRun(void) {CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false); }/// 用指定的Mode启动允许设置RunLoop超时时间 int CFRunLoopRunInMode(CFStringRef modeName, CFTimeInterval seconds, Boolean stopAfterHandle) {return CFRunLoopRunSpecific(CFRunLoopGetCurrent(), modeName, seconds, returnAfterSourceHandled); }/// RunLoop的实现 int CFRunLoopRunSpecific(runloop, modeName, seconds, stopAfterHandle) {/// 首先根据modeName找到对应modeCFRunLoopModeRef currentMode __CFRunLoopFindMode(runloop, modeName, false);/// 如果mode里没有source/timer/observer, 直接返回。if (__CFRunLoopModeIsEmpty(currentMode)) return;/// 1. 通知 Observers: RunLoop 即将进入 loop。__CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopEntry);/// 内部函数进入loop__CFRunLoopRun(runloop, currentMode, seconds, returnAfterSourceHandled) {Boolean sourceHandledThisLoop NO;int retVal 0;do {/// 2. 通知 Observers: RunLoop 即将触发 Timer 回调。__CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopBeforeTimers);/// 3. 通知 Observers: RunLoop 即将触发 Source0 (非port) 回调。__CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopBeforeSources);/// 执行被加入的block__CFRunLoopDoBlocks(runloop, currentMode);/// 4. RunLoop 触发 Source0 (非port) 回调。sourceHandledThisLoop __CFRunLoopDoSources0(runloop, currentMode, stopAfterHandle);/// 执行被加入的block__CFRunLoopDoBlocks(runloop, currentMode);/// 5. 如果有 Source1 (基于port) 处于 ready 状态直接处理这个 Source1 然后跳转去处理消息。if (__Source0DidDispatchPortLastTime) {Boolean hasMsg __CFRunLoopServiceMachPort(dispatchPort, msg)if (hasMsg) goto handle_msg;}/// 通知 Observers: RunLoop 的线程即将进入休眠(sleep)。if (!sourceHandledThisLoop) {__CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopBeforeWaiting);}/// 7. 调用 mach_msg 等待接受 mach_port 的消息。线程将进入休眠, 直到被下面某一个事件唤醒。/// • 一个基于 port 的Source 的事件。/// • 一个 Timer 到时间了/// • RunLoop 自身的超时时间到了/// • 被其他什么调用者手动唤醒__CFRunLoopServiceMachPort(waitSet, msg, sizeof(msg_buffer), livePort) {mach_msg(msg, MACH_RCV_MSG, port); // thread wait for receive msg}/// 8. 通知 Observers: RunLoop 的线程刚刚被唤醒了。__CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopAfterWaiting);/// 收到消息处理消息。handle_msg:/// 9.1 如果一个 Timer 到时间了触发这个Timer的回调。if (msg_is_timer) {__CFRunLoopDoTimers(runloop, currentMode, mach_absolute_time())} /// 9.2 如果有dispatch到main_queue的block执行block。else if (msg_is_dispatch) {__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);} /// 9.3 如果一个 Source1 (基于port) 发出事件了处理这个事件else {CFRunLoopSourceRef source1 __CFRunLoopModeFindSourceForMachPort(runloop, currentMode, livePort);sourceHandledThisLoop __CFRunLoopDoSource1(runloop, currentMode, source1, msg);if (sourceHandledThisLoop) {mach_msg(reply, MACH_SEND_MSG, reply);}}/// 执行加入到Loop的block__CFRunLoopDoBlocks(runloop, currentMode);if (sourceHandledThisLoop stopAfterHandle) {/// 进入loop时参数说处理完事件就返回。retVal kCFRunLoopRunHandledSource;} else if (timeout) {/// 超出传入参数标记的超时时间了retVal kCFRunLoopRunTimedOut;} else if (__CFRunLoopIsStopped(runloop)) {/// 被外部调用者强制停止了retVal kCFRunLoopRunStopped;} else if (__CFRunLoopModeIsEmpty(runloop, currentMode)) {/// source/timer/observer一个都没有了retVal kCFRunLoopRunFinished;}/// 如果没超时mode里没空loop也没被停止那继续loop。} while (retVal 0);}/// 10. 通知 Observers: RunLoop 即将退出。__CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit); } 复制代码可以看出线程和 RunLoop 之间是一一对应的其关系是保存在一个全局的 Dictionary 里。线程刚创建时并没有 RunLoop如果你不主动获取那它一直都不会有。RunLoop 的创建是发生在第一次获取时RunLoop 的销毁是发生在线程结束时。你只能在一个线程的内部获取其 RunLoop主线程除外。 [NSRunLoop currentRunLoop];方法调用时会先看一下字典里有没有存子线程相对用的RunLoop如果有则直接返回RunLoop如果没有则会创建一个并将与之对应的子线程存入字典中。 7. Runloop 相关类 Core Foundation中关于RunLoop的5个类 CFRunLoopRef //获得当前RunLoop和主RunLoop CFRunLoopModeRef //运行模式只能选择一种在不同模式中做不同的操作 CFRunLoopSourceRef //事件源输入源 CFRunLoopTimerRef //定时器时间 CFRunLoopObserverRef //观察者 复制代码7.1 CFRunLoopModeRef 一个Runloop包含若干个Mode, 每个Mode又包含若干个Source / Timer / Observer. 每次调用Runloop 的主函数时只能指定其中一个Mode, 这个Mode被称作 CurrentMode. 如果需要切换Mode, 只能退出Loop, 再重新指定一个Mode进入。这样做主要是为了分隔开不同组的 Source/Timer/Observer, 让其互不影响。 系统默认注册了 5 个Mode, 其中常见的有第 12 种 1. kCFRunLoopDefaultModeApp的默认Mode通常主线程是在这个Mode下运行 2. UITrackingRunLoopMode界面跟踪 Mode用于 ScrollView 追踪触摸滑动保证界面滑动时不受其他 Mode 影响 3. UIInitializationRunLoopMode: 在刚启动 App 时第进入的第一个 Mode启动完成后就不再使用 4. GSEventReceiveRunLoopMode: 接受系统事件的内部 Mode通常用不到 5. kCFRunLoopCommonModes: 这是一个占位用的Mode作为标记kCFRunLoopDefaultMode和UITrackingRunLoopMode用并不是一种真正的Mode 复制代码上面的Source/Timer/Observer 被统称为 model item 一个item 可以被同时加入多个 Mode. 但一个item被重复加入同一个mode时是不会有效果的。如果一个mode中一个item都没有则Runloop会直接退出不进入循环。 Mode 间切换 我们平时在开发中一定遇到过当我们使用NSTimer每一段时间执行一些事情时滑动UIScrollViewNSTimer就会暂停当我们停止滑动以后NSTimer又会重新恢复的情况我们通过一段代码来看一下 -(void)touchesBegan:(NSSetUITouch * *)touches withEvent:(UIEvent *)event {// [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:selector(show) userInfo:nil repeats:YES];NSTimer *timer [NSTimer timerWithTimeInterval:2.0 target:self selector:selector(show) userInfo:nil repeats:YES];// 加入到RunLoop中才可以运行// 1. 把定时器添加到RunLoop中并且选择默认运行模式NSDefaultRunLoopMode kCFRunLoopDefaultMode// [[NSRunLoop mainRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];// 当textFiled滑动的时候timer失效停止滑动时timer恢复// 原因当textFiled滑动的时候RunLoop的Mode会自动切换成UITrackingRunLoopMode模式因此timer失效当停止滑动RunLoop又会切换回NSDefaultRunLoopMode模式因此timer又会重新启动了// 2. 当我们将timer添加到UITrackingRunLoopMode模式中此时只有我们在滑动textField时timer才会运行// [[NSRunLoop mainRunLoop] addTimer:timer forMode:UITrackingRunLoopMode];// 3. 那个如何让timer在两个模式下都可以运行呢// 3.1 在两个模式下都添加timer 是可以的但是timer添加了两次并不是同一个timer// 3.2 使用站位的运行模式 NSRunLoopCommonModes标记凡是被打上NSRunLoopCommonModes标记的都可以运行下面两种模式被打上标签//0 : CFString 0x10b7fe210 [0x10a8c7a40]{contents UITrackingRunLoopMode}//2 : CFString 0x10a8e85e0 [0x10a8c7a40]{contents kCFRunLoopDefaultMode}// 因此也就是说如果我们使用NSRunLoopCommonModestimer可以在UITrackingRunLoopModekCFRunLoopDefaultMode两种模式下运行[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];NSLog(%,[NSRunLoop mainRunLoop]); } -(void)show {NSLog(-------); } 复制代码由上述代码可以看出NSTimer不管用是因为Mode的切换因为如果我们在主线程使用定时器此时RunLoop的Mode为kCFRunLoopDefaultMode即定时器属于kCFRunLoopDefaultMode那么此时我们滑动ScrollView时RunLoop的Mode会切换到UITrackingRunLoopMode因此在主线程的定时器就不在管用了调用的方法也就不再执行了当我们停止滑动时RunLoop的Mode切换回kCFRunLoopDefaultMode所有NSTimer就又管用了。 使用GCD也可以创建计时器而且更为精确 -(void)touchesBegan:(NSSetUITouch * *)touches withEvent:(UIEvent *)event {//创建队列dispatch_queue_t queue dispatch_get_global_queue(0, 0);//1.创建一个GCD定时器/*第一个参数:表明创建的是一个定时器第四个参数:队列*/dispatch_source_t timer dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);// 需要对timer进行强引用保证其不会被释放掉才会按时调用block块// 局部变量让指针强引用self.timer timer;//2.设置定时器的开始时间,间隔时间,精准度/*第1个参数:要给哪个定时器设置第2个参数:开始时间第3个参数:间隔时间第4个参数:精准度 一般为0 在允许范围内增加误差可提高程序的性能GCD的单位是纳秒 所以要*NSEC_PER_SEC*/dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 2.0 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);//3.设置定时器要执行的事情dispatch_source_set_event_handler(timer, ^{NSLog(---%--,[NSThread currentThread]);});// 启动dispatch_resume(timer); } 复制代码7.2 CFRunLoopSourceRef Source分为两种 Source0非基于Port的 用于用户主动触发的事件点击button 或点击屏幕 Source1基于Port的 通过内核和其他线程相互发送消息与内核相关 注意Source1在处理的时候会分发一些操作给Source0去处理 7.3 CFRunLoopTimer NSTimer是对RunLoopTimer的封装 (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo;(NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo;- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay inModes:(NSArray *)modes; (CADisplayLink *)displayLinkWithTarget:(id)target selector:(SEL)sel; - (void)addToRunLoop:(NSRunLoop *)runloop forMode:(NSString *)mode;复制代码7.4 CFRunLoopObserverRef CFRunLoopObserverRef是观察者能够监听RunLoop的状态改变。 我们直接来看代码给RunLoop添加监听者监听其运行状态 -(void)touchesBegan:(NSSetUITouch * *)touches withEvent:(UIEvent *)event {//创建监听者/*第一个参数 CFAllocatorRef allocator分配存储空间 CFAllocatorGetDefault()默认分配第二个参数 CFOptionFlags activities要监听的状态 kCFRunLoopAllActivities 监听所有状态第三个参数 Boolean repeatsYES:持续监听 NO:不持续第四个参数 CFIndex order优先级一般填0即可第五个参数 回调 两个参数observer:监听者 activity:监听的事件*//*所有事件typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {kCFRunLoopEntry (1UL 0), // 即将进入RunLoopkCFRunLoopBeforeTimers (1UL 1), // 即将处理TimerkCFRunLoopBeforeSources (1UL 2), // 即将处理SourcekCFRunLoopBeforeWaiting (1UL 5), //即将进入休眠kCFRunLoopAfterWaiting (1UL 6),// 刚从休眠中唤醒kCFRunLoopExit (1UL 7),// 即将退出RunLoopkCFRunLoopAllActivities 0x0FFFFFFFU};*/CFRunLoopObserverRef observer CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {switch (activity) {case kCFRunLoopEntry:NSLog(RunLoop进入);break;case kCFRunLoopBeforeTimers:NSLog(RunLoop要处理Timers了);break;case kCFRunLoopBeforeSources:NSLog(RunLoop要处理Sources了);break;case kCFRunLoopBeforeWaiting:NSLog(RunLoop要休息了);break;case kCFRunLoopAfterWaiting:NSLog(RunLoop醒来了);break;case kCFRunLoopExit:NSLog(RunLoop退出了);break;default:break;}});// 给RunLoop添加监听者/*第一个参数 CFRunLoopRef rl要监听哪个RunLoop,这里监听的是主线程的RunLoop第二个参数 CFRunLoopObserverRef observer 监听者第三个参数 CFStringRef mode 要监听RunLoop在哪种运行模式下的状态*/CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);/*CF的内存管理Core Foundation凡是带有Create、Copy、Retain等字眼的函数创建出来的对象都需要在最后做一次releaseGCD本来在iOS6.0之前也是需要我们释放的6.0之后GCD已经纳入到了ARC中所以我们不需要管了*/CFRelease(observer); } 复制代码运行结果 8. Runloop 退出 主线程销魂Runloop退出Mode中有一些Timer, Source, Observer, 这些保证Mode不为空时保证Runloop没有空转并且是在运行的当Mode中为空的时候Runloop会立刻退出。我们在启动Runloop的时候可以设置什么时候停止。[NSRunLoop currentRunLoop]runUntilDate:#(nonnull NSDate *)# [NSRunLoop currentRunLoop]runMode:#(nonnull NSString *)# beforeDate:#(nonnull NSDate *)# 复制代码9. 一些有关Runloop的问题 9.1 基于NSTimer的轮播器什么情况下会被页面滚动暂停怎样可以不被暂停为什么 NSTimer不管用是因为Mode的切换因为如果我们在主线程使用定时器此时RunLoop的Mode为kCFRunLoopDefaultMode即定时器属于kCFRunLoopDefaultMode那么此时我们滑动ScrollView时RunLoop的Mode会切换到UITrackingRunLoopMode因此在主线程的定时器就不在管用了调用的方法也就不再执行了当我们停止滑动时RunLoop的Mode切换回kCFRunLoopDefaultMode所有NSTimer就又管用了。若想定时器继续执行需要将NSTimer 注册为 kCFRunLoopCommonModes 。 9.2 延迟执行performSelecter相关方法是怎样被执行的在子线程中也是一样的吗 当调用 NSObject 的 performSelecter:afterDelay: 后实际上其内部会创建一个 Timer 并添加到当前线程的 RunLoop 中。所以如果当前线程没有 RunLoop则这个方法会失效。 当调用 performSelector:onThread: 时实际上其会创建一个 Timer 加到对应的线程去同样的如果对应线程没有 RunLoop 该方法也会失效。 9.3 事件响应和手势识别底层处理是一致的吗为什么 事件响应 苹果注册了一个 Source1 (基于 mach port 的) 用来接收系统事件其回调函数为 __IOHIDEventSystemClientQueueCallback()。 当一个硬件事件(触摸/锁屏/摇晃等)发生后首先由 IOKit.framework 生成一个 IOHIDEvent 事件并由 SpringBoard 接收。SpringBoard 只接收按键(锁屏/静音等)触摸加速接近传感器等几种 Event随后用 mach port 转发给需要的App进程。随后苹果注册的那个 Source1 就会触发回调并调用 _UIApplicationHandleEventQueue() 进行应用内部的分发。 _UIApplicationHandleEventQueue() 会把 IOHIDEvent 处理并包装成 UIEvent 进行处理或分发其中包括识别 UIGesture/处理屏幕旋转/发送给 UIWindow 等。通常事件比如 UIButton 点击、touchesBegin/Move/End/Cancel 事件都是在这个回调中完成的。 手势识别 当上面的 _UIApplicationHandleEventQueue() 识别了一个手势时其首先会调用 Cancel 将当前的 touchesBegin/Move/End 系列回调打断。随后系统将对应的 UIGestureRecognizer 标记为待处理。 苹果注册了一个 Observer 监测 BeforeWaiting (Loop即将进入休眠) 事件这个Observer的回调函数是 _UIGestureRecognizerUpdateObserver()其内部会获取所有刚被标记为待处理的 GestureRecognizer并执行GestureRecognizer的回调。 当有 UIGestureRecognizer 的变化(创建/销毁/状态改变)时这个回调都会进行相应处理。 9.4 界面刷新时是在什么时候会真正执行刷新为什么会刷新不及时 当在操作 UI 时比如改变了 Frame、更新了 UIView/CALayer 的层次时或者手动调用了 UIView/CALayer 的 setNeedsLayout/setNeedsDisplay方法后这个 UIView/CALayer 就被标记为待处理并被提交到一个全局的容器去。 苹果注册了一个 Observer 监听 BeforeWaiting(即将进入休眠) 和 Exit (即将退出Loop) 事件回调去执行一个很长的函数_ZN2CA11Transaction17observer_callbackEP19__CFRunLoopObservermPv()。这个函数里会遍历所有待处理的 UIView/CAlayer 以执行实际的绘制和调整并更新 UI 界面。所以说界面刷新并不一定是在setNeedsLayout相关的代码执行后立刻进行的。 9.5 项目程序运行中总是伴随着多次自动释放池的创建和销毁这些是在什么时候发生的呢 系统就是通过autoreleasepool {}这种方式来为我们创建自动释放池的一个线程对应一个runloop系统会为每一个runloop隐式的创建一个自动释放池所有的autoreleasePool构成一个栈式结构在每个runloop结束时当前栈顶的autoreleasePool会被销毁而且会对其中的每一个对象做一次release严格来说是你对这个对象做了几次autorelease就会做几次release不一定是一次)特别指出使用容器的block版本的枚举器的时候系统会自动添加一个autoreleasePool [array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { // 这里被一个局部autoreleasepool包围着 }]; 复制代码9.6 当我们在子线程上需要执行代理方法或者回调时怎么确保当前线程没有被销毁 首先引入一个概念Event_loop一般一个线程执行完任务后就会退出当需要保证该线程不退出可以通过类似以下方式 function do_loop() {initialize();do {var message get_next_message();process_message(message);} while (message ! quit); } 复制代码开启一个循环保证线程不退出这就是Event_loop模型。这是在很多操作系统中都使用的模型例如OS/iOS中的RunLoop。这种模型最大的作用就是管理事件/消息在有新消息到来时立刻唤醒处理没有待处理消息时线程休眠避免资源浪费。 10 Runloop 使用 10.1 AFNetworking 使用NSOperationNSURLConnection并发模型都会面临NSURLConnection下载完成前线程退出导致NSOperation对象接收不到回调的问题。AFNetWorking解决这个问题的方法是按照官方的guid NSURLConnection 上写的NSURLConnection的delegate方法需要在connection发起的线程runloop中调用于是AFNetWorking直接借鉴了Apple自己的一个Demo的实现方法单独起一个global thread内置一个runloop所有的connection都由这个runloop发起回调也是它接收不占用主线程也不耗CPU资源。 (void)networkRequestThreadEntryPoint:(id)__unused object {autoreleasepool {[[NSThread currentThread] setName:AFNetworking];NSRunLoop *runLoop [NSRunLoop currentRunLoop];[runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];[runLoop run];} } (NSThread *)networkRequestThread {static NSThread *_networkRequestThread nil;static dispatch_once_t oncePredicate;dispatch_once(oncePredicate, ^{_networkRequestThread [[NSThread alloc] initWithTarget:selfselector:selector(networkRequestThreadEntryPoint:)object:nil];[_networkRequestThread start];});return _networkRequestThread; } 复制代码类似的可以用这个方法创建一个常驻服务的线程。 10.2 TableView中实现平滑滚动延迟加载图片 利用CFRunLoopMode的特性可以将图片的加载放到NSDefaultRunLoopMode的mode里这样在滚动UITrackingRunLoopMode这个mode时不会被加载而影响到。 UIImage *downloadedImage ...; [self.imageView performSelector:selector(setImage:)withObject:downloadedImageafterDelay:0inModes:[NSDefaultRunLoopMode]]; 复制代码10.3 接到程序崩溃时的信号进行自主处理例如弹出提示等 CFRunLoopRef runLoop CFRunLoopGetCurrent(); NSArray *allModes CFBridgingRelease(CFRunLoopCopyAllModes(runLoop)); while (1) {for (NSString *mode in allModes) {CFRunLoopRunInMode((CFStringRef)mode, 0.001, false);} } 复制代码10.4 异步测试 - (BOOL)runUntilBlock:(BOOL(^)())block timeout:(NSTimeInterval)timeout {__block Boolean fulfilled NO;void (^beforeWaiting) (CFRunLoopObserverRef observer, CFRunLoopActivity activity) ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {fulfilled block();if (fulfilled) {CFRunLoopStop(CFRunLoopGetCurrent());}};CFRunLoopObserverRef observer CFRunLoopObserverCreateWithHandler(NULL, kCFRunLoopBeforeWaiting, true, 0, beforeWaiting);CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);// Run!CFRunLoopRunInMode(kCFRunLoopDefaultMode, timeout, false);CFRunLoopRemoveObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);CFRelease(observer);return fulfilled; } 复制代码
http://www.zqtcl.cn/news/898312/

相关文章:

  • 网站栏目按扭邢台手机网站建设地方
  • 青浦赵巷网站建设做网站需要编程吗
  • 公司logo设计含义株洲seo
  • 公司制作网站做论坛网站怎么赚钱吗
  • 深圳 外贸 网站建设 龙医院网站建设价格
  • 网上建网站驰易网站建设
  • 建设工程招标专业网站网站联盟广告
  • 自建站英文公司网站制作银川
  • 顺德网站建设公司信息全网推广软件
  • 网站全屏视频怎么做电子商务网站规划的原则是什么
  • 网站建设行业发展史做网站主页上主要放哪些内容
  • 成都成华网站建设小程序开发网上商城
  • 企业网站建设的重要性和必要性深圳设计网站排行
  • 山西省网站建设河南省考生服务平台官网
  • 做水产的都用什么网站wordpress 前端登陆
  • 商务网站建设网站开发一个软件开发的流程
  • 网站建设电脑和手机有区别吗公众号登录微信入口
  • 天津市建设监理协会网站三亚网络网站建设
  • 义乌进货网平台北京网优化seo优化公司
  • 在网站上放广告网站建设流程效果
  • 腾讯云学生机做网站济南网站改版
  • 开封市做网站的公司wordpress无法映射
  • 网站构建工具wordpress 主题授权
  • 大型网站开发 赚钱宁夏网站建设优化
  • 通过ip访问网站需要怎么做博客的网站页面设计
  • 高明做网站软件开发工程师是前端还是后端
  • 利用html5 监控网站性能如何能快速搜到新做网站链接
  • 做网站需要看那几点seo是什么职业岗位
  • 做游戏网站需要哪些许可100个免费推广网站下载
  • 网站管理系统是什么马鞍山网站建设制作公司