iOS开发跳槽入职小米,捋了一下iOS面试知识点
2021-07-09 本文已影响0人
iOS弗森科
前言
大概捋了一下iOS面试知识点,以此作为大纲希望自己能有目标有计划地准备面试,后面我会逐个复习一下相应的内容,同时也会添加遗漏的知识点到文本,并记录下自己的笔记分享出来。喜欢的可以收藏,大家一起努力。
以下是小编收集总结的iOS技术点+面试题分类;看看你都会了吗?
重点总结-几大分类:
- iOS底层原理
- 性能优化以及架构
- 多线程、网络
- 数据结构算法
iOS底层原理(必问):iOS开发必备能力,也是大厂面试快速筛选人才方式之一。
面试官问:
一、性能优化点
二、项目难点,怎么解决的
三、项目流程迅游有品控审核和reviewcode
四、http怎么发送数据tcp3次握手
五、智能指针,智能指针有什么缺陷
六、释放不干净(举例a=b b=a这种情况)
51、数组和链表的区别
- 数组在内存上给出了连续的空间
- 链表,内存地址上可以是不连续的,每个链表的节点包括原来的内存和下一个节点的信息(单向的一个,双向链表的话,会有两个)
数组:
- 优点: 使用方便,查询效率比链表高,内存为一连续的区域
- 缺点: 大小固定,不适合动态存储,不方便动态添加
链表:
- 优点: 可动态添加删除,大小可变
- 缺点: 只能通过顺次指针访问,查询效率低
53、谈谈你对编译、链接的理解
电子版 iOS面试题+答案:
文章由于答案太多,我做了一个PDF文档,由于简书不能上传文件,,由于文章有限,需要这些文档的,
点击腾讯文档自取吧
展示部分截图:↓↓↓↓↓
iOS面试文档54、leak工具使用
55、应用程序启动过程,启动优化
- 应用启动时是用怎样加载所有依赖的Mach-O文件的?
- 请列举你所知道main()函数之前耗时的因素都有哪些
App启动分为两种:
- 冷启动(Cold Launch):从零开始启动app
- 热启动(Warm Launch):app已在内存中,在后台存活,再次点击图标启动app
启动时间的优化,主要是针对冷启动进行优化
1、通过添加环境变量可以打印app的启动时间分析(详情请见下图)
- DYLD_PRINT_STATISTICS
- DYLD_PRINT_STATISTICS_DETAILS(比上一个详细)
- 一般400毫秒以内正常
打印结果:
Total pre-main time: 238.05 milliseconds (100.0%) // main函数调用之前(pre-main)总耗时
dylib loading time: 249.65 milliseconds (104.8%) // 动态库耗时
rebase/binding time: 126687488.8 seconds (18128259.6%)
ObjC setup time: 10.67 milliseconds (4.4%) // OC结构体准备耗时
initializer time: 52.83 milliseconds (22.1%) // 初始化耗时
slowest intializers : // 比较慢的加载
libSystem.B.dylib : 6.63 milliseconds (2.7%)
libBacktraceRecording.dylib : 6.61 milliseconds (2.7%)
libMainThreadChecker.dylib : 31.82 milliseconds (13.3%)
2、冷启动可以概括为3大阶段
- dyld
- runtime
- main
3、dyld(dynamic link editor),Apple的动态连接器,可以装载Mach-O(可执行文件、动态库等)
- 装载app的可执行文件,同时递归加载所有依赖的动态库
- 当dyld把可执行文件、动态库都装载完成后,会通知runtime进行下一步处理
4、runtime所做的事情
- 调用map_images函数中调用call_load_methods,调用所有Class和Category的+load方法
- 进行各种objc结构的初始化(注册objc类、初始化类对象等等)
- 调用C++静态初始化器和__attribure__((constructor))修饰的函数(JSONKit中存在具体应用)
- 到此为止,可执行文件和动态库中所有的符号(Class, Protocol, Selector, IMP...)都已按格式成功加载到内存中,被runtime所管理
5、总结
- app的启动由dylb主导,将可执行文件加载到内存,顺便加载所有依赖的动态库
- 并由runtime负责加载成objc定义的结构
- 所有初始化工作结束后,dyld就会调用main函数
- 接下来就是ApplicationMain函数,AppDelegate的application:didFinishLaunchingWithOptions:方法
6、按照不同的阶段优化
dyld
- 减少动态库、合并一些动态库(定期清理不必要的动态库)
- 减少objc类、分类的数量、减少selector数量(定期清理不必要的类、分类)
- 减少C++虚构函数
- Swift尽量使用struct
runtime
- 使用+initialize方法和dispatch_once取代所有的__attribute__((constructor))、C++静态构造器、Objc的+load方法
main
- 在不影响用户体验的前提下,尽可能将一些操作延迟,不要全部都放在finishLaunching方法中
- 按需加载
DYLD_PRINT_STATISTICS设置为1
56、包体积优化
安装包瘦身(ipa):资源文件、可执行文件
资源文件(图片、音频、视频等)
- 采取无损压缩(使用工具)
- 去除没有用到的资源(https://github.com/tinymind/LSUnusedResources)
可执行文件瘦身:
- 编译器优化(Xcode相关配置)
- 利用AppCode(https://www.jetbrains.com/objc/)检测未使用的代码:菜单栏 -> Code -> Inspect Code
- 生成LinkMap,可以查看可执行文件的具体组成
- 可借助第三方工具解析LinkMap文件:http://github.com/huanxsd/LinkMap
57、项目的优化、性能优化
启动速度:
- 启动过程中做的事情越少越好(尽可能将多个接口合并)
- 不在UI线程上作耗时的操作(数据的处理在子线程进行,处理完通知主线程刷新节目)
- 在合适的时机开始后台任务(例如在用户指引节目就可以开始准备加载的数据)
- 尽量减小包的大小
- 辅助工具(友盟,听云,Flurry)
页面浏览速度
- json的处理(iOS 自带的NSJSONSerialization,Jsonkit,SBJson)
- 数据的分页(后端数据多的话,就要分页返回,例如网易新闻,或者 微博记录)
- 数据压缩(大数据也可以压缩返回,减少流量,加快反应速度)
- 内容缓存(例如网易新闻的最新新闻列表都是要缓存到本地,从本地加载,可以缓存到内存,或者数据库,根据情况而定)
- 延时加载tab(比如app有5个tab,可以先加载第一个要显示的tab,其他的在显示时候加载,按需加载
- 算法的优化(核心算法的优化,例如有些app 有个 联系人姓名用汉语拼音的首字母排序)
操作流畅度优化
- Tableview 优化(tableview cell的加载优化)
- ViewController加载优化(不同view之间的跳转,可以提前准备好数据)
58、说说你自己吧
- 你在项目中技术亮点、难点
- 你的发展方向(职业规划)
- 你的优点、你的缺点
59、说说组件化,你是如何组件化解耦的
TODO(待填充);⌛️⌛️⌛️⌛️⌛️
60、静态库、动态库相关
1、什么是库?
- 共享代码,实现代码的复用,一般分为静态库和动态库。
2、静态库和动态库的区别
静态库(.a和.framework 样式):
- 链接时完整的拷贝到可执行文件,多次使用多次拷贝,造成冗余,使包变的更大
- 但是代码装载速度快,执行速度略比动态库快
动态库:(.dylib和.framework)
- 链接时不复制,程序运行时由系统加在到内存中,供系统调用,系统加在一次,多次使用,共用节省内存。
3、为什么framework既是静态又是动态?
- 系统的framework是动态的,自己创建的是静态的。
4、.a 和 .framework 的区别是什么?
- .a 是单纯的二进制文件,需要 .h文件配合,不能直接使用
- .framework是二进制文件+资源文件,可以直接使用。 .framework = .a + .h + sorrceFile(资源文件)
十三、OC对象相关
61、对 OC 中 Class 的源码理解?其中 cache 的理解?说说NSCache缓存策略
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 *` */
62、protocol中能否添加属性
- OC语言的协议里面是支持声明属性的
- 但在协议中声明属性其实和在其中定义方法一样,只是声明了getter和setter方法,并没有具体实现
63、OC内联函数 inline
作用:
- 替代宏
inline函数与宏有区别
- 解决函数调用效率的问题
- 函数之间调用,是内存地址之间的调用,当函数调用完毕之后还会返回原来函数执行的地址。
- 函数调用有时间开销,内联函数就是为了解决这一问题
inline相比于宏的优点
- 避免了宏的缺点:需要预编译.因为inline内联函数也是函数,不需要预编译.
- 编译器在调用一个内联函数时,会首先检查它的参数的类型,保证调用正确。消除了它的隐患和局限性。
- 可以使用所在类的保护成员及私有成员
inline相比于函数的优点
- inline函数避免了普通函数的,在汇编时必须调用call的缺点:取消了函数的参数压栈,减少了调用的开销,提高效率.所以执行速度确比一般函数的执行速度要快.
- 集成了宏的优点,使用时直接用代码替换(像宏一样)
64、id和NSObject ,instancetype的区别?
- id和instancetype都可以做方法的返回值。
- id类型的返回值在编译期不能判断对象的真实类型,即非关联返回类型
- instancetype类型的返回值在编译期可以判断对象的真实类型,即关联返回类型。
- id可以用来定义变量, 可以作为返回值, 可以作为形参
- instancetype只能用于作为返回值。
非关联返回类型、关联返回类型
TODO(待填充);⌛️⌛️⌛️⌛️⌛️
65、方法签名有什么作用?
TODO(待填充);⌛️⌛️⌛️⌛️⌛️
66、nil、Nil、NULL、NSNull的区别?
- nil:指向一个对象的空指针
- Nil:指向一个类的空指针,
- NULL:指向其他类型(如:基本类型、C类型)的空指针, 用于对非对象指针赋空值.
- NSNull:在集合对象中,表示空值的对象.
NSNull在Objective-C中是一个类 .NSNull有 + (NSNull *)null; 单例方法.多用于集合(NSArray,NSDictionary)中值为空的对象.
NSArray *array = [NSArray arrayWithObjects: [[NSObject alloc] init], [NSNull null], @"aaa", nil, [[NSObject alloc] init], [[NSObject alloc] init], nil];
NSLog(@"%ld", array.count);// 输出 3,NSArray以nil结尾
67、NSDictionary底层实现原理
- 在OC中NSDictionary是使用hash表来实现key和value的映射和存储的。
hash表存储过程简单介绍:
- 根据key值计算出它的hash值h;
- 假设箱子的个数是n,那么键值对应该放在第(h%n)个箱子中。
- 如果该箱子中已经有了键值对,就是用开放寻址法或者拉链法解决冲突。使用拉链法解决哈希冲突时,每个箱子其实是一个链表,属于同一个箱子的所有键值对都会排列在链表中。
68、父类的property是如何查找的?
- 子类中的propert_list、method_list、ivar_list并不包含父类
- 子类对象的_IMPL包含父类的
从以上几点回答
TODO(待填充);⌛️⌛️⌛️⌛️⌛️
69、+load与 +initialize
共同点:
- 方法只会执行一次
- 在类使用之前,就自动调用了这两个方法
区别:
- 执行时机不同()
- load方法:如果类自身没有定义,并不会调用其父类的load方法;
- initialize方法:如果类自身没有定义,就会调用其父类的initialize方法;
执行的前提条件:
- load 只要类所在文件被引用,就会执行;
- 如果类没有引用进项目,就不会有load的执行;
- initialize 需要类或者其子类的第一个方法被调用,才会执行,而且是在第一个方法执行之前,先执行;
- 即使类文件被引用进项目,但是没有使用,那么initialize就不会调用执行;
70、iOS如何实现多继承,代码书写一下
- 使用协议组合
- NSProxy
TODO(待填充);⌛️⌛️⌛️⌛️⌛️
71、类与结构体的区别
- 结构体只能封装数据,而类还可以封装行为
- 赋值:结构体是拷贝,对象之间是地址
- 结构体变量分配在栈空间(如果是一个局部变量的情况下),而对象分配在堆空间
72、crash崩溃怎么解,崩溃到底层代码
NSSetUncaughtExceptionHandler可以统计闪退
TODO(待填充);⌛️⌛️⌛️⌛️⌛️
73、属性、成员变量、set、get方法相关
- 属性可以与set方法和get方法 三者同时存在吗,如果不行,请说明原因?
换句话说就是:iOS中同时重写属性的set与get方法时,为什么访问不了下划线属性?
原因:
- 属性的setter方法和getter方法是不能同时进行重写,
- 因为,一旦你同时重写了这两个方法,那么系统就不会帮你生成这个成员变量了
解决方式:
@synthesize authType = _authType;
- 意思是,将属性的setter,getter方法,作用于这个变量。
74、isa和superclass相关
1、对象的isa指针指向哪里?superclass指针呢?(⚠️图-总结图)
- instance的isa指向class
- class的isa指向meta-class
- meta-class的isa指向基类的meta-class
- class的superclass指向父类的class(如果没有父类,superclass指针为nil)
- meta-class的superclass指向父类的meta-class
- ⚠️基类的meta-class的superclass指向基类的class
2、方法调用查找(⚠️⚠️⚠️图-instance调用对象的轨迹;图-类方法调用轨迹)
- 对象方法的调用:通过instance的isa找到class,最后找到对象方法的实现进行调用
- 类方法的调用:当调用类方法时,通过class的isa找到meta-class,最后找到类方法的实现进行调用
3、class对象的superclass指针
Student : Person : NSObject
当Student的instance对象要调用Personal的对象方法时:
- 先通过isa找到Student的class,然后通过superclass找到Person的class,最后找到对象方法的实现进行调用
4、meta-class对象的superclass指针
当Student的class要调用Person的类方法时
- 先通过isa找到Student的meta-class,然后通过superclass找到Person的meta-class,最后找到类方法的实现进行调用
image
image
image
image
image
image
75、OC的类信息存放在哪里?
- 对象方法、属性、成员变量、协议信息,存放在class对象中
- 类方法,存放在meta-class对象中
- 成员变量的具体值,存放在instance对象中
76、class、meta-class的结构
struct objc_class : objc_object {
Class ISA;
Class superclass;
cache_t cache; // 方法缓存
class_data_bits_t bits; // 用于获取具体的类信息
}
& FAST_DATA_MASK
struct class_rw_t {
uint32_t flags;
uint32_t version;
const class_ro_t *ro; //
method_array_t methods; // 方法列表
property_array_t properties; // 属性列表
protocol_array_t protocols; // 协议列表
Class firstSubclass;
Class nextSiblingClass;
char *demangledName;
}
struct class_ro_t {
uint32_t flags;
uint32_t instanceStart;
uint32_t instanceSize;
#ifdef __LP64__
uint32_t reserved;
#endif
const uint8_t * ivarLayout;
const char * name; // 类名
method_list_t * baseMethodList;
protocol_list_t * baseProtocols;
const ivar_list_t * ivars; // 成员变量列表
const uint8_t * weakIvarLayout;
property_list_t *baseProperties;
}
推荐阅读:iOS热门面试技术文集