1.介绍一下ARC下是如何实现的自动的释放内存? 可以从ARC、MRC 他们的区别的说
ARC相对于MRC,不是在编译时添加retain/release/autorelease这么简单。应该是编译期和运行期两部分共同帮助开发者管理内存。
在运行期,做的优化比较复杂,但也不能被忽略,
——————————————————————
——————————————————————
ARC 是 iOS 中管理引用计数的技术,帮助 iOS 实现垃圾自动回收,具体实现的原理是由编译器进行管理的,同时运行时库协助编译器辅助完成。主要涉及到 Clang (LLVM 编译器) 和 objc4 运行时库。
由修饰符 __strong 、 __weak 、 __autorelease 拓展开,分别延伸出引用计数、弱引用表、自动释放池等实现原理。
对象的引用关系由引用修饰符来决定,如__strong、__weak、__autorelease等等,编译器会根据不同的修饰符生成不同逻辑的代码来管理内存。
union isa_t
{
Class cls;
uintptr_t bits;
struct {
uintptr_t nonpointer : 1;//->表示使用优化的isa指针
uintptr_t has_assoc : 1;//->是否包含关联对象
uintptr_t has_cxx_dtor : 1;//->是否设置了析构函数,如果没有,释放对象更快
uintptr_t shiftcls : 33; // MACH_VM_MAX_ADDRESS 0x1000000000 ->类的指针
uintptr_t magic : 6;//->固定值,用于判断是否完成初始化
uintptr_t weakly_referenced : 1;//->对象是否被弱引用
uintptr_t deallocating : 1;//->对象是否正在销毁
uintptr_t has_sidetable_rc : 1;//1->在extra_rc存储引用计数将要溢出的时候,
借助Sidetable(散列表)存储引用计数,
has_sidetable_rc设置成1
uintptr_t extra_rc : 19; //->存储引用计数
};
};
objc_object就是 isa 基础上一层封装。
struct objc_class : objc_object {
isa_t isa; // 继承自 objc_object
Class superclass;
cache_t cache; // 方法实现缓存和 vtable
class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags
};
复
objc_class继承了objc_object,结构如下:
isa:objc_object 指向类,objc_class 指向元类。superclass:指向父类。cache:存储用户消息转发优化的方法缓存和 vtable 。bits:class_rw_t 和 class_ro_t ,保存了方法、协议、属性等列表和一些标志位。
__strong 修饰符
MRC 时代 Retain 修饰符将会使被引用的对象引用计数 + 1 ,在 ARC 中 __strong 修饰符作为其替代者,
具体原理 待讲
__weak 修饰符
weak 表示弱引用,引用计数不会增加。在原对象释放后,弱引用变量也会随之被清除,
具体原理 待讲
__autorelease 修饰符
在 ARC 环境下, __autorelease 修饰符可以将对象加入自动释放池中,由自动释放池管理释放。
自动释放池都是由一个或者多个AutoreleasePoolPage组成,page的 SIZE 为 4096 bytes ,它们通过parent和child指针组成一个双向链表。
hotPage:是当前正在使用的page,操作都是在hotPage上完成,一般处于链表末端或者倒数第二个位置。存储在 TLS 中,可以理解为一个每个线程共享一个自动释放池链表。
-
coldPage:位于链表头部的page,可能同时为hotPage。 -
POOL_**BOUNDARY**:nil的宏定义,替代之前的哨兵对象POOL_SENTINEL,在自动释放池创建时,在objc_autoreleasePoolPush中将其推入自动释放池中。在调用objc_autoreleasePoolPop时,会将池中对象按顺序释放,直至遇到最近一个POOL_BOUNDARY时停止。 -
EMPTY_POOL_**PLACEHOLDER**:当自动释放池中没有推入过任何对象时,这个时候推入一个POOL_BOUNDARY,会先将EMPTY_POOL_PLACEHOLDER存储在 TLS 中作为标识符,并且此次并不推入POOL_BOUNDARY。等再次有对象被推入自动释放池时,检查在 TLS 中取出该标识符,这个时候再推入POOL_BOUNDARY。 -
next:指向AutoreleasePoolPage指向栈顶空位的指针,每次加入新的元素都会往上移动。
2. 通过运行时的角度解释一下category。
主要是从运行时实现category添加属性的角度进行讲解。
3. 什么是KVC
KVC 键值编码,是一种间接访问实例变量和属性的方法
1. 给私有变量赋值
2. 给控件内部属性赋值(placeholderlabel.textcolor)
3. 结合runtime model和字典 转换 setvalueforkeysWithDictionary
KVC运用了一个isa-swizzling技术. isa-swizzling就是类型混合指针机制, 将2个对象的isa指针互相调换, 就是俗称的黑魔法.。实现其内部查找定位的. 默认的实现方法由NSOject提供
4. 什么是KVO
KVO是一种基于KVC实现的观察者模式
指定的被观察的对象的属性更改了,KVO会以自动或手动方式通知观察者。
5. KVC和kvo什么关系
当使用KVO观察某个类属性时,会为该类创建一个子类,子类重写setter方法时,跟KVC set时的搜索顺序是一样的,都是先搜索set,然后在搜_set。对于KVC,若不存在不会会有后续操作, 在为observe的change字典里的old和new赋值时,用到了KVC的valueForKey:
6. object-c里面怎么实现kvo的?
kvo如何实现 略
7. 比较一下GCD和NSOperation
NSOperation封装了线程的细节实现,不需要自己管理线程的生命周期和线程的同步和互斥等
GCD 苹果多核编程的解决方法
(GCD,首先要了解下面几个概念
1.1 Dispatch Queue 顾名思义,是一个用于维护任务的队列
1.2 Dispatch Source 允许我们把任务注册到系统事件上
1.3 Dispatch Groups 可以让我们把一系列任务加到一个组里,组中的每一个任务都要等 待整个组的所有任务都结束之后才结束
1.4 Dispatch Semaphores:信号量,可以让我们实现更加复杂的并发控制,
防止资源竞争
)
1. NSOperationQueue 是基于 GCD 的更高层的封装
2. 从易用性角度,GCD 由于采用 C 风格的 API,在调用上比使用面向对象风格的 NSOperation 要简单一些。
3. 从对任务的控制性来说,NSOperation 显著得好于 GCD
4. 从第三方库的角度,知名的第三方库如 AFNetworking 和 SDWebImage 背后都是使用 NSOperation
5. 对于需要复杂并发控制的需求,NSOperation 是更好的选择 不是绝对
8. App Extension 和 宿主 是如何共享数据的
他们分属不同的Targets,
1. 本地数据的共享NSUserDefaults
Apple提供了一个GroupID,每一个APP可以由于一个唯一的GroupID,通过这个GroupID可以去获取数据 [[NSUserDefaults alloc] initWithSuiteName:@"GroupID"];
2.通过keyChain
NSUserDefault是把数据存储在Libarary/Preferences目录下,本质上就是一个plist文件。所以有些敏感的数据我们最好不要用这种方式来共享。KeyChain更加安全,我们可以共享一些敏感的数据比如说userid,token等。
使用KeyChain的时候需要注意要打开KeychainSharing,并且添加相应的Bundle Id
link_with '宿主App的名字', '拓展App的名字'
方式2< Extension当中引用APP的一个或其中几个库时,可以分开target
target 'App的名字' do
pod ...
endtarget '拓展App的名字' do
pod ...
end
4. Assets.xcassets 通过勾选想要共享的target 就行
5. 类文件
方式1> 如果你需要共享的文件不是太多,或者你就是想简单粗暴一点,那就直接把相关的类文件拷贝一份到Extension当中就可以了。
方式2> Apple推荐我们通过提取一个公共的framework的方式来优雅的实现数据共享。