iOS底层原理11: 定时器, Weak, Autoreleasepool
Timer定时器循环引证问题
咱们都知道, 在咱们运用NSTimer时, addtarget假定是timer的持有者时, 就会构成循环引证线程是什么意思. 由于timer会对当时的target强引证. 那么咱们怎样处理呢?
-
手动办理timer, 在运用结束后自己关闭ti链表逆序mer并置为nil. 这个办法只适用于非循环调用的timer变量泵
-
假定是在iOS10往后, 咱们能够直接运ios15正式版本什么时候发布用ios14.7正式版timer的block结构办法, 将作业放入block中处理, 这样timer就不会强引证target
let timer = Timer(f变量ire: Date(), inter变量min表明什么类型的变量val: 1, repeats: true, block: {ios8备忘录 timer in
})
- 运用其他政策, 替换target.
- 运用NSObject类作为target
咱们自己界说一个链表NSObject类作为timer的target时, 能够运用变量类型有哪些iOios是什么意思S音讯发送/转发的机制, 将原有的类作为NSObject的一个特色, 经过音讯转发进行作业处理
@interface My指针式万用表Target : NSObject
+ (instancetype)proxyWithTarget:(id)target;链表
@property (weak, nonatomic) id target;
@end
@imp链表的创立lemen变量与函数tati指针式万用表的使用办法图解on MyTarget
+ (instanc指针式万用表的使用办法图解etype)proxyWi指针数组thTarget:(id)tar链表c言语get {
MyTarget *proxy = [[MyTargios最好玩的手游et alloc] init];
proxy.target = target;
return proxy;
}
// 假定当时类没有对应的办法指针数组和数组指针的差异结束时, 会经过音讯转发流程第二步, 回来一个实例去呼应ios是什么意思作业
// 我指针的拼音们将原ios8备忘录有会循环引证的实例回来, 就能够处理timer的循环引证
- (id)forwardingTargetForSelector:(SEL)aSelector {
retur链表初始化n self.tar线程的几种状况get;
}
- 除了NSObject, iOS还有一个基类NSProx线程和进程的差异是什么y, 能够指针数学专门作为”署理”来处理相似景象
@interface MyProxy : NSProxy
+ (inst变量名的命名规矩ancetype)proxyWithTios最好玩的手游arget:(id)t链表逆序arget;
@property (weak, nonatomic) id target;
@end
@implemios是什么意思entation MyProxy
+ (instancetype)proxyWithTarget:(id)target
{
// NSProxy政策不需要调用init,由于它本来就没有init办法
MyProxy *proxy = [MyProxy alloc];变量类型有哪些
proxy.target = target;
return proxy;
}
- (NSMethodSignature *)methodSignatureForSios下载elector链表:(SEL)sel
{
return [self.target methodSignatureForSelector:sel];
}
- (void)forwardInv变量名ocation:(NSInvocation *)invocation
{
[invocation invokeWithTarget:self.target];
}
@end
与NSObject变量是什么意思比较, NSProxy更适合做timer的target. 由于关于没有结束的办法, NSObject会经过音讯转发的流程进行3次转发, 终究抵达
forwardingTargetForSelector:
,变量名 而NSProxy则会直接调用forwar指针数学dInvocation:
, 并不会进行音讯转发流程, 功能要比NSObject高一点
比NSTimer/CADisplayLink更精准的定时器
- NSTimer/指针式万用表怎样读数CADisplayLink
咱们知道, Ti变量是什么意思mer/CADisplayLink定时器是将timer参加到当时runloop中去实施, 当runloop在每次循环中实施timer, 假定当时runloop产生卡顿的话, 那么必然会构成timer不精确.
- GCD-Timer变量
咱们能够经过GCD创立timer, GCD创立的timer与runloop无关, 所以会愈加精准
//变量英文 创立定时器
dispatch_source_t timer = dispatch线程池面试题_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
// 设置时链表查询刻
dispatch_source_set_timer(timer,
dispatch_time(DISPATCH_TIME_NOW, start * NSEC_PER_SEC),
interval * NSEC_线程池创立的四种PER_SEC, 0);
// 设置定时器使命
dispatch_source_set_event_handler(timer, ^{
});
// 发动定时器
dispatch_resume(timer)变量之间的联系;
// 关闭定时器
dispatch_source_cancel(timer);
iOS中的weak是假定结束的
iOS中, weak润饰的政策在被开释时, weak变量会被置为nil, 此刻对weak变量发送音讯则不会产生崩溃. 那么rios最好玩的手游untime是怎样将weak变量置ios体系为nil呢? 咱们经过政策的dealloc函数来看一下详细结束逻辑
- dealloc变量与函数函数
政策的deall变量之间的联系oc会调用_objc_rootDealloc()函数以及rootDealloc()函数
- (void)deal指针式万用表图片loc {
_objc_rootDealloc(self);
}
void _objc_rootDealloc(id obj)指针式万用表怎样读数 {
ASSERT(obj);
obj->rootDealloc();
}
- rootDealloc函数
- 首要判别是否为taggedPointer, 假定是则直变量英文接回来;
- 假定不是, 则判别是否是nonpoin线程和进程的差异是什么ter, 是否有weak引证表, 是否有相关政策, 是否有c++析构函数等, 假定都没有, 则直接free, 不然会调用objectios最好玩的手游_dispose()
inline void objc_指针式万用表图片object::rootDealloc()
{
if (isTaggedPointer()) return; // fixme necessary?
if (fa指针式万用表怎样读数stpath(isa.nonpointer &&
!isa.weakly_referenced &&
!isa.has_assoc &&
#if ISA_HAS_链表排序CXX_DTOR_BIT
!isa.has_cxx_dtor &变量的界说;&
#else
!isa.getClass(false链表查询)->hasCxxDto链表初始化r() &&
#e变量的界说ndif
!isa.has_sidetable_rc))指针
{
assert(!sidetable_present());
free(this);
}
else {
object_disp变量min表明什么类型的变量ose((id)this);
}
}
- object_dispose函数
object_dispose函数会调用objc_destructInstance()函数进行处理.
- 判别是否有c++的析构函数, 假定有, 则进行成员变量的开释
- 判别是否有相关政策, 假定有, 进行相关政策的移除
- 调用clearDeallocating()函数进行weak指针指针的操作
id object_dispose(id obj) {
if (!obj) return nil;
objc_destructInstance(obj);
free(obj);
return nil;
}
void *objc_destruc变量名tInstance(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.
i线程是什么意思f (cxx) object_cxxDestruct(指针式万用表怎样读数obj);
if (assoc) _object_remove_assocations(obj, /*deios下载allocating*/true);
obj->clearDeallocating();
}
return obj;
}
- clearDeallocat链表查询ing()函数
- clearDeallocating()函数中判别是否是优化过的isa指针, 实施clearDeios体系allocating_slo变量名的命名规矩w()函数
- clearDeallocating_slowios8备忘录()函数中, 会依据当时地址得到散列表table, 调用weak_clear_no_lock(), 传入参数table.weak_table和this指针
inline void objc_object::clearDeallocating() {
if (slowpath(!isa.nonpointer)) {
// Slow pa链表数据结构th for raw pointer i指针c言语sa.
sidetable_clearDeallocati线程池面试题ng();
}变量名
else if (slowpath(isa.weakly_referenced || isa.h指针c言语as_sidetable指针式万用表_rc)) {
// Slow path for non-pointer isa with weak refs and/or side table data.
clearD线程数eallocating_slow();
}
asser链表t(!sidetable_present());
}
NEVER变量之间的联系_INLINE void objc_o变量类型有哪些bject::clearDeallocating_slow() {
ASSERT(isaios模拟器.nonpointer && (isa.weakly_ref变量名erenced || isa.has_sidetable_rc));
SideTable& table = SideTables()[this];
table.lock();
if (isa.weakly_referenced) {
weak_clear_no_lock(&table.线程撕裂者weak_table, (id链表逆序)this);
}
if (isa.has_sidetable_rc) {
tab线程池面试题le.refcnt链表查询s.erase(this);
}
table.unlock();
}
- weak_clear_no_lock()
- weak_entry_for_referent()函数经过weak_table和当时指针找到对应的entry
- 遍历entry->referrers, 将一切指向当时政策的weak变量的指针置为nil
- 将entry线程数从weak_table中移除
void weak_clear_no_lock(weak_tabl链表的特色e_t *weak_tabl变量e, id referent_id) {
objc_object *refe指针数组和数组指针的差异re链表逆序nt = (objc_objec指针的拼音t *)referent_id;
weak_entry线程_t *entry = weak_entry_for_referent(weak_table, referent);
if (en指针数学try == nil) {
return;
}
weak指针数学_referrer_t *referrers;
size_t count;
if (entry->out_of_line()) {
referios是什么意思r指针式万用表ers = entry->referrers;
count = TABLE_SIZE(entry);
}
else {
referrers = entry->inline_referrers;
coun链表t = WEAK_INLINE_COUNT;
}
for (size_t i = 0; i < count; ++i) {
objc_object **refer线程数rer = referrers[i];
if (referrer) {
if (*referrer == referent) {
*referrer = nil;
}
else if (*referrer) {
_objc_in变量的界说foriOSm("__weak vios最好玩的手游ariable at %p holds %p instead of %p. "
"This is probably incorrect use of "
"objc_storeWeak() and变量之间的联系 objc_loadWeak(). "
"Break on objc_weak_error to debug.n",
ref指针数组errer, (void*)*referrer, (void*)referent);
objc_weak_e链表逆序rror();
}
}
}
we线程是什么意思ak_entry_remove(weak_table, entry);
}
AutoReleasePoo线程数l
在iOS开发过程中, 假定将政策放到AutoReleasePool中, 那么政策线程池面试题就会在pool结束时自动开释, 那么pool是怎样做到自动开释呢? 在pool结束时开释更详细的是什么机遇呢?
- 编译后的AutoReleasePool
咱们将一个AutoReleaseP指针式万用表ool运用clang指令编译, 查看编译后的代码
- AutoReleasePool变量之间的联系会被编变量类型有哪些译到一个{}的代码块中, 作为一个局部变量, 声明为__AtAutoreleasePool类型, 在代变量min表明什么类型的变量码块结束时, 会毁掉这个局ios下载部变量, 实施析构函数
- __AtAutorel链表初始化easePool对应一个结构体, 其间在结构指针式万用表怎样读数函数中, 实施了objc_autoreleasePoolPush(), 析构函数中实施了objc_autoreleasePoolPop()函数
{ __AtAuto变量名releasePool __autoreleasepoo线程池面试题l;
int a = 10;
}
struct链表排序 __AtAutoreleasePool {
__AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}
~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);}
void * atautoreleasepoo指针式万用表怎样读数lobj;
};
- Push/Pop的源码指针数学
Push/Po变量p调用的是AutoreleaseP链表查询oolPage的对应函数
void * objc_autorelea链表查询sePoolPush(v线程撕裂者oid) {
return AutoreleasePoolPage::push();
}
void objc_autoreleaseP链表初始化oolPop(void *ctxt) {
AutoreleasePoolPage::pop(ctxt);
}
- AutoreleasePoo链表和数组的差异lPage
- AutoreleasePoolPage承继自AutoreleasePoolPageData
- 其间Autorele线程池的七个参数asePoolPageData是经过parent/child组成的双向链表结构
- 每个Page除了保存自己的相关ios体系特色以外, 会将ios15正式版本什么时候发布参加Pool的政策地址保存到自己的内存中. 当一个page存满时, 会开辟新的page, 经过parent/child相关, 以便后续操作
- 每个page还有一个POOL_BOUNios8备忘录DARY(岗兵)记载不同Pool的边界地址
- next指针指向当时Pool指针式万用表怎样读数下一个能够存储政策的地址, 这样每次增加时,可线程和进程的差异是什么以经过next快速找到存储方位
class Autorel链表查询easePoolPage : private AutoreleasePoolPageData {}
struct指针数组和数组指针的差异 AutoreleasePoolPageData {
magic_t const magic;
__unsafe_unretaine指针式万用表的使用办法图解d id *next;
pthread_t const thread;
Autorel指针式万用表图片easePoolP变量与函数age * const pa变量之间的联系rent;
Autorelea变量类型有哪些sePoolPage *child;
uint32_t const depth;
uint32_t hiwat;
};
- AutoreleasePoolPage的push函数
- push(): 先判别是否需要新建一个page, 来分别进行处理
- autoreleaseNewPage(): 拿指针的拼音到当时的hotPage, 假定存在, 进入autoreleaseFullPage(), 假定不存在, 进入autoreleaseNoPage()
- autoreleaseFullPage(): 则经过page->child一向找到一个未满的page, 假定全都满了则从头创立一个page, 从头设置hotPage, 而且参线程是什么意思与page
- autoreleaseNoPage(): 经过一些列判别前置操作, 从头创立一页page, 将新page设置为链表排序hotPage, 而且将POOL_BOUNDARY(岗兵)参加新page
- autoreleaseFast(): 假定当时hotPage没满, 直接参加当时page, 假定满了就进链表入autoreleaseFullPage(), 假定其ios体系时hotPage为n变量名的命名规矩il, 则进入指针数组autoreleaseNoPage().
- 经过一系列的处理, 总算将一个政策参加了AutoReleas线程数ePool中
static inline void链表 *push() {
id *des指针c言语t;
if (slowpath(DebugPoolAllocation)) {
// Each autore指针数学lease pool线程撕裂者 starts oios15n a new pool pa线程是什么意思ge.
dest = autoreleaseNewPage(POOL_BOUNDARY);
} else {
dest = autoreleaseFast(POOL_BOUNDARY);
}
ASSERT(dest == EMPTY_POOL_ios15PLACEHOLDER || *dest == POOL_BOUNDARY);
return dest;
}
static链表查询 __attribute__((n指针式万用表怎样读数oinline)) id *autorel变量是什么意思easeNewPage(iiOSd obj) {
AutoreleasePoolios下载Page *page = hios8备忘录otPage();
if (page) return autoreleaseFull线程Page(obj, page);
else return autoreleaseNoPage(obj);
}
static __attribute__指针式万用表的使用办法图解((noinline)线程池创立的四种) id *autoreleaseFullPage(id obj, Autorelea线程数sePoolPage *page) {
do {
if (page->child) page = page->child;
else page = new AutoreleasePoolPage(page);
} while (page->full());
setHotPage(page);
return page->add(obj);
}
static __attribute__((noinline)ios是什么意思) id *autoreleaseNoPage(id obj) {
bool pushExtraBoundary = false;
if (havios下载eEmptyPoolPlaceholder()) {
pu链表的创立shExtraBoundary = true;
}
els链表c言语e if (obj != POOL_BOUNDAR指针c言语Y && DebugMissingPools) {
objc_autoreleaseNoPool(obj);
return nil;
}
else if (obj == POOL_BOUNDARY &指针c言语& !DebugPoolAllocation) {
return setEmptyPoolPlaceholder();
}
AutoreleasePoolPage *p线程的几种状况age = new AutoreleaseP变量oolPage(nil);
setHotPage(pag线程安全e);
if (pushExtraBoundary) {
page->add(指针式万用表怎样读数POOL_BOUNDARY);变量与函数
}
return指针式万用表图片 page->add(obj);
}
static inline id *autoreleaseFast(id obj) {
AutoreleasePoolPage *page = hotPage();
if (page && !page->full()) {
return page指针式万用表的使用办法图解->add(obj);
} else if (page) {
retu链表数据结构rn autoreleaseFullPage(obj, page);
} else {
return autoreleaseNoPage(obj);
}
}
- AutoreleasePoolPage的pop函数
- pop(): 获取当时h链表的特色ot链表和数组的差异Page, 以及其变量是什么意思时stop时的地址, 调用popPage()
- popPage(): 调用releaseUnti变量与函数l() 开释政策直到st线程数op标志, 一起查看当时page的child, 假定需要开释就开释
- releaseUntil(): 获取当时的hotPaios15正式版本什么时候发布ge, 假定为nil, 则经过parent指针获取父节点; 拿到hotPage往后, 经过next指针指针式万用表图片拿到最新线程的几种状况的政策, 然后将政策开释, 直到当时政策为POOL_BOUNDARY(岗兵)
static inline void po线程是什么意思p(void *token) {
AutoreleasePoolPage *page;
id *stop;
if (token == (void*)EMPTY_POOL_PLACEHOLDER) {
page = hotPage();
if (!page) {
return setHotPage(nil);
}
page = coldPag线程池的七个参数e();
token = page->begin();
} else {
page =指针 pageForPointer(token);
}
sto链表查询p = (id *)token;
if (*stop != POOL_BOUNDARY) {
if (stop == page->begin() && !page->parent) {
} else {
return badPop(token);
}
}
if (slow链表的特色path(PrintPoolHiwat || DebugPoolAllocation ||指针数学 DebugMi链表的特色ssingPools)) {
return popPag线程的几种状况eDebug(token, page, stop);
}
return popPage<false>(token, page, stop);
}
t线程撕裂者emplate<bool allowDebug> s线程安全tatic void
popPage(void *token, AutoreleasePoolPage *page, id *stop) {
page->releaseUntil(stop);
if (allowDebug && DebugP指针的拼音oolAllocation && page->empty()) {
AutoreleasePoolPage *parent = page->ios8备忘录;parent;
page->kill();
setHotPage(parent);
} e变量英文lse if (allowDebug && DebugMissingPools && page->empty() &amios15正式版本什么时候发布p;& !page->parent) {
page->kill();
setHotPage(nil);
} else if (page->变量min表明什么类型的变量;child) {
if (指针page->lessThanHalfFull()) {
page->child->kill变量与函数();
}
else if (page->child->child) {
page->child->child->kill();
}
}
}
void releaseUntil(id *stop) {链表初始化
while (this->next != stop) {
AutoreleasePoolPage *page = hotPage();
while (page->empty()) {
page = page->指针数组和数组指针的差异parent指针式万用表;
setHotPage线程池创立的四种(page);
}
page->unprotect();
id obj = *--page->next;
memset((void*)page->next, SCRIBBLE, sizeof(*page->next));
page->protect();
if (obj != POOL_BOUNios模拟器DARY) {
ob变量与函数jc_release(obj);
}
}
setHotPage(this);
}
- AutoReleasePool的实在开释机遇
- AutoReleasePool的开释与Runloop是紧密相关的, 当runloop即将进入时, 会进行push()操作; 当runloop即将休眠时, 会先进行一次pop(), 在进行一次push() 保证pu链表c言语sh/pop成对出现; 在runloop即将退出时, 调用 pop()
- 咱们直接打变量名印出主线程的run指针loop, 中心打印如下:
- 第1个ObseriOSver监听链表的创立了kCFRunLoopEn变量与函数try作业,会调用objc_autoreleaseP变量名的命名规矩oolPus指针h()
- 第2个Observer监听了kCFRunLoopBeforeWaiting作业,会调用objc_autoreleasePoolPop()、objc_autoreleasePoolPush(), 监听了kCFRunLoopBeforeExit作业,会调用obj变量是什么意思c_autoreleasePoolPop()指针c言语
CFRunL变量名oop {
current mode =ios14.4.1更新了什么 kCFRunLoopDefaultMode
common modes = {
UITrackingRu链表数据结构nLoopMode
kC指针万用表的使用办法FRunLoopDefaultMode }
common mode items = {
// Ovserver // Entry 进入的时分
CFRunLoopObserver {or链表逆序der = -2147483647, activities = 0x1,
ca变量名llout = _wrapRu变量英文nLoopWit指针式万用表图片hAutoreleasePoolHandler}
// BeforeWaiting | Exit
CFRu变量名的命名规矩nLoopObserver {order = 21474ios下载83647, activities = 0xa0,
callout = _wrapRunLoopWithAutoreleasePoolHandler}
}
}