iOS面試必考問題
1巾腕、多態(tài)面睛、繼承、封裝
封裝:就是對類中的一些字段尊搬,方法進(jìn)行保護(hù)叁鉴,不被外界所訪問到,有一種權(quán)限的控制功能佛寿,四種訪問權(quán)限修飾符:public幌墓,default,protected冀泻,private常侣,訪問權(quán)限一次遞減的,這樣我們在定義類的時候弹渔,哪些字段和方法不想暴露出去胳施,哪些字段和方法可以暴露,可以通過修飾符來完成肢专,這就是封裝舞肆;
繼承:使得我們沒必要別寫重復(fù)的代碼,可重用性很高鸟召。缺點(diǎn):繼承提高了代碼的耦合性.優(yōu)點(diǎn): 提高復(fù)用性胆绊,維護(hù)性
多態(tài):定義類型和實(shí)際類型氨鹏,一般是基于接口的形式實(shí)現(xiàn)的
2欧募、代理模式、單例模式仆抵、觀察者模式跟继、工廠模式
代理這是iOS中一種消息傳遞的方式,也可以通過這種方式來傳遞一些參數(shù)
協(xié)議:用來指定代理雙方可以做什么镣丑,必須做什么舔糖。
代理:根據(jù)指定的協(xié)議,完成委托方需要實(shí)現(xiàn)的功能莺匠。
委托:根據(jù)指定的協(xié)議金吗,指定代理去完成什么功能。
協(xié)議是什么?有什么作用?
協(xié)議:聲明一系列的方法趣竣,可由任何類實(shí)施摇庙,即使遵守該協(xié)議的類沒有共同的超類。協(xié)議方法定義了獨(dú)立于任何特定類的行為遥缕。簡單的說卫袒,協(xié)議就是定義了一個接口,其他類負(fù)責(zé)來實(shí)現(xiàn)這些接口单匣。如果你的類實(shí)現(xiàn)了一個協(xié)議的方法時夕凝,則說該類遵循此協(xié)議宝穗。
協(xié)議的作用:
1.定義一套公用的接口(Public)
@required:必須實(shí)現(xiàn)的方法
@optional:可選實(shí)現(xiàn)的方法(可以全部都不實(shí)現(xiàn))
2.委托代理(Delegate)傳值:
它本身是一個設(shè)計模式,它的意思是委托別人去做某事码秉。
比如:兩個類之間的傳值逮矛,類A調(diào)用類B的方法,類B在執(zhí)行過程中遇到問題通知類A转砖,這時候我們需要用到代理(Delegate)橱鹏。
單例:確保某一個類只有一個實(shí)例,而且自行實(shí)例化并向整個系統(tǒng)提供整個實(shí)例堪藐。
優(yōu)點(diǎn):
1莉兰、減少內(nèi)存開支和系統(tǒng)性能開銷;
2礁竞、避免對資源的多重占用糖荒;
3、優(yōu)化和共享資源訪問模捂。
缺點(diǎn):
1捶朵、單例模式?jīng)]有接口,擴(kuò)展很困難狂男;
2综看、單例模式與單一職責(zé)有沖突。
通過GCD實(shí)現(xiàn)單例方法:
(.h文件中)
+(DBManager *)sharedManager;
.m文件中的實(shí)現(xiàn):
+(DBManager *)sharedManager{
Static DBManager *manager = nil;
static dispatch_once_t token;
dispatch_once(&token,^{
if(manager == nil){
manager = [[DBManager alloc]init];
}
} );
return manager;
}
3岖食、事件響應(yīng)連
hit-Testing:找出這個觸摸點(diǎn)下面的hit-test view的過程红碑,HitTest會檢測這個點(diǎn)擊的點(diǎn)是不是發(fā)生在這個View上,如果是的話泡垃,就會去遍歷這個View的subviews析珊,直到找到最小的能夠處理事件的view,如果整了一圈沒找到能夠處理的view蔑穴,則返回自身忠寻。
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;
//該方法的處理過程:
//首先調(diào)用當(dāng)前視圖的pointInside:withEvent:方法判斷觸摸點(diǎn)是否在當(dāng)前視圖內(nèi);
//YES在,則遍歷當(dāng)前視圖的所有子視圖(subviews)存和,調(diào)用子視圖的hitTest:withEvent:奕剃;
// NO不在,當(dāng)前視圖的hitTest:withEvent:返回nil
//若第一次有子視圖的hitTest:withEvent:方法返回非空對象,則當(dāng)前視圖的hitTest:withEvent:方法就返回此對象捐腿,處理結(jié)束
//若所有子視圖的hitTest:withEvent:方法都返回nil纵朋,則當(dāng)前視圖的hitTest:withEvent:方法返回當(dāng)前視圖自身(self)
//判斷觸摸點(diǎn)是否在當(dāng)前視圖內(nèi),可以用來實(shí)現(xiàn)擴(kuò)大View的相應(yīng)區(qū)域
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event;
1.事件的產(chǎn)生
發(fā)生觸摸事件后叙量,系統(tǒng)會將該事件加入到一個由UIApplication管理的事件隊列中為什么是隊列而不是棧倡蝙?因?yàn)殛犃械奶囟ㄊ窍冗M(jìn)先出,先產(chǎn)生的事件先處理才符合常理绞佩,所以把事件添加到隊列寺鸥。
UIApplication會從事件隊列中取出最前面的事件猪钮,并將事件分發(fā)下去以便處理,通常胆建,先發(fā)送事件給應(yīng)用程序的主窗口(keyWindow)烤低。
主窗口會在視圖層次結(jié)構(gòu)中找到一個最合適的視圖來處理觸摸事件,這也是整個事件處理過程的第一步笆载。
找到合適的視圖控件后扑馁,就會調(diào)用視圖控件的touches方法來作具體的事件處理。
2.事件的傳遞
觸摸事件的傳遞是從父控件傳遞到子控件
也就是UIApplication->window->尋找處理事件最合適的view
4凉驻、NSThread腻要、NSOperation、GCD
什么是進(jìn)程
a涝登、是指在系統(tǒng)中正在運(yùn)行的一個應(yīng)用程序雄家;
b、每個進(jìn)程之間是獨(dú)立的胀滚,每個進(jìn)程運(yùn)行在其專用且受保護(hù)的內(nèi)存空間趟济;
什么是線程
a、一個進(jìn)程想要執(zhí)行任務(wù)咽笼,必須得有線程(每個進(jìn)程至少要有一條線程)顷编;
b、線程是進(jìn)程的基本執(zhí)行單元剑刑,一個進(jìn)程(程序)的所有任務(wù)都在線程中執(zhí)行媳纬;
5、MVC叛甫、MVVM等 View層架構(gòu)模式
6层宫、NSUserDefault杨伙、Plist其监、NSCode、Sqlite限匣、CoreData抖苦、File
plist文件(屬性列表)
preference(偏好設(shè)置)
NSKeyedArchiver(歸檔)
SQLite 3
CoreData (icloud 無主鍵)
7、delegate米死、Notification锌历、block
block本質(zhì)是一個數(shù)據(jù)類型,多用于參數(shù)傳遞,代替代理方法, (有多個參數(shù)需要傳遞或者多個代理方法需要實(shí)現(xiàn)還是推薦使用代理方法),少用于當(dāng)做返回值傳遞. block是一個OC對象,它的功能是保存代碼片段,預(yù)先準(zhǔn)備好代碼,并在需要的時候執(zhí)行.
8、內(nèi)存管理峦筒、內(nèi)存優(yōu)化究西、內(nèi)存泄漏
自動釋放池@autoreleasepool
自動釋放池底層怎么實(shí)現(xiàn)
自動釋放池以棧的形式實(shí)現(xiàn):當(dāng)你創(chuàng)建一個新的自動釋放池時,它將被添加到棧頂物喷。當(dāng)一個對象收到發(fā)送autorelease消息時,它被添加到當(dāng)前線程的處于棧頂?shù)淖詣俞尫懦刂?當(dāng)自動釋放池被回收時,它們從棧中被刪除,并且會給池子里面所有的對象都會做一次release操作.
OC對象的生命周期取決于引用計數(shù)卤材,我們有兩種方式可以釋放對象:一種是直接調(diào)用release釋放遮斥;另一種是調(diào)用autorelease將對象加入自動釋放池中。自動釋放池用于存放那些需要在稍后某個時刻釋放的對象扇丛。
我們的Mac以及iOS系統(tǒng)會自動創(chuàng)建一些線程术吗,例如主線程和GCD中的線程,都默認(rèn)擁有自動釋放池帆精。每次執(zhí)行 “事件循環(huán)”(event loop)時较屿,就會將其清空,這一點(diǎn)非常重要卓练,請務(wù)必牢記隘蝎!
9、堆和棧
棧襟企,是由系統(tǒng)編譯器自動管理末贾,不需要程序員手動管理,存放方法(函數(shù))的參數(shù)值整吆,局部變量的值等拱撵,棧是向低地址擴(kuò)展的數(shù)據(jù)結(jié)構(gòu),是一塊連續(xù)的內(nèi)存區(qū)域
堆表蝙,釋放工作由程序員手動管理拴测,不及時回收容易產(chǎn)生內(nèi)存泄露,堆是向高地址擴(kuò)展的數(shù)據(jù)結(jié)構(gòu)府蛇,是不連續(xù)的內(nèi)存區(qū)域集索。這是由于系統(tǒng)是用鏈表來存儲的空閑內(nèi)存地址的,自然是不連續(xù)的汇跨,而鏈表的遍歷方向是由低地址向高地址务荆。堆的大小受限于計算機(jī)系統(tǒng)中有效的虛擬內(nèi)存。由此可見穷遂,堆獲得的空間比較靈活函匕,也比較大。
棧區(qū)存放局部變量蚪黑,先進(jìn)后出盅惜,當(dāng)程序執(zhí)行出了作用于的范圍,棧區(qū)局部變量就會被銷毀忌穿,所以我們也不需要管理棧區(qū)的內(nèi)存抒寂。
1.在iOS中,堆區(qū)的內(nèi)存是所有應(yīng)用程序共享的
2.堆區(qū)內(nèi)存分配是由系統(tǒng)來負(fù)責(zé)的
3.系統(tǒng)通過鏈表來維護(hù)所有已經(jīng)分配過的內(nèi)存空間
4.系統(tǒng)只是記錄分配了多少字節(jié)給應(yīng)用程序掠剑,但是并不管理具體分配給的對象類型
5.變量使用結(jié)束后屈芜,需要釋放內(nèi)存。在OC中當(dāng)retainCount == 0時朴译,就說明沒有任何變量使用該空間井佑,那么系統(tǒng)就會直接回收
6.如果我們使用某一個變量之后糕珊,不釋放內(nèi)存,那么該內(nèi)存就會被永遠(yuǎn)占用毅糟,造成內(nèi)存泄漏
7.當(dāng)對象已經(jīng)釋放红选,但是程序中的變量仍然指向該內(nèi)存地址,這個時候姆另,如果向該對象發(fā)送消息喇肋,就會出現(xiàn)經(jīng)典的野指針錯誤
10、const迹辐、static蝶防、extern
11、readwrite ,readonly strong明吩,weak间学,retain,assign印荔,copy , nonatomic , natomic
readwrite : 是可讀可寫屬性低葫,需要生成setter 和 getter 方法;
readonly: 是可讀屬性,只生成getter方法仍律,不生成setter方法嘿悬,不希望屬性再類外改變;
strong:
weak:
retain: 表示持有特性水泉,setter方法將傳入的參數(shù)保留善涨,再賦值,傳入?yún)?shù)引用計數(shù)retaincount + 1草则;
assign: 是賦值特性钢拧,setter方法將傳入的參數(shù)賦值給實(shí)體變量,僅設(shè)置變量時炕横,assign用于簡單數(shù)據(jù)類型 如 NSInterger ,double ,float ,bool
copy: 表示賦值特性源内,setter方式將傳入的對象復(fù)制一份,需完全復(fù)制一份新的變量時:
noatomic: 非原子性操作看锉,決定編譯器生成的setter和getter 方法是否是原子性的操作姿锭;
atomic: 表示多線程安全
12、documents伯铣,tmp,app轮纫,Library
14腔寡、category、extension(類擴(kuò)展)
類別:Category 是表示一個指向分類的結(jié)構(gòu)體的指針掌唾。
1.分類是用于給原有類添加方法的,因?yàn)榉诸惖慕Y(jié)構(gòu)體指針中放前,沒有屬性列表忿磅,只有方法列表。所以< 原則上講它只能添加方法, 不能添加屬性(成員變量),實(shí)際上可以通過其它方式添加屬性> ;
2.分類中的可以寫@property, 但不會生成setter/getter方法, 也不會生成實(shí)現(xiàn)以及私有的成員變量(編譯時會報警告);
3.可以在分類中訪問原有類中.h中的屬性;
4.如果分類中有和原有類同名的方法, 會優(yōu)先調(diào)用分類中的方法, 就是說會忽略原有類的方法凭语。所以同名方法調(diào)用的優(yōu)先級為 分類 > 本類 > 父類葱她。因此在開發(fā)中盡量不要覆蓋原有類;
5.如果多個分類中都有和原有類中同名的方法, 那么調(diào)用該方法的時候執(zhí)行誰由編譯器決定;編譯器會執(zhí)行最后一個參與編譯的分類中的方法似扔。
擴(kuò)展:
- 能為某個類添加成員變量,屬性,方法;
- 一般的類擴(kuò)展寫到.m文件中;
- 一般的私有屬性寫到類擴(kuò)展中
區(qū)別:
①類別中原則上只能增加方法(能添加屬性的的原因只是通過runtime解決無setter/getter的問題而已)吨些;
②類擴(kuò)展不僅可以增加方法,還可以增加實(shí)例變量(或者屬性)炒辉,只是該實(shí)例變量默認(rèn)是@private類型的(
用范圍只能在自身類豪墅,而不是子類或其他地方);
③類擴(kuò)展中聲明的方法沒被實(shí)現(xiàn)黔寇,編譯器會報警偶器,但是類別中的方法沒被實(shí)現(xiàn)編譯器是不會有任何警告的。這是因?yàn)轭悢U(kuò)展是在編譯階段被添加到類中缝裤,而類別是在運(yùn)行時添加到類中屏轰。
④類擴(kuò)展不能像類別那樣擁有獨(dú)立的實(shí)現(xiàn)部分(@implementation部分),也就是說憋飞,類擴(kuò)展所聲明的方法必須依托對應(yīng)類的實(shí)現(xiàn)部分來實(shí)現(xiàn)亭枷。
⑤定義在 .m 文件中的類擴(kuò)展方法為私有的,定義在 .h 文件(頭文件)中的類擴(kuò)展方法為公有的搀崭。類擴(kuò)展是在 .m 文件中聲明私有方法的非常好的方式叨粘。
15、KVO瘤睹、KVC
KVC的本質(zhì)就是 (鍵值編碼)
定義: 在對象創(chuàng)建完成之后,動態(tài)(牽扯到運(yùn)行時)的給對象的屬性賦值
KVO 的本質(zhì)就是(鍵值監(jiān)聽)
定義::Key-Value Observing,它提供一種機(jī)制,當(dāng)指定的對象的屬性被修改后,則對象就會接受到通知升敲。
KVO是基于KVC實(shí)現(xiàn)的,只有我們調(diào)用KVC去訪問key值的時候KVO才會起作用轰传。
KVO只用來監(jiān)聽屬性值的變化驴党,這個發(fā)送監(jiān)聽的操作是系統(tǒng)控制的,我們控制不了获茬,我們只能控制監(jiān)聽操作
通知需要一個發(fā)送notification的對象港庄,一般是notificationCenter,來通知觀察者恕曲。KVO是直接通知到觀察對象鹏氧,并且邏輯非常清晰,實(shí)現(xiàn)步驟簡單佩谣。
通知需要一個發(fā)送notification的對象把还,一般是notificationCenter,來通知觀察者。KVO是直接通知到觀察對象吊履,并且邏輯非常清晰安皱,實(shí)現(xiàn)步驟簡單。
16艇炎、runtime
17酌伊、runloop
指導(dǎo)文章:http://www.swiftyper.com/2017/01/04/runloop-learning-note/
RunLoop 是一種事件驅(qū)動(Event Driven)模型,
但是一個 iOS 應(yīng)用一旦啟動起來后就會一直處于等待用戶事件(類似點(diǎn)擊或者觸摸事件)的狀態(tài)缀踪,除非用戶手動關(guān)閉它居砖,
當(dāng)我們使用 Xcode 創(chuàng)建一個新的 iOS 應(yīng)用時,它會自動幫我們生成 main 方法(這個方法在 main.m 文件中)辜贵,而在 main 方法里面會調(diào)用 UIApplicationMain 方法悯蝉。UIApplicationMain 會將 AppDelegate 設(shè)置為應(yīng)用程序的代碼,同時會為主線程創(chuàng)建一個 RunLoop 并啟動托慨,因此任何一個 iOS 應(yīng)用一旦啟動了鼻由,就至少存在一個 RunLoop,并且這個 RunLoop 是屬于主線程的厚棵。之所以說屬于主線程蕉世,是因?yàn)?RunLoop 與線程的關(guān)系是一一對應(yīng)的。
其實(shí)事件驅(qū)動模型的本質(zhì)就是一個死循環(huán)婆硬,然后在循環(huán)中不斷等待消息的到來狠轻,然后對其進(jìn)行處理,
RunLoop 實(shí)際上就是一個對象彬犯,它為我們提供了一個進(jìn)入事件循環(huán)的入口函數(shù)向楼,線程執(zhí)行這個函數(shù)后就會處于消息循環(huán)中,直到循環(huán)結(jié)束(比如接收到退出消息)谐区。
在 iOS 中提供了兩個 RunLoop 對象:NSRunLoop 與 CFRunLoopRef湖蜕。
其中,NSRunLoop 是基于 CFRunLoopRef 的簡單封裝宋列。NSRunLoop 的 API 是非線程安全的昭抒,而 CFRunLoopRef 的 API 是線程安全的。更重要的一點(diǎn)是 CFRunLoopRef 的代碼是開源的炼杖。
特點(diǎn):1灭返、使程序一直運(yùn)行接收用戶輸入
2、決定程序何時處理哪些Event
3坤邪、調(diào)用解耦
4熙含、節(jié)省cpu
NSDefaulRunLoopMode (默認(rèn)狀態(tài),空閑狀態(tài))
NSTrackingRunLoopMode (滑動scrollView)
NSRunLoopCommonMode (上方兩著都可執(zhí)行)
列子: 當(dāng)列表滑動的時候罩扇,NSTime 不會動婆芦。
解答: 因NSTime處于DefaulRunLoopMode,當(dāng)滑動工程被切換到了TrackingRunLoopMode去關(guān)注滑動事件了,所以我們要將time設(shè)置為NSRunLoopCommonMode
一個TableView延時加載圖片的新思路
在cell里面把設(shè)置圖片在 NSDefaulRunLoopMode中做怕磨,其滑動過程就不會設(shè)置圖片喂饥,
18消约、View層架構(gòu)、網(wǎng)絡(luò)層架構(gòu)员帮、持久層架構(gòu)或粮、組件化架構(gòu)、動態(tài)部署方案
19捞高、UITableView的優(yōu)化
20氯材、二叉樹、時間復(fù)雜度
21硝岗、TCP,UDP,HTTP
TCP也就是傳輸控制協(xié)議氢哮。它是一種面向連接的、可靠的型檀、基于字節(jié)流的[傳輸層]通信協(xié)議冗尤。主要解決數(shù)據(jù)如何在網(wǎng)絡(luò)中如何傳輸?shù)模且环N長連接胀溺。建立一個TCP連接需要有三次握手裂七,而終止一個TCP連接需要有4次握手,這是由TCP的半關(guān)閉(half-close)造成的仓坞。TCP包頭的最小長度為20字節(jié)背零。大小不受限制,是可靠協(xié)議无埃,安全送達(dá)
UDP也就是用戶數(shù)據(jù)報協(xié)議徙瓶。它與TCP相對應(yīng),是一種面向無連接的嫉称、不可靠的數(shù)據(jù)報傳輸協(xié)議侦镇,即不與對方建立連接,就直接就把數(shù)據(jù)包發(fā)送過去澎埠,因此缺乏可靠性虽缕。由于缺乏可靠性,UDP應(yīng)用一般必須允許一定量的丟包蒲稳、出錯和復(fù)制氮趋。大小限制在64k之內(nèi)無需連接,所以不可靠協(xié)議江耀,但速度快
HTTP也就是超文本傳輸協(xié)議剩胁。HTTP是Web聯(lián)網(wǎng)的基礎(chǔ),也是手機(jī)聯(lián)網(wǎng)常用的協(xié)議之一祥国,HTTP協(xié)議是建立在TCP協(xié)議之上的一種應(yīng)用昵观。HTTP連接最顯著的特點(diǎn)是客戶端發(fā)送的每次請求都需要服務(wù)器回送響應(yīng)晾腔,在請求結(jié)束后,會主動釋放連接啊犬。從建立連接到關(guān)閉連接的過程稱為“一次連接”灼擂。
TCP與UDP的區(qū)別
(1)TCP是需要連接的傳輸協(xié)議(長連接),UDP是不需要連接的傳輸協(xié)議觉至;
(2)TCP對系統(tǒng)資源要求較多剔应,UDP要求較少;
(3)TCP的程序結(jié)構(gòu)較復(fù)雜语御,UDP程序結(jié)構(gòu)較簡單峻贮;
(4)TCP是流模式,UDP是數(shù)據(jù)報模式 应闯;
(5)TCP保證數(shù)據(jù)正確性纤控,UDP可能丟包,TCP保證數(shù)據(jù)順序碉纺,UDP不保證船万。
第三方庫實(shí)現(xiàn)原理解析:
SDWebImage 如何為 UIImageView 添加圖片(面試回答)
SDWebImage 中為 UIView 提供了一個分類叫做 WebCache, 這個分類中有一個最常用的接口, sd_setImageWithURL:placeholderImage:, 這個分類同時提供了很多類似的方法, 這些方法最終會調(diào)用一個同時具有 option progressBlock completionBlock 的方法, 而在這個類最終被調(diào)用的方法首先會檢查是否傳入了 placeholderImage 以及對應(yīng)的參數(shù), 并設(shè)置 placeholderImage.
然后會獲取 SDWebImageManager 中的單例調(diào)用一個 downloadImageWithURL:... 的方法來獲取圖片, 而這個 manager 獲取圖片的過程有大體上分為兩部分, 它首先會在 SDWebImageCache 中尋找圖片是否有對應(yīng)的緩存, 它會以 url 作為數(shù)據(jù)的索引先在內(nèi)存中尋找是否有對應(yīng)的緩存, 如果緩存未命中就會在磁盤中利用 MD5 處理過的 key 來繼續(xù)查詢對應(yīng)的數(shù)據(jù), 如果找到了, 就會把磁盤中的緩存?zhèn)浞莸絻?nèi)存中.
然而, 假設(shè)我們在內(nèi)存和磁盤緩存中都沒有命中, 那么 manager 就會調(diào)用它持有的一個 SDWebImageDownloader 對象的方法 downloadImageWithURL:... 來下載圖片, 這個方法會在執(zhí)行的過程中調(diào)用另一個方法 addProgressCallback:andCompletedBlock:forURL:createCallback: 來存儲下載過程中和下載完成的回調(diào), 當(dāng)回調(diào)塊是第一次添加的時候, 方法會實(shí)例化一個 NSMutableURLRequest 和 SDWebImageDownloaderOperation, 并將后者加入 downloader 持有的下載隊列開始圖片的異步下載.
而在圖片下載完成之后, 就會在主線程設(shè)置 image 屬性, 完成整個圖像的異步下載和配置.
總結(jié)SDWebImage 的圖片加載過程其實(shí)很符合我們的直覺:
查看緩存
緩存命中
返回圖片
更新 UIImageView
緩存未命中
異步下載圖片
加入緩存
更新 UIImageView
UITableView重用cell
UITableViewCell的復(fù)用機(jī)制是,在tableview中存在一個復(fù)用池.這個復(fù)用池是一個隊列或一個鏈表.然后通過dequeueReusableCellWithIdentifier:獲取一個cell,如果當(dāng)前cell不存在,即新建一個cell,并將當(dāng)前cell添加進(jìn)復(fù)用池中.如果當(dāng)前的cell數(shù)量已經(jīng)到過tableview所能容納的個數(shù),則會在滾動到下一個cell時,自動取出之前的cell并設(shè)置內(nèi)容.
排序算法
冒泡排序
NSMutableArray *arr_M = [NSMutableArray arrayWithObjects:@1,@4,@2,@3,@5,nil];
//遍歷`數(shù)組的個數(shù)`次
/*
i = 0 的時候,j的相鄰兩個位置都要比較排一下位置:
j = 0 的時候:arr_M = 41235
j = 1 的時候:arr_M = 42135
j = 2 的時候:arr_M = 42315
j = 3 的時候:arr_M = 42351
i = 1;
…… ……
*/
for (int i = 0; i < arr_M.count; ++i) {
//遍歷數(shù)組的每一個`索引`(不包括最后一個,因?yàn)楸容^的是j+1)
for (int j = 0; j < arr_M.count-1; ++j) {
//根據(jù)索引的`相鄰兩位`進(jìn)行`比較`
if (arr_M[j] < arr_M[j+1]) {
[arr_M exchangeObjectAtIndex:j withObjectAtIndex:j+1];
}
}
}
NSLog(@"最終結(jié)果:%@",arr_M);
選擇排序
NSMutableArray *arr_M = [NSMutableArray arrayWithObjects:@1,@4,@2,@3,@5,nil];
for (int i=0; i<arr_M.count; i++) {
for (int j=i+1; j<arr_M.count; j++) {
if (arr_M[i]<arr_M[j]) {
[arr_M exchangeObjectAtIndex:i withObjectAtIndex:j];
}
}
}
NSLog(@"%@",arr_M);
copy與mutableCopy 深復(fù)制和淺復(fù)制
copy 拷貝的對象為不可修改的惜辑,而mutableCopy拷貝的對象為可改變的對象唬涧,即使被復(fù)制的對象本身為不可改變的,調(diào)用mutableCopy方法復(fù)制出來的副本也是可修改的盛撑,當(dāng)程序調(diào)用對象的copy方法來復(fù)制自身時碎节,底層需要調(diào)用copyWithZone:方法來完成實(shí)際的復(fù)制工作,copy返回實(shí)際上就是copyWithZone:方法的返回值抵卫;mutableCopy與mutableCopyWithZone:方法也是同樣的道理狮荔。
淺復(fù)制和深復(fù)制:顧名思義,淺復(fù)制介粘,并不拷貝對象本身殖氏,僅僅是拷貝指向?qū)ο蟮闹羔槪簧顝?fù)制是直接拷貝整個對象內(nèi)容到另一塊內(nèi)存中姻采。再簡單些說:淺復(fù)制就是指針拷貝雅采;深復(fù)制就是內(nèi)容拷貝。
AsyncSocket
考慮到數(shù)據(jù)量比較大的問題慨亲,所以數(shù)據(jù)大的時候我會使用分包和組包的功能去實(shí)現(xiàn)婚瓜。TCP/IP在傳輸數(shù)據(jù)的時候,一般不會大于1500字節(jié)刑棵,所以我每512字節(jié)分了
一個包巴刻。然后當(dāng)一次性數(shù)據(jù)包接收太多的時候,就出現(xiàn)了粘包的問題蛉签。因?yàn)槲以跀?shù)據(jù)傳輸?shù)臅r候使用的是json胡陪,每一個分包都是由{}括起來的沥寥,所以我就想著在包頭上加上一段基本不會重復(fù)
的分割字符串,然后服務(wù)器接收到分包的時候每次都根據(jù)這個字符串分割一下柠座,第一次分割的時候第一行絕對是空字符串 例如:@Hinagiku{“Name”=“桂雛菊”}邑雅, 我分割出來結(jié)果是:
“”,“桂雛菊”愚隧,所以說第一行我就可以直接跳過蒂阱,每次取分包的時候從第二行開始取锻全。然后后臺根據(jù)包的ID號狂塘,序號進(jìn)行組包。如果當(dāng)前分包在5分鐘內(nèi)沒有接收完畢鳄厌,就代表當(dāng)前分包接收失敗
了荞胡,要求客戶端或服務(wù)器重新發(fā)送。粘包問題解決完畢之后了嚎,我開始實(shí)現(xiàn)心跳包功能勉吻,當(dāng)時想的是幽污,每隔1分鐘發(fā)一次心跳包,服務(wù)器放一個線程。每隔幾秒鐘判斷一次迫淹,當(dāng)前的所有TCP連接的
最后一次訪問時間是多少號,如果超過了這個時間則斷開當(dāng)前連接
tag參數(shù)是為了在回調(diào)方法中匹配發(fā)起調(diào)用的方法的颈渊,不會加在傳輸數(shù)據(jù)中瘟檩。
需注意的一點(diǎn)是:發(fā)送時的tag和接收時的tag是無關(guān)的。
以read為例分析:
- (void)readDataWithTimeout:(NSTimeInterval)timeout tag:(long)tag
上面的方法會生成一個數(shù)據(jù)類:AsyncReadPacket伶氢,此類中包含tag趟径,并把此對象放入數(shù)組theReadQueue中。
在CFStream中的回調(diào)方法中癣防,會取theReadQueue最新的一個蜗巧,在回調(diào)方法中取得tag,并將tag傳
給回調(diào)方法: - (void)onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag;
如此而已