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

下载本文档

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

文档简介

iOS高频面试题及详细真实答案(实战版)一、OC基础核心面试题1.讲讲OC的@property关键字区别(strong/weak/copy/assign)这是日常开发最常用的属性修饰符,核心区别在于内存管理方式和适用场景,我结合实际开发场景说:assign:单纯赋值,不做内存计数,不会持有对象。主要用于基本数据类型(int、float、CGFloat)和枚举、结构体。如果用在对象上,对象销毁后指针会变成野指针,访问会崩溃。strong:强引用,持有对象,引用计数+1。只要有strong指针指向对象,对象就不会被销毁。绝大多数OC对象属性(NSString、数组、自定义类)都用strong,是最常用的修饰符。weak:弱引用,不增加引用计数,不持有对象。对象销毁后,weak指针会自动置空,不会产生野指针崩溃。主要解决循环引用问题,比如代理、block内部引用self、UI控件关联。copy:拷贝对象,生成一个新的内存地址的对象,新旧对象互不影响。主要用于NSString、NSArray、NSDictionary不可变集合。核心坑点:如果外界传入可变字符串NSMutableString,用strong会直接指向原对象,外界修改值,当前属性值也会跟着变;用copy会拷贝一份新值,杜绝数据篡改,保证数据安全。2.什么是循环引用?如何解决?当两个或多个对象互相用strong持有对方,导致双方引用计数都无法归零,页面退出后对象无法销毁,造成内存泄漏,这就是循环引用。常见场景和解决方案:1)代理循环引用:控制器持有View,View的代理属性又强持有控制器。解决方案:代理统一用weak修饰。2)Block循环引用:Block内部使用self,Block被self强持有,形成闭环。解决方案:使用__weak弱化self,部分场景配合__strong保证block执行期间self不销毁。3)双向持有:A强持有B,B强持有A。解决方案:其中一方改用weak修饰。3.copy和mutableCopy的区别?深浅拷贝怎么理解?浅拷贝:只拷贝指针地址,不开辟新内存,新旧指针指向同一个对象,修改一个另一个也会变。深拷贝:开辟全新内存空间,拷贝对象内容,新旧对象完全独立,互不影响。结合实际类型总结:1)不可变对象(NSString、NSArray):copy是浅拷贝,mutableCopy是深拷贝;2)可变对象(NSMutableString、NSMutableArray):copy和mutableCopy都是深拷贝;开发核心原则:不可变集合属性用copy,防止外部可变对象篡改内部数据。4.__block和__weak的区别?两者完全不同,作用场景不一样,很多人会混淆:__block:核心作用是允许Block内部修改外部变量。普通局部变量在Block内是只读的,加__block后变量会被封装成对象,支持读写修改。不解决循环引用,依然会强引用self。__weak:核心作用是弱化引用,解决循环引用,不允许修改变量,只针对对象类型,不能修饰基本数据类型。二、内存管理面试题1.OC的内存管理机制是什么?MRC和ARC区别?OC采用引用计数内存管理机制,对象引用计数大于0就存活,等于0就立即释放内存。MRC(手动引用计数):开发者手动写retain、release、autorelease管理内存,完全靠人工控制,灵活但极易出错,现在项目基本不用。ARC(自动引用计数):编译器在编译阶段自动帮我们插入内存管理代码,无需手动写release/retain。不是运行时机制,是编译期优化。ARC核心规则:谁创建、谁持有、谁释放,自动处理大部分内存逻辑,仅需手动处理循环引用、autorelease池等特殊场景。2.autorelease原理是什么?什么时候释放?autorelease是延迟释放机制,调用后不会立即让对象计数-1,而是把对象加入当前的自动释放池,标记为延迟释放。释放时机:1)主线程:默认自动释放池在每次runloop迭代结束时销毁,池内所有对象统一release;2)子线程:子线程没有默认autorelease池,必须手动创建@autoreleasepool,否则会内存泄漏;开发场景:批量创建大量临时对象时,手动加autoreleasepool可以及时回收内存,降低峰值内存。3.常见的内存泄漏场景及排查方法高频泄漏场景:1)Block未弱化self,造成循环引用;2)代理用strong修饰,未用weak;3)NSTimer、CADisplayLink强持有self,且未及时销毁;4)全局变量、静态变量长期持有页面对象;5)子线程异步任务未结束,页面退出后线程继续持有对象。排查方法:1)Xcode自带DebugMemoryGraph,实时查看内存对象、引用链条,定位泄漏根源;2)Instruments工具的Leaks模块,精准检测内存泄漏点;3)自定义页面销毁检测,监听dealloc是否执行。三、多线程面试题(重点高频)1.GCD队列和任务的区别?串行、并行、主队列、全局队列区别任务(block):需要执行的代码逻辑;队列:存放任务的容器,遵循FIFO先进先出原则。四种队列特性:1)主队列(dispatch_get_main_queue):串行队列,绑定主线程,所有UI刷新必须在主队列执行,任务逐个执行,不开启新线程。2)全局并发队列(dispatch_get_global_queue):系统自带并行队列,自动开启多条子线程,任务并发执行,适合耗时操作(网络、文件读写、解析数据)。3)自定义串行队列:只开启一条线程,任务按顺序依次执行,适合处理有序、同步的耗时任务,可解决线程安全问题。4)自定义并行队列:可开启多线程,任务并发执行,自定义优先级,可控性更高。2.sync和async区别?死锁场景及原理async异步执行:不阻塞当前线程,提交任务后立即返回,继续执行后续代码,开启新线程执行任务。sync同步执行:阻塞当前线程,等待队列中任务执行完毕后,才会继续往下走,不开启新线程。经典死锁场景:主线程同步执行主队列任务原理:当前在主线程,主队列正在执行当前代码,此时调用dispatch_sync往主队列添加新任务。主队列串行必须等当前任务结束才能执行新任务,而sync又阻塞当前任务等待新任务完成,互相等待,形成死锁。补充:串行队列内部嵌套sync执行当前队列任务,也会造成死锁。3.GCD栅栏函数、延时函数、一次性函数用法dispatch_once:保证代码在整个程序生命周期只执行一次,线程安全,专门用于单例创建。dispatch_after:延时执行任务,不精准,只是在指定时间后将任务加入队列,不是准时执行。dispatch_barrier_async栅栏函数:用于并行队列,栅栏前的任务全部执行完,才执行栅栏任务,栅栏任务执行完,再执行后续任务。核心用途:解决多读单写的线程安全问题,读操作并发执行,写操作栅栏独占执行。4.线程安全问题怎么解决?多条线程同时操作同一资源(读写数据),会出现数据错乱、崩溃等线程安全问题,常用解决方案:1)@synchronized互斥锁:简单易用,性能一般,适合简单场景;2)pthread_mutex底层互斥锁:性能更高,支持递归锁、条件锁;3)GCD栅栏函数:无锁编程,性能最优,适合多读单写场景;4)串行队列:所有读写任务放到同一串行队列,保证任务有序执行。四、UI与视图渲染面试题1.UIView和CALayer的区别与关系核心关系:UIView是CALayer的代理,View负责交互,Layer负责渲染。区别:1)UIView:继承自UIResponder,具备触摸、点击、手势等用户交互能力,依赖UIKit框架;2)CALayer:继承自NSObject,无交互能力,纯负责画面渲染、图层展示、动画绘制,依赖CoreAnimation底层框架;每一个UIView底层都默认绑定一个CALayer,我们看到的视图界面,都是Layer渲染出来的。修改View的frame、背景色、圆角,本质都是修改Layer属性。2.iOS视图渲染流程是什么?整体分为三个核心步骤,在RunLoop休眠前触发:1)布局(Layout):执行layoutSubviews,计算所有视图的frame、约束,确定控件位置大小;2)绘制(Display):执行drawRect,绘制文本、图片、自定义图形,生成位图;3)渲染(Render):Layer将位图提交给GPU,GPU进行图层合成、渲染,展示到屏幕。日常触发时机:视图首次加载、frame/约束改变、setNeedsLayout、setNeedsDisplay调用时。3.frame和bounds的区别?frame:控件相对于父视图的位置和大小,原点是父视图坐标系位置;bounds:控件相对于自身的位置和大小,原点默认是(0,0);核心场景:修改bounds的原点,不会改变控件在父视图的位置,但会改变内部子控件的布局,常用于滚动、缩放场景。五、RunLoop底层面试题1.什么是RunLoop?作用是什么?RunLoop是运行循环,本质是一个死循环,保证线程不退出,持续监听、处理事件。默认主线程开启RunLoop,所以程序启动后不会立即退出,能持续响应点击、滑动、刷新UI等事件;子线程默认没有RunLoop,执行完任务就直接销毁。核心作用:1)保持线程存活,休眠时节省CPU资源;2)监听处理触摸、手势、定时器、网络回调、UI刷新等事件;3)管理autorelease池的创建和销毁。2.RunLoop的运行模式及常用场景RunLoop有多种运行模式,同一时间只会运行一种模式,过滤不需要处理的事件:1)kCFRunLoopDefaultMode:默认模式,日常处理普通事件,页面滚动时会切换模式,暂停定时器;2)UITrackingRunLoopMode:滚动模式,列表滑动时开启,优先处理滑动事件,保证滑动流畅;3)kCFRunLoopCommonModes:占位模式,包含默认和滚动模式,定时器、延时任务设为该模式,滑动页面不会暂停。高频坑点:NSTimer不加CommonModes,滑动列表时定时器会暂停。六、性能优化面试题(实战必备)1.项目中做过哪些UI渲染优化?结合日常开发,我主要做这几点优化,解决卡顿、掉帧问题:1)减少图层离屏渲染:避免频繁设置圆角、阴影、光栅化,能预渲染的提前处理,尽量用贝塞尔曲线代替图层属性;2)Cell复用优化:严格把控cell复用,避免重复创建控件,提前布局约束,减少layout计算;3)减少drawRect重绘:drawRect会触发离屏渲染,非必要不使用,避免在循环、滚动中频繁调用;4)视图层级优化:减少多余嵌套视图,精简层级,降低GPU合成压力;5)图片优化:使用适配尺寸的图片,避免大图缩放渲染,缓存解压后的图片。2.启动优化怎么做?启动分为冷启动、热启动,优化核心是缩短main函数前后的执行耗时:1)main函数前优化:精简无用动态库、删除冗余工程配置、减少类加载数量;2)main函数后优化:拆分didFinishLaunching任务,只保留核心初始化逻辑,第三方SDK、统计、推送等非核心任务异步延迟加载;3)首屏优化:首屏控件延迟加载、预加载数据拆分,避免主线程耗时操作;4)利用Xcode启动耗时工具检测,定位耗时函数针对性优化。3.列表卡顿优化方案1)数据预处理:提前在子线程处理文本计算、图片解压、数据模型转换,主线程只负责赋值刷新;2)高度缓存:提前计算cell高度并缓存,避免滚动时重复计算约束、高度;3)按需加载:滑动时停止耗时操作,停止滑动后再加载图片、渲染内容;4)资源复用:图片、视图全局复用,避免滚动过程中频繁创建、销毁对象。七、Swift基础高频面试题1.Swift中class和struct的区别1)class是引用类型,struct是值类型;2)引用类型赋值传递的是内存地址,多个变量指向同一个对象,修改互相影响;值类型赋值传递的是副本,互相独立;3)class支持继承、重写、析构函数,struct不支持继承;4)struct线程更安全,无循环引用问题,class容易产生循环引用,需要weak解决。2.weak和unowned的区别两者都是弱引用,不增加引用计数,用于解决循环引用:1)weak:修饰可选类型,对象销毁后指针自动置nil,安全,常用;2

温馨提示

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

评论

0/150

提交评论