iOS 开发高频面试题(含详细实战答案)_第1页
iOS 开发高频面试题(含详细实战答案)_第2页
iOS 开发高频面试题(含详细实战答案)_第3页
iOS 开发高频面试题(含详细实战答案)_第4页
iOS 开发高频面试题(含详细实战答案)_第5页
已阅读5页,还剩3页未读, 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

iOS开发高频面试题(含详细实战答案)一、Objective-C基础核心1.讲讲OC的消息机制,以及sel、IMP的区别OC是动态语言,方法调用本质不是编译期确定的函数调用,而是运行时发送消息的过程。当调用[objmethod]时,底层会转换成objc_msgSend(obj,sel),流程分为三步:1.首先通过sel(方法选择器)去当前对象的类的方法缓存中查找IMP(方法实现地址);2.缓存找不到,再去类的方法列表遍历查找;3.依旧找不到,进入动态方法解析、消息转发流程,最终找不到才会崩溃。三者核心区别:-sel:方法名的唯一标识,只是名称符号,不存储方法实现,相同方法名sel唯一;-IMP:函数指针,指向方法具体的底层实现地址,是真正执行的代码逻辑;-消息机制:依托sel查找IMP,实现运行时动态调用方法。2.分类(Category)和扩展(Extension)的区别,分类能不能加属性?分类Category:1.可以在不修改原有类代码的前提下,给类新增方法,支持跨文件、跨模块扩展;2.运行时加载,多个分类同时存在时,后编译的分类方法会覆盖先编译的,不会报错;3.不能直接添加成员变量,可以通过runtime关联对象模拟属性。类扩展Extension:1.也叫匿名分类,一般写在.m文件内部,用于扩充私有属性、私有方法;2.编译期加载,和原类编译为一体,不能单独创建;3.可以添加成员变量、属性,不会覆盖原有方法。补充:分类用@property只会生成getter/setter方法声明,不会自动生成成员变量和方法实现,必须手动通过objc_setAssociatedObject、objc_getAssociatedObject实现存储。3.深拷贝、浅拷贝、可变拷贝的区别浅拷贝(copy):只拷贝对象指针,不拷贝内存数据,新旧对象指向同一块内存,修改一个另一个会跟着变;深拷贝(mutableCopy):重新开辟内存,拷贝所有数据,新旧对象内存独立,互不影响;核心规则:1.不可变对象(NSString、NSArray)调用copy:浅拷贝,指针复制;2.不可变对象调用mutableCopy:深拷贝,生成可变对象;3.可变对象(NSMutableString、NSMutableArray)调用copy、mutableCopy都是深拷贝,且copy出来的是不可变对象。开发场景:字符串、数组属性修饰符用copy,防止外部可变对象修改内部数据,保证数据安全。4.@property修饰符详解(atomic/nonatomic、strong/weak/copy)原子性:-atomic:原子操作,读写加锁,线程安全,但性能差,默认属性,开发几乎不用;-nonatomic:非原子操作,无锁,性能高,日常开发全部使用。引用类型:-strong:强引用,持有对象,计数器+1,对象不会被释放,大部分对象、自定义对象用strong;-weak:弱引用,不持有对象,计数器不变化,对象销毁后指针自动置空,解决循环引用,多用于代理、UI控件;-copy:拷贝赋值,多用于NSString、NSArray等集合类型,防止数据篡改;-assign:直接赋值,不计数、不置空,基本数据类型(int、float)、枚举用assign。二、内存管理&循环引用1.ARC下的内存管理原则,对象销毁时机ARC核心是自动引用计数,编译器在编译阶段自动插入retain、release、autorelease代码,无需手动管理。核心规则:谁强引用,谁负责释放。当一个对象的所有强引用全部失效,对象立即被销毁。常见场景:局部变量出作用域、属性重新赋值、页面pop销毁控制器。2.常见的循环引用场景及解决方案常见场景:1.控制器持有自定义View,View的代理又指向控制器;2.Block内部引用外部控制器/变量,外部又持有Block;3.两个对象互相强引用对方。解决方案:1.代理统一用weak修饰,打破双向强引用;2.Block用__weak、__strong搭配使用,外部弱引用,内部临时强引用防止提前释放;3.双向引用场景,一方用weak修饰。3.__weak、__strong、__unsafe_unretained区别-__weak:弱引用,不增加计数,对象销毁后指针自动置空,安全,常用;-__strong:默认修饰符,强引用,持有对象,计数+1;-__unsafe_unretained:无引用计数,不置空,对象销毁后指针野指针,极易崩溃,基本废弃不用。三、多线程&GCD核心1.GCD队列和任务的区别,串行、并行队列区别任务:需要执行的代码块(block),分为同步任务、异步任务;队列:存放任务的容器,遵循FIFO先进先出原则。串行队列:同一时间只执行一个任务,任务按顺序逐个执行;并行队列:同一时间可以执行多个任务,任务并发执行,执行顺序不确定。2.同步、异步任务的区别,能否在主线程执行同步任务?-同步任务(sync):阻塞当前线程,必须等待任务执行完毕,才会执行后续代码,不开启新线程;-异步任务(async):不阻塞当前线程,直接放行,系统自动分配线程执行任务。重点:主线程执行同步串行任务会死锁。主队列是串行队列,当前主线程正在等待任务执行,任务又依赖主线程执行,互相等待造成死锁。子线程执行同步任务不会死锁。3.GCD栅栏函数、延时函数、一次性函数用法dispatch_once:保证代码全局只执行一次,用于单例创建,线程安全;dispatch_after:延时执行,不阻塞当前线程,基于队列调度,不准确,适合普通延时场景;dispatch_barrier_async:栅栏函数,作用于自定义并行队列,栅栏前的任务全部执行完,再执行栅栏任务,最后执行栅栏后任务,多用于读写锁场景,解决多线程读写数据冲突。4.线程死锁的产生条件和常见场景死锁核心:多条线程互相持有对方需要的资源,且互不释放,互相等待。四大必要条件:互斥、请求保持、不可剥夺、循环等待。常见场景:主线程嵌套同步任务、多线程互相等待锁、主队列同步任务。四、UI渲染&视图原理1.UIView和CALayer的区别与关系1.UIView是界面视图,负责交互、事件响应,继承自UIResponder;2.CALayer是图层,负责界面渲染、绘制、动画,不处理触摸事件;3.每个UIView内部都默认持有一个CALayer,UIView只是图层的包装,我们看到的界面都是Layer渲染的结果;4.View的frame是Layer的frame映射,View不具备渲染能力,所有圆角、阴影、边框都是Layer的属性。2.iOS页面渲染流程、卡顿原因渲染流程:布局(Layout)->绘制(Draw)->合成(Compose)1.布局阶段:计算控件frame、约束、位置大小;2.绘制阶段:drawRect绘制文本、图片、渐变等内容;3.合成阶段:GPU将所有图层拼接渲染到屏幕。卡顿核心原因:屏幕每秒刷新60帧,每帧耗时需控制在16ms内,主线程耗时操作(复杂计算、大量绘制、频繁布局、图片解压)导致超时,就会丢帧卡顿。3.圆角、阴影的性能优化方案常规写法问题:layer.cornerRadius+masksToBounds会触发离屏渲染,大量控件会严重卡顿;优化方案:1.纯色圆角:直接用贝塞尔曲线绘制圆角,避免离屏渲染;2.图片圆角:提前裁剪圆角图片,后台/本地预处理,不依赖运行时渲染;3.阴影:设置shadowPath,明确阴影路径,减少系统计算开销,关闭masksToBounds。4.runloop是什么,主线程runloop作用RunLoop是运行循环,本质是事件循环机制,保证线程不退出、持续监听事件。主线程默认开启RunLoop,作用:1.监听用户触摸、点击、滑动等交互事件;2.监听定时器、网络回调、渲染刷新事件;3.维持程序常驻运行,线程休眠节省性能,有事件唤醒处理,无事件休眠。子线程默认无RunLoop,执行完代码立即退出,需要手动开启才能常驻。五、Swift基础&混编1.Swift中struct和class的区别1.struct是值类型,存储在栈区,赋值、传参都是值拷贝,独立互不影响;2.class是引用类型,存储在堆区,赋值、传参是指针拷贝,指向同一内存;3.struct无继承,class支持继承、多态;4.struct默认immutable,class可动态修改属性;开发中:简单数据模型用struct,需要继承、动态逻辑、生命周期的用class。2.weak、unowned、strong在Swift中的区别-strong:默认强引用,持有对象,计数+1;-weak:弱引用,可选类型,对象销毁后自动置nil,安全,用于代理、循环引用;-unowned:无主引用,非可选类型,不置nil,对象销毁后访问会崩溃,性能略高,需保证对象一定存在时使用。3.OC和Swift混编原理1.Swift调用OC:项目自动生成<项目名>-Bridging-Header.h桥接文件,引入OC头文件即可直接调用;2.OC调用Swift:编译后自动生成Swift头文件<项目名>-Swift.h,OC引入后可调用被@objc、dynamic修饰的Swift类和方法;3.纯Swift独有语法、结构体无法被OC识别,必须兼容OC运行时才可混编。六、性能优化&项目实战1.列表卡顿优化(UITableView/UICollectionView)1.单元格复用:严格使用dequeueReusableCell,不重复创建cell;2.预计算cell高度:提前缓存cell高度,避免实时动态计算耗时;3.减少离屏渲染:圆角、阴影优化,避免大量图层嵌套;4.异步绘制:复杂文本、图片异步绘制,不阻塞主线程;5.滑动时停止耗时操作:滑动暂停图片加载、动画,滑动结束再恢复;6.视图层级扁平化,减少多余嵌套View。2.启动优化怎么做?启动分为:pre-main阶段、main之后阶段pre-main优化:1.减少无用动态库、冗余第三方库;2.减少类、方法数量,删除冗余代码;3.优化编译配置,开启二进制重排。main之后优化:1.首页无关的初始化、网络请求、SDK初始化异步延后执行;2.拆分启动任务,分级初始化,优先渲染首页UI;3.避免启动主线程耗时计算、IO操作。3.内存泄漏怎么排查?常见泄漏场景排查工具:XcodeInstruments(Leaks)、DebugMemoryGraph、MLeaksFinder常见泄漏场景:1.Block循环引用、代理未用weak;2.定时器未销毁、常驻线程未退出;3.全局变量、静态变量持有页面对象;4.通知、KVO未移除,页面销毁依旧监听。七、底层原理&进阶考点1.KVO底层原理,手动触发KVOKVO本质是运行时动态生成子类,重写被监听属性的setter方法。1.给对象添加KVO监听后,runtime动态创建原类的子类,修改对象isa指针指向新子类;2.新子类重写属性setter,在赋值前后触发willChangeValue、didChangeValue;3.外部监听回调,完成值变化监听。手动触发:手动调用willChangeValueForKey、didChangeValueForKey即可主动触发监听回调。注意:KVO只监听setter赋值,直接修改成员变量不会触发KVO。2.KVO和Notification的区别1.KVO:监听单个对象的属性变化,一对一监听,无需手动移除(ARC下注意时机),精准高效;2.通知:全局消息广播,一对多,跨页面、跨模块通信,必须手动移除,否则会内存泄漏、重复回调

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

最新文档

评论

0/150

提交评论