CADisplayLink、NSTimer使用注意

CADisplayLink:保證調(diào)用頻率和屏幕的刷幀頻率一致,60FPS
NSProxy對象不需要調(diào)用init,因為它本來就沒有init方法,少了一步從父類搜索代碼的流程,直接進入消息轉(zhuǎn)發(fā)
如果進去NSObject的話,需要從父類搜索代碼,然后動態(tài)方法解析
GCD定時器

GCD定時器代碼封裝
#import <Foundation/Foundation.h>
@interface MJTimer : NSObject
+ (NSString *)execTask:(void(^)(void))task
start:(NSTimeInterval)start
interval:(NSTimeInterval)interval
repeats:(BOOL)repeats
async:(BOOL)async;
+ (NSString *)execTask:(id)target
selector:(SEL)selector
start:(NSTimeInterval)start
interval:(NSTimeInterval)interval
repeats:(BOOL)repeats
async:(BOOL)async;
+ (void)cancelTask:(NSString *)name;
@end
#import "MJTimer.h"
@implementation MJTimer
static NSMutableDictionary *timers_;
dispatch_semaphore_t semaphore_;
+ (void)initialize
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
timers_ = [NSMutableDictionary dictionary];
semaphore_ = dispatch_semaphore_create(1);
});
}
+ (NSString *)execTask:(void (^)(void))task start:(NSTimeInterval)start interval:(NSTimeInterval)interval repeats:(BOOL)repeats async:(BOOL)async
{
if (!task || start < 0 || (interval <= 0 && repeats)) return nil;
// 隊列
dispatch_queue_t queue = async ? dispatch_get_global_queue(0, 0) : dispatch_get_main_queue();
// 創(chuàng)建定時器
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
// 設(shè)置時間
dispatch_source_set_timer(timer,
dispatch_time(DISPATCH_TIME_NOW, start * NSEC_PER_SEC),
interval * NSEC_PER_SEC, 0);
dispatch_semaphore_wait(semaphore_, DISPATCH_TIME_FOREVER);
// 定時器的唯一標識
NSString *name = [NSString stringWithFormat:@"%zd", timers_.count];
// 存放到字典中
timers_[name] = timer;
dispatch_semaphore_signal(semaphore_);
// 設(shè)置回調(diào)
dispatch_source_set_event_handler(timer, ^{
task();
if (!repeats) { // 不重復的任務(wù)
[self cancelTask:name];
}
});
// 啟動定時器
dispatch_resume(timer);
return name;
}
+ (NSString *)execTask:(id)target selector:(SEL)selector start:(NSTimeInterval)start interval:(NSTimeInterval)interval repeats:(BOOL)repeats async:(BOOL)async
{
if (!target || !selector) return nil;
return [self execTask:^{
if ([target respondsToSelector:selector]) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[target performSelector:selector];
#pragma clang diagnostic pop
}
} start:start interval:interval repeats:repeats async:async];
}
+ (void)cancelTask:(NSString *)name
{
if (name.length == 0) return;
dispatch_semaphore_wait(semaphore_, DISPATCH_TIME_FOREVER);
dispatch_source_t timer = timers_[name];
if (timer) {
dispatch_source_cancel(timer);
[timers_ removeObjectForKey:name];
}
dispatch_semaphore_signal(semaphore_);
}
@end
iOS程序的內(nèi)存布局

Tagged Pointer

判斷是否是Tagged Pointer

面試題
1、思考以下2段代碼能發(fā)生什么事?有什么區(qū)別?

答案:
第一個會報壞內(nèi)存釋放錯誤,因為ARC本身就是MRC轉(zhuǎn)換過來的,name其實內(nèi)部會實現(xiàn)[_name release];釋放的操作,同時有好幾千挑數(shù)據(jù)同時訪問一塊內(nèi)存地址,有可能釋放多次,就會報錯壞內(nèi)存
改進:
1、nonatomic改成atomic
2、進行加鎖 解鎖操作在線程里面
第二個不會報錯,abc是調(diào)用了Tagged Pointer方法,不會調(diào)用set方法,也不會觸發(fā)release,直接是指針地址值賦值給對象
看地址的最后一位是否為0,0的話 為對象,1的話為Tagged Pointer
OC對象的內(nèi)存管理

