02 内存释放/KVC/O/GCD/宿主

338 阅读7分钟

1.介绍一下ARC下是如何实现的自动的释放内存? 可以从ARC、MRC 他们的区别的说

ARC相对于MRC,不是在编译时添加retain/release/autorelease这么简单。应该是编译期和运行期两部分共同帮助开发者管理内存。

**在编译期**,ARC用的是更底层的C接口实现的retain/release/autorelease,这样做性能更好,也是为什么不能在ARC环境下手动retain/release/autorelease,同时对同一上下文的同一对象的成对retain/release操作进行优化(即忽略掉不必要的操作);

在运行期,做的优化比较复杂,但也不能被忽略,

——————————————————————
——————————————————————

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 ,它们通过parentchild指针组成一个双向链表。

  • 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更加安全,我们可以共享一些敏感的数据比如说useridtoken等。

使用KeyChain的时候需要注意要打开KeychainSharing,并且添加相应的Bundle Id

3\. pod文件共享 `CocoaPods`来管理第三方库,那么在`Extension`里面可以import 但是是无法直接使用这些文件的。 方式1< 你可以在项目的`podfile`文件里面加入这句话:建立了pod文件的连接
  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的方式来优雅的实现数据共享。