面试题

作者: CocoaJason | 来源:发表于2019-04-24 15:09 被阅读0次

    线程间通信

    当使用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指向了根元类,根元类指向了自己。

    image

    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(@"添加变量失败");
    
    

    内容·来源
    https://www.cnblogs.com/jiaoxiangjie/p/8527008.html

    相关文章

      网友评论

          本文标题:面试题

          本文链接:https://www.haomeiwen.com/subject/adeggqtx.html