自己的网站建设,百度推广渠道户,电子邮箱怎么申请,网页一键生成app软件RunLoop
1. 讲讲RunLoop#xff0c;项目中有用到过吗#xff1f;
RunLoop 的基本作用#xff1a;保持程序的持续运行#xff0c;节省 CPU 的资源#xff0c;提高程序的性能 #xff08; 没有事情#xff0c;就请休眠#xff0c;不要功耗。有事情#xff0c;就处理项目中有用到过吗
RunLoop 的基本作用保持程序的持续运行节省 CPU 的资源提高程序的性能 没有事情就请休眠不要功耗。有事情就处理。 简单举个例子如果用Xcode的Command Line Tool文件来写OC在代码里创建一个NSTimer它并不能正常运行因为这个程序和APP不同程序运行一次直接结束这时候我们需要把Runloop给Run起来NSTimer就能用了。
2. RunLoop内部实现逻辑 3. runloop与线程的关系
每条线程都有唯一的一个与之对应的 RunLoop 对象runloops[thread] runloop RunLoop 保存在一个全局的 Dictionary 里线程作为 Key, RunLoop 作为 Value线程刚创建时并没有 RunLoop 对象 RunLoop 会在第一次获取它时创建RunLoop 会在线程结束时销毁主线程的runloop对象是默认就存在且默认是打开的其他线程的runloop对象只有在第一次获取runloop对象时才会创建而且默认是关闭状态需要我们手动调用run方法运行。
4. timer与runloop的关系
我们创建timer时之所以timer能运行是因为创建timer时一般情况下是在主线程中创建这时会默认将timer以defaultRunloopModel的类型加入主线程而主线程的runloop对象默认是打开的从而timer可以运行。
系统会将NSTimer自动加入NSDefaultRunLoopMode模式中,所以以下两段代码含义相同
[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:selector(run) userInfo:nil repeats:YES];NSTimer *timer [NSTimer timerWithTimeInterval:2.0 target:self selector:selector(run) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];对于NSTimer在滑动时停止工作的问题
当我们不做任何操作的时候RunLoop处于NSDefaultRunLoopMode下当我们进行拖拽时RunLoop就结束NSDefaultRunLoopMode切换到了UITrackingRunLoopMode模式下这个模式下没有添加该NSTimer以及其事件所以我们的NSTimer就不工作了当我们松开鼠标时候RunLoop就结束UITrackingRunLoopMode模式又切换回NSDefaultRunLoopMode模式所以NSTimer就又开始正常工作了
想要解决这个问题也很简单我们直接让NSTimer在两种mode下都能工作就完了这就用到我们之前不太清楚其用法的NSRunLoopCommonModes了
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];当然你也可以把NSTimer分别加入到NSDefaultRunLoopMode和UITrackingRunLoopMode这两种写法是相同的因为系统的mode是默认在_commonModes中的
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode];5. 如何解决timer不准确的问题
可能造成NSTimer不准确的原因
在RunLoop循环过程中被NSTimer触发事件阻塞了导致循环不能及时进行下去延误之后NSTimer触发时间。在RunLoop循环过程中在某一时刻主线程发生了阻塞情况导致循环不能及时进行下去厌恶NSTimer触发时间。在RunLoop循环过程中发生了模式的转换(比如UIScrollView的滑动) 导致原有模式下的NSTimer不会正常触发。
以上情况都是由于NSTimer所依赖的run loops会被多种原因干扰正常循环所以要想解决NSTimer精度问题就要避免所依赖的run loops被外界干扰。
注意虽然第三种情况可以指定NSTimer所处模式为NSRunLoopCommonModes但是这种解决方法并不能改变run loops在特定模式下不能处理其余模式事件的本质。
终极解决办法:
尽量避免将NSTimer放入容易受到影响的主线程run loops中。尽量避免将耗时操作放入NSTimer依赖的线程中。尽量避免在NSTimer触发事件中进行耗时操作如果不能避免将耗时操作移至其余线程进行。
6. RunLoop是怎么响应用户操作的具体流程是怎么样的
在 Objective-C 中RunLoop 是一个事件循环机制用于处理用户操作、定时器事件、网络请求等异步任务RunLoop 的主要作是监听事件并分发给相应的处理器。
下面是 RunLoop 响应用户操作的大致流程
当用户进行某个操作例如点击按钮时操作系统会将该事件发送给应用程序。应用程序主线程会收到这个事件并将其添加当前线程的 RunLoop 中。RunLoop 开始运行并进入一个循环状态不断地检查是否事件需要处理。如果有事件需要处理RunLoop 会将事件从队列中取出并将其分发给相应的处理器例如事件响应方法。处理器执行相应的代码处理事件可能包括更新用户界面、执行业务逻辑等操作。处理完事件后RunLoop 继续循环等下一个事件的到来。RunLoop 的关键是在循环中不断检查事件并及时分发给处理器这样可以保证用户操作的响应速度并且免阻塞主线程。
需要注意的是RunLoop 并不是只处理用户操作它还负责处理其他步任务如定时器事件、网络请求等。RunLoop 的设计目的是为了提供一种效的事件处理制使应用程序能够时响应用户操作其他异步任务同时保持界面的畅性。
7. 说说RunLoop的几种状态
五种状态
kCFRunLoopDefaultModeApp的默认Mode通常主线程是在这个Mode下运行UITrackingRunLoopMode界面跟踪Mode用于ScrollView追踪触摸滑动保证界面滑动时不受其他 Mode 影响UIInitializationRunLoopMode在刚启动 App 时第进入的第一个 Mode启动完成后就不再使用会切换到kCFRunLoopDefaultModeGSEventReceiveRunLoopMode接受系统事件的内部 Mode通常用不到kCFRunLoopCommonModes并不是一种模式 只是一个标记当mode标记为common时将mode添加到runloop中的_commonModes中。runloop中的_commonModes实际上是一个Mode的集合可使用CFRunLoopAddCommonMode()将Mode放到_commonModes中。每当RunLoop的内容发生变化时RunLoop都会将_commonModeItems里的同步到具有Common标记的所有的Mode里
8. RunLoop的mode有什么作用
用来控制一些特殊操作只能在指定模式下运行,一般可以通过指定操作的运行mode 来控制执行时机,以提高用户体验。 系统默认注册了 5 个 Mode
kCFRunLoopDefaultMode:App 的默认 Mode,通常主线程是在这个 Mode下运行,对应 OC 中的:NSDefaultRunLoopModeUITrackingRunLoopMode:界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响kCFRunLoopCommonModes:这是一个标记 Mode,不是一种真正的 Mode,事件可以运行在所有标有 common modes 标记的模式中,对应 OC 中的NSRunLoopCommonModes , 带 有 common modes 标 记 的 模 式 有 :UITrackingRunLoopMode 和 kCFRunLoopDefaultModeUIInitializationRunLoopMode:在启动 App 时进入的第一个 Mode,启动完成后就不再使用GSEventReceiveRunLoopMode:接受系统事件的内部 Mode,通常用不到
AutoreleasePool
在RunLoop这块讲到了很多AutoreleasePool的知识在这补充一下。 自动释放池的PUSH流程 自动释放池的POP流程
1.临时变量什么时候释放
如果在正常情况下一般是超出其作用域就会立即释放如果将临时变量加入了自动释放池会延迟释放即在runloop休眠或者autoreleasepool作用域之后释放
2.AutoreleasePool原理
自动释放池的本质是一个AutoreleasePooIPage结构体对象是一个栈结构存储的页每一个AutoreleasePoolPage都是以双向链表的形式连接自动释放池的压栈和出栈主要是通过结构体的构造函数和析构函数调用底层的obic autoreleasePoolPudh和obic_autoreleasePoolPop实际上是调用AutoreleasePoolPage的push和pop两个方法每次调用push操作其实就是创建一个新的AutoreleasePooIPage,而AutoreleasePoolPage的具体操作就是插入一个POOL BOUNDARY并返回插入POOL BOUNDARY的内存地址。而push内部调用autoreleaseFast方法处理主要有以下三种情况 当page存在且不满时调用add方法将对象添加至page的next指针处并next递增 当page存在且已满时调用autoreleaseFulIPage初始化一个新的page然后调用add方法将对象添加至page栈中 当page不存在时调用autoreleaseNoPage创建一个hotPage然后调用add方法将对象添加至page栈中 当执行pop操作时会传入一个值这个值就是push操作的返回值即POOL_BOUNDARY的内存地址token所以pop内部的实现就是根据token找到哨兵对象所处的page中然后使用 objc_release释放token之前的对象并把next指针到正确位置
3.AutoreleasePool能否嵌套使用
可以嵌套使用其目的是可以控制应用程序的内存峰值使其不要太高可以嵌套的原因是因为自动释放池是以栈为节点通过双向链表的形式连接的且是和线程一一对应的自动释放池的多层嵌套其实就是不停的pushs哨兵对象在pop时会先释放里面的在释放外面的
4.哪些对象可以加入AutoreleasePoolalloc创建可以吗
在MRC下使用new、alloc、copy关键字生成的对象和retain了的对象需要手动释放不会被添加到自动释放池中在MRC下设置为autorelease的对象不需要手动释放会直接进入自动释放池所有autorelease的对象在出了作用域之后会被自动添加到最近创建的自动释放池中在ARC下只需要关注引用计数因为创建都是在主线程进行的系统会自动为主线程创建AutoreleasePool所以创建会自动放入自动释放池
5.AutoreleasePool的释放时机是什么时候
在没有手动添加AutoreleasePool的情况下Auturelease对象是在当前的runloop迭代结束的时候进行释放而它能否释放的原因是系统在每个runloop的迭代中都加入了自动释放池的push和pop。
App启动后苹果在主线程RunLoop里注册了两个Observer其回调都是 _wrapRunLoopWithAutoreleasePoolHandler()。第一个0bserver监视的事件是Entry即将进入 Loop其回调内会调用_objc_autoreleasePooIPush()创建自动释放池其order是-2147483647优先级最高保证创建释放池发生在其他所有回调之前。第二个Observer监视了两个事件:BeforeWating(准备进入休)时调用 obic_autoreleasePoolPop()和 obic autoreleasePooPush释放旧的池井创建新池; ·Exit·即将很出Loop)时调用 obic_autoreleasePooIPop(来释放自动释放池。这个Obserer的order是 2147483647优先级最低保证其释放池子发生在其他所有回调之后。
6.thread和AutoreleasePool的关系
每个线程包括主线程在内都维护了自己的自动释放池堆栈结构新的自动释放池在被创建时会被添加到栈顶;当自动释放池销毁时会从栈中移除对于当前线程来说会将自动释放的对象放入自动释放池的栈顶;在线程停止时会自动释放掉与该线程关联的所有自动释放池
总结每个线程都有与之关联的自动释放池堆栈结构。新的pool在创建时会被压栈到栈顶pool销毁时会被出栈。对于当前线程来说。释放对象会被压栈到栈顶线程停止时会自动释放与之关联的自动释放池
7.RunLoop和AutoreleasePool的关系
主程序的RunLoop在每次事件循环之前会自动创建一个AutoreleasePool。并且会在事件循环结束时执行drain操作释放其中的对象。
多线程
1. 你理解的多线程
好处
1.使用线程可以把占据时间长的程序中的任务放到后台去处理2.用户界面可以更加吸引人这样比如用户点击了一个按钮去触发某些事件的处理可以d出一个进度条来显示处理的进度3.程序的运行速度可能加快4.在一些等待的任务实现上如用户输入、文件读写和网络收发数据等线程就比较有用了。
缺点
1.如果有大量的线程,会影响性能,因为操作系统需要在它们之间切换。2.更多的线程需要更多的内存空间。3.线程的中止需要考虑其对程序运行的影响。4.通常块模型数据是在多个线程间共享的需要防止线程死锁情况的发生。
2. iOS多线程的方式有哪几种你更倾向于哪一种
实现多线程的方法 更倾向于GCD和NSOperation封装更高级使用起来更方便。
3. 有用过GCD吗
1.在其他线程请求完数据在主线程刷新UI //让处理在主线程中执行dispatch_async(dispatch_get main_queue(), ^{/**只在主线程可以执行的处理*例如用户界面更新*/});2.Manager封装网络请求时候可以用GCD实现单例
- (void)once { //GCD的一次性代码(只执行一次)static dispatch_once_t onceToken;dispatch_once(onceToken, ^{// 只执行1次的代码(这里面默认是线程安全的)});
}3.GCD的定时器
- (void)after {NSLog(run -- 0);dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{// 2秒后异步执行这里的代码...NSLog(run -- 2);});
}4.信号量加锁保证线程安全 dispatch_semaphore_t semalook dispatch_semaphore_create(1);dispatch_async(dispatch_get_global_queue(0, 0), ^{dispatch_semaphore_wait(semalook, DISPATCH_TIME_FOREVER);NSLog(1开始);sleep(2);NSLog(1结束);dispatch_semaphore_signal(semalook);});dispatch_async(dispatch_get_global_queue(0, 0), ^{dispatch_semaphore_wait(semalook, DISPATCH_TIME_FOREVER);NSLog(2开始);sleep(2);NSLog(2结束);dispatch_semaphore_signal(semalook);});
4. GCD的队列类型
可以使用dispatch_queue_create来创建对象需要传入两个参数第一个参数表示队列的唯一标识符用于DEBUG可为空第二个参数用来识别是串行队列还是并发队列。DISPATCH_QUEUE_SERIAL表示串行队列DISPATCH_QUEUE_CONCURRENT表示并发队列。
// 串行队列的创建方法
dispatch_queue_t queue dispatch_queue_create(test.queue, DISPATCH_QUEUE_SERIAL);
// 并发队列的创建方法
dispatch_queue_t queue dispatch_queue_create(test.queue, DISPATCH_QUEUE_CONCURRENT);5. 说一下OperationQueue和GCD的区别以及各自的优势
区别
GCD执行效率更高而且由于队列中执行的是由block构成的任务这是一个轻量级的数据结构写起来更方便GCD只支持FIFO的队列而NSOperationQueue可以通过设置最大并发数设置优先级添加依赖关系等调整执行顺序NSOperationQueue甚至可以跨队列设置依赖关系但是GCD只能通过设置串行队列或者在队列内添加barrier(dispatch_barrier_async)任务才能控制执行顺序,较为复杂NSOperationQueue因为面向对象所以支持KVO可以监测operation是否正在执行isExecuted、是否结束isFinished、是否取消isCanceld
实际项目开发中很多时候只是会用到异步操作不会有特别复杂的线程关系管理所以苹果推崇的且优化完善、运行快速的GCD是首选如果考虑异步操作之间的事务性顺序行依赖关系比如多线程并发下载GCD需要自己写更多的代码来实现而NSOperationQueue已经内建了这些支持不论是GCD还是NSOperationQueue我们接触的都是任务和队列都没有直接接触到线程事实上线程管理也的确不需要我们操心系统对于线程的创建调度管理和释放都做得很好。而NSThread需要我们自己去管理线程的生命周期还要考虑线程同步、加锁问题造成一些性能上的开销
6. 线程安全的处理手段有哪些
互斥锁Mutex使用互斥锁可以确保同一时间只有一个线程访问共享资源。在Objective-C中可以使用synchronized关键字来创建互斥锁。
synchronized (self) {// 访问共享资源的代码
}自旋锁Spin Lock自旋锁一种忙等待的锁它会不断地尝试获取锁直到成功为止。在Objective-C中可以使用os_unfair_lock来创建自旋锁。
os_unfair_lock_t lock (OS_UNFAIR_LOCK_INIT);
os_unfair_lock_lock(lock);
// 访问共享资源的代码
os_unfair_lock_unlock(lock);信号量Semaphore信量是一种数器用于控制同时访问某个资源的线程数量。在Objective-C中可以使用dispatch_semaphore来创建信号量。
dispatch_semaphore_t semaphore dispatch_semaphore_create(1);
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
// 访问共享资源的代码
dispatch_semaphore_signal(semaphore);原子操作Atomic Operations原子操作是一种特的操作可以确保对共享资源的读写操作是原子的即不会被其他线程中断。在Objective-C中可以使用atomic键字来声明属性原子类型。
property (atomic, strong) NSObject *sharedObject;串行队列Serial Queue使用串行队列可以确保任务按顺序执行从而避多个线程同时访问共享资源。可以使用GCD(Grand Central Dispatch来创建串行队列。
dispatch_queue_t serialQueue dispatch_queue_create(com.example.serialQueue DISPATCH_QUEUE_SERIAL);
dispatch_async(serialQueue, ^{// 访问共享资源的代码
});7. OC的锁有哪些
【iOS】—— iOS中的相关锁