runtime怎么添加屬性闲延、方法等
? ivar表示成員變量
?
class_addIvar
?
class_addMethod
?
class_addProperty
?
class_addProtocol
?
class_replaceProperty
是否可以把比較耗時的操作放在NSNotificationCenter中
?首先必須明確通知在哪個線程中發(fā)出陋葡,那么處理接受到通知的方法也在這個線程中調(diào)用
?如果在異步線程發(fā)的通知,那么可以執(zhí)行比較耗時的操作霞捡;
?如果在主線程發(fā)的通知讼撒,那么就不可以執(zhí)行比較耗時的操作
runtime如何實現(xiàn)weak屬性
?首先要搞清楚weak屬性的特點
weak策略表明該屬性定義了一種“非擁有關系” (nonowning relationship)。
為這種屬性設置新值時,設置方法既不保留新值煎娇,也不釋放舊值。此特質(zhì)同assign類似;
然而在屬性所指的對象遭到摧毀時贪染,屬性值也會清空(nil
out)
?那么runtime如何實現(xiàn)weak變量的自動置nil缓呛?
runtime對注冊的類,會進行布局杭隙,會將weak對象放入一個hash表中哟绊。
用weak指向的對象內(nèi)存地址作為key,當此對象的引用計數(shù)為0的時候會調(diào)用對象的dealloc方法痰憎,
假設weak指向的對象內(nèi)存地址是a票髓,那么就會以a為key,在這個weak hash表中搜索铣耘,找到所有以a為key的weak對象洽沟,從而設置為nil。
weak屬性需要在dealloc中置nil么
?在ARC環(huán)境無論是強指針還是弱指針都無需在dealloc設置為nil蜗细,ARC會自動幫我們處理
?即便是編譯器不幫我們做這些裆操,weak也不需要在dealloc中置nil
?在屬性所指的對象遭到摧毀時,屬性值也會清空
//模擬下weak的setter方法炉媒,大致如下
- (void)setObject:(NSObject *)object
{
objc_setAssociatedObject(self,"object", object,OBJC_ASSOCIATION_ASSIGN);
[object cyl_runAtDealloc:^{
_object =nil;
}];
}
一個Objective-C對象如何進行內(nèi)存布局踪区?(考慮有父類的情況)
?所有父類的成員變量和自己的成員變量都會存放在該對象所對應的存儲空間中
?父類的方法和自己的方法都會緩存在類對象的方法緩存中,類方法是緩存在元類對象中
?每一個對象內(nèi)部都有一個isa指針,指向他的類對象,類對象中存放著本對象的如下信息
○對象方法列表
○成員變量的列表
○屬性列表
每個Objective-C對象都有相同的結構吊骤,如下圖所示
Objective-C 對象的結構圖
ISA指針
根類(NSObject)的實例變量
倒數(shù)第二層父類的實例變量
...
父類的實例變量
類的實例變量
?根類對象就是NSObject缎岗,它的super class指針指向nil
類對象既然稱為對象,那它也是一個實例白粉。類對象中也有一個isa指針指向它的元類(metaclass)密强,即類對象是元類的實例茅郎。元類內(nèi)部存放的是類方法列表,根元類的isa指針指向自己或渤,superclass指針指向NSObject類
#一個objc對象的isa的指針指向什么系冗?有什么作用?
?每一個對象內(nèi)部都有一個isa指針薪鹦,這個指針是指向它的真實類型
?根據(jù)這個指針就能知道將來調(diào)用哪個類的方法
下面的代碼輸出什么掌敬?
@implementationSon :
Father
- (id)init
{
self= [superinit];
if(self) {
NSLog(@"%@",NSStringFromClass([self class]));
NSLog(@"%@",NSStringFromClass([super class]));
}
return
self;
}
@end
?答案:都輸出Son
?這個題目主要是考察關于objc中對self和super的理解:
○self是類的隱藏參數(shù),指向當前調(diào)用方法的這個類的實例池磁。而super本質(zhì)是一個編譯器標示符奔害,和self是指向的同一個消息接受者
○當使用self調(diào)用方法時,會從當前類的方法列表中開始找地熄,如果沒有华临,就從父類中再找;
○而當使用super時端考,則從父類的方法列表中開始找雅潭。然后調(diào)用父類的這個方法
○調(diào)用[self class]時,會轉(zhuǎn)化成objc_msgSend函數(shù)
idobjc_msgSend(id
self,SELop, ...)
○調(diào)用[super class]時却特,會轉(zhuǎn)化成objc_msgSendSuper函數(shù)
idobjc_msgSendSuper(structobjc_super *super,SELop, ...)
○第一個參數(shù)是objc_super這樣一個結構體扶供,其定義如下
structobjc_super {
__unsafe_unretained
idreceiver;
__unsafe_unretainedClass super_class;
};
○第一個成員是receiver,類似于上面的objc_msgSend函數(shù)第一個參數(shù)self
○第二個成員是記錄當前類的父類是什么,告訴程序從父類中開始找方法裂明,找到方法后椿浓,最后內(nèi)部是使用objc_msgSend(objc_super->receiver,@selector(class))去調(diào)用,
此時已經(jīng)和[self class]調(diào)用相同了闽晦,故上述輸出結果仍然返回Son
○ objc
Runtime開源代碼對- (Class)class方法的實現(xiàn)
-(Class)class{
returnobject_getClass(self);
}
runtime如何通過selector找到對應的IMP地址扳碍?(分別考慮類方法和實例方法)
?每一個類對象中都一個對象方法列表(對象方法緩存)
?類方法列表是存放在類對象中isa指針指向的元類對象中(類方法緩存)
?方法列表中每個方法結構體中記錄著方法的名稱,方法實現(xiàn),以及參數(shù)類型,其實selector本質(zhì)就是方法名稱,通過這個方法名稱就可以在方法列表中找到對應的方法實現(xiàn).
?當我們發(fā)送一個消息給一個NSObject對象時仙蛉,這條消息會在對象的類對象方法列表里查找
?當我們發(fā)送一個消息給一個類時笋敞,這條消息會在類的Meta Class對象的方法列表里查找
objc中的類方法和實例方法有什么本質(zhì)區(qū)別和聯(lián)系
?類方法:
○類方法是屬于類對象的
○類方法只能通過類對象調(diào)用
○類方法中的self是類對象
○類方法可以調(diào)用其他的類方法
○類方法中不能訪問成員變量
○類方法中不能直接調(diào)用對象方法
○類方法是存儲在元類對象的方法緩存中
?實例方法:
○實例方法是屬于實例對象的
○實例方法只能通過實例對象調(diào)用
○實例方法中的self是實例對象
○實例方法中可以訪問成員變量
○實例方法中直接調(diào)用實例方法
○實例方法中可以調(diào)用類方法(通過類名)
○實例方法是存放在類對象的方法緩存中
使用runtime Associate方法關聯(lián)的對象,需要在主對象dealloc的時候釋放么捅儒?
?無論在MRC下還是ARC下均不需要
?被關聯(lián)的對象在生命周期內(nèi)要比對象本身釋放的晚很多液样,它們會在被NSObject -dealloc調(diào)用的object_dispose()方法中釋放
?補充:對象的內(nèi)存銷毀時間表振亮,分四個步驟
1.調(diào)用-release:引用計數(shù)變?yōu)榱?/p>
*對象正在被銷毀巧还,生命周期即將結束.
*不能再有新的__weak弱引用,否則將指向nil.
*調(diào)用[selfdealloc]
2.父類調(diào)用-dealloc
*繼承關系中最直接繼承的父類再調(diào)用-dealloc
*如果是MRC代碼 則會手動釋放實例變量們(iVars)
*繼承關系中每一層的父類 都再調(diào)用-dealloc
3.
NSObject調(diào)-dealloc
*只做一件事:調(diào)用Objective-C runtime中的object_dispose()方法
4.調(diào)用object_dispose()
*為C++的實例變量們(iVars)調(diào)用destructors
*為ARC狀態(tài)下的 實例變量們(iVars) 調(diào)用-release
*解除所有使用runtime Associate方法關聯(lián)的對象
*解除所有__weak引用
*調(diào)用free()
_objc_msgForward函數(shù)是做什么的坊秸?直接調(diào)用它將會發(fā)生什么麸祷?
?
_objc_msgForward是IMP類型,用于消息轉(zhuǎn)發(fā)的:當向一個對象發(fā)送一條消息褒搔,但它并沒有實現(xiàn)的時候阶牍,_objc_msgForward會嘗試做消息轉(zhuǎn)發(fā)
?直接調(diào)用_objc_msgForward是非常危險的事喷面,這是把雙刃刀,如果用不好會直接導致程序Crash走孽,但是如果用得好惧辈,能做很多非常酷的事
?
JSPatch就是直接調(diào)用_objc_msgForward來實現(xiàn)其核心功能的
?詳細解說參見這里的第一個問題解答
能否向編譯后得到的類中增加實例變量磕瓷?能否向運行時創(chuàng)建的類中添加實例變量盒齿?為什么?
?不能向編譯后得到的類中增加實例變量困食;
?能向運行時創(chuàng)建的類中添加實例變量边翁;
?分析如下:
○因為編譯后的類已經(jīng)注冊在runtime中,類結構體中的objc_ivar_list實例變量的鏈表和instance_size實例變量的內(nèi)存大小已經(jīng)確定硕盹,同時runtime會調(diào)用class_setIvarLayout或class_setWeakIvarLayout來處理strong
weak引用符匾,所以不能向存在的類中添加實例變量
○運行時創(chuàng)建的類是可以添加實例變量,調(diào)用class_addIvar函數(shù)瘩例,但是得在調(diào)用objc_allocateClassPair之后啊胶,objc_registerClassPair之前,原因同上仰剿。
runloop和線程有什么關系创淡?
?每條線程都有唯一的一個RunLoop對象與之對應的
?主線程的RunLoop是自動創(chuàng)建并啟動
?子線程的RunLoop需要手動創(chuàng)建
?子線程的RunLoop創(chuàng)建步驟如下:
○在子線程中調(diào)用[NSRunLoop
currentRunLoop]創(chuàng)建RunLoop對象(懶加載,只創(chuàng)建一次)
○獲得RunLoop對象后要調(diào)用run方法來啟動一個運行循環(huán)
//啟動RunLoop
[[NSRunLoop
currentRunLoop] run];
○
RunLoop的其他啟動方法
//第一個參數(shù):指定運行模式
//第二個參數(shù):指定RunLoop的過期時間南吮,即:到了這個時間后RunLoop就失效了
[[NSRunLoop
currentRunLoop] runMode:kCFRunLoopDefaultMode beforeDate:[NSDate
distantFuture]];
runloop的mode作用是什么琳彩?
?用來控制一些特殊操作只能在指定模式下運行,一般可以通過指定操作的運行mode來控制執(zhí)行時機部凑,以提高用戶體驗
?系統(tǒng)默認注冊了5個Mode
○
kCFRunLoopDefaultMode:App的默認Mode露乏,通常主線程是在這個Mode下運行,對應OC中的:NSDefaultRunLoopMode
○
UITrackingRunLoopMode:界面跟蹤Mode涂邀,用于ScrollView追蹤觸摸滑動瘟仿,保證界面滑動時不受其他Mode影響
○
kCFRunLoopCommonModes:這是一個標記Mode,不是一種真正的Mode比勉,事件可以運行在所有標有common modes標記的模式中劳较,對應OC中的NSRunLoopCommonModes,帶有common modes標記的模式有:UITrackingRunLoopMode和kCFRunLoopDefaultMode
○
UIInitializationRunLoopMode:在啟動App時進入的第一個Mode浩聋,啟動完成后就不再使用
○
GSEventReceiveRunLoopMode:接受系統(tǒng)事件的內(nèi)部Mode观蜗,通常用不到
以+scheduledTimerWithTimeInterval...的方式觸發(fā)的timer,在滑動頁面上的列表時衣洁,timer會暫定回調(diào)墓捻,為什么?如何解決坊夫?
?這里強調(diào)一點:在主線程中以+scheduledTimerWithTimeInterval...的方式觸發(fā)的timer默認是運行在NSDefaultRunLoopMode模式下的砖第,當滑動頁面上的列表時撤卢,進入了UITrackingRunLoopMode模式,這時候timer就會停止
?可以修改timer的運行模式為NSRunLoopCommonModes梧兼,這樣定時器就可以一直運行了
?以下是我的筆記補充:
○在子線程中通過scheduledTimerWithTimeInterval:...方法來構建NSTimer
§方法內(nèi)部已經(jīng)創(chuàng)建NSTimer對象放吩,并加入到RunLoop中,運行模式為NSDefaultRunLoopMode
§由于Mode有timer對象羽杰,所以RunLoop就開始監(jiān)聽定時器事件了屎慢,從而開始進入運行循環(huán)
§這個方法僅僅是創(chuàng)建RunLoop對象,并不會主動啟動RunLoop忽洛,需要再調(diào)用run方法來啟動
○如果在主線程中通過scheduledTimerWithTimeInterval:...方法來構建NSTimer腻惠,就不需要主動啟動RunLoop對象,因為主線程的RunLoop對象在程序運行起來就已經(jīng)被啟動了
// userInfo參數(shù):用來給NSTimer的userInfo屬性賦值欲虚,userInfo是只讀的集灌,只能在構建NSTimer對象時賦值
[NSTimer
scheduledTimerWithTimeInterval:1.0 target:selfselector:@selector(run:) userInfo:@"ya了個hoo"repeats:YES];
// scheduledTimer...方法創(chuàng)建出來NSTimer雖然已經(jīng)指定了默認模式,但是【允許你修改模式】
[[NSRunLoop
currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
//【僅在子線程】需要手動啟動RunLoop對象复哆,進入運行循環(huán)
[[NSRunLoop
currentRunLoop] run];
#猜想runloop內(nèi)部是如何實現(xiàn)的欣喧?
?從字面意思看:運行循環(huán)、跑圈梯找;
?本質(zhì):內(nèi)部就是do-while循環(huán)唆阿,在這個循環(huán)內(nèi)部不斷地處理各種事件(任務),比如:Source锈锤、Timer驯鳖、Observer;
?每條線程都有唯一一個RunLoop對象與之對應久免,主線程的RunLoop默認已經(jīng)啟動浅辙,子線程的RunLoop需要手動啟動;
?每次RunLoop啟動時阎姥,只能指定其中一個Mode记舆,這個Mode被稱作CurrentMode,如果需要切換Mode呼巴,只能退出Loop泽腮,再重新指定一個Mode進入,這樣做主要是為了隔離不同Mode中的Source衣赶、Timer诊赊、Observer,讓其互不影響屑埋;
?附上RunLoop的運行圖
不手動指定autoreleasepool的前提下豪筝,一個autorealese對象在什么時刻釋放痰滋?(比如在一個vc的viewDidLoad中創(chuàng)建)
?分兩種情況:手動干預釋放時機摘能、系統(tǒng)自動去釋放
○手動干預釋放時機:指定autoreleasepool就是所謂的:當前作用域大括號結束時就立即釋放
○系統(tǒng)自動去釋放:不手動指定autoreleasepool续崖,Autorelease對象會在當前的runloop迭代結束時釋放,下面詳細說明釋放時機
§ RunLoop中的三個狀態(tài)會處理自動釋放池团搞,通過打印代碼發(fā)現(xiàn)有兩個Observer監(jiān)聽到狀態(tài)值為:1和160(32+128)
□ kCFRunLoopEntry(1)//第一次進入會創(chuàng)建一個自動釋放池
□ kCFRunLoopBeforeWaiting(32)//進入休眠狀態(tài)前先銷毀自動釋放池严望,再創(chuàng)建一個新的自動釋放池
□ kCFRunLoopExit(128)//退出RunLoop時銷毀最后一次創(chuàng)建的自動釋放池
?如果在一個vc的viewDidLoad中創(chuàng)建一個Autorelease對象,那么該對象會在viewDidAppear方法執(zhí)行前就被銷毀了(是這樣的嗎逻恐?像吻??)
#蘋果是如何實現(xiàn)autoreleasepool的复隆?
? autoreleasepool以一個隊列數(shù)組的形式實現(xiàn),主要通過下列三個函數(shù)完成.
objc_autoreleasepoolPush
objc_autoreleasepoolPop
objc_aurorelease
?看函數(shù)名就可以知道拨匆,對autorelease分別執(zhí)行push,和pop操作挽拂。銷毀對象時執(zhí)行release操作
GCD的隊列(dispatch_queue_t)分哪兩種類型惭每?背后的線程模型是什么樣的?
?串行隊列
?并行隊列
? dispatch_global_queue();是全局并發(fā)隊列
? dispatch_main_queue();是一種特殊串行隊列
?背后的線程模型:自定義隊列dispatch_queue_t queue;可以自定義是并行:DISPATCH_QUEUE_CONCURRENT或者
串行DISPATCH_QUEUE_SERIAL
#蘋果為什么要廢棄dispatch_get_current_queue亏栈?
?容易誤用造成死鎖
如何用GCD同步若干個異步調(diào)用台腥?(如根據(jù)若干個url異步加載多張圖片,然后在都下載完成后合成一張整圖)
?必須是并發(fā)隊列才起作用
?需求分析
○首先绒北,分別異步執(zhí)行2個耗時的操作
○其次黎侈,等2個異步操作都執(zhí)行完畢后,再回到主線程執(zhí)行一些操作
?使用隊列組實現(xiàn)上面的需求
//創(chuàng)建隊列組
dispatch_group_t
group =? dispatch_group_create();
//獲取全局并發(fā)隊列
dispatch_queue_t
queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//往隊列組中添加耗時操作
dispatch_group_async(group,
queue, ^{
//執(zhí)行耗時的異步操作1
});
//往隊列組中添加耗時操作
dispatch_group_async(group,
queue, ^{
//執(zhí)行耗時的異步操作2
});
//當并發(fā)隊列組中的任務執(zhí)行完畢后才會執(zhí)行這里的代碼
dispatch_group_notify(group,
queue, ^{
//如果這里還有基于上面兩個任務的結果繼續(xù)執(zhí)行一些代碼闷游,建議還是放到子線程中峻汉,等代碼執(zhí)行完畢后在回到主線程
//回到主線程
dispatch_async(group, dispatch_get_main_queue(), ^{
//執(zhí)行相關代碼...
});
});
#dispatch_barrier_async的作用是什么?
?函數(shù)定義
dispatch_barrier_async(dispatch_queue_t
queue, dispatch_block_t block);
?必須是并發(fā)隊列脐往,要是串行隊列俱济,這個函數(shù)就沒啥意義了
?注意:這個函數(shù)的第一個參數(shù)queue不能是全局的并發(fā)隊列
?作用:在它前面的任務執(zhí)行結束后它才執(zhí)行,在它后面的任務等它執(zhí)行完成后才會執(zhí)
?示例代碼
-(void)barrier
{
dispatch_queue_t queue = dispatch_queue_create("12342234",DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"----1-----%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"----2-----%@",[NSThread currentThread]);
});
//在它前面的任務執(zhí)行結束后它才執(zhí)行钙勃,在它后面的任務等它執(zhí)行完成后才會執(zhí)行
dispatch_barrier_async(queue, ^{
NSLog(@"----barrier-----%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"----3-----%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"----4-----%@",[NSThread currentThread]);
});
}
#以下代碼運行結果如何蛛碌?
- (void)viewDidLoad
{
[superviewDidLoad];
NSLog(@"1");
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"2");
});
NSLog(@"3");
}
?答案:主線程死鎖