一、說一下對自動釋放池的理解
ARC中,主線程默認(rèn)開啟一個runloop,runloop自動創(chuàng)建一個autoreleasepool,autorelease對象會自動被加入autoreleasepool中,一次runloop后清空自動釋放池,用__autoreleasing修飾符修飾,或類方法創(chuàng)建會自動加入autoreleasepool中,添加到最近的autoreleasepool中。autoreleasepool是被一個棧區(qū)管理,當(dāng)需要釋放一個autoreleasepool時,會根據(jù)其中的哨兵對象(一個nil指針),對這個對象所在的autoreleasepool中所有對象引用計數(shù)-1,清空所有對象的內(nèi)存地址,然后next指針會指向一個合適的位置,next根據(jù)表示begin()和end()來約束autoreleasepool是否存滿對象,是否需要創(chuàng)建新的autoreleasepool存儲新的autorelease對象。
二、說一下都有那些修飾詞并闡述作用
nonatomic、atomic、assign、weak、retain、strong、copy、readonly、readwrite
nonatomic、atomic區(qū)別:
atomic:設(shè)置成員變量的@property屬性時,默認(rèn)是atomic,提供多線程安全,但不是絕對安全。
nonatomic:禁止多線程,變量保護,提高安全性。
atomic是OC使用的一種線程保護技術(shù),防止在一個線程上的任務(wù)未完成時,被其他線程讀取,造成數(shù)據(jù)錯誤,這種機制是耗費資源的。一般不需要多線程間通訊編程,可以使用nonatomic。
Assign、 weak的區(qū)別
引用計數(shù)都不會?1。
assign 修飾基本數(shù)據(jù)類型,例如int float,在MRC環(huán)境下不會自動置為nil。
weak 修飾的變量會在棧中自動清空,賦值為nil。
eg:delegate在MRC環(huán)境下,用assign修飾,是為了不造成循環(huán)引用,需要在-dealloc方法中手動設(shè)置self.delegate=nil;以免造成野指針。而在ARC環(huán)境下,直接用weak修飾就可以了。
原理:一個對象,所有指向它的weak指針都存放在一個hash表中,key是對象所占內(nèi)存地址,value是一個weak指針數(shù)組,當(dāng)對象被銷毀的時候,系統(tǒng)會遍歷這個數(shù)組中的所有weak指針,并將它們都會指向nil。
retain、strong、copy的區(qū)別
retain 是MRC下使用的修飾詞,在ARC下可用strong替代。
當(dāng)修飾不可變對象時,三者的作用是一樣的,對象不會跟著原本的引用對象的改變而改變,因為是淺拷貝的指針,當(dāng)源數(shù)值發(fā)生改變,就會產(chǎn)生新的指針地址。
當(dāng)修飾可變對象的時候,retain和strong是一樣的,對象會跟著原本引用對象的數(shù)值改變而改變,因為原對象是可變的,不會出現(xiàn)新的內(nèi)存。而copy的對象不會跟著改變,因為copy是深拷貝的新的內(nèi)容,開辟了新的存儲空間和指針。
readonly、readwrite的區(qū)別
readonly 這個屬性只生成了getter方法,沒有setter方法,只能讀。
readwrite 這個屬性getter setter方法都有,可讀可改。
三、ARC和MRC的區(qū)別
ARC下,內(nèi)存交給系統(tǒng)自動管理,系統(tǒng)會在編譯的時候自動插入retain/release,而MRC下,需要手動管理對象的引用計數(shù),需要手動發(fā)送release或者autorelease消息。
四、簡述category和extension的區(qū)別
category 運行時決定,只能添加方法,不能添加實例變量(可以添加屬性)。
extension 編譯時決定,不僅可以添加方法,還可以添加實例變量和屬性,默認(rèn)是私有的。但是extension沒有獨立的實現(xiàn)部分。
category細(xì)聊:
1)分類的方法名如果和原類中方法名沖突,優(yōu)先分類。
2)Category的方法可以被子類繼承。
3)利用runtime倒序遍歷方法列表,可以調(diào)用被覆蓋的原類的方法。
4)一個類的兩個category,如果存在方法名相同的情況,根據(jù)buildPhases->Compile Sources里面的從上至下順序編譯情況去調(diào)用,即后編譯的會被直接調(diào)用。
5)擴展一個類的方式,category比繼承要好,繼承需要定義子類,類目不需要定義子類來增加現(xiàn)有類的方法,且不會影響其他類和原有類的關(guān)系。
五、說一下對內(nèi)存管理的理解
內(nèi)存管理機制
1、引用計數(shù):當(dāng)我們創(chuàng)建一個對象時,其引用計數(shù)會?1,系統(tǒng)會分配內(nèi)存給到這個對象。每次引用這個對象,它的引用計數(shù)都會?1。當(dāng)指針不再指向這個對象,那么它的引用計數(shù)會?1,直到引用計數(shù)為0,它的內(nèi)存會釋放掉,對象被銷毀。在ARC環(huán)境下,對象會被系統(tǒng)釋放,不需要手動釋放,避免了內(nèi)存泄漏和野指針的出現(xiàn)。
2、循環(huán)引用:當(dāng)幾個對象互相強引用時,會造成內(nèi)存泄漏,導(dǎo)致程序崩潰。例如,delegate如果用strong修飾,那么就會造成兩個類之間的互相強引用,此時引入弱引用概念,即用weak修飾delegate,不會使delegate的引用計數(shù)?1,當(dāng)delegate對象銷毀時,指針會指向nil。
3、內(nèi)存泄漏:沒有及時釋放掉內(nèi)存,例如計時器沒有釋放。
實際開發(fā)中如何避免內(nèi)存泄漏
1、防止循環(huán)引用,使用weak弱引用,例如修飾delegate。
2、及時釋放對象內(nèi)存,例如創(chuàng)建計時器NSTimer,在計時結(jié)束的時候,一定要調(diào)用invalidate方法,將timer置為nil。
3、使用懶加載的方式創(chuàng)建對象,在使用對象的時候再進(jìn)行實例化,還可以同時加入一些處理邏輯,這樣利于內(nèi)存管理。
六、循環(huán)引用
舉例:
1、UIViewController中創(chuàng)建UITableView,當(dāng)實例一個tableView時,控制器的view對tableView進(jìn)行了強引用,此時tableView的delegate又引用了控制器,這就形成了循環(huán)引用,此時需要用weak去修飾delegate。
2、block內(nèi)部調(diào)用了外部變量(控制器本身或者控制器的屬性)
此時需要用__weak typeof(self) weakSelf = self;來進(jìn)行對控制器的弱引用,然后在block內(nèi)部去調(diào)用。
3、關(guān)于定時器
使用定時器時,要主動調(diào)用invalidated,一旦和控制器形成循環(huán)引用,控制器不會被自動銷毀,不會調(diào)用dealloc方法,如果invalidated放在dealloc中,是不會被執(zhí)行的。那么我們可以通過攔截點擊返回按鈕的方法,在該方法中主動調(diào)用invalidated方法,或者寫在ViewWillDisappear方法中。請記住,創(chuàng)建一個NSTimer對象,會形成一個runloop,不止控制器會對該timer持有,iOS系統(tǒng)也會對其強引用,所以不要以為設(shè)置timer=nil就可以釋放timer了,只有調(diào)用invalidated才會讓iOS系統(tǒng)釋放對timer的強引用。所以,[timer invalidated];timer=nil;要同時書寫,這是一種良好的代碼習(xí)慣和規(guī)范。
七、關(guān)于block
1、block是一個OC的對象,它封裝了一段代碼,這段代碼可以在任何時候執(zhí)行。Block可以作為函數(shù)參數(shù)或者函數(shù)的返回值,而其本身又可以帶輸入?yún)?shù)或返回值??梢郧短锥x,可以定義在方法內(nèi)部和外部。
2、block的循環(huán)引用:在使用block的地方,self.block,self持有了block,在block代碼塊里處理邏輯,如果用到了self.xx,那么block又會持有self,這樣當(dāng)你離開界面的時候,并不會走self的dealloc析構(gòu)函數(shù),self不會釋放,這是因為self和block的循環(huán)引用,導(dǎo)致了內(nèi)存泄漏。為了避免這種情況,要使用__weak 在block外面修飾一下self,在block代碼塊里使用weakself處理邏輯,weakself只是指向了self,它在一個弱引用表里,self的引用計數(shù)沒有+1。
3、按照上面的方式調(diào)用self的屬性,有可能會失敗,原因是,weakself會因為析構(gòu)函數(shù)被釋放,那么weakself.xx就會是nil。為了避免這種情況,在block代碼塊里,需要用__strong修飾一下weakself,那么相當(dāng)于一個臨時的strong指針指向了self,當(dāng)邏輯處理完畢,strong銷毀,不會產(chǎn)生內(nèi)存泄漏問題。
八、關(guān)于WKWebview
1、相較于UIWebview,WKWebview占內(nèi)存小,加載快,增加了加載進(jìn)度條。
2、出現(xiàn)白屏的原因和解決辦法:
① 網(wǎng)頁內(nèi)存過大會白屏,需要在webViewWebContentProcessDidTerminate方法中調(diào)用[webview reload];方法。
② url中有中文字符,需要后臺配合修改;
③ h5中使用了第三方組件,加載失敗,先是白屏。一般正式環(huán)境下會恢復(fù)正常。3、關(guān)于cookie
為了解決加載H5界面無法攜帶原生界面登錄用戶信息問題,除了在url上拼接用戶uid等信息的方法,還可以在請求的時候,攔截請求頭,將這些信息拼接起來,使用request的set value方法,將這些信息傳遞過去。4、原生界面和H5界面的交互:
JS調(diào)用OC方法:WKWebview中有一個WKScriptMessageHandler協(xié)議,想要實現(xiàn)交互,需要定義一個方法名,使用[self.webView.configuration.userContentController addScriptMessageHandler:self name:@"methodname"]方法設(shè)置,并通過userContentController:didReceiveScriptMessage方法,獲取對應(yīng)名稱的數(shù)據(jù)體進(jìn)行解析。
OC調(diào)用JS:通過evaluateJavaScript方法調(diào)用JS方法,需要在HTML文件中定義方法名,在原生這里調(diào)用。5、獲取網(wǎng)頁高度
① 在webviewdidFinishNavigation中獲取高度。
② 執(zhí)行evaluateJavaScript方法獲取網(wǎng)頁寬高,計算比例,再根據(jù)手機界面設(shè)置的寬高去設(shè)置。
③ 在HTML標(biāo)簽中加入特殊字符去接收高度,OC這邊通過evaluateJavaScript方法獲取。
九、關(guān)于多線程
1、關(guān)于多線程都有哪些了解?
NSThread NSOperation GCD 當(dāng)我們需要大量耗時操作時候,可以在多線程中去執(zhí)行任務(wù),避免主線程阻塞。2、NSThread使用:
兩種方法:
① 創(chuàng)建實例對象:
NSThread*thread = [[NSThread alloc] initWithTarget:self selector:@selector(upload)? object:@"參數(shù)"];
[thread start];
② 類方法創(chuàng)建:
[NSThread detachNewThreadSelector:@selector(upload) toTarget:@"" withObject:@""];
③ NSThread可以設(shè)置優(yōu)先級? 取值范圍 0.0 ~ 1.0 之間 最高是1.0 默認(rèn)優(yōu)先級是0.5
舉例:thread.threadPriority = 1.0;
④ 強制結(jié)束任務(wù)調(diào)用exit方法。3、CGD使用
CGD會把任務(wù)從隊列中取出,自動放到對應(yīng)的子線程中執(zhí)行,不需要程序員去管理線程,遵循FIFO原則,先進(jìn)先出,后進(jìn)后出。
如何使用:
① 創(chuàng)建隊列
//創(chuàng)建一個并行隊列? ?
dispatch_queue_t queue1 = dispatch_queue_create("queue1", DISPATCH_QUEUE_CONCURRENT);? ?
//創(chuàng)建一個串行隊列? ?
dispatch_queue_t queue2 = dispatch_queue_create("queue2", DISPATCH_QUEUE_SERIAL);
//獲取全局并發(fā)隊列? ?
dispatch_queue_t queue3 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);? ?
//獲取主隊列(一個串行隊列,放在主隊列的任務(wù)都會在主線程執(zhí)行)? ?
dispatch_queue_t queue4 = dispatch_get_main_queue();
② 執(zhí)行任務(wù)
dispatch_async(queue, ^{? ? ? 異步操作? ? ? });
dispatch_sync(queue, ^{? ? ? ? 同步操作? ? ? });
總結(jié):
同一串行隊列中,同步+同步 死鎖。同步+異步 正常。異步+同步 死鎖。異步+異步 正常。
同一并行隊列中,都正常。
例如,需要請求1結(jié)束后再執(zhí)行請求2,需要用到dispatch_group_t。
① enter leave方法

