其他分享
首页 > 其他分享> >  2021iOS高频(基础+底层)面试题汇总

 2021iOS高频(基础+底层)面试题汇总

作者:互联网

目录

 

关键字copy,weak,assign,strong,nonatomic

Weak的底层实现原理

runtime理解

1.Category 的实现原理?

2.isa指针的理解,对象的isa指针指向哪里?isa指针有哪两种类型?

3.Objective-C 如何实现多重继承? 

 4.runtime 如何实现 weak 属性?

5.讲一下 OC 的消息机制

6、runtime中重要的数据结构:

7、runtime如何通过selector找到对应的IMP地址?

8.runtime具体应用 

 Runloop篇

1.Runloop 和线程的关系?

2.RunLoop的运行模式

3.runloop内部逻辑?

4.autoreleasePool 在何时被释放?

5.GCD 在Runloop中的使用?

6.AFNetworking 中如何运用 Runloop?

7.PerformSelector 的实现原理?

8.PerformSelector:afterDelay:这个方法在子线程中是否起作用?

9.事件响应的过程?

10.手势识别的过程?

11.CADispalyTimer和Timer哪个更精确

12.可以用Runloop实现什么功能?

多线程 

1.进程与线程

2.什么是多线程?

3.多线程的优点和缺点

4.多线程的 并行 和 并发 有什么区别?

5.iOS中实现多线程的几种方案,各自有什么特点?

6.多个网络请求完成后执行下一步

7.多个网络请求顺序执行后执行下一步

8.异步操作两组数据时, 执行完第一组之后, 才能执行第二组

9.多线程中的死锁?

10.GCD执行原理?

11. 什么时候选择使用GCD,什么时候选择NSOperation?

性能优化

CPU和GPU

离屏渲染

卡顿检测

耗电的主要来源

耗电优化

网络优化

定位优化

硬件检测优化

APP的启动

安装包瘦身


关键字copy,weak,assign,strong,nonatomic

assign:一般用来修饰基本的数据类型,包括基础数据类型 (NSInteger,CGFloat)和C数据类型(int, float, double, char, 等等),为什么呢?assign声明的属性是不会增加引用计数的,也就是说声明的属性释放后,就没有了,即使其他对象用到了它,也无法留住它,只会crash。但是,即使被释放,指针却还在,成为了野指针,如果新的对象被分配到了这个内存地址上,又会crash,所以一般只用来声明基本的数据类型,因为它们会被分配到栈上,而栈会由系统自动处理,不会造成野指针。

retain: 与assign相对,我们要解决对象被其他对象引用后释放造成的问题,就要用retain来声明。retain声明后的对象会更改引用计数,那么每次被引用,引用计数都会+1,释放后就会-1,即使这个对象本身释放了,只要还有对象在引用它,就会持有,不会造成什么问题,只有当引用计数为0时,就被dealloc析构函数回收内存了。

copy: 最常见到copy声明的应该是NSString。copy与retain的区别在于retain的引用是拷贝指针地址,而copy是拷贝对象本身,也就是说retain是浅复制,copy是深复制,如果是浅复制,当修改对象值时,都会被修改,而深复制不会。之所以在NSString这类有可变类型的对象上使用,是因为它们有可能和对应的可变类型如NSMutableString之间进行赋值操作,为了防止内容被改变,使用copy去深复制一份。copy工作由copy方法执行,此属性只对那些实现了NSCopying协议的对象类型有效 。

以上三个可以在MRC中使用,但是weak和strong就只能在ARC中使用,也就是自动引用计数,这时就不能手动去进行retain、release等操作了,ARC会帮我们完成这些工作。

weak: weak其实类似于assign,叫弱引用,也是不增加引用计数。一般只有在防止循环引用时使用,比如父类引用了子类,子类又去引用父类。IBOutlet、Delegate一般用的就是weak,这是因为它们会在类外部被调用,防止循环引用。

strong: 相对的,strong就类似与retain了,叫强引用,会增加引用计数,类内部使用的属性一般都是strong修饰的,现在ARC已经基本替代了MRC,所以我们最常见的就是strong了。一般用于自定义的对象

nonatomic: 在修饰属性时,我们往往还会加一个nonatomic,这又是什么呢?它的名字叫非原子访问。对应的有atomic,是原子性的访问。我们知道,在使用多线程时为了避免在写操作时同时进行写导致问题,经常会对要写的对象进行加锁,也就是同一时刻只允许一个线程去操作它。如果一个属性是由atomic修饰的,那么系统就会进行线程保护,防止多个写操作同时进行。这有好处,但也有坏处,那就是消耗系统资源,所以对于iPhone这种小型设备,如果不是进行多线程的写操作,就可以使用nonatomic,取消线程保护,提高性能。

Weak的底层实现原理

