推荐系统直接学习小码哥iOS底层原理班---MJ老师的课确实不错,强推一波。ios
CADisplayLink(
保证调用频率和屏幕的刷帧频率一致,60FPS(60次/s)
)、NSTimer会对target产生强引用,若是target又对它们产生强引用,那么就会引起循环引用c++
以下代码是释放不掉的bash
- (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__);
}
复制代码
block是捕获变量,timer是传递参数。ide
不过若是是NSTimer的block版本用__weak是能够的函数
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:[MJProxy proxyWithTarget:self] selector:@selector(timerTest) userInfo:nil repeats:YES];
复制代码
- (id)forwardingTargetForSelector:(SEL)aSelector
{
return self.target;
}
复制代码
好比removeSuperView时之类吧oop
NSProxy专门用来作消息转发布局
NSProxy在本类没有该方法的状况下会直接进入消息转发(methodSignatureForSelector:) 与 (forwardInvocation:
),并不会去查找父类,动态方法解析等等。学习
原生方法若是未主动实现,内部直接进入消息转发。好比class,isKindOfClass等等优化
GCD的定时器会更加准时ui
NSTimer依赖于RunLoop,若是RunLoop的任务过于繁重,可能会致使NSTimer不许时
而GCD定时器依赖于系统内核,并不依赖Runloop
从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);
复制代码
使用return关键字只会管理setget方法中的内存,dealloc中仍然须要本身释放。
一个全局table
![]()
refcnts是一个存放着对象引用计数的散列表 weak_table存放着若引用的指针与对象
当一个对象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 {
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); //释放释放池
复制代码
每一个AutoreleasePoolPage对象占用4096字节内存,除了用来存放它内部的成员变量,剩下的空间用来存放autorelease对象的地址
在调用objc_autoreleasePoolPush()
时,插入POLL_BOUNDARY
并返回地址0x1038
每一个调用autorelease
的对象都会被插入到AutoreleasePoolPage中
在调用objc_autoreleasePoolPop(0x1038)
时,从当前位置到0x1038
全部的对象都会被执行release
操做。
能够经过如下私有函数来查看自动释放池的状况
extern void _objc_autoreleasePoolPrint(void);
复制代码
hotPage
的活跃AutoreleasePoolPage
被系统持有page
之间经过双向链表连接push/autorelease
操做时当前page已满,将会建立一个page
或跳转到下一个page
。iOS在主线程的Runloop中注册了2个Observer,监听了三个状态。并适时操做AutoreleasePool
第1个Observer监听了kCFRunLoopEntry事件
在进入runloop时,会调用objc_autoreleasePoolPush()
第2个Observer监听了kCFRunLoopBeforeExit事件
在退出runloop时,会调用objc_autoreleasePoolPop()
第2个Observer还监听了kCFRunLoopBeforeWaiting事件
在当前循环结束,准备休眠时时,会调用objc_autoreleasePoolPop()随后再调用一次objc_autoreleasePoolPush()
借用群里一位大佬的解释
通常除了init其余基本上都是autorelease的,包括C函数返回对象
也就是说init方法放回的对象,默认是会被retain/release
,而其余的对象默认会autorelease
。
很显然的,两者的释放时机不一样,因此才会有以下状况发生。