面试IOS面试专题

iOS底层原理总结 -- iOS面试题

2019-11-25  本文已影响0人  小李小李一路有你

总结一些iOS的底层面试题。巩固一下iOS的相关基础知识。

如有出入,还望各位大神指出。

OC对象

1. NSObject对象的本质是什么?

2. 一个NSObject对象占用多少内存?

3. 对象的isa指针指向哪里?

4. OC类的信息存储在哪里?

5. 说说你对函数调用的理解吧。

KVO

1. iOS用什么方式实现对一个对象的KVO?(KVO的本质是什么?)

KVC

1. 使用KVC会不会调用KVO?

2. KVC的赋值和取值过程是怎么样的?原理是什么?

Category:

1. Category的使用场合是什么?

2. Category中的属性是否也存在类对象中?如果存在是怎么生成和存在的?如果不存在,它存在的位置在哪里?

3. Category的使用原理是什么?实现过程

4. Category和Extension的区别是什么?

5. Category中有load方法吗?load是什么时候调用的?

6. load、initialize方法的区别是什么?它们在Category中的调用顺序?以及出现继承时他们之间的调用过程?当一个类有分类的时候为什么+load能多次调用儿initialize值调用了一次?

7. Category能否添加成员变量?如果可以,如何给Category添加成员变量?