runtime 会维护一个weak表,用于为维护指向对象的所有weak指针,weak是一个hash表,key为所指对象的指针,value为所指对象的指针地址数组

实现过程如下:

runtime理解

https://www.jianshu.com/p/ad97a1e91ba3

1.Category 的实现原理?

2.isa指针的理解,对象的isa指针指向哪里?isa指针有哪两种类型?

3.Objective-C 如何实现多重继承? 

Object-c的类没有多继承,只支持单继承,如果要实现多继承的话,可使用如下几种方式间接实现

 4.runtime 如何实现 weak 属性?

weak 此特质表明该属性定义了一种「非拥有关系」(nonowning relationship)。为这种属性设置新值时,设置方法既不持有新值(新指向的对象),也不释放旧值(原来指向的对象)。

runtime 对注册的类,会进行内存布局,从一个粗粒度的概念上来讲,这时候会有一个 hash 表,这是一个全局表,表中是用 weak 指向的对象内存地址作为 key,用所有指向该对象的 weak 指针表作为 value。当此对象的引用计数为 0 的时候会 dealloc,假如该对象内存地址是 a,那么就会以 a 为 key,在这个 weak 表中搜索,找到所有以 a 为键的 weak 对象,从而设置为 nil。

runtime 如何实现 weak 属性具体流程大致分为 3 步:

5.讲一下 OC 的消息机制

objc_msgSend它具体是如何发送消息:

  1.首先根据receiver对象的isa指针获取它对应的class

  2.优先在class的cache查找method方法,如果找不到,再到methodLists查找

  3.如果没有在class找到,再到super_class查找

  4.一旦找到method这个方法,就执行它实现的IMP。(若能找到,则将method加 入到cache中,以方便下次查找,并通过method中的函数指针跳转到对应的函数中去执行。)

       5.如果,在最顶层的父类(一般也就NSObject)中依然找不到相应的方法时,程序在运行时会挂掉并抛出异常unrecognized selector sent to XXX

6、runtime中重要的数据结构:

SEL  表示方法选择器。

Id      id是通用类型指针

Class 表示对象所属的类。

Method   表示类中的某个方法。

Ivar    表示类中的实例变量。

IMP本质上就是一个函数指针,指向方法的实现。

Cache  主要用来缓存。

7、runtime如何通过selector找到对应的IMP地址?

每一个类对象中都一个对象方法列表(对象方法缓存)

8.runtime具体应用 

 Runloop篇

https://www.jianshu.com/p/85f14af8e7cf

Runloop就是一个运行循环,它保证了在没有任务的时候线程不退出,有任务的时候即使响应。Runloop跟线程,事件响应,手势识别,页面更新,定时器都有着紧密联系。

深入了解推荐ibireme的这篇深入理解RunLoop 

1.Runloop 和线程的关系?

2.RunLoop的运行模式

3.runloop内部逻辑?

4.autoreleasePool 在何时被释放?

5.GCD 在Runloop中的使用?

6.AFNetworking 中如何运用 Runloop?

7.PerformSelector 的实现原理?

8.PerformSelector:afterDelay:这个方法在子线程中是否起作用?

9.事件响应的过程?

10.手势识别的过程?

11.CADispalyTimer和Timer哪个更精确

CADisplayLink 更精确

12.可以用Runloop实现什么功能?

Runloop 开发中运用

AutoreleasePool、事件响应、手势识别、界面更新、定时器、PerformSelecter、GCD、网络请求AFNetworking(AFURLConnectionOperation 这个类是基于 NSURLConnection 构建的,其希望能在后台线程接收 Delegate 回调。为此 AFNetworking 单独创建了一个线程,并在这个线程中启动了一个 RunLoop)

滑动与图片刷新:当tableview的cell上有需要从网络获取的图片的时候,滚动tableView,异步线程会去加载图片,加载完成后主线程就会设置cell的图片,但是会造成卡顿。可以让设置图片的任务在CFRunLoopDefaultMode下进行,当滚动tableView的时候,RunLoop是在 UITrackingRunLoopMode 下进行,不去设置图片,而是当停止的时候,再去设置图片。

实际上 RunLoop 底层也会用到 GCD 的东西,当调用 dispatch_async(dispatch_get_main_queue(), block) 时,libDispatch 会向主线程的 RunLoop 发送消息,RunLoop会被唤醒,并从消息中取得这个 block,并在回调 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__() 里执行这个 block。但这个逻辑仅限于 dispatch 到主线程,dispatch 到其他线程仍然是由 libDispatch 处理的。

 

多线程 

1.进程与线程

2.什么是多线程?

3.多线程的优点和缺点

4.多线程的 并行 和 并发 有什么区别?

5.iOS中实现多线程的几种方案,各自有什么特点?

