早年间那些iOS面试题,不知道现在还有没有用?无处安放,就留这里吧。
1、runtime相关问题
1.1 结构模型
1.1.1 介绍下runtime的内存模型(isa、对象、类、meta class、结构体的存储信息等)
- runtime对NSObject的定义
runtime对NSObject(见NSObject
.h)的定义如下:
@interface NSObject <NSObject> {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-interface-ivars"
Class isa OBJC_ISA_AVAILABILITY;
#pragma clang diagnostic pop
}
+ (void)load;
+ (void)initialize;
- (instancetype)init
略...
NSObject类中有一个Class类型的指针isa,也就是说OC对象都有一个指向该类的指针,即isa指针。
- runtime对Object的定义
runtime对Object的定义,(见Object.h)如下:
@interface Object
{
Class isa; /* A pointer to the instance's class structure */
}
/* Initializing classes and instances */
+ (id)initialize;
- (id)init;
略...
runtime对对象的定义如上,对象也是有一个指向对象对应的类的isa指针。
- runtime对Class的定义
runtime对Class的定义,(见objc.h)如下:
typedef struct objc_class *Class;
Class对象其实是一个指向objc_class结构体的指针
这么看来对象是一个objc_class类型的结构体。
- runtime对objc_class的定义
runtime对objc_class的定义,(见runtime.h)如下:
struct objc_class {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
//父类指针
Class _Nullable super_class OBJC2_UNAVAILABLE;
//类名
const char * _Nonnull name OBJC2_UNAVAILABLE;
//版本
long version OBJC2_UNAVAILABLE;
//?
long info OBJC2_UNAVAILABLE;
//实例大小
long instance_size OBJC2_UNAVAILABLE;
//属性列表
struct objc_ivar_list * _Nullable ivars OBJC2_UNAVAILABLE;
//方法列表
struct objc_method_list * _Nullable * _Nullable methodLists OBJC2_UNAVAILABLE;
//缓存方法列表
struct objc_cache * _Nonnull cache OBJC2_UNAVAILABLE;
//协议列表
struct objc_protocol_list * _Nullable protocols OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;
/* Use `Class` instead of `struct objc_class *` */
以属性列表objc_ivar_list为例,看下runtime对它的定义(见runtime.h):
struct objc_ivar_list {
//属性数
int ivar_count OBJC2_UNAVAILABLE;
#ifdef __LP64__
//在内存中空间首地址?
int space OBJC2_UNAVAILABLE;
#endif
/* variable length structure */
//这里这个1是变长结构体的语法,因为不同类的实例变量数量不定,所以这里定义了一个变长结构体。
struct objc_ivar ivar_list[1] OBJC2_UNAVAILABLE;
} OBJC2_UNAVAILABLE;
objc_ivar的定义:
struct objc_ivar {
char * _Nullable ivar_name OBJC2_UNAVAILABLE;
char * _Nullable ivar_type OBJC2_UNAVAILABLE;
int ivar_offset OBJC2_UNAVAILABLE;
#ifdef __LP64__
int space OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;
这里猜测属性在内存中是连续的一段内存存储的,这里的space是记录了第一个属性的地址。objc_ivar中记录了当前这个属性的名称(ivar_name),属性类型(ivar_type),内存偏移量(ivar_offset)。通过ivar_offset、count和space可以将每个属性都存储起来。
objc_ivar_list是通过什么数据结构存储属性数据的呢?
怎么读objc_ivar_list里的呢?
- isa
isa是Class类型的一个指针,对象的isa指针指向对象的元类,类对象的isa指针指向类对象的元类。
- meta class
元类,对象的元类即对象所属的类。
类对象的元类是该类的父类。
类对象,比如Class:
typedef struct objc_class *Class;
1.1.2 为什么要设计metaclass
objc_class定义中有一个isa指针,在objc_class定义的类里面,这个指针指向该类的所属类,这个类虽然是个类,在这里看更像是一个对象。
类的isa指针指向该类的所属类,也就是元类,通过isa指针找到元类,就可以找到元类里的方法等。
对象的isa指针指向该对象所属的类,通过isa指针找到该对象的元类,从而找到该对象的属性、方法等。
isa指针是让“类/对象”找到“所属类”,suprClass指针是让“对象”找到自己的“父类”。
类找到元类,才算是找到了类的方法,类方法;对象找到元类,才算是找到了自己的根。
isa指针可以让类/对象找打自己的“根”,即类/对象是从哪里来的。
1.1.3 class_copyIvarList & class_copyPropertyList区别
class_copyIvarList
OBJC_EXPORT Ivar _Nonnull * _Nullable
class_copyIvarList(Class _Nullable cls, unsigned int * _Nullable outCount)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
class_copyPropertyList
OBJC_EXPORT objc_property_t _Nonnull * _Nullable
class_copyPropertyList(Class _Nullable cls, unsigned int * _Nullable outCount)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
class_copyIvarList可以获取到属性和变量,class_copyPropertyList只是获取所有属性的列表。
获取变量和属性列表的代码如下:
Person.h
#import <Foundation/Foundation.h>
#import "Car.h"
@interface Person : NSObject
{
NSUInteger _age;
}
@property (nonatomic, strong) Car *car;
- (void)setAge:(NSUInteger)age;
- (NSUInteger)age;
@end
Person.m
#import "Person.h"
@implementation Person
- (void)setAge:(NSUInteger)age {
_age = age;
}
- (NSUInteger)age {
return _age;
}
@end
- 获取属性列表
unsigned int count = 0;
objc_property_t *property_t = class_copyPropertyList([p class], &count);
for (int i = 0; i < count; i ++) {
objc_property_t property = property_t[i];
NSString *propertyName = [NSString stringWithUTF8String:property_getName(property)];
NSLog(@"%d %@",i,propertyName);
}
free(property_t);
打印如下:
0 car
只是打印了属性列表。
- 获取变量列表
//获取所有变量
unsigned int count = 0;
Ivar *ivarList = class_copyIvarList([p class], &count);
for (int i = 0; i < count; i ++) {
Ivar ivar = ivarList[i];
NSString *ivarName = [NSString stringWithUTF8String:ivar_getName(ivar)];
NSLog(@"%d %@",i,ivarName);
//这里获取到的是所有“_xx”类型的变量,如果需要获取属性,可做如下处理
//ivarName = [ivarName substringFromIndex:1];
}
free(ivarList);
打印如下:
0 _age 1 _car
打印了变量列表。
1.1.4 class_rw_t 和 class_rw_t 的区别
class_rw_t结构体内有一个指向class_ro_t结构体的指针。
这里摘抄网上的说法:“每个类都对应有一个class_ro_t结构体和一个class_rw_t结构体。在编译期间,class_ro_t结构体就已经确定,objc_class中的bits的data部分存放着该结构体的地址。在runtime运行之后,具体说来是在运行runtime的realizeClass 方法时,会生成class_rw_t结构体,该结构体包含了class_ro_t,并且更新data部分,换成class_rw_t结构体的地址。”
- class_rw_t
内部信息可读可写的
内部包含的信息来源时runtime时动态添加的,比如分类中的方法会在运行时添加到method_array_t中
- class_rw_t
内部信息只读
内部为类编译器生成的信息,不可添加和删除。
1.1.5 category如何被加载的,两个category的load方法的加载顺序,两个category的同名方法的加载顺序
1.1.6 category & extension区别,能给NSObject添加Extension吗,结果如何
1.1.7 消息转发机制,消息转发机制和其他语言的消息机制优劣对比
1.1.8 在方法调用的时候,方法查询-> 动态解析-> 消息转发 之前做了什么
1.1.9 IMP、SEL、Method的区别和使用场景
1.1.10 load、initialize方法的区别什么?在继承关系中他们有什么区别
1.1.11 说说消息转发机制的优劣
1.2 内存管理
1.2.1 weak的实现原理?SideTable的结构是什么样的
1.2.2 关联对象的应用?系统如何实现关联对象的
1.2.3 关联对象的如何进行内存管理的?关联对象如何实现weak属性
1.2.4 Autoreleasepool的原理?所使用的的数据结构是什么
1.2.5 ARC的实现原理?ARC下对retain & release做了哪些优化
1.2.6 ARC下哪些情况会造成内存泄漏
1.3 其他
1.3.1 Method Swizzle注意事项
1.3.2 属性修饰符atomic的内部实现是怎么样的?能保证线程安全吗
1.3.3 iOS 中内省的几个方法有哪些?内部实现原理是什么
1.3.4 class、objc_getClass、object_getclass 方法有什么区别?
2、NSNotification相关
苹果并没有开源相关代码,但是可以读下GNUStep的源码,基本上实现方式很具有参考性
GNUStep地址:https://github.com/gnustep/libs-base
2.1 实现原理(结构设计、通知如何存储的、name&observer&SEL之间的关系等
2.2 通知的发送时同步的,还是异步的
2. 3 NSNotificationCenter接受消息和发送消息是在一个线程里吗?如何异步发送消息
2.4 NSNotificationQueue是异步还是同步发送?在哪个线程响应
2.5 NSNotificationQueue和runloop的关系
2.6 如何保证通知接收的线程在主线程
2.7 页面销毁时不移除通知会崩溃吗
2.8 多次添加同一个通知会是什么结果?多次移除通知呢
2.9 下面的方式能接收到通知吗?为什么
// 发送通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleNotification:) name:@"TestNotification" object:@1];
// 接收通知
[NSNotificationCenter.defaultCenter postNotificationName:@"TestNotification" object:nil];
3、Runloop & KVO
3.1 runloop
runloop对于一个标准的iOS开发来说都不陌生,应该说熟悉runloop是标配,下面就随便列几个典型问题吧
3.1.1 app如何接收到触摸事件的
3.1.2 为什么只有主线程的runloop是开启的
3.1.3 为什么只在主线程刷新UI
3.1.4 PerformSelector和runloop的关系
3.1.5 如何使线程保活
3.2 KVO
同runloop一样,这也是标配的知识点了,同样列出几个典型问题
3.2.1 实现原理
3.2.2 如何手动关闭kvo
3.2.3 通过KVC修改属性会触发KVO么
3.2.4 哪些情况下使用kvo会崩溃,怎么防护崩溃
3.2.5 kvo的优缺点
4、Block
4.1 block的内部实现,结构体是什么样的
4.2 block是类吗,有哪些类型
4.3 一个int变量被 __block 修饰与否的区别?block的变量截获
4.4 block在修改NSMutableArray,需不需要添加__block
4.5 怎么进行内存管理的
4.6 block可以用strong修饰吗
4.7 解决循环引用时为什么要用__strong、__weak修饰
4.8 block发生copy时机
4.9 Block访问对象类型的auto变量时,在ARC和MRC下有什么区别
5、多线程
主要以GCD为主
5.1 iOS开发中有多少类型的线程?分别对比
5.2 GCD有哪些队列,默认提供哪些队列
5.3 GCD有哪些方法api
5.4 GCD主线程 & 主队列的关系
5.5 如何实现同步,有多少方式就说多少
5.6 dispatch_once实现原理
5.7 什么情况下会死锁
5.8 有哪些类型的线程锁,分别介绍下作用和使用场景
5.9 NSOperationQueue中的maxConcurrentOperationCount默认值
5.10 NSTimer、CADisplayLink、dispatch_source_t 的优劣
6、视图&图像相关
6.1 AutoLayout的原理,性能如何
6.2 UIView & CALayer的区别
6.3 事件响应链
6.4 drawrect & layoutsubviews调用时机
6.5 UI的刷新原理
6.6 隐式动画 & 显示动画区别
6.7 什么是离屏渲染
6.8 imageName & imageWithContentsOfFile区别
6.9 多个相同的图片,会重复加载吗
6.10 图片是什么时候解码的,如何优化
6.11 图片渲染怎么优化
6.12 如果GPU的刷新率超过了iOS屏幕60Hz刷新率是什么现象,怎么解决
7、性能优化
7.1 如何做启动优化,如何监控
7.2 如何做卡顿优化,如何监控
7.3 如何做耗电优化,如何监控
7.4 如何做网络优化,如何监控
8、性能优化
8.1 苹果使用证书的目的是什么
8.2 AppStore安装app时的认证流程
8.3 开发者怎么在debug模式下把app安装到设备呢
9、架构设计
9.1 典型源码的学习
1 AFN
2 SDWebImage
3 JSPatch、Aspects(虽然一个不可用、另一个不维护,但是这两个库都很精炼巧妙,很适合学习)
4 Weex/RN, 笔者认为这种前端和客户端紧密联系的库是必须要知道其原理的
5 CTMediator、其他router库,这些都是常见的路由库,开发中基本上都会用到
6 请圈友们在评论下面补充吧
9.2 架构设计
1 手动埋点、自动化埋点、可视化埋点
2 MVC、MVP、MVVM设计模式
3 常见的设计模式
4 单例的弊端
5 常见的路由方案,以及优缺点对比
6 如果保证项目的稳定性
7 设计一个图片缓存框架(LRU)
8 如何设计一个git diff
9 设计一个线程池?画出你的架构图
10 你的app架构是什么,有什么优缺点、为什么这么做、怎么改进
10、其他问题
1 PerformSelector & NSInvocation优劣对比
2 oc怎么实现多继承?怎么面向切面(可以参考Aspects深度解析-iOS面向切面编程)
3 哪些bug会导致崩溃,如何防护崩溃
4 怎么监控崩溃
5 app的启动过程(考察LLVM编译过程、静态链接、动态链接、runtime初始化)
6 沙盒目录的每个文件夹划分的作用
7 简述下match-o文件结构
11、系统基础知识
1 进程和线程的区别
2 HTTPS的握手过程
3 什么是中间人攻击?怎么预防
4 TCP的握手过程?为什么进行三次握手,四次挥手
5 堆和栈区的区别?谁的占用内存空间大
6 加密算法:对称加密算法和非对称加密算法区别
7 常见的对称加密和非对称加密算法有哪些
8 MD5、Sha1、Sha256区别
9 charles抓包过程?不使用charles,4G网络如何抓包
12、数据结构与算法
对于移动开发者来说,一般不会遇到非常难的算法,大多以数据结构为主,笔者列出一些必会的算法,当然有时间了可以去LeetCode上刷刷题
LeetCode地址:https://leetcode.com/
1 八大排序算法
2 栈&队列
3 字符串处理
4 链表
5 二叉树相关操作
6 深搜广搜
7 基本的动态规划题、贪心算法、二分查找