线程间通信
当使用dispath-async函数开辟线程执行任务的完成时,我们需要使用dispatch_async(dispatch_get_main_queue(), ^{ });
函数回到主线程内刷新UI。并完成通信
dispatch_async(dispatch_get_main_queue(), ^{ });
GCD的一些常用的函数?(group,barrier,信号量,线程同步)
group
我们使用队列组来开辟线程时,队列组中的队列任务是并发,当所有的队列组中的所有任务完成时候,才可以调用队列组完成任务。
/**创建自己的队列*/
dispatch_queue_t dispatchQueue = dispatch_queue_create("ted.queue.next", DISPATCH_QUEUE_CONCURRENT);
/**创建一个队列组*/
dispatch_group_t dispatchGroup = dispatch_group_create();
/**将队列任务添加到队列组中*/
dispatch_group_async(dispatchGroup, dispatchQueue, ^(){
NSLog(@"dispatch-1");
});
/**将队列任务添加到队列组中*/
dispatch_group_async(dispatchGroup, dispatchQueue, ^(){
NSLog(@"dspatch-2");
});
/**队列组完成调用函数*/
dispatch_group_notify(dispatchGroup, dispatch_get_main_queue(), ^(){
NSLog(@"end");
})
barrier
表示栅栏,当在并发队列里面使用栅栏时候,栅栏之前的并发任务开始并发执行,执行完毕后,执行栅栏内的任务,等栅栏任务执行完毕后,再并发执行栅栏后的任务。
dispatch_queue_t concurrentQueue = dispatch_queue_create("my.concurrent.queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(concurrentQueue, ^(){
NSLog(@"dispatch-1");
});
dispatch_async(concurrentQueue, ^(){
NSLog(@"dispatch-2");
});
dispatch_barrier_async(concurrentQueue, ^(){
NSLog(@"dispatch-barrier");
});
dispatch_async(concurrentQueue, ^(){
NSLog(@"dispatch-3");
});
dispatch_async(concurrentQueue, ^(){
NSLog(@"dispatch-4");
});
信号量
Semaphore是通过‘计数’的方式来标识线程是否是等待或继续执行的。
dispatch_semaphore_create(int) // 创建一个信号,并初始化信号的计数大小
/* 等待信号,并且判断信号量,如果信号量计数大于等于你创建时候的信号量的计数,就可以通过,继续执行,并且将你传入的信号计数减1,
* 如果传入的信号计数小于你创建的计数,就表示等待,等待信号计数的变化
* 如果等待的时间超过你传入的时间,也会继续下面操作
* 第一个参数:semaphore 表示信号量
* 第二个参数:表示等待的时间
* 返回int 如果传入的信号计数大于等于你创建信号的计数时候,返回0. 反之,返回的不等于0
*/
int result = dispatch_semaphore_wait(dispatch_semaphore_t semaphore,time outTime);// 表示等待,也是阻碍线程
// 表示将信号技术+1
dispatch_semaphore_signl(dispatch_semaphore_t semaphore);
实现线程的同步
串行队列,分组,信号量。也是可以使用并发队列。
//加入队列
dispatch_async(concurrentQueue, ^{
//1.先去网上下载图片
dispatch_sync(concurrentQueue, ^{
});
//2.在主线程展示到界面里
dispatch_sync(dispatch_get_main_queue(), ^{
});
});
如何使用队列来避免资源抢夺?
当我们使用多线程来访问同一个数据的时候,就有可能造成数据的不准确性。这个时候我么可以使用线程锁的来来绑定。也是可以使用串行队列来完成。如:fmdb就是使用FMDatabaseQueue,来解决多线程抢夺资源。
NSCache优于NSDictionary的几点?
- nscache 是可以自动释放内存的。
- nscache是线程安全的,我们可以在不同的线程中添加,删除和查询缓存中的对象。
- 一个缓存对象不会拷贝key对象。
为什么在默认情况下无法修改被block捕获的变量? __block都做了什么?
默认情况下,block里面的变量,拷贝进去的是变量的值,而不是指向变量的内存的指针。
当使用__block修饰后的变量,拷贝到block里面的就是指向变量的指针,所以我们就可以修改变量的值。
objc在向一个对象发送消息时,发生了什么?
根据对象的isa指针找到类对象id,在查询类对象里面的methodLists方法函数列表,如果没有在好到,在沿着superClass,寻找父类,再在父类methodLists方法列表里面查询,最终找到SEL,根据id和SEL确认IMP(指针函数),在发送消息;
什么时候会报unrecognized selector错误?iOS有哪些机制来避免走到这一步?
当发送消息的时候,我们会根据类里面的methodLists列表去查询我们要动用的SEL,当查询不到的时候,我们会一直沿着父类查询,当最终查询不到的时候我们会报unrecognized selector错误
当系统查询不到方法的时候,会调用+(BOOL)resolveInstanceMethod:(SEL)sel动态解释的方法来给我一次机会来添加,调用不到的方法。或者我们可以再次使用-(id)forwardingTargetForSelector:(SEL)aSelector重定向的方法来告诉系统,该调用什么方法,一来保证不会崩溃。
能否向编译后得到的类中增加实例变量?能否向运行时创建的类中添加实例变量?为什么?
1.不能向编译后得到的类增加实例变量
2.能向运行时创建的类中添加实例变量
1.编译后的类已经注册在runtime中,类结构体中的objc_ivar_list实例变量的链表和instance_size实例变量的内存大小已经确定,runtime会调用class_setvarlayout或class_setWeaklvarLayout来处理strong weak引用.所以不能向存在的类中添加实例变量
2.运行时创建的类是可以添加实例变量,调用class_addIvar函数.但是的在调用objc_allocateClassPair之后,objc_registerClassPair之前,原因同上.
runtime如何实现weak变量的自动置nil?
runtime 对注册的类, 会进行布局,对于 weak 对象会放入一个 hash 表中。 用 weak 指向的对象内存地址作为 key,当此对象的引用计数为0的时候会 dealloc,假如 weak 指向的对象内存地址是a,那么就会以a为键, 在这个 weak 表中搜索,找到所有以a为键的 weak 对象,从而设置为 nil。
给类添加一个属性后,在类结构体里哪些元素会发生变化?
instance_size :实例的内存大小
objc_ivar_list *ivars:属性列表
RunLoop
runloop是来做什么的?runloop和线程有什么关系?主线程默认开启了runloop么?子线程呢?
runloop:字面意思就是跑圈,其实也就是一个循环跑圈,用来处理线程里面的事件和消息。
runloop和线程的关系:每个线程如果想继续运行,不被释放,就必须有一个runloop来不停的跑圈,以来处理线程里面的各个事件和消息。
主线程默认是开启一个runloop。也就是这个runloop才能保证我们程序正常的运行。子线程是默认没有开始runloop的
runloop的mode是用来做什么的?有几种mode?
model:是runloop里面的模式,不同的模式下的runloop处理的事件和消息有一定的差别。
系统默认注册了5个Mode:
(1)kCFRunLoopDefaultMode: App的默认 Mode,通常主线程是在这个 Mode 下运行的。
(2)UITrackingRunLoopMode: 界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响。
(3)UIInitializationRunLoopMode: 在刚启动 App 时第进入的第一个 Mode,启动完成后就不再使用。
(4)GSEventReceiveRunLoopMode: 接受系统事件的内部 Mode,通常用不到。
(5)kCFRunLoopCommonModes: 这是一个占位的 Mode,没有实际作用。
注意iOS 对以上5中model进行了封装
NSDefaultRunLoopMode;
NSRunLoopCommonModes
为什么把NSTimer对象以NSDefaultRunLoopMode(kCFRunLoopDefaultMode)添加到主运行循环以后,滑动scrollview的时候NSTimer却不动了?
nstime对象是在
NSDefaultRunLoopMode
下面调用消息的,但是当我们滑动scrollview的时候,NSDefaultRunLoopMode
模式就自动切换到UITrackingRunLoopMode
模式下面,却不可以继续响应nstime发送的消息。所以如果想在滑动scrollview的情况下面还调用nstime的消息,我们可以把nsrunloop的模式更改为NSRunLoopCommonModes
4.苹果是如何实现Autorelease Pool的?
Autorelease Pool作用:缓存池,可以避免我们经常写relase的一种方式。其实就是延迟release,将创建的对象,添加到最近的autoreleasePool中,等到autoreleasePool作用域结束的时候,会将里面所有的对象的引用计数器-1.
autorelease
类结构
1.isa指针?(对象的isa,类对象的isa,元类的isa都要说)
在oc中,类也是对象,所属元类。所以经常说:
万物皆对象
对象的isa指针指向所属的类
类的isa指针指向了所属的元类
元类的isa指向了根元类,根元类指向了自己。
2.类方法和实例方法有什么区别?
调用的方式不同,类方法必须使用类调用,在方法里面不能调用属性,类方法里面也必须调用类方法。存储在元类结构体里面的
methodLists
里面
实例方法必须使用实例对象调用,可以在实例方法里面使用属性,实例方法也必须调用实例方法。存储在类结构体里面的methodLists
里面
有没有用过运行时,用它都能做什么?(交换方法,创建类,给新创建的类增加方法,改变isa指针)
交换方法:一般写在类的+(void)load方法里面
/** 获取原始setBackgroundColor方法 */
Method originalM = class_getInstanceMethod([self class], @selector(setBackgroundColor:));
/** 获取自定义的pb_setBackgroundColor方法 */
Method exchangeM = class_getInstanceMethod([self class], @selector(pb_setBackgroundColor:));
/** 交换方法 */
method_exchangeImplementations(originalM, exchangeM);
创建类:
Class MyClass = objc_allocateClassPair([NSObject class], "Person", 0);
添加方法
/**参数一、类名参数
二、SEL 添加的方法名字参数
三、IMP指针 (IMP就是Implementation的缩写,它是指向一个方法实现的指针,每一个方法都有一个对应的IMP)
参数四、其中types参数为"i@:@“,按顺序分别表示:具体类型可参照[官方文档](https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html)i 返回值类型int,若是v则表示void@ 参数id(self): SEL(_cmd)@ id(str)
V@:表示返回值是void 带有SEL参数 (An object (whether statically typed or typed id))
*/
class_addMethod(Person, @selector(addMethodForMyClass:), (IMP)addMethodForMyClass, "V@:");
添加实例变量
/**参数一、类名参数
二、属性名称参数
三、开辟字节长度参数
四、对其方式参数
五、参数类型 “@” 官方解释 An object (whether statically typed or typed id) (对象 静态类型或者id类型) 具体类型可参照[官方文档](https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html)return: BOOL 是否添加成功
*/
BOOL isSuccess = class_addIvar(Person, "name", sizeof(NSString *), 0, "@");
isSuccess?NSLog(@"添加变量成功"):NSLog(@"添加变量失败");
网友评论