内容简介:本文属笔记性质,主要针对自己理解不太透彻的地方进行记录。推荐系统直接学习小码哥iOS底层原理班---MJ老师的课确实不错,强推一波。CADisplayLink(
本文属笔记性质,主要针对自己理解不太透彻的地方进行记录。
推荐系统直接学习小码哥iOS底层原理班---MJ老师的课确实不错,强推一波。
别的
CADisplayLink与NSTimer
CADisplayLink( 保证调用频率和屏幕的刷帧频率一致,60FPS(60次/s)
)、NSTimer会对target产生强引用,如果target又对它们产生强引用,那么就会引发循环引用
target导致循环引用
如下代码是释放不掉的
- (void)viewDidLoad { [super viewDidLoad]; // 保证调用频率和屏幕的刷帧频率一致,60FPS self.link = [CADisplayLink displayLinkWithTarget:[MJProxy proxyWithTarget:self] selector:@selector(linkTest)]; [self.link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode]; } - (void)linkTest { NSLog(@"%s", __func__); } 复制代码
__weak为什么解决target的强引用
block是捕获变量,timer是传递参数。
- block在捕获变量时根据变量类型自行进行若引用处理。
- timer作为参数传递时,内部接收到的都是对象的地址值,无法获取引用类型。
不过如果是NSTimer的block版本用__weak是可以的
中间代理
- 用代理隔离self与timer
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:[MJProxy proxyWithTarget:self] selector:@selector(timerTest) userInfo:nil repeats:YES]; 复制代码
- 用消息转发将selector发送回self
- (id)forwardingTargetForSelector:(SEL)aSelector { return self.target; } 复制代码
其他方式释放timer
比如removeSuperView时之类吧
NSObject与NSProxy
NSProxy专门用来做消息转发
- 消息转发速度快
NSProxy在本类没有该方法的情况下会直接进入消息转发( methodSignatureForSelector:) 与 (forwardInvocation:
),并不会去查找父类,动态方法解析等等。
- 大部分方法都能正确转发
原生方法如果未主动实现,内部直接进入消息转发。比如class,isKindOfClass等等
GCD定时器
GCD的定时器会更加准时
NSTimer依赖于RunLoop,如果RunLoop的任务过于繁重,可能会导致NSTimer不准时
而GCD定时器依赖于系统内核,并不依赖Runloop
内存布局
Tagged Pointer
从64bit开始,iOS引入了Tagged Pointer技术,用于优化NSNumber、NSDate、NSString等小对象的存储
-
在没有使用Tagged Pointer之前,NSNumber与正常的OC对象一样:
需要动态分配内存、维护引用计数等,NSNumber指针存储的是堆中NSNumber对象的地址值。
-
使用Tagged Pointer之后,NSNumber指针里面存储的数据变成了:Tag + Data,也就是将数据直接存储在了指针中
-
当指针不够存储数据时,才会使用动态分配内存的方式来存储数据
-
objc_msgSend能识别Tagged
-
Pointer,比如NSNumber的intValue方法,直接从指针提取数据,节省了以前的调用开销
-
如何判断一个指针是否为Tagged Pointer? class
iOS平台,最高有效位是1(第64bit)
Mac平台,最低有效位是1(16进制下为7)
通常来讲,判断最后一位不是0即可
NSLog(@"Person实例的内存地址=%p---指针变量p的内存地址=%p---指针变量p保存的内存地址=%p", p, &p, p); 复制代码
MRC的内存管理
- 在iOS中,使用引用计数来管理OC对象的内存
- 一个新创建的OC对象引用计数默认是1,当引用计数减为0,OC对象就会销毁,释放其占用的内存空间
- 调用retain会让OC对象的引用计数+1,调用release会让OC对象的引用计数-1
内存管理的经验总结
- 当调用alloc、new、copy、mutableCopy方法返回了一个对象,在不需要这个对象时,要调用release或者autorelease来释放它
- 想拥有某个对象,就让它的引用计数+1;不想再拥有某个对象,就让它的引用计数-1
MRC
使用return关键字只会管理setget方法中的内存,dealloc中仍然需要自己释放。
copy和mutableCopy
引用计数
- 在64bit中,引用计数可以直接存储在优化过的isa指针中
- 如果引用计数过大,isa中改为1并且将计数存储到SideTable中
SideTable
一个全局table
refcnts是一个存放着对象引用计数的散列表 weak_table存放着若引用的指针与对象
weak
当一个对象A被若引用指针持有,将会以[&A,weak指针表]的形式添加进SideTable中
当对象A被释放,可以根据&A查找到所有指向他的weak指针并进行释放
- (void)dealloc { _objc_rootDealloc(self); } _objc_rootDealloc(id obj) { assert(obj); obj->rootDealloc(); } objc_object::rootDealloc() { if (isTaggedPointer()) return; // fixme necessary? if (fastpath(isa.nonpointer && //新isa指针 !isa.weakly_referenced && //查看该对象是否被若引用了 !isa.has_assoc && //关联对象 !isa.has_cxx_dtor && //c++析构器 !isa.has_sidetable_rc)) //大额引用计数 { assert(!sidetable_present()); free(this); //直接释放 } else { object_dispose((id)this); } } void *objc_destructInstance(id obj) { if (obj) { // Read all of the flags at once for performance. bool cxx = obj->hasCxxDtor(); bool assoc = obj->hasAssociatedObjects(); // This order is important. if (cxx) object_cxxDestruct(obj); if (assoc) _object_remove_assocations(obj); obj->clearDeallocating(); //将指向当前对象的弱指针置位nil } return obj; } objc_object::clearDeallocating_slow() { assert(isa.nonpointer && (isa.weakly_referenced || isa.has_sidetable_rc)); SideTable& table = SideTables()[this]; //获得全局的SideTable table.lock(); if (isa.weakly_referenced) { //从表中根据对象地址,释放所有指向他的弱引用指针 weak_clear_no_lock(&table.weak_table, (id)this); } if (isa.has_sidetable_rc) { table.refcnts.erase(this); } table.unlock(); } 复制代码
AutoreleasePool
@autoreleasepool { for (int i = 0; i < 1000; i++) { MJPerson *person = [[[MJPerson alloc] init] autorelease]; } } 复制代码
cpp中
{ __AtAutoreleasePool __autoreleasepool; //结构体变量 MJPerson *person = ((MJPerson *(*)(id, SEL))(void *)objc_msgSend)((id)((MJPerson *(*)(id, SEL))(void *)objc_msgSend)((id)((MJPerson *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("MJPerson"), sel_registerName("alloc")), sel_registerName("init")), sel_registerName("autorelease")); } struct __AtAutoreleasePool { __AtAutoreleasePool() { // 构造函数,在创建结构体的时候调用 atautoreleasepoolobj = objc_autoreleasePoolPush(); } ~__AtAutoreleasePool() { // 析构函数,在结构体销毁的时候调用 objc_autoreleasePoolPop(atautoreleasepoolobj); } void * atautoreleasepoolobj; }; 复制代码
所以本质上就等于
atautoreleasepoolobj = objc_autoreleasePoolPush(); //创建释放池 MJPerson *person = [[[MJPerson alloc] init] autorelease]; objc_autoreleasePoolPop(atautoreleasepoolobj); //释放释放池 复制代码
AutoreleasePool的结构
每个AutoreleasePoolPage对象占用4096字节内存,除了用来存放它内部的成员变量,剩下的空间用来存放autorelease对象的地址
push,pop,autorelease
-
在调用
objc_autoreleasePoolPush()
时,插入POLL_BOUNDARY
并返回地址0x1038 -
每个调用
autorelease
的对象都会被插入到AutoreleasePoolPage中 -
在调用
objc_autoreleasePoolPop(0x1038)
时,从当前位置到0x1038
所有的对象都会被执行release
操作。
可以通过以下私有函数来查看自动释放池的情况
extern void _objc_autoreleasePoolPrint(void); 复制代码
AutoreleasePool的维护
- 始终有一个被标记
hotPage
的活跃AutoreleasePoolPage
被系统持有 -
page
之间通过双向链表链接 - 如果
push/autorelease
操作时当前page已满,将会创建一个page
或跳转到下一个page
。
runloop与AutoreleasePool
iOS在主线程的Runloop中注册了2个Observer,监听了三个状态。并适时操作AutoreleasePool
-
第1个Observer监听了kCFRunLoopEntry事件
在进入runloop时,会调用objc_autoreleasePoolPush()
-
第2个Observer监听了kCFRunLoopBeforeExit事件
在退出runloop时,会调用objc_autoreleasePoolPop()
-
第2个Observer还监听了kCFRunLoopBeforeWaiting事件
在当前循环结束,准备休眠时时,会调用objc_autoreleasePoolPop()随后再调用一次objc_autoreleasePoolPush()
autorelease对象
借用群里一位大佬的解释
一般除了init其他基本上都是autorelease的,包括C函数返回对象
也就是说init方法放回的对象,默认是会被 retain/release
,而其他的对象默认会 autorelease
。
很显然的,二者的释放时机不同,所以才会有如下情况发生。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 带你深入理解内存对齐最底层原理
- avue 1.5.2 优化大量底层代码,crud 和 form 底层公用
- Synchronized 关键字使用、底层原理、JDK1.6 之后的底层优化以及 和ReenTrantLock 的对比
- Docker 底层原理浅析
- NSDictionary底层实现原理
- PHP 数组底层实现
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Beginning Google Maps API 3
Gabriel Svennerberg / Apress / 2010-07-27 / $39.99
This book is about the next generation of the Google Maps API. It will provide the reader with the skills and knowledge necessary to incorporate Google Maps v3 on web pages in both desktop and mobile ......一起来看看 《Beginning Google Maps API 3》 这本书的介绍吧!