② 信號量方法

4、NSOperation的使用
基于GCD的封裝,更加面向?qū)ο蟆H绻呛唵蔚膬蓚€網(wǎng)絡(luò)請求,請求A優(yōu)先級大于請求B,就可以用NSOperation。使用addDependency方法,來設(shè)置先后順序。如果同時下載多個圖片,可以使用NSOperation,設(shè)置maxConcurrentOperationCount最大并發(fā)數(shù),然后使用NSBlockOperation創(chuàng)建對象去執(zhí)行任務(wù)。5、其他應(yīng)用:
① 調(diào)用NSObject方法,[self perform.....]
② 延時處理 dispatch_after
③ 使用定時器
④ 創(chuàng)建單例,代碼只會執(zhí)行一次 dispatch_once_t onceToken;
⑤ 柵欄函數(shù),前面的任務(wù)執(zhí)行完畢后才會執(zhí)行后面的任務(wù)。
dispatch_barrier_async(<#dispatch_queue_t? _Nonnull queue#>, <#^(void)block#>)
十、手勢傳遞
1、當(dāng)子view大小超出了父view,怎么獲取并響應(yīng)點擊事件?
重新父view的hintTest方法,遍歷父view上所有的子view,判斷這個point是否在子view上,如果在,返回子view去響應(yīng)事件,如果都沒有,返回父view去響應(yīng)事件。

2、擴大UIButton的點擊范圍
① 創(chuàng)建分類(Category)并重寫UIButton的pointInside:withEvent:方法

②創(chuàng)建分類(Category)并重寫UIButton的hitTest::withEvent:方法

十一、離屏渲染
設(shè)置了layer會有離屏渲染
①、②、③、④、⑤、⑥、⑦、⑧、⑨、⑩ ?、?、?、?、?、?、?、?、?、?