```objc
#import<objc/runtime.h>

const void *DLNameKey = &DLNameKey
///> 添加关联对象
void objc_setAssociatedObject(
id object,          ///>  给哪一个对象添加关联对象
const void * key,   ///>   指针(赋值取值的key)  &DLNameKey
id value,           ///>  关联的值
objc_AssociationPolicy policy ///>  关联策略 下方表格
)

eg : objc_setAssociatedObject(self,@selector(name),name,OBJC_ASSOCIATION_COPY_NONATOMIC);


///> 获得关联对象
id objc_getAssociatedObject(
id object,           ///>  哪一个对象的关联对象
const void * key     ///>   指针(赋值取值的key) 
)
eg:
objc_getAssociatedObject(self,@selector(name));
/// _cmd  == @selector(name); 
objc_getAssociatedObject(self,_cmd);

///> 移除所有的关联对象
void objc_removeAssociatedObjects(
id object       ///>
)
  - objc_AssociationPolicy(关联策略)

|objc_AssociationPolicy(关联策略) |对应的修饰符|
|:---|:---|:---|:---|
|OBJC_ASSOCIATION_ASSIGN | assign  |
|OBJC_ASSOCIATION_RETAIN_NONATOMIC |  strong, nonatomic | 
|OBJC_ASSOCIATION_COPY_NONATOMIC |  copy, nonatomic | 
|OBJC_ASSOCIATION_RETAIN |  strong, atomic | 
|OBJC_ASSOCIATION_COPY |  copy, atomic | 


Block

1. block的原理是怎样的?本质是什么

2. 看代码解释原因

  int main(int argc, const char *argv[]){
    @autoreleasepool{
      int age = 10;
      void  (^block)(void) = ^{
          NSLog(@" age is %d ",age);
      };
      age = 20;
      block();
    }
  }
  /*
  输出结果为? 为什么?
  输出结果是: 10
  如果没有修饰符  默认是auto
  为了能访问外部的变量 
  block有一个变量捕获的机制 
  因为他是局部变量 并且没有用static修饰 
  所以它被捕获到block中是 一个值,外部再次改变时 block中的age不会改变。
  */
变量类型 捕获到Block内部 访问方式
局部变量 auto 值传递
局部变量 static 指针传递
全局变量 直接访问
int main(int argc, const char *argv[]){
  @autoreleasepool{
    int age = 10;
    static int height = 10;
    void  (^block)(void) = ^{
        NSLog(@" age is %d, height is %d",age, height);
    };
    age = 20;
    height = 20;
    block();
  }
}
/*
输出结果为? 为什么?
age is 10, height is 20
局部变量用static 修饰之后 捕获到block中的是 height的指针,
因此修改通过指针修改变量之后 外部的变量也被修改了
*/
int age = 10;
static int height = 10;
int main(int argc, const char *argv[]){
  @autoreleasepool{
    
    void  (^block)(void) = ^{
        NSLog(@" age is %d, height is %d",age, height);
    };
    age = 20;
    height = 20;
    block();
  }
}
/*
输出结果为? 为什么?
age is 20, height is 20
 因为 age 和 height是全局变量不需要捕获直接就可以修改
 
 全局变量 对应该就可以访问,
 局部变量 需要跨函数访问,所以需要捕获
因此修改通过指针修改变量之后 外部的变量也被修改了
*/

int main(int argc, const char *argv[]){
  @autoreleasepool{
    
    void  (^block)(void) = ^{
        NSLog(@" self %p",self);
    };
    block();
  }
}
/*
self 会不会被捕获?
因为函数默认会有两个参数 void test(DLPerson *self, SEL _cmd)
所以self 也是一个局部变量

访问@property(nonatmic, copy) NSString *name;
因为他是成员变量, 访问的时候 用self.name 或者 self->_name 访问  所以 block在内部会捕获self。
*/

3. 既然block是一个OC对象,那么block的对象类型是什么?

4. 在什么情况下 ARC环境下,编译器会根据情况自动将栈上的block复制到堆上?

5. __weak的作用是什么?有什么使用注意点?

6. __block的作用是什么?有什么使用注意点?

7. __block的属性修饰词是什么?为什么?使用block有哪些注意点?

8. block在修饰NSMutableArray,需不需要添加__block?

Runtime

22. 讲一下OC的消息机制

23. 消息转发机制流程

​ 在objc_msgSend有三大阶段

24. 什么是runtime? 平时项目中有用过吗?

26. iskindOfClass 和 isMemberOfClass的区别?

Runloop

1. 讲讲Runloop片在项目中的应用

Runloop

多线程

1. iOS中常见的多线程方案

技术方案 简介 语言 线程生命周期 使用频率
pthread 1. 一套通用的多线程API
2. 适用于Unix/Linux/Windows等系统
3. 跨平台、可移植
4. 使用难度大
C 程序员管理 几乎不用
NSThread 1. 使用更加面向对象
2. 简单易用,可直接操作线程对象
OC 程序员管理 偶尔使用
GCD 1. 旨在代替NSThread等线程技术
2. 充分利用设备的多核
C 自动管理 经常使用
NSOperation 1. 基于GCD(底层是GCD)
2. 比GCD多了一些简单使用的功能
3. 使用更加面向对象
OC 自动管理 经常使用

2. GCD的常用函数

3. GCD的队列

4. 组合队列执行表

并发队列 手动创建串行队列 主队列
同步 没有开启新线程
串行执行任务
没有开启新线程
串行执行任务
没有开启新线程
串行执行任务
异步 开启新线程
并行执行任务
开启新线程
串行执行任务
没有开启新线程
串行执行任务

5. GCD的线程锁

- (void)interview01{
    ///> 会发生死锁,
    NSLog(@"任务1");
    dispatch_queue_t queue = dispatch_get_main_queue();
    dispatch_sync(queue, ^{
        NSLog(@"任务2");
    });
    NSLog(@"任务3");
    ///      dispatch_sync 需要立马在当前线程 同步执行任务  当前在主线程中
    ///      而主队列需要等 主线程的东西执行完之后才会执行。 所以造成了死锁
}

- (void)interview02{
    ///> 不会发生死锁
    NSLog(@"任务1");
    dispatch_queue_t queue = dispatch_get_main_queue();
    
    dispatch_async(queue, ^{
        NSLog(@"任务2");
    });
    NSLog(@"任务3");
    //  dispatch_sync 不需要需要立马在当前线程 同步执行任务 所以等待主线程执行结束之后才执行的
}

- (void)interview03{
    ///> 会产生死锁
    NSLog(@"任务1");
    dispatch_queue_t queue = dispatch_queue_create("muqueue", DISPATCH_QUEUE_SERIAL); /// 同步;
    dispatch_async(queue, ^{
        NSLog(@"任务2");
        dispatch_sync(queue, ^{  //死锁
            NSLog(@"任务3");
        });
        NSLog(@"任务4");
    });
    NSLog(@"任务5");   
}

- (void)interview04{
    ///> 不会产生死锁
    NSLog(@"任务1");
    dispatch_queue_t queue = dispatch_queue_create("muqueue", DISPATCH_QUEUE_SERIAL); /// 串行;
    //    dispatch_queue_t queue2 = dispatch_queue_create("muqueue2", DISPATCH_QUEUE_CONCURRENT); /// 并发;
    dispatch_queue_t queue2 = dispatch_queue_create("muqueue2", DISPATCH_QUEUE_SERIAL); /// 串行; 也不会
    dispatch_async(queue, ^{
        NSLog(@"任务2");
        dispatch_sync(queue2, ^{  //死锁
            NSLog(@"任务3");
        });
        NSLog(@"任务4");
    });
    NSLog(@"任务5");
    //不会产生死锁   因为两个任务不在同一个队列之中, 所以不存在互相等待的问题。
}

- (void)interview05{
    ///> 不会产生死锁
    NSLog(@"任务1  thread:%@",[NSThread currentThread]);
    dispatch_queue_t queue = dispatch_queue_create("muqueue2", DISPATCH_QUEUE_CONCURRENT); /// 并发;
    dispatch_async(queue, ^{
        NSLog(@"任务2 thread:%@",[NSThread currentThread]);
        dispatch_sync(queue, ^{
            NSLog(@"任务3  thread:%@",[NSThread currentThread]);
        });
        NSLog(@"任务4  thread:%@",[NSThread currentThread]);
    });
    NSLog(@"任务5  thread:%@",[NSThread currentThread]);
    //不会产生死锁   因为两个任务不在同一个队列之中, 所以不存在互相等待的问题。
}

6. GCD的线程锁-- runloop有关的锁

- (void)test{
    NSLog(@"2");
}

- (void)touchesBegan03{
//    NSThread *thread = [[NSThread alloc]initWithBlock:^{
//        NSLog(@"1");
//
//    }];
//    [thread start];
//    [self performSelector:@selector(test) onThread:thread withObject:nil waitUntilDone:YES];
//    运行后会崩溃  因为子线程 performSelector方法 没有开启runloop, 当执行test的时候这个线程已经没有了。
    
    NSThread *thread = [[NSThread alloc]initWithBlock:^{
        NSLog(@"1");
        [[NSRunLoop  currentRunLoop] addPort:[[NSPort alloc]init] forMode:NSDefaultRunLoopMode];
        [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
    }];
    [thread start];
    [self performSelector:@selector(test) onThread:thread withObject:nil waitUntilDone:YES];
    /// r添加开启runloop后  在线程中有runloop存在线程就不会死掉, 之后调用performSelect就没有问题了
}

- (void)touchesBegan02{
    /// 创建全局并发队列
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    dispatch_async(queue, ^{
        NSLog(@"1");

        //[self performSelector:@selector(test) withObject:nil];///  打印结果  1  2  3   等价于[self test]
        /// 这句代码点进去发现是在Runloop中的方法
        /// 本质就是向Runloop中添加了一个定时器。  子线程默认是没有启动 Runloop的
        
        [self performSelector:@selector(test) withObject:nil afterDelay:.0]; ///  打印结果  1  3
        NSLog(@"3");
        /// 启动runloop
        [[NSRunLoop  currentRunLoop] addPort:[[NSPort alloc]init] forMode:NSDefaultRunLoopMode];
        [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
    });
}
- (void)touchesBegan01{
    /// 创建全局并发队列
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    dispatch_async(queue, ^{
        NSLog(@"1");

//        [self performSelector:@selector(test) withObject:nil];///  打印结果  1  2  3   等价于[self test]
        /// 这句代码点进去发现是在Runloop中的方法
//     本质就是向Runloop中添加了一个定时器。  子线程默认是没有启动 Runloop的

        [self performSelector:@selector(test) withObject:nil afterDelay:.0]; ///  打印结果  1  3
        NSLog(@"3");
    });
}

7. GCD组队列的使用

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_queue_create("muqueue", DISPATCH_QUEUE_CONCURRENT);// 并发队列
    dispatch_group_async(group, queue, ^{
        for (int i = 0; i < 5; i++) {
            NSLog(@"任务1   thread  --- %@",[NSThread currentThread]);
        }
    });
    dispatch_group_async(group, queue, ^{
        for (int i = 0; i < 5; i++) {
            NSLog(@"任务2   thread  --- %@",[NSThread currentThread]);
        }
    });
    
    ///> 回到主线程执行 任务3
//    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
//        for (int i = 0; i < 5; i++) {
//            NSLog(@"任务3   thread  --- %@",[NSThread currentThread]);
//        }
//    });
    
    ///> 执行完任务1、2之后再执行任务3、4 
    dispatch_group_notify(group, queue, ^{
        for (int i = 0; i < 5; i++) {
            NSLog(@"任务3   thread  --- %@",[NSThread currentThread]);
        }
    });
    dispatch_group_notify(group, queue, ^{
        for (int i = 0; i < 5; i++) {
            NSLog(@"任务4   thread  --- %@",[NSThread currentThread]);
        }
    });
}

8. 多线程安全隐患的解决方案

内存管理

性能优化

1. 什么是CPU和GPU

2. 卡顿原因

2. 卡顿优化 - CPU

3. 卡顿优化 - GPU

4. 离屏渲染

4. 卡顿检测

5. 耗电优化

6. 安装包瘦身

构架设计

1. 设计模式

上一篇 下一篇

猜你喜欢

热点阅读