1.内存布局
截图1.png- stack: 方法调用
- heap: 通过alloc等分配的对象
- bss: 未初始化的全局变量
- data: 已初始化的全局变量
- text:程序代码
2.内存管理方案
- TaggedPointer(小对象,如NSNumber)
- NONPOINTER_ISA(64位架构下,iOS应用程序,)
- 散列表
(1) NONPOINTER_ISA
arm64架构
截图2.png
(2)散列表
SideTables()结构
截图4.png
截图5.png
思考:为什么不是一个SideTable?
截图6.png
为了解决效率问题引入分离锁
截图7.png
思考:怎样实现快速分流?
SideTables的本质是一张Hash表
截图8.png
Hash查找
截图9.png
3.数据结构
(1)自旋锁(Spinlock_t)
Spinlock_t
是"忙等"的锁,适用于轻量访问。
(2)RefcountMap
截图10.pngsize_t
截图11.png
(3) 弱引用表
截图12.png4.MRC&ARC相关
(1)MRC
手动引用计数
截图13.png
(2) ARC
自动引用计数
- ARC是
LLVM
和Runtime
协作的结果, - ARC中禁止调用`retain/release/retainCount/dealloc
- ARC中新增
weak
、strong
属性关键字
5.引用计数管理
(1)实现原理分析
- alloc
- retain
- release
- retainCount
- dealloc
allloc实现
经过一系列调用,最终调用了C函数calloc,此时并没有设置引用计数为1。
retain实现
截图14.png
release实现
截图15.png
retainCount实现
截图16.png
dealloc实现
截图17.png
object_dispose()实现
截图18.png
objc_destructInstance()实现
截图19.png
clearDeallocating()实现
截图20.png
6.弱引用管理
截图21.png截图22.png
7.自动释放池
- 是以栈为节点通过双向链表的形式组合而成。
- 是和线程一一对应的
编译器会将@autoreleasepool{}
改写为:
void * cox = objc_autoreleasePoolPush();
{}中代码
objc_autoreleasePoolPop(ctx);
objc_autoreleasePoolPush
objc_autoreleasePoolPop
一次pop实际上相当于一次批量的pop操作
双向列表
栈
截图26.png
AutoreleasePoolPage
截图28.png
AutoreleasePoolPage::push
[obj autorelease]
AutoreleasePoolPage::pop
- 根据传入的哨兵对象找到对应的位置
- 给上次push操作之后添加的对象依次发送release消息
- 回退next指针到正确的位置
8.循环引用
- 自循环引用
- 相互循环引用
- 多循环引用
(1) 自循环引用
截图31.png(2) 相互循环引用
截图32.png(3) 多循环引用
截图33.png(4) 考点
- 代理
- Block
- NSTimer
- 大环引用
思考:如何破除循环引用?
(a)避免产生循环引用
(b)在合适的时机手动断环
具体的解决方案有哪些?
__weak
__block
__unsafe_unretained
__weak破解
__block破解
注意:
-
MRC
下,__block修饰对象不会增加其引用计数,避免了循环引用 -
ARC
下,__block修饰对象会被强引用,无法避免
循环引用,需要手动解环
。
__unsafe_unretained破解
- 修饰对象不会增加其引用计数,
避免
了循环引用 - 如果被修饰对象在某一时机被释放,会产生悬垂指针!
(5)循环引用示例
- NSTimer的使用示例
截图35.png
36.png
解决方案
37.png
在NSTimer和对象之间添加一个中间对象,NSTimer对中间对象实行一个强引用,同时中间对象分别对NSTimer 和对象实行一个弱引用,对于一个重复的定时器而言,当当前页面退出之后,页面释放了对广告栏的强引用,当下次定时器的回调时间回来之后,可以判断广告栏是否被释放掉,如果被释放掉了,在定时器的回调中将定时器进行valite和置为nil
#import <Foundation/Foundation.h>
@interface NSTimer (WeakTimer)
+ (NSTimer *)scheduledWeakTimerWithTimeInterval:(NSTimeInterval)interval
target:(id)aTarget
selector:(SEL)aSelector
userInfo:(id)userInfo
repeats:(BOOL)yesOrNo;
@end
#import "NSTimer+WeakTimer.h"
@interface TimerWeakObject:NSObject
@property (nonatomic,weak)id target;
@property (nonatomic,assign)SEL selector;
@property (nonatomic,weak)NSTimer *timer;
- (void)fire:(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)yesOrNo{
TimerWeakObject *object = [[TimerWeakObject alloc] init];
object.target = aTarget;
object.selector = aSelector;
object.timer = [NSTimer scheduledWeakTimerWithTimeInterval:interval
target:aTarget
selector:aSelector
userInfo:userInfo
repeats:yesOrNo];
return object.timer;
}
@end
9.问题
(1) 什么是ARC ?
arc是由llvm编译器和runtime共同协作来为我们实现自动引用计数进行管理
(2) 为什么weak指针指向的对象在废弃之后会被自动置为nil?
当对象被废弃之后,dealloc方法内部实现中会调用清除弱引用的方法,在清除弱引用的方法中,会通过哈希算法查找被废弃对象在弱引用表中的位置,提取它所对应的弱引用指针一个列表数组,进行for循环遍历把每一个弱引用指针置为nil
(3) 苹果是如何实现AutoreleasePool的?
AutoreleasePool是以栈为节点,由双向链表的形式,来合成的数据结构。
网友评论