量化指标:前后Crash量的变化线上bug数的变化开发成本节约了多少全面型邮箱、联系方式等基本信息要全面履历过的公司要全面简历内容基本信息工作经历项目经验擅长技能主导xxx项目背景改善原有代码臃肿,不易维护需求快速迭代的成本较高稍有不慎,就会引发bug方案根据业务合理运用桥接、命令等设计模式,对原有业务进行分层、解耦、降低代码重合度。效果日崩溃率<x%左右重构之后代码行数由30W行减少到20W行。专业技能擅长利用自顶向下逐级重构等思想进行技术重构擅长Feed流性能优化,理解UI从创建到渲染到屏幕上的整体流程、机制掌握MVC、MVVM等主流框架,熟悉桥接、命令、适配器等常见设计模式熟悉ObjC、内存管理、block、runloop相关底层原理了解Swift,可以看懂C/C++,理解JSPatch理解敏捷开发流程,Design Revierwer, Code Reviewer能通过多线程、runtime机制去解决项目开发过程中所遇到的问题。视图响应机制UITableView相关cell 的重用机制滚出屏幕之后,到达重用池里,加载新控件,从重用池里拿到新的视图数据源的同步问题并发访问,数据拷贝串行队列UIView和CALayer的关系UIView为其提供内容,以及负责处理触摸等事件,参加响应链, CALayer 负责显示内容contents视图事件传递机制主要使用两个函数监测返回响应的视图- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;判断试图是否在试图内- (UIView *)pointInside:(CGPoint)point withEvent:(UIEvent *)event;hitTest流程响应链视图事件响应- (UIView *)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;- (UIView *)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;- (UIView *)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;图像显示原理CPU:Layout: UI布局、文字计算Display drawrectprepare: 图片编解码Commit: 提交位图GPU:GPU渲染管线顶点着色图元装配光栅化片段着色片段处理=> FrameBufferUI卡顿、掉帧的原因16.7ms之内完成虚啊然滑动优化方案(针对scrollview等子类)CPU:对象创建、调整对象、销毁对象预排版(布局计算、文本计算)预渲染(文本等异步绘制,图片编解码等)GPU:纹理渲染(避免离屏渲染)视图混合UIView的绘制原理[UIView setNeedsDisplay]的时候什么时候渲染的UI系统的绘制流程异步绘制[layer.delegate displayLayer:]“代理负责生成对应的bitmap设置该bitmap作为layer.contents属性的值 离屏渲染On-Screen Rendering:当前屏幕渲染,指的GPU的渲染操作是在当前用于显示的屏幕缓冲区中进行Off-Screen Rendering:离屏渲染,指的是GPU在当前屏幕缓冲区以外新开辟一个缓冲区进行渲染操作何时会触发:设置圆角:(当和maskToBounds一起使用时)图层蒙版阴影光栅化为何要避免在触发离屏渲染的时候,会增加GPU的工作量,导致CPU和GPU工作耗时加起来超过16.7ms,可能会导致UI的卡顿和掉帧,所以需要避免离屏渲染。创建新的渲染缓冲区:会有内存上的开销上下文切换:因为有多通道渲染管线,最终需要把多通道的渲染结果,做一个合成,涉及到上下文切换。系统的UI事件传递机制时怎样的?使UITableView滚动更流畅得方案或思路都有哪些?什么是离屏渲染?UIView 和 CALayer之间的关系时怎样的?(单一职责原则)Objective-C分类、关联对象、扩展、代理、通知、KVO、KVC、属性关键字分类(Category)你用分类做了哪些事情:生命私有方法分解体积庞大的类文件把framework的私有方法公开特点:运行时决议,通过runtime添加到类上面可以为系统类添加分类分类中都可以添加哪些内容实例方法 - 类方法 + 协议 protocol属性(不能添加实例变量)struct category_t { const char *name; classref_t cls; struct method_list_t * instanceMethods; struct method_list_t *classMethods; struct protocol_list_t *protocols; struct property_list_t *instanceProperties; method_list_t *methodsForMeta(bool isMeta); property_list_t *propertiesForMeta(bool isMeta);}分类的调用栈只分析类当中实例方法添加的逻辑 默认ismeta = NO获取cls中未完成整合的所有分类将分类cats拼接到cls上三个二维数组mLists、proplists、protolists倒叙访问分类(编译顺序)同名分类谁生效?取决于编译顺序根据宿主类当中的rw数据针对分类中关于内存管理相关方法情况下的一些特殊处理写入到宿主类。addedLists, addedcounts 1. 列表中原有元素总数 1. 拼接之后的总数 1. 重新分配内存 1. 重新设置元素总数 1. 内存移动memmove[[],[],[][原1],[原2]] 1. 覆盖宿主类方法memcopy -> 分类方法会覆盖宿主类方法的原因。查找到first会提前返回。分类添加的方法可以覆盖原类方法,但是原方法仍然存在。同名分类方法谁能生效取决于编译顺序名字相同的分类会引起编译报错。关联对象能否给分类添加“成员变量”?可以用关联对象技术来添加id objc_getAssociatedObejct(id obejct, const void *key)id obcj_setAssociatedObject(id object, const void *key, id value, objc_associationPolicy policy)void objc_removeAssociatedObjectes(id object);关联对象的本质关联对象由AssociationsManager管理在AssociationsHashMap存储,对象为ObjcAssociation所有对象的关联内容都在同一个全局容器中。所以关联对象,并不是添加到对应的对象上面。扩展一般扩展用来做什么声明私有属性声明私有方法声明私有成员变量特点:编译时决议只以声明的形式存在,多数情况下寄生于宿主类的.m中不能为系统类添加扩展代理 (Delegate)准确的说是一种软件的设计模式ios当中以@protocol形式体现传递方式1对1实现方式可以定义的内容属性方法(可以被标识 required,optional)一般声明为weak以规避循环引用。通知是使用观察者模式来实现的用于跨层传递信息的机制传递方式为1对多如何实现通知机制?使用DCEventDataController解释 KVOKVO是Key-value observing的缩写KVO是Objective-C对观察者设计模式的又一实现。Apple使用了isa混写(isa-swizzing)来实现KVO通过kvc设置value能否生效?可以 [obj setValue:@2 forKey:@"value"];通过成员变量直接赋值value能否生效?不可以,需要触发setter方法,才可以实现,或者使用 [self willChangeValueForKey:@"value"],[self didChangeValueForKey:@"value"]来手动触发KVO总结:成功触发KVO的三种方式使用setter方法改变值使用setValue:forKey:改变值手动触发KVO才会生效[self willChangeValueForKey:@"value"],[self didChangeValueForKey:@"value"]KVCKVC是Key-value coding的缩写-(id)valueForKey:(NSString*)key- (void)setValue:(id)value forKey:(NSString*)key会破坏面向对象的编程思想。valueForKey相似名称的成员变量SetValueForkey属性关键字读写属性readonlyreadwrite(default)原子性atomic(default)🤔可以保证赋值或获取是保证线程安全的。但是对对象进行操作,是不安全的,比如对数组的赋值和获取是线程安全的,但是添加和删除对象,保证不了。Nonatomic引用计数retain/strongassign/unsafe_unretainedweakcopy### assign修饰基本数据类型,如int,bool修饰对象类型时,不改变其引用计数会产生悬垂指针weak不改变被修饰对象的引用计数所指对象在释放之后会自动置为nil类型区别1区别2相同weak修饰对象释放之后指向nil不改变对象的引用计数Assign修饰对象和基本数据类型释放之后指向原地址不改变对象的引用计数copy@property(copy)NSMutableArray* array;会有什么问题?浅拷贝会增加引用计数。未发生新的内存分配深拷贝不会增加引用计数会产生新的内存分配源对象类型拷贝方式目标对象类型拷贝类型mutable对象copy不可变深拷贝mutable对象mutableCopy可变深拷贝immutable对象copy不可变浅拷贝immutable对象mutableCopy可变深拷贝可变对象的copy和mutablecopy都是深拷贝不可变对象的copy是浅拷贝,mutablecopy是深拷贝copy方法返回的对象都是不可变对象会导致不可见的异常问题。如果赋值过来的是NSMutableArray,copy之后是NSArray如果赋值过来的是NSArray,copy之后是NSArray。但是声明的是mutablearray,所以不可避免的会执行add delete等操作。题MRC下如何重写retain修饰变量的setter方法?@property (nonatomic, retain) id obj;- (void)setObj:(id)obj { if (_obj != obj) { [_obj release]; _obj = [obj retain]; }}简述分类实现原理?由运行时决议的,不同分类中含有同名方法,谁最终生效,取决于谁最后编译,最后参与编译的那个同名函数会最后生效,假如添加的方法,含有同名的宿主方法,那么会覆盖掉宿主方法,宿主方法不是被舍弃,而是被移动到该变量内存的最后面。KVO的实现原理是怎样的?是系统对于观察者模式的一种实现,运用了isa混写技术,来动态运行时去未某个类添加一个子类,重写他的setter方法,把原有的isa类的指针指向新创建的类上面。能否为分类添加成员变量?通过关联对象技术来给分类添加成员变量。Runtime数据结构、类对象与元类对象、消息传递机制、方法缓存、消息转发、Method-Swizzling、动态添加方法、动态方法解析。数据结构objc_object、objc_class、isa指针、method_tobjc_object实际上id 等于 objc_objectIsa_t关于isa操作相关弱引用相关关联对象相关内存管理相关objc_class实际上Class就是objc_class继承自objc_objectClass superClasscache_t cacheclass_data_bits_t bits;isa指针共用体isa_t关于对象,其指向类对象。“实例 --isa--> Class关于类对象,其指向元类对象“Class --isa--> MetaClasscache_t用于快速查找方法执行函数是可增量扩展的哈希表结构是局部性原理的最佳应用class_data_bits_tClass_data_bits_t主要是对class_rw_t的封装class_rw_t代表了类相关的读写信息,对class_ro_t的封装Class_ro_t代表了类的相关的只读信息Class_rw_t class_ro_tprotocolspropertiesmethodsclass_ro_tname NSClassfromString就是用这个字段获取的ivars propertiesprotocolsmethodListstruct method_tSEL name;Const char* types;IMP imp;Type Encodings整体结构对象、类对象、元类对象类对象存储实例方法列表等信息元类对象存储类方法类表等信息消息传递void objc_msgSend(void /* id self, SEL op*/)[self class]转化成 objc_msgSend(self, @selector(class)),这个步骤发生在编译器期间void objc_msgSendSuepr(void /* structure objc_super *super, SEL op*/)stuct objc_super { __unsafe_unretained id receiver; --> self}[super class] 转化成 objc_msgSendSuper(super, @selector(class))缓存查找例:给定值是SEL,目标值是对应bucket_t中的IMPcache_key_t --f(ey)--> butter_tf(key) = key & mask在当前类中查找对于已排序好的列表,采用二分查找算法查找方法对应执行函数对于没有排序的列表,采用一般遍历查找方法对应执行函数父类逐级查找resolveInstanceMethod:@implementation RuntimeObject+ (BOOL)resolveInstanceMethod:(SEL)sel { if (sel == @selector(test)) { NSLog(@"resolveInstanceMethod:"); return NO; } else { return [super resolveInstanceMethod:sel]; } }- (id)forwardingTargetForSelector:(SEL)aSelector { NSLog(@"forwardingTargetForSelector"); return nil;}- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector { if (aSelector == @selector(test)) { NSLog(@"methodSignatureForSelector"); return [NSMethodSignature signatureWithObjCTypes:"v@:"]; } else { return [super methodSignatureForSelector:aSelector]; }}- (void)forwardInvocation:(NSInvocation *)anInvocation { NSLog(@"forwardInvocation");}@endMethod-Swizzling交换函数+(void)load { Method test = class_getInstanceMethod(self, @selector(test)); Method otherTest = class_getInstanceMethod(self, @selector(otherTest)); method_exchangeImplementations(test, otherTest);}- (void)test { NSLog(@"test");}- (void)otherTest { [self otherTest]; NSLog(@"otherTest");}/**output:testotherTest*/动态添加函数void testImp(void) { NSLog(@"test invoke");}+ (BOOL)resolveInstanceMethod:(SEL)sel { if (sel == @selector(test)) { NSLog(@"resolveInstanceMethod:"); class_addMethod(self, @selector(test), testImp, "v@:"); return NO; } else { return [super resolveInstanceMethod:sel]; } }/**output:resolveInstanceMethod:test invoke*/动态方法解析@dynamic动态运行时语言将函数决议推迟到运行时。编译时语言在编译器进行函数决议。Q:[obj foo]和objc_msgSend()函数有什么关系?A: 发送消息,会在编译器处理之后,变成了objc_msgSend(obj, foo)了,然后开始了消息传递的过程了。Q:runtime如何通过Selector找到对应IMP地址的?查找当前类实例所对应的类对象的缓存,是否有selector对应的imp实现,如果缓存命中,那么就把命中的缓存函数,返回给调用方,如果没有命中,那么在根据当前类的方法列表,去查找selector对应的具体的imp实现,当前类如果没有命中的话,再根据当前类的superclass指针逐级查找父类的方法列表,然后查找所对应的imp实现。Q:能否想编译后的类中添加实例变量?不能添加的。但是可以在动态添加的类的当中增加实例变量。内存管理内存布局、内存管理方案、数据结构、ARC & MRC、引用计数、弱引用、自动释放池、循环引用。内存布局stack:方法调用heap:通过alloc分配的对象bss:未初始化的全局变量等。data:已初始化的全局变量等。text:程序代码段。内存管理方案TaggedPointerNONPOINTER_ISA散列表objc-runtime-680NONPOINTER_ISAarm64架构下:SideTables()因为多线程对表进行操作,需要加锁来处理🤔,存在效率问题。所以引入了分离锁怎么实现快速分流?SideTables的本质是一张hash表例:给定值时对象内存地址,目标是数组下标索引。Spinlock_t 自旋锁Spinlock_t是“忙等”的锁:如果当前锁已被其他线程获取,那么当前线程会不停的探测锁是否被释放,如果被释放,那么立即获取。其他锁都是把自己休眠。适用于轻量反问。适用于哪些场景呢?《多线程》RefcountMap 引用计数表size_tweak_table_t 弱引用表MRC 手动引用计数alloc: 分配内存空间retain: +1release: -1retainCount:引用计数值autorelease:在@autoreleasepoll结束的时候,调用release操作。dealloc: [super dealloc]来放弃父类的引用计数ARC自动引用计数ARC是LLVM和Runtime协作的结果。ARC中禁止手动调用 retain/release/retainCount/deallocARC新增weak strong属性关键字引用计数管理alloc实现经过一系列调用,最终调用了C函数Calloc,此时并没有设置引用计数为1retain实现SideTable& table = SideTables()[this];size_t& refcntStorage = table.refcnts[this];refcntStorage += SIDE_TABLE_RC_ONE;release 实现SideTable& table = SideTables()[this];RefcountMap::iterator it = table.refcnts.find(this);it->second -= SIDE_TABLE_RC_ONE;retainCount实现SideTable& table = SideTables()[this];size_t refcnt_result = 1;RefcountMap::iterator it = table.refcnts.find(this);refcnt_result += it-> second >> SIDE_TABLE_RC_SHIFT;dealloc实现判断当前对象是否使用了非指针型isa判断当前对象是否有弱引用判断当前对象是否有关联对象判断当前对象是否有c++的部分/使用了ARC判断当前对象,是否使用了sidetable存储了RC弱引用管理{ id __weak obj1 = obk;}//----->{ id obj1; objc_initWeak(&obj1, obj);}自动释放池autoreleasePool的实现原理是怎样的?为什么autoreleasepool可以嵌套?@autoreleasepool {}// -----> void *ctx = objc_autoreleasePoolPush();{ }objc_autoreleasePoolPop(ctx);是以栈为结点通过双向链表的形式组合而成的是和线程一一对应的。//AutoreleasePoolPageid* next;AutoreleasePoolPage* const parent;AutoreleasePoolPage* child;pthread_t const thread; 在当次runloop将要结束的时候,调用AutoreleasePoolPage::pop()多层签到就是多次插入哨兵对象。在for循环中alloc图片数据等内存消耗较大的场景手动插入autoreleasePool循环引用自循环引用互相循环引用多循环引用代理blockNSTimer大环引用如何破除循环引用避免产生循环引用在合适的时机手动断环具体的解决方案__weak__block__unsafe_unretained__weak__block“MRC下,__block修饰的对象不会增加其引用计数,避免了循环引用ARC下,__block修饰的对象会被强引用,无法避免循环引用,需要手动解环__unsafe_unretained修饰对象不会增加其引用计数,避免了循环引用如果被修饰对象在某一时机被释放,会产生悬垂指针。NSTimer的循环引用问题 WeakBox! private(set) var referenceHolder: [WeakBox] = [] struct WeakBox { weak var object: DisplayLinkDriver? }@interface TimerWeakObject()@property (nonatomic, weak) id target;@property (nonatomic, assign) SEL selector;@property (nonatomic, weak) NSTimer* timer;@end@implementation TimerWeakObject- (void)fire:(NSTimer*)timer { if (self.target){ if ([self.target respondsToSelector:self.selector]) { [self.target performSelector:self.selector withObject:timer.userInfo]; } } else { [self.timer invalidate]; }}@end@implementation NSTimer(WeakTimer)+ (NSTimer*)scheduledWeakTimerWithTimeInterval:(NSTimeInterval)interval target:(id)aTarget selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)repeats { TimerWeakObject *object = [[TimerWeakObject alloc] init]; object.target = aTarget; object.selector = aSelector; object.timer = [NSTimer scheduledTimerWithTimeInterval:interval target:object selector:@selector(fire:) userInfo:userInfo repeats:repeats]; return object.timer;}@endQ:什么是ARC?A:ARC是LLVM和Runtime协作的来为我们实现自动引用计数的管理Q:为什么weak指针指向的对象在废弃之后会被置为nilA:当对象被废弃之后,dealloc内部方法的实现当中,会调用清楚弱引用的方法,在清楚弱引用的方法当中,会通过hash算法来查找被废弃对象在弱引用表当中的位置,来提取他所对应的弱引用指针的列表数组,然后进行for循环遍历,把每一个weak指针都置为nil。Q:苹果是如何实现AutoreleasePool的?A:是以栈为结点,由双向链表形式合成的数据结构Q:什么是循环引用?你遇到过哪些循环引用,是怎样解决的?A:NSTimer问题解析。BlockBlock、截获变量、__block修饰符、block的内存管理block是将函数及其执行上下文封装起来的对象。{ int multiplier = 6; int (^Block)(int) = ^int(int num) { return num * multiplier; } Block(2);}struct __block_impl { void *isa; // isa指针,Block是对象的标志 int Flags; int Reserved; void *FuncPtr; // 函数指针}struct __MCBlock_method_block_impl_0 { struct __block_impl impl; struct __MCBlock__method_block_desc_0* Desc; int multiplier; __MCBlock__method_block_impl_0(void *fp, struct __MCBlock__method_block_desc_0 *desc, int _multiplier, int flags=0) : multiplier(_multiplier) { implementation.isa = &_NSCorcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }}static void _I_MCBlock_method(MCBlock *self, SEL _cmd) { int (*Block)(int) = ((int(*)(int))&__MCBlock__method_block_impl_0((void *)__MCBlock__method_block_func_0, &__MCBlock__method_block_desc_0_DATA, multiplier)) ((int (*)(__block_impl *, int))((__block_impl *)Block)->FuncPtr)((__block_impl*)Block, 2);}static int __MCBlock__method_block_func_0(struct __MCBlock_method_block_impl_0 *__cseld, int num) { int multiplier = __cseld->multiplier; return num * multiplier;}什么是block调用实际上是函数的调用((int (*)(__block_impl *, int))((__block_impl *)Block)->FuncPtr)((__block_impl*)Block, 2);截获变量{ int multiplier = 6; int (^Block)(int) = ^int(int num) { return num * multiplier; } multiplier = 4; NSLog(@"result is %d", Block(2));}局部变量基本数据类型对象类型静态局部变量全局变量静态全局变量对于基本数据类型的局部变量截获其值对于对象类型的局部变量连同所有权修饰符一起截获以指针形式截获局部静态变量。不截获全局变量、静态全局变量。__block 修饰符一般情况下,对被截获变量进行赋值操作需要添加__block修饰符{ //使用 NSMutableArray *array = [NSMutableArray array]; void (^block)(void) = ^{ [array addObject:@123]; } Block();}{ //使用 NSMutableArray *array = nil; void (^block)(void) = ^{ array = [NSMutableArray array]; } Block();}对变量进行赋值时,不需要__block修饰符的情况:静态局部变量、全局变量、静态全局变量。{ __block MCBlock* blockSelf = self; _blk = ^int(int num) { int result = num * blockSelf.var; blockSelf = nil; return result; }; _blk(3);}Q:什么是Block?Q:为什么block会产生循环引用
量化指标:
全面型
简历内容
主导xxx项目
背景
方案
根据业务合理运用桥接、命令等设计模式,对原有业务进行分层、解耦、降低代码重合度。
效果
日崩溃率<x%左右
重构之后代码行数由30W行减少到20W行。
专业技能
视图响应机制
UITableView相关
cell 的重用机制
滚出屏幕之后,到达重用池里,加载新控件,从重用池里拿到新的视图
数据源的同步问题
UIView和CALayer的关系
UIView为其提供内容,以及负责处理触摸等事件,参加响应链,
CALayer 负责显示内容contents
视图事件传递机制
主要使用两个函数
监测返回响应的视图
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;
判断试图是否在试图内
- (UIView *)pointInside:(CGPoint)point withEvent:(UIEvent *)event;
hitTest流程
响应链
视图事件响应
- (UIView *)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
- (UIView *)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
- (UIView *)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
图像显示原理
CPU:
GPU:GPU渲染管线
=> FrameBuffer
UI卡顿、掉帧的原因
16.7ms之内完成虚啊然
滑动优化方案(针对scrollview等子类)
CPU:
GPU:
UIView的绘制原理
[UIView setNeedsDisplay]的时候什么时候渲染的UI
系统的绘制流程
异步绘制
[layer.delegate displayLayer:]
离屏渲染
何时会触发:
为何要避免
在触发离屏渲染的时候,会增加GPU的工作量,导致CPU和GPU工作耗时加起来超过16.7ms,可能会导致UI的卡顿和掉帧,所以需要避免离屏渲染。
Objective-C
分类、关联对象、扩展、代理、通知、KVO、KVC、属性关键字
分类(Category)
你用分类做了哪些事情:
特点:
分类中都可以添加哪些内容
分类的调用栈
只分析类当中实例方法添加的逻辑 默认ismeta = NO
1. 列表中原有元素总数
1. 拼接之后的总数
1. 重新分配内存
1. 重新设置元素总数
1. 内存移动memmove[[],[],[][原1],[原2]]
1. 覆盖宿主类方法memcopy -> 分类方法会覆盖宿主类方法的原因。查找到first会提前返回。
关联对象
能否给分类添加“成员变量”?可以用关联对象技术来添加
id objc_getAssociatedObejct(id obejct, const void *key)
id obcj_setAssociatedObject(id object, const void *key, id value, objc_associationPolicy policy)
void objc_removeAssociatedObjectes(id object);
关联对象的本质
关联对象由AssociationsManager管理在AssociationsHashMap存储,对象为ObjcAssociation
所有对象的关联内容都在同一个全局容器中。
所以关联对象,并不是添加到对应的对象上面。
扩展
一般扩展用来做什么
特点:
代理 (Delegate)
实现方式
可以定义的内容
一般声明为weak以规避循环引用。
通知
如何实现通知机制?
使用DCEventDataController解释
KVO
[self willChangeValueForKey:@"value"],[self didChangeValueForKey:@"value"]来手动触发KVO
总结:成功触发KVO的三种方式
KVC
KVC是Key-value coding的缩写
-(id)valueForKey:(NSString*)key
- (void)setValue:(id)value forKey:(NSString*)key
会破坏面向对象的编程思想。
valueForKey
相似名称的成员变量
SetValueForkey
属性关键字
###
assign
weak
类型
区别1
区别2
相同
weak
修饰对象
释放之后指向nil
不改变对象的引用计数
Assign
修饰对象和基本数据类型
释放之后指向原地址
不改变对象的引用计数
copy
@property(copy)NSMutableArray* array;会有什么问题?
源对象类型
拷贝方式
目标对象类型
拷贝类型
mutable对象
copy
不可变
深拷贝
mutable对象
mutableCopy
可变
深拷贝
immutable对象
copy
不可变
浅拷贝
immutable对象
mutableCopy
可变
深拷贝
会导致不可见的异常问题。
但是声明的是mutablearray,所以不可避免的会执行add delete等操作。
题
MRC下如何重写retain修饰变量的setter方法?
简述分类实现原理?
由运行时决议的,不同分类中含有同名方法,谁最终生效,取决于谁最后编译,最后参与编译的那个同名函数会最后生效,假如添加的方法,含有同名的宿主方法,那么会覆盖掉宿主方法,宿主方法不是被舍弃,而是被移动到该变量内存的最后面。
KVO的实现原理是怎样的?
是系统对于观察者模式的一种实现,运用了isa混写技术,来动态运行时去未某个类添加一个子类,重写他的setter方法,把原有的isa类的指针指向新创建的类上面。
能否为分类添加成员变量?
通过关联对象技术来给分类添加成员变量。
Runtime
数据结构、类对象与元类对象、消息传递机制、方法缓存、消息转发、Method-Swizzling、动态添加方法、动态方法解析。
数据结构
objc_object、objc_class、isa指针、method_t
objc_object
实际上id 等于 objc_object
objc_class
实际上Class就是objc_class继承自objc_object
isa指针
共用体isa_t
关于对象,其指向类对象。
关于类对象,其指向元类对象
cache_t
class_data_bits_t
Class_rw_t
class_ro_t
struct method_t
Type Encodings
整体结构
对象、类对象、元类对象
消息传递
void objc_msgSend(void /* id self, SEL op*/)
[self class]转化成 objc_msgSend(self, @selector(class)),这个步骤发生在编译器期间
void objc_msgSendSuepr(void /* structure objc_super *super, SEL op*/)
[super class] 转化成 objc_msgSendSuper(super, @selector(class))
缓存查找
例:给定值是SEL,目标值是对应bucket_t中的IMP
cache_key_t --f(ey)--> butter_t
f(key) = key & mask
在当前类中查找
父类逐级查找
resolveInstanceMethod:
Method-Swizzling
交换函数
动态添加函数
动态方法解析
@dynamic
Q:[obj foo]和objc_msgSend()函数有什么关系?
A: 发送消息,会在编译器处理之后,变成了objc_msgSend(obj, foo)了,然后开始了消息传递的过程了。
Q:runtime如何通过Selector找到对应IMP地址的?
查找当前类实例所对应的类对象的缓存,是否有selector对应的imp实现,如果缓存命中,那么就把命中的缓存函数,返回给调用方,如果没有命中,那么在根据当前类的方法列表,去查找selector对应的具体的imp实现,当前类如果没有命中的话,再根据当前类的superclass指针逐级查找父类的方法列表,然后查找所对应的imp实现。
Q:能否想编译后的类中添加实例变量?
不能添加的。但是可以在动态添加的类的当中增加实例变量。
内存管理
内存布局、内存管理方案、数据结构、ARC & MRC、引用计数、弱引用、自动释放池、循环引用。
内存布局
stack:方法调用
heap:通过alloc分配的对象
bss:未初始化的全局变量等。
data:已初始化的全局变量等。
text:程序代码段。
内存管理方案
objc-runtime-680
NONPOINTER_ISA
arm64架构下:
SideTables()
因为多线程对表进行操作,需要加锁来处理🤔,存在效率问题。所以引入了分离锁
怎么实现快速分流?
SideTables的本质是一张hash表
例:给定值时对象内存地址,目标是数组下标索引。
Spinlock_t 自旋锁
适用于哪些场景呢?《多线程》
RefcountMap 引用计数表
size_t
weak_table_t 弱引用表
MRC 手动引用计数
ARC自动引用计数
引用计数管理
alloc实现
经过一系列调用,最终调用了C函数Calloc,此时并没有设置引用计数为1
retain实现
release 实现
retainCount实现
dealloc实现
弱引用管理
自动释放池
循环引用
自循环引用
互相循环引用
多循环引用
如何破除循环引用
具体的解决方案
__weak
__block
__unsafe_unretained
NSTimer的循环引用问题
WeakBox!
Q:什么是ARC?
A:ARC是LLVM和Runtime协作的来为我们实现自动引用计数的管理
Q:为什么weak指针指向的对象在废弃之后会被置为nil
A:当对象被废弃之后,dealloc内部方法的实现当中,会调用清楚弱引用的方法,在清楚弱引用的方法当中,会通过hash算法来查找被废弃对象在弱引用表当中的位置,来提取他所对应的弱引用指针的列表数组,然后进行for循环遍历,把每一个weak指针都置为nil。
Q:苹果是如何实现AutoreleasePool的?
A:是以栈为结点,由双向链表形式合成的数据结构
Q:什么是循环引用?你遇到过哪些循环引用,是怎样解决的?
A:NSTimer问题解析。
Block
Block、截获变量、__block修饰符、block的内存管理
什么是block调用
((int (*)(__block_impl *, int))((__block_impl *)Block)->FuncPtr)((__block_impl*)Block, 2);
截获变量
__block 修饰符
对变量进行赋值时,不需要__block修饰符的情况:静态局部变量、全局变量、静态全局变量。
Q:什么是Block?
Q:为什么block会产生循环引用