6.多个网络请求完成后执行下一步

NSString *str = @"http://xxxx.com/";
NSURL *url = [NSURL URLWithString:str];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLSession *session = [NSURLSession sharedSession];

dispatch_group_t downloadGroup = dispatch_group_create();
for (int i=0; i<10; i++) {
    dispatch_group_enter(downloadGroup);

    NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        NSLog(@"%d---%d",i,i);
        dispatch_group_leave(downloadGroup);
    }];
    [task resume];
}

dispatch_group_notify(downloadGroup, dispatch_get_main_queue(), ^{
    NSLog(@"end");
});

NSString *str = @"http://xxxx.com/";
NSURL *url = [NSURL URLWithString:str];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLSession *session = [NSURLSession sharedSession];

dispatch_semaphore_t sem = dispatch_semaphore_create(0);
for (int i=0; i<10; i++) {

    NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        NSLog(@"%d---%d",i,i);
        count++;
        if (count==10) {
            dispatch_semaphore_signal(sem);
            count = 0;
        }
    }];
    [task resume];
}
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);

dispatch_async(dispatch_get_main_queue(), ^{
    NSLog(@"end");
});

7.多个网络请求顺序执行后执行下一步

NSString *str = @"http://www.jianshu.com/p/6930f335adba";
NSURL *url = [NSURL URLWithString:str];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLSession *session = [NSURLSession sharedSession];

dispatch_semaphore_t sem = dispatch_semaphore_create(0);
for (int i=0; i<10; i++) {

    NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {

        NSLog(@"%d---%d",i,i);
        dispatch_semaphore_signal(sem);
    }];

    [task resume];
    dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
}

dispatch_async(dispatch_get_main_queue(), ^{
    NSLog(@"end");
});

8.异步操作两组数据时, 执行完第一组之后, 才能执行第二组

dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);

dispatch_async(queue, ^{
    NSLog(@"第一次任务的主线程为: %@", [NSThread currentThread]);
});

dispatch_async(queue, ^{
    NSLog(@"第二次任务的主线程为: %@", [NSThread currentThread]);
});

dispatch_barrier_async(queue, ^{
    NSLog(@"第一次任务, 第二次任务执行完毕, 继续执行");
});

dispatch_async(queue, ^{
    NSLog(@"第三次任务的主线程为: %@", [NSThread currentThread]);
});

dispatch_async(queue, ^{
    NSLog(@"第四次任务的主线程为: %@", [NSThread currentThread]);
});

9.多线程中的死锁?

死锁是由于多个线程(进程)在执行过程中,因为争夺资源而造成的互相等待现象,你可以理解为卡主了。产生死锁的必要条件有四个:

dispatch_sync(dispatch_get_main_queue(), ^{
    NSLog(@"2");
});

NSLog(@"1");
// 什么也不会打印,直接报错

10.GCD执行原理?

11. 什么时候选择使用GCD,什么时候选择NSOperation

项目中使用NSOperation的优点是NSOperation是对线程的高度抽象,在项目中使用它,会使项目的程序结构更好,子类化NSOperation的设计思路,是具有面向对象的优点(复用、封装),使得实现是多线程支持,而接口简单,建议在复杂项目中使用。

项目中使用GCD的优点是GCD本身非常简单、易用,对于不复杂的多线程操作,会节省代码量,而Block参数的使用,会是代码更为易读,建议在简单项目中使用。

性能优化

面试中常常问道性能优化的问题,其中有几个主要的

CPU和GPU

在屏幕成像的过程中,CPU和GPU起着至关重要的作用

卡顿解决的主要思路

离屏渲染

在OpenGL中,GPU有2种渲染方式:

离屏渲染消耗性能的原因

卡顿检测

耗电的主要来源

耗电优化

网络优化

定位优化

硬件检测优化

用户移动、摇晃、倾斜设备时,会产生动作(motion)事件,这些事件由加速度计、陀螺仪、磁力计等硬件检测。在不需要检测的场合,应该及时关闭这些硬件

APP的启动

APP启动时间的优化,主要是针对冷启动进行优化
通过添加环境变量可以打印出APP的启动时间分析(Edit scheme -> Run -> Arguments)
DYLD_PRINT_STATISTICS设置为1
如果需要更详细的信息,那就将DYLD_PRINT_STATISTICS_DETAILS设置为1

APP的启动 - dyld

dyld(dynamic link editor),Apple的动态链接器,可以用来装载Mach-O文件(可执行文件、动态库等)

APP的启动 - runtime

总结一下

APP的启动优化

安装包瘦身

LinkMap

 

 

标签:面试题,多线程,对象,weak,dispatch,线程,2021iOS,高频,RunLoop
来源: https://blog.csdn.net/tttgggxxx/article/details/117125828