1酒繁、weak關(guān)鍵字的作用
weak的作用是弱引用滓彰,它修飾的對(duì)象在釋放時(shí)會(huì)置為nil,避免錯(cuò)誤的內(nèi)存訪問(wèn)州袒。一般用于delegate郎哭、block菇存、NSTimer中邦蜜,避免循環(huán)引用造成的內(nèi)存泄漏問(wèn)題畦徘。
weak修飾的對(duì)象釋放時(shí),weak指針自動(dòng)置為nil的原理关筒?
runtime維護(hù)了一張weak表杯缺,存儲(chǔ)了指向某個(gè)對(duì)象的weak指針地址萍肆。weak表其實(shí)是一個(gè)哈希表,key是指向某個(gè)對(duì)象的地址包雀,value是指向某個(gè)對(duì)象的weak指針地址數(shù)組才写,當(dāng)該對(duì)象被銷(xiāo)毀時(shí),會(huì)根據(jù)該對(duì)象的地址(key)獲取指向該對(duì)象的weak指針數(shù)組赞草,然后遍歷該數(shù)組將weak指針依次置為nil吆鹤,從weak表中刪除該記錄,最后從引用計(jì)數(shù)表中刪除廢棄對(duì)象的地址為鍵值的記錄沾凄。
系統(tǒng)如何知道哪些對(duì)象是被__weak修飾過(guò)的搭独?
- 在arm64架構(gòu)之前廊镜,isa就是一個(gè)普通的指針嗤朴,存儲(chǔ)著Class、Meta-Class對(duì)象的內(nèi)存地址
- 從arm64架構(gòu)開(kāi)始股缸,對(duì)isa進(jìn)行了優(yōu)化敦姻,變成了一個(gè)共用體(union)結(jié)構(gòu)歧杏,還使用位域來(lái)存儲(chǔ)更多的信息
- isa的結(jié)構(gòu)中有一個(gè)
weakly_referenced
的成員變量犬绒,該成員變量記錄了對(duì)象是否被弱引用指向過(guò)。
//isa的底層數(shù)據(jù)結(jié)構(gòu)
union isa_t {
Class cls;
uintptr_t bits;
struct {
uintptr_t nonpointer : 1; \
uintptr_t has_assoc : 1; \
uintptr_t has_cxx_dtor : 1; \
uintptr_t shiftcls : 44; /*MACH_VM_MAX_ADDRESS 0x7fffffe00000*/ \
uintptr_t magic : 6; \
uintptr_t weakly_referenced : 1; \
uintptr_t deallocating : 1; \
uintptr_t has_sidetable_rc : 1; \
uintptr_t extra_rc : 8
};
};
2、引用循環(huán)
當(dāng)兩個(gè)不同的對(duì)象各有一個(gè)強(qiáng)引用指向?qū)Ψ降臅r(shí)候拗秘,就會(huì)造成循環(huán)引用祈惶。
NSTimer是如何造成循環(huán)引用的行瑞?
在ViewController(簡(jiǎn)稱(chēng)VC)中使用timer屬性時(shí),VC強(qiáng)引用timer突照,timer的target又是VC讹蘑,就造成了循環(huán)引用筑舅。當(dāng)你在VC的dealloc方法中調(diào)用timer的invalidate方法來(lái)銷(xiāo)毀timer時(shí)翠拣,會(huì)發(fā)現(xiàn)pop出當(dāng)前VC時(shí),并沒(méi)有調(diào)用dealloc方法蛮粮,VC在等timer釋放后才走dealloc然想,而timer的釋放在dealloc中,所以就造成了循環(huán)引用令哟。
如何解決NSTimer的循環(huán)引用屏富?
- 蘋(píng)果新的api接口解決方案(iOS10.0以上可用)
- 使用NSProxy方案
- 對(duì)NSTimer進(jìn)行封裝
3滑燃、OC對(duì)象的本質(zhì)
一個(gè)NSObject對(duì)象占用多少內(nèi)存表窘?
- 系統(tǒng)分配了16個(gè)字節(jié)給NSObject對(duì)象(通過(guò)malloc_size函數(shù)獲得)乐严;
- 但NSObject對(duì)象內(nèi)部只使用了8個(gè)字節(jié)的空間(64bit環(huán)境下,可以通過(guò)class_getInstanceSize函數(shù)獲得)捂敌;
對(duì)象的isa指針指向哪里占婉?
- instance對(duì)象的isa指向class對(duì)象甫恩;
- class對(duì)象的isa指向meta-class對(duì)象磺箕;
- meta-class對(duì)象的isa指向基類(lèi)的meta-class對(duì)象松靡;
OC的類(lèi)信息存放在哪里?
- 對(duì)象方法岛马、成員變量蛛枚、屬性、協(xié)議信息存放在class對(duì)象中;
- 類(lèi)方法盲镶,存放在meta-class對(duì)象中溉贿;
- 成員變量的具體值浦旱,存放在instance對(duì)象中颁湖;
4、TCP擁堵抢蚀、TCP丟包問(wèn)題
5皿曲、https配置流程
http與https的區(qū)別吴侦?
1备韧、https需要到ca申請(qǐng)證書(shū)盯蝴,一般免費(fèi)證書(shū)很少,需要交費(fèi)虑绵;
2翅睛、http是超文本傳輸協(xié)議,信息是明文傳輸疏旨,https是具有安全性的SSL加密傳輸協(xié)議檐涝;
3法挨、http和https的連接方式不同凡纳,用的端口也不同荐糜,前者是80,后者是443延塑;
4页畦、http的連接很簡(jiǎn)單研儒,是無(wú)狀態(tài)的端朵;https協(xié)議是由SSL+HTTP協(xié)議構(gòu)建的可進(jìn)行加密傳輸冲呢、身份認(rèn)證的網(wǎng)絡(luò)協(xié)議,比http協(xié)議安全邻薯;
iOS配置流程厕诡?
項(xiàng)目中網(wǎng)絡(luò)交互基于AFN灵嫌,要求AFN版本在3.0以上。代碼部分:
// 設(shè)置AFN請(qǐng)求管理者的時(shí)候猖凛,添加SSL認(rèn)證:
// 1.獲得請(qǐng)求管理者
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
// 2.加上這個(gè)函數(shù)辨泳,https ssl 驗(yàn)證漠吻。
[manager setSecurityPolicy:[self customSecurityPolicy]];
// https ssl 驗(yàn)證函數(shù)
- (AFSecurityPolicy *)customSecurityPolicy
{
// 先導(dǎo)入證書(shū)
NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"xxx" ofType:@"cer"];//證書(shū)的路徑
NSData *cerData = [NSData dataWithContentsOfFile:cerPath];
// AFSSLPinningModeCertificate 使用證書(shū)驗(yàn)證模式
AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
securityPolicy.pinnedCertificates = [NSSet setWithObject:cerData];
// allowInvalidCertificates 是否允許無(wú)效證書(shū)(也就是自建的證書(shū)),默認(rèn)為NO
securityPolicy.allowInvalidCertificates = NO;
//validatesDomainName 是否需要驗(yàn)證域名扔傅,默認(rèn)為YES;
securityPolicy.validatesDomainName = YES;
return securityPolicy;
}
// 3.關(guān)于證書(shū)
從沃通獲取到HTTPS證書(shū)后烫饼,會(huì)得到一個(gè)有密碼的壓縮包文件杠纵,使用for other server里面的domain.crt的證書(shū)文件比藻。
6、atomic屬性作用慢叨?
atomic修飾的對(duì)象拍谐,setter和getter方法是線程安全的(因?yàn)樵趕etter和getter賦值取值的時(shí)候添加了自旋鎖)轩拨,但不能保證整個(gè)對(duì)象的線程安全院喜。
為什么說(shuō)atomic沒(méi)辦法保證整個(gè)對(duì)象的線程安全?
1.對(duì)于NSArray類(lèi)型 @property(atomic)NSArray *array我們用atomic修飾够坐,數(shù)組的添加和刪除并不是線程安全的,這是因?yàn)閿?shù)組比較特殊巫员,我們要分成兩部分考慮甲棍,一部分是&array也就是這個(gè)數(shù)組本身感猛,另一部分是他所指向的內(nèi)存部分陪白。atomic限制的只是&array部分咱士,對(duì)于它指向的對(duì)象沒(méi)有任何限制。
atomic表示锐膜,我TM也很冤暗勒怠:沙选BА:薮辍斧抱!
2.當(dāng)線程A進(jìn)行寫(xiě)操作辉浦,這時(shí)其他線程的讀或者寫(xiě)操作會(huì)因?yàn)樵摬僮鞫却芙肌.?dāng)A線程的寫(xiě)操作結(jié)束后拖陆,B線程進(jìn)行寫(xiě)操作依啰,然后當(dāng)A線程需要讀操作時(shí)速警,卻獲得了在B線程中的值闷旧,這就破壞了線程安全忙灼,如果有線程C在A線程讀操作前release了該屬性钝侠,那么還會(huì)導(dǎo)致程序崩潰机错。所以?xún)H僅使用atomic并不會(huì)使得線程安全弱匪,我們還要為線程添加lock來(lái)確保線程的安全萧诫。
個(gè)人覺(jué)得這個(gè)就有點(diǎn)杠精的意味了帘饶,atomic還要管到你方法外面去了及刻?缴饭?骆莹?幕垦??不過(guò)面試人家問(wèn)你還要這么答蒸走,要嚴(yán)謹(jǐn)T芈怠嫁艇!步咪,
7猾漫、iOS用什么方式實(shí)現(xiàn)對(duì)一個(gè)對(duì)象的KVO悯周?(KVO的本質(zhì)是什么禽翼?)
- 利用runtime API動(dòng)態(tài)生成一個(gè)子類(lèi)闰挡,并且讓instance對(duì)象的isa指向這個(gè)全新的子類(lèi)长酗;
- 當(dāng)修改instance對(duì)象的屬性時(shí)夺脾,會(huì)調(diào)用Foundation的
_NSSetxxxValueAndNotify
函數(shù)劳翰; -
_NSSetxxxValueAndNotify
內(nèi)部會(huì)調(diào)用:
willChangeValueForKey:
父類(lèi)原來(lái)的setter方法
didChangeValueForKey:
-
didChangeValueForKey:
方法內(nèi)部會(huì)觸發(fā)監(jiān)聽(tīng)器(Observer)的監(jiān)聽(tīng)方法observeValueForKeyPath: ofObject: change: context:
如何手動(dòng)觸發(fā)KVO佳簸?
- 手動(dòng)調(diào)用
willChangeValueForKey:
和didChangeValueForKey:
方法
直接修改成員變量會(huì)觸發(fā)KVO嗎生均?
- 不會(huì)觸發(fā)KVO
8马胧、通過(guò)KVC修改屬性會(huì)觸發(fā)KVO嗎佩脊?
- 會(huì)觸發(fā)KVO
KVC的賦值和取值過(guò)程是怎樣的威彰?原理是什么歇盼?
賦值過(guò)程即[setValue: forKey:]
方法實(shí)現(xiàn)原理:
- 按照
setKey:
豹缀、_setKey:
順序查找方法 - a> 找到了方法邢笙,傳遞參數(shù)調(diào)用方法
- b> 沒(méi)找到方法氮惯,查看
+(BOOL)accessInstanceVariablesDirectly
方法的返回值 - 返回值為YES筐骇,按照
_key铛纬、_isKey告唆、key擒悬、isKey
順序來(lái)查找成員變量
找到成員變量直接賦值
找不到成員變量調(diào)用setValue: forUndefinedKey:
方法稻艰,并拋出異常NSUnknownKeyException
- 返回值為NO懂牧,調(diào)用
setValue: forUndefinedKey:
方法,并拋出異常NSUnknownKeyException
-
+(BOOL)accessInstanceVariablesDirectly
方法的默認(rèn)返回值是YES
取值過(guò)程:
- 首先按照
getKey、key僧凤、isKey畜侦、_key
順序查找方法 - a>找到了方法,調(diào)用方法取值
- b>未找到方法躯保,查看
+(BOOL)accessInstanceVariablesDirectly
方法的返回值 - 返回值為YES,按照
_key途事、_isKey验懊、key、isKey
順序查找成員變量
找到成員變量尸变,就返回該成員變量的值
未找到成員變量义图,調(diào)用valueForUndefinedKey:
方法,并拋出異常NSUnknownKeyException
- 返回值為NO振惰,調(diào)用
valueForUndefinedKey:
方法歌溉,并拋出異常NSUnknownKeyException
9、Category的實(shí)現(xiàn)原理骑晶?
- Category編譯后的底層結(jié)構(gòu)是
struct category_t
痛垛,里面存儲(chǔ)著分類(lèi)的對(duì)象方法、類(lèi)方法桶蛔、屬性匙头、協(xié)議信息 - 在程序運(yùn)行的時(shí)候,runtime會(huì)將Category的數(shù)據(jù)仔雷,合并到類(lèi)信息中(類(lèi)對(duì)象蹂析、元類(lèi)對(duì)象中)
Category的加載處理過(guò)程?
- 通過(guò)runtime加載某個(gè)類(lèi)的所有Category數(shù)據(jù)
- 把所有Category的方法碟婆、屬性电抚、協(xié)議數(shù)據(jù),合并到一個(gè)大數(shù)組中
后參與編譯的Category數(shù)據(jù)竖共,會(huì)在數(shù)組的前面 - 將合并后的分類(lèi)數(shù)據(jù)(方法蝙叛、屬性、協(xié)議)公给,插入到類(lèi)原來(lái)數(shù)據(jù)的前面
Category和Class Extension的區(qū)別是什么借帘?
- Class Extension在編譯的時(shí)候,它的數(shù)據(jù)就已經(jīng)包含在類(lèi)信息中
- Category是在運(yùn)行時(shí),才會(huì)將數(shù)據(jù)合并到類(lèi)信息中
Category中有l(wèi)oad方法嗎?load方法在什么時(shí)候調(diào)用的艺蝴?load方法能繼承嗎?
- 有l(wèi)oad方法
- load方法在runtime加載類(lèi)际起、分類(lèi)的時(shí)候調(diào)用
- 可以繼承,但是一般情況下不會(huì)主動(dòng)去調(diào)用load方法,都是讓系統(tǒng)自動(dòng)調(diào)用
load加叁、initialize方法的區(qū)別是什么倦沧?它們?cè)贑ategory中的調(diào)用順序?以及出現(xiàn)繼承時(shí)它們之間的調(diào)用過(guò)程它匕?
區(qū)別
調(diào)用方式
1> load是根據(jù)函數(shù)地址直接調(diào)用
2> initialize是通過(guò)objc_msgSend調(diào)用調(diào)用時(shí)刻
1> load是runtime加載類(lèi)展融、分類(lèi)的時(shí)候調(diào)用(只會(huì)調(diào)用一次)
2> initialize是類(lèi)第一次接收到消息的時(shí)候調(diào)用,每個(gè)類(lèi)只會(huì)initialize一次(父類(lèi)的initialize方法可能會(huì)被調(diào)用多次)
調(diào)用順序
-
load
1> 先調(diào)用類(lèi)的load
a) 先編譯的類(lèi)豫柬,優(yōu)先調(diào)用
b) 調(diào)用子類(lèi)的load方法之前告希,會(huì)先調(diào)用父類(lèi)的load方法2> 再調(diào)用分類(lèi)的load
a) 先編譯的分類(lèi),優(yōu)先調(diào)用 initialize
1> 先初始化父類(lèi)
2> 再初始化子類(lèi)(可能最終調(diào)用的是父類(lèi)的initialize方法)
3> 如果子類(lèi)沒(méi)有實(shí)現(xiàn)initialize方法烧给,會(huì)調(diào)用父類(lèi)的initialize燕偶,所以父類(lèi)的initialize可能會(huì)被調(diào)用多次
4> 如果分類(lèi)實(shí)現(xiàn)了initialize方法,就覆蓋類(lèi)本身的initialize調(diào)用
Category能否添加成員變量础嫡?如果可以指么,如何添加?
- 不能直接給Category添加成員變量榴鼎,但可以通過(guò)runtime的API間接實(shí)現(xiàn)Category有成員變量的效果伯诬。
- 添加關(guān)聯(lián)對(duì)象:
void objc_setAssociatedObject(id object, const void * key, id value, objc_AssociationPolicy policy)
- 獲得關(guān)聯(lián)對(duì)象:
id objc_getAssociatedObject(id object, const void * key)
- 移除所有關(guān)聯(lián)對(duì)象:
void objc_removeAssociatedObjects(id object)
10、講一下OC的消息機(jī)制巫财?
- OC的方法調(diào)用其實(shí)都會(huì)轉(zhuǎn)成
objc_msgSend
函數(shù)的調(diào)用盗似,給receiver(方法調(diào)用者)發(fā)送了一條消息(方法名)。 -
objc_msgSend
函數(shù)底層有三大階段:
1>消息發(fā)送(當(dāng)前類(lèi)平项、父類(lèi)中查找方法)
2>動(dòng)態(tài)方法解析(消息發(fā)送階段未找到方法實(shí)現(xiàn),開(kāi)發(fā)者可以在這個(gè)階段實(shí)現(xiàn)+resolveInstanceMethod:
或+resolveClassMethod:
方法來(lái)動(dòng)態(tài)添加方法實(shí)現(xiàn)闽瓢。動(dòng)態(tài)解析過(guò)后接癌,會(huì)重新走 消息發(fā)送 的流程,并且是直接“從receiverClass的cache中查找方法”這一步開(kāi)始執(zhí)行)
3>消息轉(zhuǎn)發(fā)(如果在 動(dòng)態(tài)方法解析 過(guò)程沒(méi)有做動(dòng)態(tài)添加方法實(shí)現(xiàn)的處理扣讼,那么程序會(huì)進(jìn)入消息轉(zhuǎn)發(fā)過(guò)程)
- 首先會(huì)調(diào)用
forwardingTargetForSelector:
方法A缺猛,返回值為nil,會(huì)繼續(xù)調(diào)用methodSignatureForSelector:
方法B届谈,若返回值為nil,程序會(huì)報(bào)錯(cuò)弯汰,拋出找不到方法選擇器的經(jīng)典錯(cuò)誤艰山。 - 若方法A
forwardingTargetForSelector:
的返回值不為nil,就代表轉(zhuǎn)發(fā)成功咏闪,這條消息(方法實(shí)現(xiàn))由返回值處理:objc_msgSend(返回值, SEL)
- 若方法B
methodSignatureForSelector:
的返回值不為nil曙搬,會(huì)繼續(xù)調(diào)用forwardInvocation:
方法C,開(kāi)發(fā)者可以在該方法中自定義任何邏輯。 - 注:以上消息轉(zhuǎn)發(fā)過(guò)程涉及到的方法A/B/C纵装,都有對(duì)象方法征讲、類(lèi)方法兩個(gè)版本(可以是減號(hào)-方法,也可以是加號(hào)+方法)
11橡娄、對(duì)runtime的理解诗箍?在項(xiàng)目中的應(yīng)用場(chǎng)景有哪些?
- 簡(jiǎn)單來(lái)說(shuō)挽唉,OC是一門(mén)動(dòng)態(tài)性比較強(qiáng)的編程語(yǔ)言滤祖,允許很多操作推遲到程序運(yùn)行時(shí)再進(jìn)行。
- OC的動(dòng)態(tài)性就是由
runtime
來(lái)支撐和實(shí)現(xiàn)的瓶籽,runtime
是一套C語(yǔ)言的API匠童,封裝了很多動(dòng)態(tài)性相關(guān)的函數(shù)。 - 平時(shí)編寫(xiě)的OC代碼塑顺,底層都是轉(zhuǎn)換成了
runtime
API進(jìn)行調(diào)用汤求。
應(yīng)用場(chǎng)景
- 利用關(guān)聯(lián)對(duì)象(
objc_setAssociatedObject
)給分類(lèi)添加屬性。 - 遍歷類(lèi)的所有成員變量(修改文本框占位文字顏色严拒、字典轉(zhuǎn)模型扬绪、自動(dòng)歸解檔)
- 交換方法實(shí)現(xiàn)(交換系統(tǒng)的方法)
- 利用消息轉(zhuǎn)發(fā)機(jī)制解決方法找不到的異常問(wèn)題
12、block的本質(zhì)糙俗?
-
block
本質(zhì)也是一個(gè)OC對(duì)象勒奇,它內(nèi)部也有一個(gè)isa指針。 -
block
是封裝了函數(shù)調(diào)用以及函數(shù)調(diào)用環(huán)境的OC對(duì)象巧骚。
block的變量捕獲機(jī)制
- 為了保證block內(nèi)部能正常訪問(wèn)外部的變量赊颠,block有個(gè)變量捕獲機(jī)制:
block的類(lèi)型
-
block
有三種類(lèi)型,可通過(guò)class方法或isa指針查看具體類(lèi)型劈彪,最終都是繼承自NSBlock類(lèi)型:
1竣蹦、沒(méi)有訪問(wèn)auto變量: __NSGlobalBlock__ (_NSConcreteGlobalBlock)
2、訪問(wèn)了auto變量: __NSStackBlock__ (_NSConcreteStackBlock)
3沧奴、__NSStackBlock__調(diào)用了copy: __NSMallocBlock__ (_NSConcreteMallocBlock)
- 每一種類(lèi)型的
block
調(diào)用了copy
后的結(jié)果如下: