iOS面经

作者: 咸鱼有只喵 | 来源:发表于2022-02-15 14:28 被阅读0次

    高频问题:
    OOM:

    监控可以用didReceiveMemoryWarning

    也可以类似flex ,通过malloc_get_all_zones可以获取所有堆区的对象,通过objc_getClass获取对应的对象名,通过class_getInstanceSize获取单个对象的大小。进行引用分析,获取当前内存占用

    Facebook的方案是排除法,判断终止进程原因,排除已知原因

    腾讯的做法是OOMDetector

    可以通过malloc_logger实现对于malloc/free的监控,记录内存

    AutoreleasePoolARC的关系?

    AutoreleasePool可以理解为一个对象,在ARC环境下,这个对象本身也是自动计数的,当这个pool的引用计数为0时,就被清掉了,这是手动声明时候的情况,当系统自动为我们建立AutoreleasePool以及在runloop结束时释放就是系统的事情了。但同时AutoreleasePool不同于一般的对象指针,它可以持有多个对象,当pool被release的时候,其会向其持有的所有对象发送release消息,在非ARC环境下用来延长对象的作用域是很有效的。我们知道ARC只是编译器在帮我们自动添加retain和release,而并非垃圾回收器,这样以来,就会不可避免的产生一些内存问题,

    http://blog.sunnyxx.com/2014/10/15/behind-autorelease/

    .a.framework 的区别是什么?

    .a 是单纯的二进制文件,.framework是二进制问价+资源文件。
    其中.a 不能直接使用,需要 .h文件配合,而.framework则可以直接使用。
    .framework = .a + .h + sorrceFile(资源文件)

    RunLoop

    所以,RunLoop 实际上就是一个对象,这个对象管理了其需要处理的事件和消息,并提供了一个入口函数来执行上面 Event Loop 的逻辑。线程执行了这个函数后,就会一直处于这个函数内部 “接受消息->等待->处理” 的循环中,直到这个循环结束(比如传入 quit 的消息),函数返回。

    线程和 RunLoop 之间是一一对应的,其关系是保存在一个全局的 Dictionary 里。线程刚创建时并没有 RunLoop,如果你不主动获取,那它一直都不会有。RunLoop 的创建是发生在第一次获取时,RunLoop 的销毁是发生在线程结束时。你只能在一个线程的内部获取其 RunLoop(主线程除外)

    一个HTTP流程:

    url - DNS查ip ,dns自己有cache,未命中再去找dns服务器 -UDP - ip -Mac地址

    埋点系统设计

    GCD.串行并行队列

    二叉搜索树的判断

    string strong copy的区别

    当原字符串是NSString时,由于是不可变字符串,所以,不管使用strong还是copy修饰,都是指向原来的对象,copy操作只是做了一次浅拷贝。

    而当源字符串是NSMutableString时,strong只是将源字符串的引用计数加1,而copy则是对原字符串做了次深拷贝,从而生成了一个新的对象,并且copy的对象指向这个新对象。另外需要注意的是,这个copy属性对象的类型始终是NSString,而不是NSMutableString,如果想让拷贝过来的对象是可变的,就要使用mutableCopy。 所以,如果源字符串是NSMutableString的时候,使用strong只会增加引用计数。 但是copy会执行一次深拷贝,会造成不必要的内存浪费。而如果原字符串是NSString时,strong和copy效果一样,就不会有这个问题。

    一般怎么检查野指针,zombie对象怎么知道是野指针

    https://www.jianshu.com/p/7bb92761f2f5

    消息转发

    从全局来看,消息转发机制共分为3大步骤:

    1.Method resolution 方法解析处理阶段

    2.Fast forwarding 快速转发阶段

    3.Normal forwarding 常规转发阶段

    动态卡片vm,类似字节lua

    概念上类似于阿里开源的LuaView

    分类为什么不能添加属性

    分类添加方法的原理和顺序

    • 最后添加分类的方法 new_method ,从整个方法的内存从首端开始覆盖
    • 编译顺序越靠后的分类方法的优先级越高

    所以在调用实例对象的方法时,会优先调用分类中的实例对象方法,具体的分类顺序需要在 XCode 中设置分类文件的编译顺序,在 Project-BuildPhases-Compile Sources 中进行设置

    https://juejin.cn/post/6953474692363583525

    https://fatofyoung.github.io/2018/06/13/OC%E5%88%86%E7%B1%BB%E5%8E%9F%E7%90%86/

    uicontrol手势冲突

    https://juejin.cn/post/6908553699732226061

    怎么解决单双击冲突

    requireGestureRecognizerToFail 设置手势的优先级,同样,分页控制器在左滑和系统左滑冲突时也可以这么解决

    iOS 事件(UITouch、UIControl、UIGestureRecognizer)传递机制 - 简书 (jianshu.com) 聊聊NSInvocation和NSMethodSignature_Deft_MKJing的博客-CSDN博客

    1. UITableView继承链?UITableView -> UIScrollView -> UIView -> UIResponder -> NSObject

    2. UIresponder和UIcontrol的区别是啥?UIControl继承自UIView,UIReponder是iOS用于处理触摸事件
      的基类,UIControl对其进行了深度包装,以target-action的模式进行调用。

    3. 触摸事件传递流程是啥样的?分为事件传递链和响应链,传递链通过hittest函数和pointside递归的
      判断子view是否可以响应事件,直到递归遍历到最后一个view,响应链则是消费事件的过程,从子 view向其的vc,superView传递触摸事件。优先级的话手势识别高于view的触摸事件,系统的 uicontrol高于手势,自定义的低于手势。

    4. 如何扩大UIbutton点击范围?两种方法,重写pointinside和hittest

    5. 如何扩大手势点击范围?或者扩大一个小图片的点击范围?图片话调节内边距即可

    6. 获取当前viwe的vc方法?方法一:通过响应链机制,寻找view的下一级响应者,因为view的下一级
      响应链是其vc 方法二:通过KeyWindows获取对应的rootVC一直朝下遍历,获取presetedVC知
      道最后一个VC

    7. 消息转发阶段流程是什么样的?具体有哪些方法?resolveMethod -> forwardingTarget -> methodSinature->forwardInvocation 第三步会获取对应的方法签名,如果能获取到可以在第四步 直接将这个方法以NSInvocation形式转发给其他对象处理

    8. NSInvocation和NSMethodSignature的区别是啥?NSMethodSignature是方法编码过后的签名,例如 NSString的类方法isEqualToString: 的方法签名为B24@0:8@16,如下所示 @encode(BOOL) (B) 返回值 @encode(id) (@) 默认第一个参数 self
      @encode(SEL) (:)默认第二个参数 _cmd
      @encode(NSString ) (@)
      实际上的第一个参数
      NSString* iOS中的method即由SEL、IMP和签名组成,SEL是method检索的方式,可以通过SEL在method list中找到对应的方法,IMP则是函数指针指向方法的本身,签名包括了函数头中的各种信息。 NSInvocation则是消息在OC中对象的形式存在,是一种更高级的转发机制可以通过添加target、 签名、参数这些强行执行方法

    9. 消息转发可以做什么?怎么做的?热更新、多重代理和多继承

    10. Runtim相关函数有哪些?贼多,建议查 Objective-C Runtime | Apple Developer Documentation

    11. 线程锁有哪些?iOS多线程安全-13种线程锁%"#$ (juejin.cn)

    12. semaphore和nslock有啥区别?优缺点是啥?

    13. 怎么通过gcd实现读写锁?Dispatch_barrity_async

    14. 循环引用是啥?有哪些会造成?iOS解除Block循环引用,你只知道_weak就out啦知更鸟
      CoolLee的博客-CSDN博客
      block中的强引用是对self指针的强引用所以简单使用weak/strong就可以解决循环引用问题,但
      是NSTimer中的循环引用是对整个对象的强引用

    15. NStimer循环引用是怎么造成?怎么解决?NSTimer会强引用整个对象,对象持有Timer就会引起强
      引用。方法1:及时结束Timer运行,使用invalidata结束运行并置整个Timer为nil 方法2:target不指向自身,指向一个其他对象 方法3:使用NSProxy替代self,消息转发阶段转发消息回原类

    16. NSProxy是啥?有啥用?NSProxy是一个等同于NSObject的基类,实现了<NSObject>的协议,自身没 有任何实现,主要用于转发消息的基类,可以继承自这个基类,实现消息转发中的方法可以将消 息转发给其他对象。可以用于实现oc中本不支持的多继承,解决NSTimer中的循环引用。

    NSProxyNSObject的区别?

    1. 虽然都需要实现了<NSObject>协议,主要差别在消息转发流程中, NSObject会走完完整的消息转发机制,NSProxy直接回调-methodSignatureForSelector:/- forwardInvocation:,消息转发过程比class NSObject要简单得多,NSProxy会将自省相关的selector
      直接forward到-forwardInvocation:回调中,例如 - (BOOL)isKindOfClass:(Class)aClass;

      • (BOOL)isMemberOfClass:(Class)aClass;
      • (BOOL)conformsToProtocol:(Protocol *)aProtocol; - (BOOL)respondsToSelector:(SEL)aSelector;
    2. _cmd是啥?表示这个方法本身

    3. block如何获取外部变量?

    4. +load方法和initialize方法加载顺序?

    IOSkeychain

    Socket 场链接 内购 gcd

    如何捕获**crash **

    https://www.jianshu.com/p/5fcf7bb7955f

    动态库和静态库

    block 为什么用copy修饰 Block实现原理

    https://juejin.cn/post/6844904040954871815

    setObject:forKey:setValue:forKey:

    kvckvo

    kvc set:简单来说就是如果没有找到Set<Key>方法的话,会按照_key,_iskey,key,iskey的顺序搜索成员并进行赋值操作。

    当你观察一个对象时,一个新的类会动态被创建。这个类继承自该对象的原本的类,并重写了被观察属性的 setter 方法。自然,重写的 setter 方***负责在调用原 setter方法之前和之后,通知所有观察对象值的更改。最后把这个对象的 isa 指针 ( isa 指针告诉 Runtime 系统这个对象的类是什么 ) 指向这个新创建的子类,对象就神奇的变成了新创建的子类的实例。

    原来,这个中间类,继承自原本的那个类。不仅如此,Apple 还重写了 -class 方法,企图欺骗我们这个类没有变,就是原本那个类。更具体的信息,去跑一下 Mike Ash 的那篇文章里的代码就能明白,这里就不再重复。

    https://juejin.cn/post/6844903602545229831

    介绍下oc

    RunTime

    首先 OC 是 C 语言的超集,因为 runtime 这个库使得C语言有了面向对象的能力:
    OC 对象可以用C语言中的结构体表示,而方法可以用C函数来实现,这些结构体和函数被 runtime 函数封装后,我们就可以在程序运行时创建,检查,修改类、对象和它们的方法了。

    OC 是一门动态语言,它将很多静态语言在编译和链接时期做的事放到了运行时来处理。
    这种特性意味着Objective-C不仅需要一个编译器,还需要一个运行时系统来执行编译的代码。这个运行时系统即Objc Runtime。Objc Runtime基本上是用C和汇编写的。

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

    静态库和动态库:

    https://juejin.cn/post/7040003550977458207

    编译和链接:

    http://www.360doc.com/content/17/0111/22/32626470_621879084.shtml

    集合类型 copy 操作和 mutableCopy 操作

    一个类遵循 NSCopying, NSMutableCopying 协议并且实现相对应的初始化方法后,这个类就具有对象拷贝的能力。如果你自定义类没有遵守协议直接调用 copy / mutableCopy 程序会奔溃。拷贝协议的具体使用我这里不做扩展感兴趣的可以自行 Google 一下。有一种观点:copy 操作就是浅复制,mutableCopy 就是深复制,这是一个非常错误的理解。以下是我的一些总结:

    • 可变的集合对象 + copy 得到一个新的对象(新对象不可变) 深复制
    • 可变的集合对象 + mutablecopy 得到一个新的对象(新对象可变) 深复制
    • 不可变集合对象 + copy 没有得到新的对象(地址的引用) 浅复制
    • 不可变集合对象 + mutablecopy 得到一个新的对象(新对象可变) 深复制

    怎么去动态关联一个weak属性

    持有一个strong的容器,让其持有该weak属性,获取关联对象时获取其属性即可

    首先,给category属性是需要使用runtime中的关联来实现set和get方法,但runtime没有提供weak ,虽然runtime没有开放weak解决方案,但objc对象是可以实现weak的,所以让需要被修饰的对象去持有一个strong对象,然后当被修饰的对象释放的时候,持有的对象也会被释放,那么我们就可以捕捉到释放的事件,进而使用OBJC_ASSOCIATION_ASSIGN来实现弱引用

    对象的内存布局

    struct objc_class {

    Class isa;

    Class super_class;

    const charchar *name;

    long version;

    long info;

    long instance_size;

    struct objc_ivar_list *ivars;

    struct objc_method_list **methodLists;

    struct objc_cache *cache;

    struct objc_protocol_list *protocols;

    } OBJC2_UNAVAILABLE;

    二叉树的层序遍历

    消息转发的优化

    在 2020 年中,Apple 针对 Objective-C 做了三项优化

    • 类数据结构变化:节约了系统更多的内存。
    • 相对方法地址:节约了内存,并且提高了性能。
    • Tagged Pointer 格式的变化:提高了 msgSend 性能

    Selfthis 的底层实现,是编译期还是运行期做的。答案是编译期间

    this指针本质: 当一个对象调用某成员函数时编译器会隐式传入一个参数, 这个参数就是this指针

    this指针中存放的就是这个对象的首地址。

    其实编译器在生成程序时加入了获取对象首地址的相关代码,并把获取的首地址存放在了寄存器ECX中(VC++编译器是放在ECX中,其它编译器有可能不同)。

    成员函数的其它参数正常都是存放在栈中,而this指针参数则是存放在寄存器中。

    我们都知道:self 是类的隐藏参数,指向当前调用方法的这个类的实例。那 super 呢?

    很多人会想当然的认为“ super 和 self 类似,应该是指向父类的指针吧!”。这是很普遍的一个误区。其实 super 是一个 Magic Keyword, 它本质是一个编译器标示符,和 self 是指向的同一个消息接受者!他们两个的不同点在于:super 会告诉编译器,调用 class 这个方法时,要去父类的方法,而不是本类里的。

    上面的例子不管调用[self class]还是[super class],接受消息的对象都是当前 Son *xxx 这个对象。

    当使用 self 调用方法时,会从当前类的方法列表中开始找,如果没有,就从父类中再找;而当使用 super 时,则从父类的方法列表中开始找。然后调用父类的这个方法。

    **OC Property **

    https://juejin.cn/post/6844903735651647502

    https://github.com/ChenYilong/iOSInterviewQuestions/blob/master/01%E3%80%8A%E6%8B%9B%E8%81%98%E4%B8%80%E4%B8%AA%E9%9D%A0%E8%B0%B1%E7%9A%84iOS%E3%80%8B%E9%9D%A2%E8%AF%95%E9%A2%98%E5%8F%82%E8%80%83%E7%AD%94%E6%A1%88/%E3%80%8A%E6%8B%9B%E8%81%98%E4%B8%80%E4%B8%AA%E9%9D%A0%E8%B0%B1%E7%9A%84iOS%E3%80%8B%E9%9D%A2%E8%AF%95%E9%A2%98%E5%8F%82%E8%80%83%E7%AD%94%E6%A1%88%EF%BC%88%E4%B8%8A%EF%BC%89.md#6-property-%E7%9A%84%E6%9C%AC%E8%B4%A8%E6%98%AF%E4%BB%80%E4%B9%88ivargettersetter-%E6%98%AF%E5%A6%82%E4%BD%95%E7%94%9F%E6%88%90%E5%B9%B6%E6%B7%BB%E5%8A%A0%E5%88%B0%E8%BF%99%E4%B8%AA%E7%B1%BB%E4%B8%AD%E7%9A%84

    20. 一个objc对象的isa的指针指向什么?有什么作用?

    isa 顾名思义 is a 表示对象所属的类。

    isa 指向他的类对象,从而可以找到对象上的方法。

    同一个类的不同对象,他们的 isa 指针是一样的。

    算法36进制加法

    https://blog.csdn.net/lyl194458/article/details/100192501

    UnicodeUTF-8 有什么区别?

    简单来说: Unicode 是「字符集」 UTF-8 是「编码规则」 其中: 字符集:为每一个「

    为什么说OC是动态语言

    为什么说OC是动态语言呢?我们可以从下面几个方面来说明:

    1.动态类型:即运行时再决定对象的类型,简单说就是id类型,任何对象都可以被指定为Id类型,只有在运行时才能决定是什么类型。像内置的明确的基本数据类型都属于静态类型(int,NSString等)。静态类型在编译的时候就能被识别出来,所以当程序发生了类型不对应,编译器就会发出警告。而动态类型在编译器编译的时候是不能被识别的,要等到运行时,即程序运行的时候才会根据语境来识别。所以这里就有两个概念要区分:编译时跟运行时

    2.动态绑定:基于动态类型,在某个实例对象被确定之后其数据类型便被确定了,该对象的属性和响应的消息也被完全确定,这就是动态绑定。比如我们向一个NSObject对象发送-respondsToSelector:或者-instancesRespondToSelector:等来确定对象是否可以对某个SEL做出响应,而在OC消息转发机制被触发之前,对应类的+resolveClassMethod:和+resolveInstanceMethod:将会被调用,在此时有机会动态的向类或者实例添加新的方法,也即类的实现是可以动态绑定的;isKindOfClass也是一样的道理。

    3.动态加载:所谓动态加载就是我们做开发的时候icon图片在retina设备上要多添加一个@2x的图片,当设备更换的时候图片也会自动的替换

    4.多态:运行时机制是多态的基础。多态主要是将数据类型的确定由编译时推迟到了运行时。不同对象以自己的方式响应相同消息的能力叫做多态。例如所有的动物都有同一个方法eat,但每个动物的eat方式不同,也就是不同的对象以自己的方式响应了相同的信息(响应了eat这个选择器)。

    Category的本质是一个_category_t结构体,其结构如下:

    1、分类中只能添加“方法”,不能增加成员变量。如果分类中声明了一个属性,那么分类只会生成这个属性的set、get方法声明,也就是不会有实现。 2、分类中可以/只能访问原有类中.h中的属性。如果想要访问本类中的私有变量,分类和子类一样,只能通过方法来访问。 3、在本类和分类有相同的方法时,优先调用分类的方法再调用本类的方法。 4、如果多个分类中都有和原有类中同名的方法,那么调用该方法的时候执行谁由编译器决定;编译器会执行最后一个参与编译的分类中的方法。

    Categroy的加载过程

    在编译时只是一个包含类名,类指针,方法列表,属性列表,协议列表的_category_t结构体;

    在运行时通过runtime加载分类数据,把分类的方法、属性、协议数据合并到一个大数组中,后参与编译的分类会在数组的前面(i–倒数遍历数组添加的)(这也说明了分类中相同的方法后参与编译的分类会被调用);

    合并后的分类数据会插入到原来类数据的前面(相同的方法,分类会优先于原来调用)。

    编译顺序是可以手动设置的:TARGETS->BuildPhases->Complle Sources。(自己可以验证一下,这里不再赘述)

    CategoryClass Extension 的区别

    Class Extension 是在编译后合并到类中。

    Category 是runtime 运行时 合并到类中。

    weak 实现原理的概括

    Runtime维护了一个weak表,用于存储指向某个对象的所有weak指针。weak表其实是一个hash(哈希)表,Key是所指对象的地址,Value是weak指针的地址(这个地址的值是所指对象指针的地址)数组。

    weak 的实现原理可以概括一下三步:

    1、初始化时:runtime会调用objc_initWeak函数,初始化一个新的weak指针指向对象的地址。
    2、添加引用时:objc_initWeak函数会调用 objc_storeWeak() 函数, objc_storeWeak() 的作用是更新指针指向,创建对应的弱引用表。
    3、释放时,调用clearDeallocating函数。clearDeallocating函数首先根据对象地址获取所有weak指针地址的数组,然后遍历这个数组把其中的数据设为nil,最后把这个entry从weak表中删除,最后清理对象的记录。

    腾讯1

    Category 在什么时候加载、为什么不能动态添加属性 关联对象关联的应该是成员变量

    https://tech.meituan.com/2015/03/03/diveintocategory.html

    Oc的js通信的几种方式

    为什小游戏不用阿里的GCanvas

    premin的流程

    propoety的本质

    Weak的内部实现。

    https://www.jianshu.com/p/13c4fb1cedea

    算法leetcode 62 不同路径

    Bilibili1

    Category的实现原理、关联对象的原理、加载的顺序和流程;

    UIimageView加载一张图片的过程、大图片怎么优化

    埋点框架

    UIview和Calayer的关系

    流媒体常用的协议

    死锁的产生和常见场景

    GCD和NS Operation

    Bili2

    基本问的都是项目、、没啥好提的

    字节2

    iOS xcodebuild后经历了哪些过程,一个oc类怎么加载的

    Category的方法列表怎么加到当前class的,为什么最后一个方法的优先级最高

    dispath barrier是什么,用来干嘛的

    设计一个线程安全的读写 读读并发,读写互斥 写写互斥

    UItableview cell复用的原理,怎么去设计一套cell复用

    https://www.cnblogs.com/zhangyang17/p/3601512.html

    UIView的渲染过程、渲染管线怎么工作

    https://mp.weixin.qq.com/s?__biz=MzAwNDY1ODY2OQ==&mid=400417748&idx=1&sn=0c5f6747dd192c5a0eea32bb4650c160&scene=0#wechat_redirect‘/ a 0 p

    oc 编译过程 -objc - all_load的参数作用

    字节3

    描述下目前app的架构,有什么优化方向,组件化的后续发展、facebook和google怎么设计的架构和组件化

    线程通信原理:

    https://juejin.cn/post/6844904152082939917

    runloop通信:

    https://segmentfault.com/a/1190000021994646

    iOS进程和进程程的通信原理,wkwebview和主进程通信原理,runloop通信的原理

    消息转发、IOS系统内部对消息转发做了什么优化

    Metal 和 opengl的区别,做了哪些优化

    Swift 5的新特性,对应async wait原理

    怎么做启动优化,怎么设计启动框架,启动线程调度和依赖怎么管理,主线程可以依赖异步线程的任务么

    AOP面向切片编程在oc设计里那个场景用到了

    算法题

    链表交错重排

    招银1

    UIview和calayer

    Xcode编译一个app经过的过程

    携程1面

    NSOperation 线程依赖的原理 如果没有这个,自己怎么实现一套线程依赖?(我回答的是信号量)

    Weak实现的原理kv是什么

    启动优化做了哪些事情

    离屏渲染为什么会导致性能消耗

    https://juejin.cn/post/6847902222567604231

    怎么异步渲染一个imageView、imageView的性能优化

    https://www.jianshu.com/p/7d8a82115060

    https://www.jianshu.com/p/43ac91be0cf4

    异步渲染主要做什么

    为什么UIview层级过多会影响性能

    描述下iOS里面的锁,讲一下用到锁的场景
    OOM:

    监控可以用didReceiveMemoryWarning

    也可以类似flex ,通过malloc_get_all_zones可以获取所有堆区的对象,通过objc_getClass获取对应的对象名,通过class_getInstanceSize获取单个对象的大小。进行引用分析,获取当前内存占用

    Facebook的方案是排除法,判断终止进程原因,排除已知原因

    腾讯的做法是OOMDetector

    可以通过malloc_logger实现对于malloc/free的监控,记录内存

    AutoreleasePoolARC的关系?

    AutoreleasePool可以理解为一个对象,在ARC环境下,这个对象本身也是自动计数的,当这个pool的引用计数为0时,就被清掉了,这是手动声明时候的情况,当系统自动为我们建立AutoreleasePool以及在runloop结束时释放就是系统的事情了。但同时AutoreleasePool不同于一般的对象指针,它可以持有多个对象,当pool被release的时候,其会向其持有的所有对象发送release消息,在非ARC环境下用来延长对象的作用域是很有效的。我们知道ARC只是编译器在帮我们自动添加retain和release,而并非垃圾回收器,这样以来,就会不可避免的产生一些内存问题,

    http://blog.sunnyxx.com/2014/10/15/behind-autorelease/

    .a.framework 的区别是什么?

    .a 是单纯的二进制文件,.framework是二进制问价+资源文件。
    其中.a 不能直接使用,需要 .h文件配合,而.framework则可以直接使用。
    .framework = .a + .h + sorrceFile(资源文件)

    RunLoop

    所以,RunLoop 实际上就是一个对象,这个对象管理了其需要处理的事件和消息,并提供了一个入口函数来执行上面 Event Loop 的逻辑。线程执行了这个函数后,就会一直处于这个函数内部 “接受消息->等待->处理” 的循环中,直到这个循环结束(比如传入 quit 的消息),函数返回。

    线程和 RunLoop 之间是一一对应的,其关系是保存在一个全局的 Dictionary 里。线程刚创建时并没有 RunLoop,如果你不主动获取,那它一直都不会有。RunLoop 的创建是发生在第一次获取时,RunLoop 的销毁是发生在线程结束时。你只能在一个线程的内部获取其 RunLoop(主线程除外)

    一个HTTP流程:

    url - DNS查ip ,dns自己有cache,未命中再去找dns服务器 -UDP - ip -Mac地址

    埋点系统设计

    GCD.串行并行队列

    二叉搜索树的判断

    string strong copy的区别

    当原字符串是NSString时,由于是不可变字符串,所以,不管使用strong还是copy修饰,都是指向原来的对象,copy操作只是做了一次浅拷贝。

    而当源字符串是NSMutableString时,strong只是将源字符串的引用计数加1,而copy则是对原字符串做了次深拷贝,从而生成了一个新的对象,并且copy的对象指向这个新对象。另外需要注意的是,这个copy属性对象的类型始终是NSString,而不是NSMutableString,如果想让拷贝过来的对象是可变的,就要使用mutableCopy。 所以,如果源字符串是NSMutableString的时候,使用strong只会增加引用计数。 但是copy会执行一次深拷贝,会造成不必要的内存浪费。而如果原字符串是NSString时,strong和copy效果一样,就不会有这个问题。

    一般怎么检查野指针,zombie对象怎么知道是野指针

    https://www.jianshu.com/p/7bb92761f2f5

    消息转发

    从全局来看,消息转发机制共分为3大步骤:

    1.Method resolution 方法解析处理阶段

    2.Fast forwarding 快速转发阶段

    3.Normal forwarding 常规转发阶段

    动态卡片vm,类似字节lua

    概念上类似于阿里开源的LuaView

    分类为什么不能添加属性

    分类添加方法的原理和顺序

    • 最后添加分类的方法 new_method ,从整个方法的内存从首端开始覆盖
    • 编译顺序越靠后的分类方法的优先级越高

    所以在调用实例对象的方法时,会优先调用分类中的实例对象方法,具体的分类顺序需要在 XCode 中设置分类文件的编译顺序,在 Project-BuildPhases-Compile Sources 中进行设置

    https://juejin.cn/post/6953474692363583525

    https://fatofyoung.github.io/2018/06/13/OC%E5%88%86%E7%B1%BB%E5%8E%9F%E7%90%86/

    uicontrol手势冲突

    https://juejin.cn/post/6908553699732226061

    怎么解决单双击冲突

    requireGestureRecognizerToFail 设置手势的优先级,同样,分页控制器在左滑和系统左滑冲突时也可以这么解决

    iOS 事件(UITouch、UIControl、UIGestureRecognizer)传递机制 - 简书 (jianshu.com) 聊聊NSInvocation和NSMethodSignature_Deft_MKJing的博客-CSDN博客

    1. UITableView继承链?UITableView -> UIScrollView -> UIView -> UIResponder -> NSObject

    2. UIresponder和UIcontrol的区别是啥?UIControl继承自UIView,UIReponder是iOS用于处理触摸事件
      的基类,UIControl对其进行了深度包装,以target-action的模式进行调用。

    3. 触摸事件传递流程是啥样的?分为事件传递链和响应链,传递链通过hittest函数和pointside递归的
      判断子view是否可以响应事件,直到递归遍历到最后一个view,响应链则是消费事件的过程,从子 view向其的vc,superView传递触摸事件。优先级的话手势识别高于view的触摸事件,系统的 uicontrol高于手势,自定义的低于手势。

    4. 如何扩大UIbutton点击范围?两种方法,重写pointinside和hittest

    5. 如何扩大手势点击范围?或者扩大一个小图片的点击范围?图片话调节内边距即可

    6. 获取当前viwe的vc方法?方法一:通过响应链机制,寻找view的下一级响应者,因为view的下一级
      响应链是其vc 方法二:通过KeyWindows获取对应的rootVC一直朝下遍历,获取presetedVC知
      道最后一个VC

    7. 消息转发阶段流程是什么样的?具体有哪些方法?resolveMethod -> forwardingTarget -> methodSinature->forwardInvocation 第三步会获取对应的方法签名,如果能获取到可以在第四步 直接将这个方法以NSInvocation形式转发给其他对象处理

    8. NSInvocation和NSMethodSignature的区别是啥?NSMethodSignature是方法编码过后的签名,例如 NSString的类方法isEqualToString: 的方法签名为B24@0:8@16,如下所示 @encode(BOOL) (B) 返回值 @encode(id) (@) 默认第一个参数 self
      @encode(SEL) (:)默认第二个参数 _cmd
      @encode(NSString ) (@)
      实际上的第一个参数
      NSString* iOS中的method即由SEL、IMP和签名组成,SEL是method检索的方式,可以通过SEL在method list中找到对应的方法,IMP则是函数指针指向方法的本身,签名包括了函数头中的各种信息。 NSInvocation则是消息在OC中对象的形式存在,是一种更高级的转发机制可以通过添加target、 签名、参数这些强行执行方法

    9. 消息转发可以做什么?怎么做的?热更新、多重代理和多继承

    10. Runtim相关函数有哪些?贼多,建议查 Objective-C Runtime | Apple Developer Documentation

    11. 线程锁有哪些?iOS多线程安全-13种线程锁%"#$ (juejin.cn)

    12. semaphore和nslock有啥区别?优缺点是啥?

    13. 怎么通过gcd实现读写锁?Dispatch_barrity_async

    14. 循环引用是啥?有哪些会造成?iOS解除Block循环引用,你只知道_weak就out啦知更鸟
      CoolLee的博客-CSDN博客
      block中的强引用是对self指针的强引用所以简单使用weak/strong就可以解决循环引用问题,但
      是NSTimer中的循环引用是对整个对象的强引用

    15. NStimer循环引用是怎么造成?怎么解决?NSTimer会强引用整个对象,对象持有Timer就会引起强
      引用。方法1:及时结束Timer运行,使用invalidata结束运行并置整个Timer为nil 方法2:target不指向自身,指向一个其他对象 方法3:使用NSProxy替代self,消息转发阶段转发消息回原类

    16. NSProxy是啥?有啥用?NSProxy是一个等同于NSObject的基类,实现了<NSObject>的协议,自身没 有任何实现,主要用于转发消息的基类,可以继承自这个基类,实现消息转发中的方法可以将消 息转发给其他对象。可以用于实现oc中本不支持的多继承,解决NSTimer中的循环引用。

    NSProxyNSObject的区别?

    1. 虽然都需要实现了<NSObject>协议,主要差别在消息转发流程中, NSObject会走完完整的消息转发机制,NSProxy直接回调-methodSignatureForSelector:/- forwardInvocation:,消息转发过程比class NSObject要简单得多,NSProxy会将自省相关的selector
      直接forward到-forwardInvocation:回调中,例如 - (BOOL)isKindOfClass:(Class)aClass;

      • (BOOL)isMemberOfClass:(Class)aClass;
      • (BOOL)conformsToProtocol:(Protocol *)aProtocol; - (BOOL)respondsToSelector:(SEL)aSelector;
    2. _cmd是啥?表示这个方法本身

    3. block如何获取外部变量?

    4. +load方法和initialize方法加载顺序?

    IOSkeychain

    Socket 场链接 内购 gcd

    如何捕获**crash **

    https://www.jianshu.com/p/5fcf7bb7955f

    动态库和静态库

    block 为什么用copy修饰 Block实现原理

    https://juejin.cn/post/6844904040954871815

    setObject:forKey:setValue:forKey:

    kvckvo

    kvc set:简单来说就是如果没有找到Set<Key>方法的话,会按照_key,_iskey,key,iskey的顺序搜索成员并进行赋值操作。

    当你观察一个对象时,一个新的类会动态被创建。这个类继承自该对象的原本的类,并重写了被观察属性的 setter 方法。自然,重写的 setter 方***负责在调用原 setter方法之前和之后,通知所有观察对象值的更改。最后把这个对象的 isa 指针 ( isa 指针告诉 Runtime 系统这个对象的类是什么 ) 指向这个新创建的子类,对象就神奇的变成了新创建的子类的实例。

    原来,这个中间类,继承自原本的那个类。不仅如此,Apple 还重写了 -class 方法,企图欺骗我们这个类没有变,就是原本那个类。更具体的信息,去跑一下 Mike Ash 的那篇文章里的代码就能明白,这里就不再重复。

    https://juejin.cn/post/6844903602545229831

    介绍下oc

    RunTime

    首先 OC 是 C 语言的超集,因为 runtime 这个库使得C语言有了面向对象的能力:
    OC 对象可以用C语言中的结构体表示,而方法可以用C函数来实现,这些结构体和函数被 runtime 函数封装后,我们就可以在程序运行时创建,检查,修改类、对象和它们的方法了。

    OC 是一门动态语言,它将很多静态语言在编译和链接时期做的事放到了运行时来处理。
    这种特性意味着Objective-C不仅需要一个编译器,还需要一个运行时系统来执行编译的代码。这个运行时系统即Objc Runtime。Objc Runtime基本上是用C和汇编写的。

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

    静态库和动态库:

    https://juejin.cn/post/7040003550977458207

    编译和链接:

    http://www.360doc.com/content/17/0111/22/32626470_621879084.shtml

    集合类型 copy 操作和 mutableCopy 操作

    一个类遵循 NSCopying, NSMutableCopying 协议并且实现相对应的初始化方法后,这个类就具有对象拷贝的能力。如果你自定义类没有遵守协议直接调用 copy / mutableCopy 程序会奔溃。拷贝协议的具体使用我这里不做扩展感兴趣的可以自行 Google 一下。有一种观点:copy 操作就是浅复制,mutableCopy 就是深复制,这是一个非常错误的理解。以下是我的一些总结:

    • 可变的集合对象 + copy 得到一个新的对象(新对象不可变) 深复制
    • 可变的集合对象 + mutablecopy 得到一个新的对象(新对象可变) 深复制
    • 不可变集合对象 + copy 没有得到新的对象(地址的引用) 浅复制
    • 不可变集合对象 + mutablecopy 得到一个新的对象(新对象可变) 深复制

    怎么去动态关联一个weak属性

    持有一个strong的容器,让其持有该weak属性,获取关联对象时获取其属性即可

    首先,给category属性是需要使用runtime中的关联来实现set和get方法,但runtime没有提供weak ,虽然runtime没有开放weak解决方案,但objc对象是可以实现weak的,所以让需要被修饰的对象去持有一个strong对象,然后当被修饰的对象释放的时候,持有的对象也会被释放,那么我们就可以捕捉到释放的事件,进而使用OBJC_ASSOCIATION_ASSIGN来实现弱引用

    对象的内存布局

    struct objc_class {

    Class isa;

    Class super_class;

    const charchar *name;

    long version;

    long info;

    long instance_size;

    struct objc_ivar_list *ivars;

    struct objc_method_list **methodLists;

    struct objc_cache *cache;

    struct objc_protocol_list *protocols;

    } OBJC2_UNAVAILABLE;

    二叉树的层序遍历

    消息转发的优化

    在 2020 年中,Apple 针对 Objective-C 做了三项优化

    • 类数据结构变化:节约了系统更多的内存。
    • 相对方法地址:节约了内存,并且提高了性能。
    • Tagged Pointer 格式的变化:提高了 msgSend 性能

    Selfthis 的底层实现,是编译期还是运行期做的。答案是编译期间

    this指针本质: 当一个对象调用某成员函数时编译器会隐式传入一个参数, 这个参数就是this指针

    this指针中存放的就是这个对象的首地址。

    其实编译器在生成程序时加入了获取对象首地址的相关代码,并把获取的首地址存放在了寄存器ECX中(VC++编译器是放在ECX中,其它编译器有可能不同)。

    成员函数的其它参数正常都是存放在栈中,而this指针参数则是存放在寄存器中。

    我们都知道:self 是类的隐藏参数,指向当前调用方法的这个类的实例。那 super 呢?

    很多人会想当然的认为“ super 和 self 类似,应该是指向父类的指针吧!”。这是很普遍的一个误区。其实 super 是一个 Magic Keyword, 它本质是一个编译器标示符,和 self 是指向的同一个消息接受者!他们两个的不同点在于:super 会告诉编译器,调用 class 这个方法时,要去父类的方法,而不是本类里的。

    上面的例子不管调用[self class]还是[super class],接受消息的对象都是当前 Son *xxx 这个对象。

    当使用 self 调用方法时,会从当前类的方法列表中开始找,如果没有,就从父类中再找;而当使用 super 时,则从父类的方法列表中开始找。然后调用父类的这个方法。

    **OC Property **

    https://juejin.cn/post/6844903735651647502

    https://github.com/ChenYilong/iOSInterviewQuestions/blob/master/01%E3%80%8A%E6%8B%9B%E8%81%98%E4%B8%80%E4%B8%AA%E9%9D%A0%E8%B0%B1%E7%9A%84iOS%E3%80%8B%E9%9D%A2%E8%AF%95%E9%A2%98%E5%8F%82%E8%80%83%E7%AD%94%E6%A1%88/%E3%80%8A%E6%8B%9B%E8%81%98%E4%B8%80%E4%B8%AA%E9%9D%A0%E8%B0%B1%E7%9A%84iOS%E3%80%8B%E9%9D%A2%E8%AF%95%E9%A2%98%E5%8F%82%E8%80%83%E7%AD%94%E6%A1%88%EF%BC%88%E4%B8%8A%EF%BC%89.md#6-property-%E7%9A%84%E6%9C%AC%E8%B4%A8%E6%98%AF%E4%BB%80%E4%B9%88ivargettersetter-%E6%98%AF%E5%A6%82%E4%BD%95%E7%94%9F%E6%88%90%E5%B9%B6%E6%B7%BB%E5%8A%A0%E5%88%B0%E8%BF%99%E4%B8%AA%E7%B1%BB%E4%B8%AD%E7%9A%84

    20. 一个objc对象的isa的指针指向什么?有什么作用?

    isa 顾名思义 is a 表示对象所属的类。

    isa 指向他的类对象,从而可以找到对象上的方法。

    同一个类的不同对象,他们的 isa 指针是一样的。

    算法36进制加法

    https://blog.csdn.net/lyl194458/article/details/100192501

    UnicodeUTF-8 有什么区别?

    简单来说: Unicode 是「字符集」 UTF-8 是「编码规则」 其中: 字符集:为每一个「

    为什么说OC是动态语言

    为什么说OC是动态语言呢?我们可以从下面几个方面来说明:

    1.动态类型:即运行时再决定对象的类型,简单说就是id类型,任何对象都可以被指定为Id类型,只有在运行时才能决定是什么类型。像内置的明确的基本数据类型都属于静态类型(int,NSString等)。静态类型在编译的时候就能被识别出来,所以当程序发生了类型不对应,编译器就会发出警告。而动态类型在编译器编译的时候是不能被识别的,要等到运行时,即程序运行的时候才会根据语境来识别。所以这里就有两个概念要区分:编译时跟运行时

    2.动态绑定:基于动态类型,在某个实例对象被确定之后其数据类型便被确定了,该对象的属性和响应的消息也被完全确定,这就是动态绑定。比如我们向一个NSObject对象发送-respondsToSelector:或者-instancesRespondToSelector:等来确定对象是否可以对某个SEL做出响应,而在OC消息转发机制被触发之前,对应类的+resolveClassMethod:和+resolveInstanceMethod:将会被调用,在此时有机会动态的向类或者实例添加新的方法,也即类的实现是可以动态绑定的;isKindOfClass也是一样的道理。

    3.动态加载:所谓动态加载就是我们做开发的时候icon图片在retina设备上要多添加一个@2x的图片,当设备更换的时候图片也会自动的替换

    4.多态:运行时机制是多态的基础。多态主要是将数据类型的确定由编译时推迟到了运行时。不同对象以自己的方式响应相同消息的能力叫做多态。例如所有的动物都有同一个方法eat,但每个动物的eat方式不同,也就是不同的对象以自己的方式响应了相同的信息(响应了eat这个选择器)。

    Category的本质是一个_category_t结构体,其结构如下:

    1、分类中只能添加“方法”,不能增加成员变量。如果分类中声明了一个属性,那么分类只会生成这个属性的set、get方法声明,也就是不会有实现。 2、分类中可以/只能访问原有类中.h中的属性。如果想要访问本类中的私有变量,分类和子类一样,只能通过方法来访问。 3、在本类和分类有相同的方法时,优先调用分类的方法再调用本类的方法。 4、如果多个分类中都有和原有类中同名的方法,那么调用该方法的时候执行谁由编译器决定;编译器会执行最后一个参与编译的分类中的方法。

    Categroy的加载过程

    在编译时只是一个包含类名,类指针,方法列表,属性列表,协议列表的_category_t结构体;

    在运行时通过runtime加载分类数据,把分类的方法、属性、协议数据合并到一个大数组中,后参与编译的分类会在数组的前面(i–倒数遍历数组添加的)(这也说明了分类中相同的方法后参与编译的分类会被调用);

    合并后的分类数据会插入到原来类数据的前面(相同的方法,分类会优先于原来调用)。

    编译顺序是可以手动设置的:TARGETS->BuildPhases->Complle Sources。(自己可以验证一下,这里不再赘述)

    CategoryClass Extension 的区别

    Class Extension 是在编译后合并到类中。

    Category 是runtime 运行时 合并到类中。

    weak 实现原理的概括

    Runtime维护了一个weak表,用于存储指向某个对象的所有weak指针。weak表其实是一个hash(哈希)表,Key是所指对象的地址,Value是weak指针的地址(这个地址的值是所指对象指针的地址)数组。

    weak 的实现原理可以概括一下三步:

    1、初始化时:runtime会调用objc_initWeak函数,初始化一个新的weak指针指向对象的地址。
    2、添加引用时:objc_initWeak函数会调用 objc_storeWeak() 函数, objc_storeWeak() 的作用是更新指针指向,创建对应的弱引用表。
    3、释放时,调用clearDeallocating函数。clearDeallocating函数首先根据对象地址获取所有weak指针地址的数组,然后遍历这个数组把其中的数据设为nil,最后把这个entry从weak表中删除,最后清理对象的记录。

    相关文章

      网友评论

        本文标题:iOS面经

        本文链接:https://www.haomeiwen.com/subject/rofllrtx.html