父類的delloc放到子類的后邊
Copy和MutableCopy
// 拷貝的目的:產(chǎn)生一個副本對象,跟源對象互不影響
// 修改了源對象,不會影響副本對象
// 修改了副本對象,不會影響源對象
/*
iOS提供了2個拷貝方法
1.copy,不可變拷貝,產(chǎn)生不可變副本
2.mutableCopy,可變拷貝,產(chǎn)生可變副本
深拷貝和淺拷貝
1.深拷貝:內(nèi)容拷貝,產(chǎn)生新的對象
2.淺拷貝:指針拷貝,沒有產(chǎn)生新的對象
*/



引用計數(shù)的存儲

Weak的實現(xiàn)原理
將弱引用存放到哈希表里面,對象銷毀,他就取出我們當前對象的弱引用表,把當前對象的弱引用表里面的數(shù)據(jù)都清除掉
weak是有一個弱引用表維護,在調(diào)用dealloc的以后他會便利weak的弱引用表,完了進行釋放
__strong:強引用
__weak:弱引用,指向的對象銷毀時,對應(yīng)的指針也會銷毀
__unsafe_unretained:不安全的弱引用,指向的對象銷毀時,對應(yīng)的指針不會銷毀,也就是野指針
- (void)viewDidLoad {
[super viewDidLoad];
// ARC是LLVM編譯器和Runtime系統(tǒng)相互協(xié)作的一個結(jié)果
__strong MJPerson *person1;
__weak MJPerson *person2;
__unsafe_unretained MJPerson *person3;
NSLog(@"111");
{
MJPerson *person = [[MJPerson alloc] init];
person3 = person;
}
NSLog(@"222 - %@", person3);
}

ARC都幫我們做了什么?
ARC其實是LLVM+Runtime實現(xiàn)的,LLVM編譯器自動幫我們處理了retain、release、autorelease等引用計數(shù)操作,在程序運行時,像弱引用的存在,則需要Runtime進行操作。
ARC其實就是LLVM編譯器和Runtime相互協(xié)作的一個結(jié)果
自動釋放池

/*
// @autoreleasepool內(nèi)部結(jié)構(gòu)
struct __AtAutoreleasePool {
__AtAutoreleasePool() { // 構(gòu)造函數(shù),在創(chuàng)建結(jié)構(gòu)體的時候調(diào)用
atautoreleasepoolobj = objc_autoreleasePoolPush();
}
~__AtAutoreleasePool() { // 析構(gòu)函數(shù),在結(jié)構(gòu)體銷毀的時候調(diào)用
objc_autoreleasePoolPop(atautoreleasepoolobj);
}
void * atautoreleasepoolobj;
};
{
__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"));
}
atautoreleasepoolobj = objc_autoreleasePoolPush();
MJPerson *person = [[[MJPerson alloc] init] autorelease];
objc_autoreleasePoolPop(atautoreleasepoolobj);
*/
AutoreleasePoolPage的結(jié)構(gòu)
達到先進后出的效果,
雙向鏈表:簡單的來說就是存放2個指針,一個可以查找到下邊的對象的指針,一個可以查到上面對象的指針


autorelease對象在什么時機會被調(diào)用release
autorelease實際上只是把對release的調(diào)用延遲了,對于每一個Autorelease,系統(tǒng)只是把該Object放入了當前的Autorelease pool中,當該pool被釋放時,該pool中的所有Object會被調(diào)用Release。對于每一個Runloop, 系統(tǒng)會隱式創(chuàng)建一個Autorelease pool,這樣所有的release pool會構(gòu)成一個象CallStack一樣的一個棧式結(jié)構(gòu),在每一個Runloop結(jié)束時,當前棧頂?shù)腁utorelease pool會被銷毀,這樣這個pool里的每個Object(就是autorelease的對象)會被release。那什么是一個Runloop呢? 一個UI事件,Timer call, delegate call, 都會是一個新的Runloop。那什么是一個Runloop呢? 一個UI事件,Timer call, delegate call, 都會是一個新的Runloop
RunLoop和Autorelease
