AFHTTPRequestOperationManager
// 創(chuàng)建
AFHTTPRequestOperationManager *mgr = [AFHTTPRequestOperationManager manager];
// GET
- (AFHTTPRequestOperation *)GET:(NSString *)URLString
parameters:(id)parameters
success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure
// POST
- (AFHTTPRequestOperation *)POST:(NSString *)URLString
parameters:(id)parameters
success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure
// 上傳
- (AFHTTPRequestOperation *)POST:(NSString *)URLString
parameters:(id)parameters
constructingBodyWithBlock:(void (^)(id <AFMultipartFormData> formData))block
success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure
// 監(jiān)控
AFNetworkReachabilityManager *manager = [AFNetworkReachabilityManager sharedManager];
[manager startMonitoring];//監(jiān)控網(wǎng)絡(luò)連接狀態(tài)必須先調(diào)startMonitoring方法
[manager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
NSLog(@"%d", status);
}];
網(wǎng)絡(luò)傳輸協(xié)議UDP、TCP簇搅、Http、Socket软吐、XMPP
1瘩将、應(yīng)用層http?iOS1發(fā)信息給服務(wù)器凹耙,iOS2如果想要知道iOS1發(fā)的信息是什么姿现,只有iOS2客戶端向服務(wù)器發(fā)送請(qǐng)求,服務(wù)器才會(huì)將服務(wù)器的數(shù)據(jù)返回給客戶端肖抱。因此iOS2需要以輪詢的方式不斷向服務(wù)器發(fā)送請(qǐng)求查看是否有其它客服端給自己發(fā)送信息备典。就像收快遞的時(shí)候,你只知道快遞要來意述,但是并不會(huì)知道具體什么時(shí)候快遞會(huì)到提佣。所以一會(huì)去看一下吮蛹,一會(huì)兒又去看一下
2、應(yīng)用層Socket拌屏?Socket協(xié)議的服務(wù)器會(huì)主動(dòng)將消息發(fā)送給客戶端潮针。當(dāng)iOS1將消息發(fā)送給服務(wù)器后,服務(wù)器會(huì)馬上將消息轉(zhuǎn)發(fā)到iOS2,服務(wù)器可以和客戶端主動(dòng)交互倚喂,相對(duì)于http協(xié)議而言每篷,轉(zhuǎn)發(fā)速度比較快。XMPP是基于Socket的封裝端圈,環(huán)信是XMPP的封裝雳攘。
4、傳輸層TCP枫笛?控制傳輸協(xié)議吨灭,三次握手,四次揮手刑巧。類似于打電話喧兄,先接通對(duì)方后再一句話一句話地口述內(nèi)容傳輸給對(duì)方“〕可以有效地保證對(duì)方能夠及時(shí)接收到傳遞的信息
5吠冤、傳輸層UDP?用戶數(shù)據(jù)報(bào)協(xié)議恭理。協(xié)議通信的前提是知道服務(wù)器的ip號(hào)和端口號(hào)拯辙。UDP協(xié)議傳輸跟發(fā)郵件類似。UDP只負(fù)責(zé)發(fā)送颜价,而且每次發(fā)送都需要對(duì)方的IP號(hào)和端口號(hào)涯保。所以有時(shí)候發(fā)送郵件對(duì)方接受不到,同樣別人給我發(fā)郵件周伦,但是我并沒有接受到
6夕春、TCP與UDP區(qū)別?發(fā)文檔兩種方式专挪,后者是郵件及志,并不能保證你對(duì)方一定接受到。前者則是先打電話寨腔,接通后建立連接成功速侈,不是通過郵件貌嫡,而是直接通過口述直接把內(nèi)容傳輸給對(duì)方废封,三次握手(打電話,接通随夸,建立連接)
OAuth2.0原理
用戶身份驗(yàn)證和授權(quán)方式靖避。也就是這么說潭枣,我們想要提取到新浪API服務(wù)器里的信息,但是不能使用用戶的登錄名和密碼去訪問幻捏,因?yàn)閺母旧蟻碚f都不會(huì)讓第三方應(yīng)用獲取到用戶名和登陸密碼盆犁。那又通過什么方式來請(qǐng)求登陸用戶的信息呢?自然通過在用戶使用新浪的授權(quán)服務(wù)器進(jìn)行用戶名和密碼的第三方應(yīng)用授權(quán)登陸成功后篡九。當(dāng)然前提是必須這個(gè)第三方應(yīng)用已經(jīng)在新浪的開發(fā)平臺(tái)上已經(jīng)創(chuàng)建了引用且獲取到了AppKey和相關(guān)的秘鑰已經(jīng)審核通過谐岁。那么這時(shí)候新浪的授權(quán)服務(wù)器會(huì)返回給第三方引用一個(gè)Access Token,也就是一段字符串榛臼,第三方應(yīng)用可以將授權(quán)服務(wù)器返回回來的字符串結(jié)合在請(qǐng)求地址中伊佃,這時(shí)候就能夠獲取到用戶的新浪API信息了。為了防止抓包的人去獲取到這個(gè)字符串沛善,新浪對(duì)返回回來的字符串還設(shè)置了生命周期航揉。新浪避免用戶信息泄露到其他客戶端的服務(wù)器上,新浪里面有一個(gè)專門對(duì)用戶做授權(quán)的服務(wù)器金刁,目的是記錄用戶是否授權(quán)第三方應(yīng)用登陸微博帅涂。授權(quán)服務(wù)器在接收到驗(yàn)證授權(quán)請(qǐng)求時(shí),會(huì)按照OAuth2.0協(xié)議對(duì)本請(qǐng)求的請(qǐng)求頭部尤蛮、請(qǐng)求參數(shù)進(jìn)行檢驗(yàn)媳友,若請(qǐng)求不合法或驗(yàn)證未通過,授權(quán)服務(wù)器還會(huì)返回相應(yīng)的錯(cuò)誤信息产捞。
GET請(qǐng)求VsPOST請(qǐng)求
方便和安全——除了往服務(wù)器發(fā)送請(qǐng)求頭以外醇锚,其它任何請(qǐng)求體都不會(huì)發(fā)送∨髁伲可以很好地保護(hù)服務(wù)器的封裝性焊唬。GET對(duì)于服務(wù)器安全,POST對(duì)于用戶安全看靠。而且POST向服務(wù)器請(qǐng)求數(shù)據(jù)或者發(fā)送數(shù)據(jù)時(shí)使用起來比較靈活求晶。
GET:
·當(dāng)訪問的時(shí)候,參數(shù)暴露在外面衷笋,任何人都可以看見參數(shù)信息芳杏,從這種角度來講,相對(duì)POST來說不安全
·通過GET方式拼接參數(shù)向服務(wù)器發(fā)送請(qǐng)求時(shí)辟宗,瀏覽器里面網(wǎng)址的長度有上限
POST:
·使用相對(duì)來說沒有GET便捷
·POST請(qǐng)求過程中爵赵,除了往服務(wù)器發(fā)送請(qǐng)求頭信息以外,還需要給服務(wù)器發(fā)送請(qǐng)求體泊脐,從這個(gè)角度來講空幻,破壞了服務(wù)器的封裝性,POST相對(duì)GET是不安全的
YTKNetwork基礎(chǔ)用法網(wǎng)絡(luò)配置類YTKNetworkConfig的功能使用方法
1容客、配置所有網(wǎng)絡(luò)請(qǐng)求的主機(jī)地址和CDN地址2秕铛、與url過濾類一起來統(tǒng)一對(duì)所有的url進(jìn)行更改 3约郁、本質(zhì)是一個(gè)單例類,所以直接實(shí)例化對(duì)象但两,設(shè)置對(duì)象的baseUrl和cdnUrl屬性
網(wǎng)絡(luò)請(qǐng)求類YTKRequest
這相當(dāng)于我們的BaseRequest鬓梅,以后每一個(gè)網(wǎng)絡(luò)請(qǐng)求對(duì)象都將繼承于它,一個(gè)網(wǎng)絡(luò)請(qǐng)求一個(gè)對(duì)象谨湘。使用就是通過覆蓋父類的方法來建立特定的網(wǎng)絡(luò)請(qǐng)求對(duì)象绽快,主要就是覆蓋requestUrl、requestMethod紧阔、requestArgument等方法坊罢,需要特別注意的是因?yàn)樵敿?xì)網(wǎng)址已經(jīng)設(shè)置在YTKNetworkConfig類的baseUrl屬性里,因此這里的requestUrl就不用再寫服務(wù)器的主機(jī)地址了擅耽。請(qǐng)求數(shù)據(jù)之前先判斷已有的JSON數(shù)據(jù)是否無效的屬性:jsonValidator檢查網(wǎng)絡(luò)請(qǐng)求到的JSON與已有的JSON是否一致:statusCodeValidator屬性如果我們要POST文件就需要用到屬性:constructingBodyBlock
如何打破Block回調(diào)中的循環(huán)引用在block里直接用self
就是在網(wǎng)絡(luò)請(qǐng)求結(jié)束之后活孩,調(diào)用將Block置空的方法來防止循環(huán)引用,這樣就可以少些很多的typeof self weafSelf;使用方法乖仇?pod ‘YTKNetwork’诱鞠。將每一個(gè)網(wǎng)絡(luò)請(qǐng)求都封裝成對(duì)象,每一個(gè)請(qǐng)求都需要繼承YTKRequest類这敬,通過覆蓋父類的一些方法來構(gòu)造制定的網(wǎng)絡(luò)請(qǐng)求航夺。就是在網(wǎng)絡(luò)請(qǐng)求結(jié)束之后,調(diào)用將Block置空的方法來防止循環(huán)引用崔涂,這樣就可以少些很多的__weak typeof (self) weafSelf = self;
網(wǎng)絡(luò)封裝的適用場(chǎng)景
1阳掐、需要緩存2、網(wǎng)絡(luò)請(qǐng)求之間存在依賴冷蚂,即B網(wǎng)絡(luò)請(qǐng)求是否開始與A網(wǎng)絡(luò)請(qǐng)求的結(jié)果直接相關(guān)3缭保、依賴特定的版本號(hào)來判斷緩存的內(nèi)容已過期4、不僅可以實(shí)現(xiàn)Block回調(diào)蝙茶,更是可以通過設(shè)置delegate屬性等于self艺骂,同時(shí)調(diào)用start方法進(jìn)行委托回調(diào)。按時(shí)間緩存網(wǎng)絡(luò)請(qǐng)求內(nèi)容隆夯?意思是說按一定的時(shí)間規(guī)律來進(jìn)行緩存還是說超過了一定時(shí)間后已經(jīng)緩存的內(nèi)容失效钳恕,其實(shí)兩者并不矛盾,正是因?yàn)榫彺娴膬?nèi)容在一段時(shí)間之后失效蹄衷,所以需要重新請(qǐng)求數(shù)據(jù)忧额,進(jìn)行緩存。什么叫按版本號(hào)緩存網(wǎng)絡(luò)請(qǐng)求地址愧口?網(wǎng)絡(luò)請(qǐng)求中統(tǒng)一設(shè)置CDN地址睦番,什么是CDN地址?全稱content delivery Network,內(nèi)容分發(fā)網(wǎng)絡(luò)。通過在網(wǎng)絡(luò)各處放置節(jié)點(diǎn)服務(wù)器實(shí)現(xiàn)在現(xiàn)有的服務(wù)器基礎(chǔ)上補(bǔ)充一層智能虛擬網(wǎng)絡(luò)托嚣,智能引導(dǎo)用戶到最近的服務(wù)器上巩检,是內(nèi)容傳輸?shù)母臁⒏€(wěn)定示启。相互依賴的網(wǎng)絡(luò)請(qǐng)求的發(fā)送兢哭?就是一個(gè)網(wǎng)絡(luò)請(qǐng)求的啟動(dòng)開關(guān)取決于另一個(gè)網(wǎng)絡(luò)請(qǐng)求的請(qǐng)求結(jié)果。
驗(yàn)證JSON的合法性
因?yàn)閺姆?wù)器獲得的JSON數(shù)據(jù)不一定總是可信賴的丑搔,如果數(shù)據(jù)是從有故障的服務(wù)器返回了一個(gè)錯(cuò)誤的格式,就非常容易造成客戶端崩潰提揍。就加入說正常情況下啤月,我們登錄個(gè)人中心,返回的昵稱總是字符串劳跃,用戶年齡都是數(shù)字谎仲,可是假如返回的文件格式出現(xiàn)問題,那么客戶端必崩無疑刨仑。所以驗(yàn)證的方法就是重寫父類基礎(chǔ)網(wǎng)絡(luò)請(qǐng)求YTKRequest類的- (id)jsonValidator方法郑诺。返回的是一個(gè)字典,這個(gè)字典里面的鍵就是字符串形參杉武,鍵值對(duì)的值就是【NSString class】辙诞。
在YTKNetworkConfig的對(duì)象里同時(shí)設(shè)置了baseUrl和cdnUrl屬性?
1轻抱、默認(rèn)使用baseUrl2飞涂、如果想要使用cdnUrl,就必須重寫基礎(chǔ)網(wǎng)絡(luò)請(qǐng)求YTKRequest類的-(BOOL)useCDN方法并返回YES
實(shí)現(xiàn)斷點(diǎn)續(xù)傳下載祈搜?
重寫基礎(chǔ)網(wǎng)絡(luò)請(qǐng)求YTKRequest類的resumableDownloadPath方法较店,直接返回一個(gè)文件路徑字符串。這里面就涉及到庫路徑Lib Path容燕。
緩存路徑cachePath如何實(shí)現(xiàn)緩存數(shù)據(jù)的存攘撼省?
首先就是通過重寫基礎(chǔ)網(wǎng)絡(luò)請(qǐng)求YTKRequest類的- (NSInteger的)cacheTimeInSeconds方法設(shè)定高速緩存數(shù)據(jù)的有效秒數(shù)蘸秘,如果緩存的數(shù)據(jù)沒有到期官卡,在緩存期內(nèi)調(diào)用start方法或是startWithCompletionBlockWithSuccess方法時(shí)實(shí)際上并不會(huì)發(fā)送真正的請(qǐng)求,而是直接返回當(dāng)初高速緩存的數(shù)據(jù)醋虏,只有在緩存過期時(shí)味抖,才會(huì)真正地發(fā)送網(wǎng)絡(luò)請(qǐng)求。
高級(jí)用法
批量編輯網(wǎng)絡(luò)請(qǐng)求路徑
首先分析url參數(shù)過濾的類的實(shí)現(xiàn)原理灰粮,其實(shí)本質(zhì)就是通過一個(gè)全新的自定義類來整合基礎(chǔ)的url和新增的參數(shù)字典仔涩,然后介紹url參數(shù)過濾的類的使用方法。1粘舟、新建一個(gè)url參數(shù)過濾的類熔脂,這個(gè)類的.h方法里面有一個(gè)協(xié)議佩研,協(xié)議里面主要實(shí)現(xiàn)了兩個(gè)方法,第一個(gè)方法輸入?yún)?shù)是一個(gè)字典霞揉,返回一個(gè)自定義類的對(duì)象旬薯;第二個(gè)方法就是輸入?yún)?shù)一為原始url的字符串,參數(shù)二為基礎(chǔ)網(wǎng)絡(luò)請(qǐng)求類的對(duì)象适秩,返回參數(shù)是一個(gè)全新的url字符串绊序。2、.m方法里面首先包括一個(gè)名為參數(shù)arguments的全局變量字典秽荞。前面說過返回的參數(shù)為url參數(shù)過濾的類的對(duì)象本身骤公,恰好這個(gè)url參數(shù)過濾的類的對(duì)象的初始化就需要傳入argument字典。讓我奇怪的是為什么是url參數(shù)過濾的類生成類時(shí)的self對(duì)象來調(diào)用alloc來開辟內(nèi)存空間扬跋。而且又返回一個(gè)url參數(shù)過濾的類的對(duì)象阶捆,好奇怪。3钦听、在輸入原始url和增加的request兩個(gè)參數(shù)之后返回一個(gè)全新的url字符串洒试。這里面務(wù)必注意這里是通過網(wǎng)絡(luò)請(qǐng)求私有類來調(diào)用一個(gè)方法拼接原始的url和新增的參數(shù)字典,最后返回一個(gè)字符串朴上,反而當(dāng)初傳進(jìn)來的request網(wǎng)絡(luò)請(qǐng)求類對(duì)象并沒有使用垒棋。
把客戶端的版本號(hào)添加到所有的網(wǎng)絡(luò)請(qǐng)求url之中
其實(shí)就是在設(shè)置根控制器為可見的方法里使用兩個(gè)類來實(shí)現(xiàn)網(wǎng)絡(luò)參數(shù)的統(tǒng)一添加,所有的url都變成全新的url痪宰,當(dāng)務(wù)之急還是先通過url參數(shù)過濾的類調(diào)用類方法傳入?yún)?shù)實(shí)例化一個(gè)url參數(shù)過濾的類的對(duì)象捕犬。然后再通過網(wǎng)絡(luò)配置這個(gè)單例類對(duì)象調(diào)用添加url參數(shù)過濾的類的對(duì)象的方法,其目的就是將添加后的參數(shù)推廣到所有網(wǎng)絡(luò)請(qǐng)求url酵镜。
同一時(shí)間發(fā)送批量的網(wǎng)絡(luò)請(qǐng)求并統(tǒng)一處理同時(shí)請(qǐng)求成功時(shí)的回調(diào)
1碉碉、實(shí)例化4個(gè)Api對(duì)象2、把這4個(gè)對(duì)象以數(shù)組的元素的形式傳入初始化YTKBatchRequest對(duì)象的數(shù)組形參里3淮韭、YTKBatchRequest對(duì)象調(diào)用startWithCompletionBlockWithSuccess方法4垢粮、在4個(gè)Api對(duì)象全部請(qǐng)求成功的Block回調(diào)里實(shí)例化一個(gè)臨時(shí)數(shù)組并賦值為YTKBatchRequest對(duì)象的requestArray屬性5、再各自使用4個(gè)Api類實(shí)例四個(gè)Api對(duì)象靠粪,一一對(duì)應(yīng)requestArray的每一個(gè)元素蜡吧,但然需要強(qiáng)轉(zhuǎn)6、現(xiàn)在就跟一個(gè)Api請(qǐng)求成功獲得的request沒有什么區(qū)別了占键。
加載緩存數(shù)據(jù)的高級(jí)用法
由于加載慢昔善,所以先調(diào)用方法[api cacheJson]顯示上次緩存的內(nèi)容,加載成功后畔乙,再用最新的內(nèi)容替換上次的內(nèi)容君仆,如是斷網(wǎng)狀態(tài)也先顯示上次緩存中的內(nèi)容。當(dāng)初不是還需要Api先來判斷緩存的數(shù)據(jù)是否在有效時(shí)間內(nèi),然后再?zèng)Q定是否是真的請(qǐng)求數(shù)據(jù)返咱。而現(xiàn)在就是直接調(diào)用- (id)cacheJson方法獲得上次緩存的內(nèi)容钥庇。前提必須重寫父類設(shè)定緩存有效時(shí)間的方法- (NSInteger)cacheTimeInSeconds返回一個(gè)大于0的值,這樣才能開啟基礎(chǔ)網(wǎng)絡(luò)請(qǐng)求類的緩存功能咖摹。因?yàn)槟J(rèn)情況下緩存數(shù)據(jù)的有效時(shí)間為0秒评姨。
上傳文件獲得一個(gè)訪問文件的Api
關(guān)鍵就是重寫基礎(chǔ)網(wǎng)絡(luò)請(qǐng)求類的- (AFConstructingBlock)constructingBodyBlock方法,在這個(gè)方法里直return一個(gè)Block代碼塊萤晴,返回參數(shù)為void直接忽略沒寫吐句,輸入?yún)?shù)為一個(gè)滿足協(xié)議的formData值,數(shù)據(jù)類型自然不確定是id啦店读。這就相當(dāng)于直接return一個(gè)NSString *tempString = @“”嗦枢,只不過這里的tempString是一個(gè)^(idformData)輸入?yún)?shù)為fromData的Block,而且這個(gè)地方直接給Block賦值了,真的是既聲明又賦值两入。賦值的代碼里面首先就是壓縮圖片成NSData數(shù)據(jù)净宵。最后通過formData調(diào)用appendPartWithFileData方法正式上傳圖片敲才。尤其注意這里需要傳入三個(gè)參數(shù)裹纳,其一是文件的二進(jìn)制數(shù)據(jù),其二是文件名字紧武,其三是文件夾的名字剃氧,其四很關(guān)鍵,需要傳入文件類型阻星,圖片通常都是image/jpeg朋鞍。另外文件名字和文件夾的名字可以相同,而且通常來說就是image妥箕。
統(tǒng)一設(shè)置網(wǎng)絡(luò)請(qǐng)求Api的請(qǐng)求頭HeaderField
重寫覆蓋基礎(chǔ)網(wǎng)絡(luò)請(qǐng)求類的-(NSDictionnary*)requestHeaderFieldValueDictionary方法返回一個(gè)請(qǐng)求頭的鍵值對(duì)的字典滥酥。務(wù)必注意,請(qǐng)求頭字典的鍵和值都必須是NSString類型畦幢。
自定義網(wǎng)絡(luò)請(qǐng)求類對(duì)象
重寫最最基礎(chǔ)網(wǎng)絡(luò)請(qǐng)求YTKBaseRequest類的- (NSURLRequest *)buildCustomUrlRequest
方法坎吻,返回一個(gè)基礎(chǔ)網(wǎng)絡(luò)請(qǐng)求類的對(duì)象。尤其注意的是宇葱,只要返回的基礎(chǔ)網(wǎng)絡(luò)請(qǐng)求類的對(duì)象非nil不為空瘦真,那么會(huì)忽略其它一切自定義request的方法。
按時(shí)間緩存網(wǎng)絡(luò)請(qǐng)求內(nèi)容
意思是說按一定的時(shí)間規(guī)律來進(jìn)行緩存還是說超過了一定時(shí)間后已經(jīng)緩存的內(nèi)容失效黍瞧,其實(shí)兩者并不矛盾诸尽,正是因?yàn)榫彺娴膬?nèi)容在一段時(shí)間之后失效,所以需要重新請(qǐng)求數(shù)據(jù)印颤,進(jìn)行緩存您机。
按版本號(hào)緩存網(wǎng)絡(luò)請(qǐng)求地址
網(wǎng)絡(luò)請(qǐng)求中統(tǒng)一設(shè)置CDN地址,什么是CDN地址?
全稱content delivery Network,內(nèi)容分發(fā)網(wǎng)絡(luò)往产。通過在網(wǎng)絡(luò)各處放置節(jié)點(diǎn)服務(wù)器實(shí)現(xiàn)在現(xiàn)有的服務(wù)器基礎(chǔ)上補(bǔ)充一層智能虛擬網(wǎng)絡(luò)被碗,智能引導(dǎo)用戶到最近的服務(wù)器上,是內(nèi)容傳輸?shù)母旆麓濉⒏€(wěn)定锐朴。
相互依賴的網(wǎng)絡(luò)請(qǐng)求的發(fā)送
就是一個(gè)網(wǎng)絡(luò)請(qǐng)求的啟動(dòng)開關(guān)取決于另一個(gè)網(wǎng)絡(luò)請(qǐng)求的請(qǐng)求結(jié)果。
一次性更改所有的網(wǎng)絡(luò)請(qǐng)求路徑
首先分析url參數(shù)過濾的類的實(shí)現(xiàn)原理蔼囊,其實(shí)本質(zhì)就是通過一個(gè)全新的自定義類來整合基礎(chǔ)的url和新增的參數(shù)字典焚志,然后介紹url參數(shù)過濾的類的使用方法。
1畏鼓、新建一個(gè)url參數(shù)過濾的類酱酬,這個(gè)類的.h方法里面有一個(gè)協(xié)議,協(xié)議里面主要實(shí)現(xiàn)了兩個(gè)方法云矫,第一個(gè)方法輸入?yún)?shù)是一個(gè)字典膳沽,返回一個(gè)自定義類的對(duì)象;第二個(gè)方法就是輸入?yún)?shù)一為原始url的字符串让禀,參數(shù)二為基礎(chǔ)網(wǎng)絡(luò)請(qǐng)求類的對(duì)象挑社,返回參數(shù)是一個(gè)全新的url字符串。
2巡揍、.m方法里面首先包括一個(gè)名為參數(shù)arguments的全局變量字典痛阻。前面說過返回的參數(shù)為url參數(shù)過濾的類的對(duì)象本身,恰好這個(gè)url參數(shù)過濾的類的對(duì)象的初始化就需要傳入argument字典腮敌。讓我奇怪的是為什么是url參數(shù)過濾的類生成類時(shí)的self對(duì)象來調(diào)用alloc來開辟內(nèi)存空間阱当。而且又返回一個(gè)url參數(shù)過濾的類的對(duì)象,好奇怪糜工。
3弊添、在輸入原始url和增加的request兩個(gè)參數(shù)之后返回一個(gè)全新的url字符串。這里面務(wù)必注意這里是通過網(wǎng)絡(luò)請(qǐng)求私有類來調(diào)用一個(gè)方法拼接原始的url和新增的參數(shù)字典捌木,最后返回一個(gè)字符串油坝,反而當(dāng)初傳進(jìn)來的request網(wǎng)絡(luò)請(qǐng)求類對(duì)象并沒有使用。
get請(qǐng)求參數(shù)會(huì)暴露在外面钮莲,從這個(gè)角度來講免钻,get相對(duì)不安全,但是post請(qǐng)求將請(qǐng)求參數(shù)封裝到請(qǐng)求體中上傳到服務(wù)器崔拥,上傳的數(shù)據(jù)可能會(huì)有問題极舔,會(huì)對(duì)服務(wù)器進(jìn)行攻擊,從這個(gè)角度來講链瓦,post相對(duì)服務(wù)器而言并不安全拆魏,而且get請(qǐng)求的請(qǐng)求參數(shù)長度受限盯桦,但post的請(qǐng)求參數(shù)由于事先封裝,所以并不限制長度渤刃。
ASI網(wǎng)絡(luò)請(qǐng)求
當(dāng)然再進(jìn)行對(duì)ASI的進(jìn)一步封裝使用之前拥峦,必須先要明白的就是將ASI導(dǎo)入到工程中所需要做的準(zhǔn)備。CFNetwork.framework/SystemConfiguration.framework/MobileCoreServices.framework/CoreGraphics.framework/libz.dylib/libxml2.dylib卖子。最后libxml2還需要設(shè)置連接選項(xiàng):點(diǎn)擊build setting搜索usr點(diǎn)擊Header Seacher Path添加路徑(/usr/include/libxml2)略号。而且可能會(huì)出現(xiàn)一個(gè)問題就是: 如果Xcode版本高于6.3,增添依賴框架時(shí)搜libz.dylib和libxml2.dylib會(huì)有問題洋闽,而且在點(diǎn)擊build setting搜索usr也會(huì)有問題玄柠!當(dāng)然還必須考慮到ASI是以手動(dòng)管理為主,因此必須添加編譯混編(-fno-objc-arc).
//通過POST下載數(shù)據(jù)
- (void)postWetherData {
// 1.接口
NSString *path = @"http://www.webxml.com.cn/WebServices/WeatherWebService.asmx/getWeatherbyCityName";
// 2.請(qǐng)求體-直接轉(zhuǎn)碼
NSString *httpBody = [@"theCityName=北京" URLEncodedString];
// 3.將接口轉(zhuǎn)換成URL
NSURL *url = [NSURL URLWithString:path];
// 4.創(chuàng)建請(qǐng)求
ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];
// (1)設(shè)置請(qǐng)求方式
[request setRequestMethod:@"POST"];
// (2)設(shè)置來自上傳流信息
[request addRequestHeader:@"Content-Type" value:@"application/x-www-form-urlencoded"];
[request addRequestHeader:@"Content-Length" value:[NSString stringWithFormat:@"%ld",(long)httpBody.length]];
// (3)設(shè)置請(qǐng)求體
[request appendPostData:[httpBody dataUsingEncoding:NSUTF8StringEncoding]];
// (4)設(shè)置tag值
request.tag = 2000;
[self setRequest:request andRequestType:Enum_PostWeatherData];
// 5.將線程添加到隊(duì)列里面
[_netWorkQueue addOperation:request];
}
- (void)getWeatherData {
// 1.創(chuàng)建線程
NSString *path = [@"http://www.webxml.com.cn/WebServices/WeatherWebService.asmx/getWeatherbyCityName?theCityName=北京" URLEncodedString];
NSURL *url = [NSURL URLWithString:path];
ASIHTTPRequest *asiHttpRequest = [ASIHTTPRequest requestWithURL:url];
asiHttpRequest.tag = 1000;
[self setRequest:asiHttpRequest andRequestType:Enum_GetWeatherData];
// 2.添加線程到隊(duì)列
[_netWorkQueue addOperation:asiHttpRequest];
}
ASI網(wǎng)絡(luò)庫封裝:ASI作為AFN出來之前廣為人知的網(wǎng)絡(luò)請(qǐng)求庫诫舅,封裝ASI變得尤為重要∮鹄現(xiàn)在談?wù)勎覍?duì)于ASI封裝的理解,到底是以謳歌怎樣的邏輯刊懈,首先我們必須知道一點(diǎn)就是ASI里面三個(gè)很重要的類这弧。分別是:ASIHTTPRequest/ASIFormDataRequest/ASINetworkQueue這三個(gè)類⌒檠矗可是我們需要這三個(gè)類到底做些什么呢匾浪?現(xiàn)在來說,我知道的就是線程的問題泽疆,這么說吧户矢!ASIHTTPRequest繼承于NSOperation,ASINetworkQueue繼承于NSOperationQueue玲献,而ASIFormDataRequest繼承于ASIHTTPRequest殉疼,主要用于網(wǎng)絡(luò)的Post請(qǐng)求,當(dāng)然也具備get請(qǐng)求的功能“颇辏現(xiàn)在來說瓢娜,我們從線程和對(duì)列的角度來分析如何封裝這個(gè)ASI庫的出發(fā)點(diǎn)是什么呢?首先礼预,我們希望通過ASINetworkQueue創(chuàng)建一個(gè)隊(duì)列的單例用于管理添加到隊(duì)列里的任務(wù)或者說是線程眠砾。當(dāng)然話都說到這一步肯定就是表明ASINetworkQueue創(chuàng)建的單例隊(duì)列是一個(gè)并行隊(duì)列了。這個(gè)隊(duì)列用于管理所有添加到這個(gè)隊(duì)列里的任務(wù)托酸。既然說到管理褒颈,那就必須談到具體管理什么?當(dāng)然是要管理隊(duì)列里的每一個(gè)任務(wù)励堡。問題就來了谷丸,為什么必須將ASI封裝成以一個(gè)單例管理器。首先就是需要明白到底為什么要封裝ASI的使用应结,原因其實(shí)在做項(xiàng)目的時(shí)候就已經(jīng)體現(xiàn)出來價(jià)值了刨疼,就是在一個(gè)控制器中泉唁,如果視圖里的一個(gè)控件有一個(gè)網(wǎng)絡(luò)接口,也就是說揩慕,一個(gè)控制器需要多個(gè)網(wǎng)絡(luò)接口的數(shù)據(jù)才能得到實(shí)現(xiàn)亭畜。如果對(duì)于同步請(qǐng)求來說還比較容易,只是顯得代碼比較冗雜而已迎卤,但是如果是異步請(qǐng)求還會(huì)有許多的委托協(xié)議里的方法拴鸵,這樣不僅會(huì)造成控制器里的代碼超多,而且會(huì)增加很多很多的委托方法蜗搔,設(shè)想一下宝踪,如果TableView需要ASI從網(wǎng)絡(luò)請(qǐng)求數(shù)據(jù),上面哪一個(gè)WebView也需要從不同于TableView的網(wǎng)絡(luò)接口獲取數(shù)據(jù)碍扔,而且兩個(gè)都必須是異步操作瘩燥,這樣問題就很復(fù)雜了,因?yàn)槊恳粋€(gè)異步請(qǐng)求都需要三個(gè)委托的協(xié)議方法不同,而且協(xié)議里的方法居然還那么一致厉膀,這樣就會(huì)有六個(gè)協(xié)議方法呀!而且還兩兩重復(fù)二拐。必須紊亂呀服鹅!因此這對(duì)于ASI式的網(wǎng)絡(luò)請(qǐng)求來說,必須通過封裝來避免這樣的情況百新,可是企软,為什么封裝ASI就能避免這樣的情況呢?我的初步想法就是饭望,控制器里根本不應(yīng)該下載數(shù)據(jù)和處理數(shù)據(jù)仗哨,需要的僅僅是將已經(jīng)下載完成的且分析處理后存到數(shù)據(jù)模型里并用可變數(shù)組接收后的一個(gè)數(shù)組或字典傳遞給控制器,這就是封裝的意義所在铅辞,你或許會(huì)問厌漂,如何才能將第三方庫下載完成并處理好的數(shù)據(jù)傳遞到控制器里呢??當(dāng)然首先想到的就是協(xié)議代理啦斟珊?但是后來你會(huì)發(fā)現(xiàn)這會(huì)帶來一個(gè)全新的問題苇倡?一個(gè)不知不覺就崩潰的問題,原因就是對(duì)象的委托指向了一個(gè)可能隨時(shí)被銷毀的控制器對(duì)象囤踩。
好吧旨椒,現(xiàn)在先說一說,為什么必須通過單例式管理器來對(duì)ASI的功能進(jìn)行調(diào)用呢堵漱?首先來說吧综慎!創(chuàng)建一個(gè)單例管理器。這一類繼承于NSObject怔锌,首先就是導(dǎo)入ASIHTTPRequest.h這個(gè)頭文件寥粹。然后在.h文件聲明一個(gè)創(chuàng)建ASI單例管理器對(duì)象的類方法变过。前面已經(jīng)說過,單例的聲明方法通常格式為shared或default開頭涝涤,然后加上管理器類名∶恼現(xiàn)在就是在.m文件里實(shí)現(xiàn)單例對(duì)象的具體創(chuàng)建了。那么問題來了阔拳,首先不明白的就是為什么對(duì)一個(gè)第三方庫進(jìn)行使用前的第二次封裝崭孤,又或者說為第三方庫添加一個(gè)管理器。為什么說到這兒糊肠,又產(chǎn)生一個(gè)疑問辨宠?為什么給第三方庫添加了管理器就可以解決在控制器里直接重復(fù)使用第三方庫的功能所帶來的不利影響呢?
繼續(xù)回歸主題談一下為什么給第三方庫添加一個(gè)管理器單例货裹,必須將第三方庫實(shí)例化的一個(gè)對(duì)象設(shè)置成單例管理器類的一個(gè)屬性呢嗤形?而且還是強(qiáng)引用!當(dāng)然弧圆,這么說好像不對(duì)赋兵,因?yàn)楣芾鞟SI這個(gè)第三方庫的單例類的屬性并不是ASIHTTPRequest類實(shí)例化的對(duì)象,反而是繼承于NSOperationQueue 的ASINetworkQueue類實(shí)例化的一個(gè)隊(duì)列對(duì)象搔预。其目的是想要通過單例類實(shí)例化的屬性通過隊(duì)列這個(gè)屬性來管理添加到隊(duì)列里面的所有線程霹期。好像說起來很玄乎哈?為什么通過一個(gè)單例類實(shí)例化的單例對(duì)象的一個(gè)隊(duì)列屬性就能管理這個(gè)隊(duì)列里所有的線程呢拯田?聽起來就很神奇到不可思議呀历造?這樣問為什么很難找到合理的解釋,只能說問問題的角度不對(duì)船庇?那應(yīng)該怎么問呢吭产?
首先考慮這個(gè)創(chuàng)建ASI的單例管理類就已經(jīng)很無語。當(dāng)然我也不是什么都不知道溢十,我對(duì)于創(chuàng)建一個(gè)單例類對(duì)象所知道的就是:1垮刹、單例類對(duì)象都是通過類方法創(chuàng)建 2达吞、在.m文件里實(shí)現(xiàn)的這個(gè)類方法本質(zhì)上還是一個(gè)開辟空間的過程张弛。但是需要注意:單例對(duì)象的內(nèi)存空間只會(huì)被開辟一次,主要通過static來保證已經(jīng)創(chuàng)建的單例對(duì)象不會(huì)因?yàn)橹貜?fù)調(diào)用創(chuàng)建單例對(duì)象的代碼或就重復(fù)執(zhí)行包含創(chuàng)建單例對(duì)象的方法造成多次開辟空間的不良后果酪劫。同時(shí)考慮到分線程1和分線程2可能調(diào)用這個(gè)創(chuàng)建單例對(duì)象的方法吞鸭,造成多次開辟內(nèi)存空間,因此為了防止受到分線程的影響覆糟,有幾種上鎖方式刻剥,最常用的方式就是通過static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{})方法進(jìn)行上鎖。3滩字、第一次創(chuàng)建單例類對(duì)象時(shí)造虏,自然內(nèi)存狀態(tài)為static HttpManager *httpManager = nil空御吞,所以需要開辟內(nèi)存空間。這里我就有問題了漓藕,按理說開辟內(nèi)存應(yīng)該使用類名加上alloc的方式來開辟內(nèi)存空間陶珠,為什么這里竟然使用[super allocWithZone:NULL]來開辟內(nèi)存空間呢?4享钞、當(dāng)然我也明白就是開辟內(nèi)存空間的時(shí)候也就涉及到了一個(gè)init的初始化問題揍诽。當(dāng)然上次我已經(jīng)說明白了,初始化init的具體代碼是什么關(guān)鍵還是取決于所繼承的類到底是什么栗竖?這個(gè)初始化方法的意義在于暑脆,只要有對(duì)象被初始化就會(huì)調(diào)用這個(gè)在父類初始化方法基礎(chǔ)上增加的一些初始化設(shè)置。5狐肢、這個(gè)時(shí)候就完全被init里增添的初始化設(shè)置代碼搞凌亂了添吗,最關(guān)鍵的還是因?yàn)槲腋静恢浪鶆?chuàng)建的單例對(duì)象的列隊(duì)屬性竟然會(huì)有這么多的方法可以設(shè)置。換句話說份名,這個(gè)屬性的初始化賦值有點(diǎn)復(fù)雜根资。問題的根本在于這個(gè)屬性根本不同于以往的什么字符串或是什么長整型呀!根本不能直接用等號(hào)進(jìn)行賦值嘛同窘!原因就是這個(gè)屬性本質(zhì)上是常規(guī)意義上的對(duì)象玄帕,而且是必須通過類方法開辟空間的對(duì)象。不開辟空間就無法正常工作的對(duì)象想邦。主要是想在初始化方法里為單例對(duì)象的隊(duì)列對(duì)象屬性設(shè)置幾個(gè)方法裤纹,分別是:setRequestDidStartSelector、setRequestDidFinishSelector丧没、setRequestDidFailSelector鹰椒、setQueueDidFinishSelector。最后最關(guān)鍵的就是還需要添加一個(gè)[_netWorkQueue go]啟動(dòng)隊(duì)列的方法呕童。
現(xiàn)在管理類的單例類對(duì)象已經(jīng)創(chuàng)建完成漆际,現(xiàn)在的問題就是圍繞著怎么添加線程任務(wù)到這個(gè)單例類對(duì)象的隊(duì)列對(duì)象屬性里。當(dāng)然無論你添加的線程任務(wù)請(qǐng)求是什么夺饲,有一點(diǎn)必須注意奸汇,就是都少不了添加線程任務(wù)請(qǐng)求到隊(duì)列中的[_netWorkQueue addOperation:request]方法 。1往声、首先我不明白的就是為什么創(chuàng)建Http請(qǐng)求時(shí)要新建一個(gè)方法擂找,然后在這個(gè)方法里面創(chuàng)建線程屑迂,并且將線程對(duì)象添加到隊(duì)列中偎谁,這時(shí)候CPU會(huì)對(duì)添加的線程進(jìn)行時(shí)間片的分配管理寇荧,數(shù)據(jù)下載成功與失敗都會(huì)調(diào)用相關(guān)的回調(diào)方法痊银。當(dāng)然這些方法作為隊(duì)列對(duì)象屬性的方法枉昏,早已經(jīng)在實(shí)例化單例對(duì)象的時(shí)候就已經(jīng)在初始化方法里為隊(duì)列對(duì)象屬性設(shè)置了這些回調(diào)方法吹泡。2氏捞、因?yàn)殛?duì)列中的任何一個(gè)線程任務(wù)在進(jìn)行結(jié)束時(shí)都會(huì)調(diào)用線程結(jié)束時(shí)的方法舷蟀。那么我如果想要通過這個(gè)線程任務(wù)結(jié)束時(shí)回調(diào)的方法來傳遞線程任務(wù)下載完成大數(shù)據(jù)到控制器,有一個(gè)很大的問題需要解決败明,就是需要準(zhǔn)確地知道到底是哪一個(gè)任務(wù)線程調(diào)用了回調(diào)方法礁遣!只有準(zhǔn)確的知道是哪一個(gè)任務(wù)線程現(xiàn)在完成了任務(wù),才能夠知道該把任務(wù)線程下載完成的數(shù)據(jù)往需要他的控制器傳遞呀肩刃!首先想到的解決方案就是通過tag屬性來標(biāo)記線程任務(wù)(其實(shí)這些所說的線程任務(wù)通常都是任務(wù)請(qǐng)求request)祟霍。但是你會(huì)發(fā)現(xiàn)雖然我們打印Tag號(hào)就能能夠知道正在調(diào)用隊(duì)列回調(diào)方法的任務(wù)到底是哪一個(gè),但是隨著任務(wù)的越來越多盈包,你會(huì)發(fā)現(xiàn)就算你知道任務(wù)tag號(hào)沸呐,然后再根據(jù)tag號(hào)尋找到相對(duì)應(yīng)的任務(wù)也是一個(gè)巨大的工作量呀!造成代碼的可讀性極差呢燥!如果能有一種標(biāo)識(shí)直接就是任務(wù)的名稱就好了崭添,而且你會(huì)發(fā)現(xiàn),如果能夠直接獲取到任務(wù)的名稱對(duì)于以后使用通知來分發(fā)信息也是一個(gè)極大的方便叛氨。所以呼渣,解決方法就是通過枚舉直接通過任務(wù)的標(biāo)識(shí)找到任務(wù)名。換句話說吧寞埠!就是給每一個(gè)線程任務(wù)都綁定一個(gè)包含線程任務(wù)自身名稱的字典屁置。如何實(shí)現(xiàn),當(dāng)然是創(chuàng)建一個(gè)單例類對(duì)象可以調(diào)用的方法來為線程任務(wù)綁定一個(gè)包含自身信息的字典仁连。那么我就又不明白了蓝角,為什么必須要通過線程請(qǐng)求任務(wù)的userInfo屬性來作為線程任務(wù)的標(biāo)識(shí)符呢?這還得從開始來說起饭冬,要想知道現(xiàn)在調(diào)用隊(duì)列對(duì)象屬性里回調(diào)方法的線程任務(wù)請(qǐng)求到底是哪一個(gè)使鹅?唯一的辦法就是將線程任務(wù)請(qǐng)求的標(biāo)識(shí)符存儲(chǔ)在線程任務(wù)請(qǐng)求的屬性里,然后通過判斷線程任務(wù)請(qǐng)求的屬性就可以知道現(xiàn)在是那一個(gè)線程任務(wù)請(qǐng)求了昌抠,所以必須有一個(gè)線程任務(wù)請(qǐng)求的屬性存儲(chǔ)東西來區(qū)分現(xiàn)在到底哪一個(gè)線程任務(wù)請(qǐng)求在調(diào)用隊(duì)列的回調(diào)方法患朱。能夠肩負(fù)起這項(xiàng)任務(wù)的屬性就只有Tag和userInfo了。前者是一個(gè)數(shù)字不夠直觀炊苫,后者是一個(gè)字典需要用到枚舉裁厅。對(duì)啊劝评?此時(shí)我就迷茫了姐直,如何將線程任務(wù)請(qǐng)求的標(biāo)識(shí)符賦值到請(qǐng)求的userInfo屬性里呢?也就是說如何將信息存入字典蒋畜,當(dāng)然就是通常來說的字典賦值方法并不難吧!就是先初始化創(chuàng)建一個(gè)字典撞叽,然后把字典賦值給userInfo屬性就可以了姻成!可是我就又不明白了插龄,這跟枚舉有毛關(guān)系呀!對(duì)于字典來說科展,不就是一個(gè)鍵對(duì)應(yīng)一個(gè)值么均牢?這個(gè)鍵就是線程任務(wù)請(qǐng)求類型,鍵所對(duì)應(yīng)的值就是線程任務(wù)請(qǐng)求的名字才睹!所以根本不需要使用到枚舉嘛徘跪!換句話說,使用枚舉的意義到底是什么琅攘?當(dāng)然使用宏定義我還可以理解垮庐,就是因?yàn)楹甓x的名稱寫的時(shí)候有提示就很方便不用當(dāng)我們使用的時(shí)候再去拷貝嘛!這就是宏定義的意義和價(jià)值坞琴∩诓椋可是我還是不明白使用枚舉的意義在哪兒?其實(shí)枚舉的本質(zhì)上也就是一個(gè)個(gè)鍵值對(duì)的集合嘛剧辐,那么我不明白的就是如何根據(jù)枚舉的一個(gè)鍵或是說一個(gè)值找到相應(yīng)地一個(gè)值或是一個(gè)鍵寒亥!原來使用枚舉竟然也是出于方便比較的考慮,試想一下荧关,如果不用枚舉溉奕,那么很明顯通過userInfo字典的@[@"requestTypeKey"]提取出來的值對(duì)應(yīng)的肯定是線程任務(wù)請(qǐng)求名稱的字符串。當(dāng)然現(xiàn)在已經(jīng)存入了userInfo字典忍啤,當(dāng)我們使用線程任務(wù)請(qǐng)求的userInfo屬性提取出字典然后使用@[@"requestTypeKey”]提取出線程任務(wù)請(qǐng)求名稱的字符串時(shí)怎么作對(duì)比又成了一個(gè)難題腐宋。其實(shí)話說到這兒,好像并不會(huì)因?yàn)椴皇褂妹杜e就變得不方便檀轨,而且如果你說寫枚舉的作用僅僅是想要在寫線程任務(wù)請(qǐng)求名稱的時(shí)候有提示那么我想說通過宏定義也可以做到胸竞。但是如果不是userInfo屬性而是使用的是tag屬性,那么枚舉就變得非常有必要了参萄。所以更加有必要捋一捋枚舉的思路卫枝,方便以后直接使用tag屬性時(shí)使用,可是枚舉的意義竟然不大讹挎?暫且放一放校赤!
當(dāng)然還有一個(gè)最重要的問題就是在創(chuàng)建任務(wù)請(qǐng)求的時(shí)候必須能夠準(zhǔn)確地理解get請(qǐng)求和post請(qǐng)求的區(qū)別!換句話說就是必須知道怎么使用ASI進(jìn)行g(shù)et請(qǐng)求和post請(qǐng)求筒溃!這個(gè)后面有說明马篮!
第二個(gè)問題就是為什么不直接在線程任務(wù)請(qǐng)求里將包含線程任務(wù)名稱的字典賦值給請(qǐng)求的userInfo屬性!而是通過這個(gè)類實(shí)例化的對(duì)象來調(diào)用一個(gè)給請(qǐng)求的userInfo字典屬性賦值的方法怜奖。這樣做又是意義何在浑测?如果你說這樣做的目的是想將創(chuàng)建包含線程任務(wù)請(qǐng)求名稱的字典的創(chuàng)建方法封裝起來,那么我會(huì)問你,為何不直接將@{}內(nèi)容賦值給userInfo屬性呢迁央?當(dāng)然這純屬娛樂掷匠,我們要時(shí)刻謹(jǐn)記一點(diǎn)就是,我這句話想要實(shí)現(xiàn)一個(gè)什么功能岖圈?以后是否還會(huì)再用讹语?那么接下來就需要思考需要傳遞哪些參數(shù)才能保證功能的實(shí)現(xiàn),封裝加封裝接著就是調(diào)用方法蜂科!
現(xiàn)在就是更加深刻理解哪些回調(diào)方法的時(shí)候了顽决?為什么我們這么強(qiáng)調(diào)必須準(zhǔn)確的知道現(xiàn)在到底是哪一個(gè)線程任務(wù)請(qǐng)求在調(diào)用隊(duì)列初始化時(shí)就形成的回調(diào)方法!因?yàn)槲覀冎挥兄懒说降资悄囊粋€(gè)線程任務(wù)請(qǐng)求才能進(jìn)行下一步操作导匣!下一步操作通常都是說在一個(gè)線程任務(wù)進(jìn)行結(jié)束后會(huì)調(diào)用的setRequestDidFinishSelector方法才菠。這就要求我們必須通過switch或者說if else(else if)來準(zhǔn)確地判讀此時(shí)此刻進(jìn)入setRequestDidFinishSelector方法的線程任務(wù)請(qǐng)求的名稱。這樣就能根據(jù)線程任務(wù)請(qǐng)求的名稱將下載完成的數(shù)據(jù)傳遞到需要的地方逐抑!
當(dāng)然這個(gè)時(shí)候問題又來了鸠儿,就是我雖然已經(jīng)知道線程任務(wù)請(qǐng)求結(jié)束后,主要有responseString/responseData這兩個(gè)屬性來保存任務(wù)下載完成后的數(shù)據(jù)厕氨。當(dāng)然也可能還有其它的種類哈进每!現(xiàn)在首先的疑問就是為什么不直接用NSString或是NSData將responseString/responseData屬性里的值進(jìn)行接收傳遞出去,非要將這些屬性的值以鍵值對(duì)的形式傳遞出去呢命斧?而且還會(huì)在這個(gè)鍵值對(duì)的基礎(chǔ)上添加一個(gè)鍵值對(duì)田晚,一個(gè)說明是否下載成功的鍵值對(duì)!所以如此說來国葬,這個(gè)需要被傳遞的字典總共有兩個(gè)鍵值對(duì)贤徒,一個(gè)是說明是否下載成功的鍵值對(duì),一個(gè)是以將會(huì)被傳遞的下載數(shù)據(jù)為值的鍵值對(duì)汇四。如何將這個(gè)包含下載數(shù)據(jù)的字典傳遞傳遞出去呢接奈?當(dāng)然是通過協(xié)議代理來進(jìn)行傳值。還記得當(dāng)初大明湖畔前的協(xié)議代理嘛通孽?要想把值傳出去序宦,就必須調(diào)用一個(gè)協(xié)議方法。那么想要調(diào)用協(xié)議方法背苦,首先得有這個(gè)方法的聲明吧互捌!然后必須加上一個(gè)可以執(zhí)行這個(gè)方法的協(xié)議屬性呀!只要具備了這三件套就可以將對(duì)象傳遞出去了行剂,當(dāng)然我們知道使用一個(gè)協(xié)議總共分7步秕噪,已經(jīng)執(zhí)行了3步,還剩4步必須寫在接收對(duì)象的那個(gè)控制器里厚宰,主要是:調(diào)入包含協(xié)議的頭文件腌巾、引用協(xié)議、創(chuàng)建委托代理、用協(xié)議方法接收傳遞過來的對(duì)象壤躲!這樣就可以直接將下載數(shù)據(jù)的過程分理出控制器了城菊!通過一個(gè)封裝就可以實(shí)現(xiàn)下載過程备燃,更同時(shí)通過協(xié)議代理將下載完成后的數(shù)據(jù)傳遞到控制器碉克,而且的而且還可以對(duì)下載完成的數(shù)據(jù)先進(jìn)行數(shù)據(jù)模型的封裝再傳值到控制器!
現(xiàn)在已經(jīng)完美地將網(wǎng)絡(luò)請(qǐng)求的過程脫離控制器并齐,同時(shí)可以將網(wǎng)絡(luò)請(qǐng)求完成的數(shù)據(jù)通過協(xié)議代理將整理后的數(shù)據(jù)傳遞到控制里漏麦!可是通過協(xié)議傳值必須有一個(gè)前提,就是通過委托所指向的對(duì)象况褪,必須在調(diào)用哪個(gè)協(xié)議方法的時(shí)候存在呀撕贞!如果說同步操作或是在主線程進(jìn)行所有的事情才能不用考慮這個(gè)問題,那么異步操作很容易發(fā)生的崩潰的一個(gè)場(chǎng)景就是测垛,當(dāng)我從控制器1跳到控制器2時(shí)捏膨,控制器2里面設(shè)置了一個(gè)協(xié)議代理用于接收從網(wǎng)絡(luò)封裝庫傳遞過來的數(shù)據(jù),我們知道接收到從網(wǎng)絡(luò)封裝類獲取到下載數(shù)據(jù)的前提是必須將控制器類對(duì)象指向網(wǎng)絡(luò)封裝庫的實(shí)例化對(duì)象的委托屬性食侮!也就是說網(wǎng)絡(luò)請(qǐng)求類的對(duì)象的委托屬性現(xiàn)在指向了控制器的對(duì)象号涯,只要網(wǎng)絡(luò)請(qǐng)求類的對(duì)象的委托屬性指向了控制器對(duì)象,那個(gè)用于接收從網(wǎng)絡(luò)請(qǐng)求類的下載數(shù)據(jù)的方法將會(huì)被激活锯七!但是喲链快!但是你要記住,就是本來說如果是主線程眉尸,通常寫到這個(gè)位置就已經(jīng)可以完美地保證傳值成功了域蜗,但是如果遇到分線程處理就的另有意外了!因?yàn)橥耆羞@么一種情況就是加入我剛剛從控制器1調(diào)到控制器2噪猾,那么自然執(zhí)行控制器2里面的代碼霉祸,建立了委托協(xié)議的鏈接,這樣將會(huì)啟動(dòng)協(xié)議代理傳值的方法袱蜡?這時(shí)候我就有一個(gè)問題了丝蹭,就是當(dāng)我跳到控制器2時(shí)肯定會(huì)馬上啟動(dòng)協(xié)議代理傳值方法,那么這是就分兩種情況了戒劫,第一種情況是網(wǎng)絡(luò)請(qǐng)求類已經(jīng)數(shù)據(jù)下載完成直接就調(diào)用協(xié)議里的方法半夷,第二種情況就是只有當(dāng)我調(diào)到控制器2的時(shí)候,只有當(dāng)創(chuàng)建了協(xié)議代理委托之后迅细,網(wǎng)絡(luò)請(qǐng)求類才會(huì)開始添加分線程任務(wù)到隊(duì)列里巫橄。那么這時(shí)候就會(huì)出現(xiàn)一個(gè)常見的問題就是,當(dāng)跳到控制器2的時(shí)候茵典,這是添加了一個(gè)分線程的請(qǐng)求任務(wù)湘换,這需要時(shí)間呀!而且大天朝的網(wǎng)這么慢,如果用戶這時(shí)候選擇不等帶加載數(shù)據(jù)彩倚,直接銷毀控制器回到控制器1筹我,這樣會(huì)發(fā)生一個(gè)什么情況呢?崩潰帆离,而且是毫不猶豫的崩潰蔬蕊!原因就是是控制器2啟動(dòng)了分線程下載數(shù)據(jù)的方法,當(dāng)分線程任務(wù)請(qǐng)求數(shù)據(jù)成功之后本來想要通過協(xié)議代理將下載完成的數(shù)據(jù)傳到控制里的代理方法時(shí)哥谷,卻發(fā)現(xiàn)整個(gè)代理都不存在了岸夯!自然就崩潰了,其實(shí)崩潰的原因不在這兒们妥,真正的原因應(yīng)該從指針的角度去考慮猜扮,就是最初跳到控制器2的時(shí)候,網(wǎng)絡(luò)請(qǐng)求類的對(duì)象的委托屬性指針指向了控制器2的這片內(nèi)存空間监婶,當(dāng)控制器2被銷毀時(shí)內(nèi)存被回收自然發(fā)現(xiàn)委托屬性指針指向的內(nèi)存空間竟然沒有了旅赢,所以發(fā)生了崩潰,但是這也還是無法解釋惑惶!原因就是我分線程任務(wù)請(qǐng)求結(jié)束之后在調(diào)用協(xié)議代理傳值的方法前有過判斷呀煮盼?這個(gè)判斷就已經(jīng)包括了判斷網(wǎng)絡(luò)請(qǐng)求類對(duì)象的委托屬性是否為真,而且也判斷了這個(gè)協(xié)議代理方法已經(jīng)被實(shí)現(xiàn)才開始調(diào)用協(xié)議代理的方法進(jìn)行傳值集惋,所以按理說是不會(huì)發(fā)生崩潰的呀孕似!而且要記得當(dāng)初的解釋,就是說我們的前提認(rèn)為是當(dāng)我們調(diào)到控制器2的時(shí)候才開始進(jìn)行網(wǎng)絡(luò)請(qǐng)求這個(gè)分線程任務(wù)刮刑。但是我們也必須知道在控制器調(diào)用添加網(wǎng)絡(luò)請(qǐng)求任務(wù)線程到隊(duì)列里必須時(shí)在控制器里通過網(wǎng)絡(luò)請(qǐng)求類的實(shí)例化對(duì)象進(jìn)行調(diào)用這個(gè)創(chuàng)建網(wǎng)絡(luò)請(qǐng)求任務(wù)的過程呀喉祭!相當(dāng)于一個(gè)例子,你有本事創(chuàng)建了一個(gè)網(wǎng)絡(luò)請(qǐng)求雷绢,你卻沒有耐心等到網(wǎng)絡(luò)請(qǐng)求結(jié)束將數(shù)據(jù)傳回來泛烙!所以我就更疑惑了,如果我在網(wǎng)絡(luò)請(qǐng)求任務(wù)結(jié)束后調(diào)用協(xié)議代理傳值方法前判斷網(wǎng)絡(luò)請(qǐng)求類的對(duì)象的委托屬性是否為真和協(xié)議代理的方法是否被實(shí)現(xiàn)翘紊,按理說就不會(huì)發(fā)生崩潰了呀蔽氨!就算你跳到控制器2又馬上注銷控制器2返回到控制器1也不會(huì)造成程序的崩潰呀!一切都因?yàn)榫W(wǎng)絡(luò)請(qǐng)求類的對(duì)象在調(diào)用協(xié)議代理傳值方法前都會(huì)首先判斷是否網(wǎng)絡(luò)請(qǐng)求類的對(duì)象的委托屬性是否為空以及協(xié)議代理的傳值方法是否已經(jīng)被實(shí)現(xiàn)帆疟。這樣就完全可以避免因?yàn)橹羔樦赶蚩諆?nèi)存或調(diào)用不存在的方法而引起的崩潰鹉究!但是你會(huì)發(fā)現(xiàn)盡管現(xiàn)在程序不會(huì)發(fā)生崩潰,但會(huì)因?yàn)榭刂破鞯奶D(zhuǎn)帶來一個(gè)全新的問題踪宠,就是:當(dāng)我在控制器1里創(chuàng)建了一個(gè)網(wǎng)絡(luò)請(qǐng)求任務(wù)添加到網(wǎng)絡(luò)請(qǐng)求類的隊(duì)列中自赔,這時(shí)候就已經(jīng)開始控制器1頁面的數(shù)據(jù)的請(qǐng)求。當(dāng)然因?yàn)槭欠志€程嘛柳琢!所以主線程繼續(xù)前進(jìn)绍妨,分線程進(jìn)行下載數(shù)據(jù)的操作润脸,我們也知道,分線程在全網(wǎng)絡(luò)請(qǐng)求任務(wù)結(jié)束之后會(huì)通過協(xié)議代理將下載完成的數(shù)據(jù)傳遞到使用協(xié)議代理的控制器里他去。也就是說毙驯,當(dāng)分線程網(wǎng)絡(luò)請(qǐng)求任務(wù)結(jié)束之后,網(wǎng)絡(luò)請(qǐng)求類的對(duì)象的委托屬性指向哪一個(gè)控制器灾测,那么下載完成的數(shù)據(jù)就傳遞到哪一個(gè)控制器里的協(xié)議代理方法里爆价。可是呀可是行施,網(wǎng)絡(luò)請(qǐng)求類是一個(gè)單例類允坚,自然網(wǎng)路請(qǐng)求類的對(duì)象也是一個(gè)單例對(duì)象魂那,換句話說蛾号,網(wǎng)絡(luò)請(qǐng)求類的對(duì)象的委托屬性可以在任何一個(gè)控制器里被修改。如果控制器1創(chuàng)建了一個(gè)網(wǎng)絡(luò)請(qǐng)求任務(wù)添加到網(wǎng)絡(luò)請(qǐng)求類的對(duì)象的隊(duì)列屬性中涯雅,控制器2也創(chuàng)建了一個(gè)網(wǎng)絡(luò)請(qǐng)求任務(wù)添加到網(wǎng)絡(luò)請(qǐng)求類的對(duì)象的隊(duì)列屬性中鲜结,也就是說,兩個(gè)控制器都實(shí)例化了網(wǎng)絡(luò)請(qǐng)求類的單例對(duì)象活逆,而且都創(chuàng)建了單例對(duì)象的委托屬性指向了控制器本身精刷!其目的就是通過將委托屬性指向自身控制器,從而調(diào)用控制器本身里接收下載完成的數(shù)據(jù)的協(xié)議代理的方法蔗候!如此就會(huì)產(chǎn)生一個(gè)問題就是怒允,依然當(dāng)我從控制器1調(diào)到控制器2時(shí),因?yàn)榫W(wǎng)絡(luò)請(qǐng)求類的對(duì)象是一個(gè)單例锈遥,與因此在控制器1里面創(chuàng)建的請(qǐng)求還未結(jié)束呢纫事?那個(gè)控制器1里面的委托屬性的指向已經(jīng)從控制器1變成了控制器2 . 因此你會(huì)發(fā)現(xiàn)本該傳遞到控制器1里面的數(shù)據(jù)竟然現(xiàn)在傳遞到了控制器2里面,一切都是因?yàn)閱卫惖奈袑傩园l(fā)生了改變所灸!于是問題說到這兒丽惶,你就肯定會(huì)問了,為什么非要把網(wǎng)絡(luò)請(qǐng)求類設(shè)置成單例類呢爬立?這真是一個(gè)好問題钾唬,可以先換一個(gè)角度來想一想,如果不使用單例會(huì)造成什么不良后果侠驯!首當(dāng)其沖的就是單例對(duì)象初始化方法里對(duì)隊(duì)列屬性的賦值就會(huì)出現(xiàn)問題抡秆,因?yàn)閷?duì)列必須作為網(wǎng)絡(luò)請(qǐng)求類的對(duì)象的屬性,那么如果不把網(wǎng)絡(luò)請(qǐng)求類的對(duì)象設(shè)置成單例類對(duì)象吟策,那么隊(duì)列作為一個(gè)屬性也會(huì)被多次初始化儒士,這樣造成不必要的內(nèi)存消耗呀!本來一個(gè)隊(duì)列就可以管理所有的網(wǎng)絡(luò)請(qǐng)求任務(wù)踊挠,為什么要設(shè)置這么多隊(duì)列且多次為隊(duì)列開辟內(nèi)存空間造成不必要的消耗乍桂!而且網(wǎng)絡(luò)請(qǐng)求類實(shí)例化的對(duì)象如果是一個(gè)單例還具備諸多優(yōu)點(diǎn)冲杀,比如說:方便管理,節(jié)省內(nèi)存睹酌,創(chuàng)建方便等優(yōu)點(diǎn)权谁!那么如何解決那個(gè)單例類對(duì)象的委托屬性被更改的問題呢?說了這么多憋沿,終于引出解決方案了旺芽,按照老師的話說:一個(gè)高手寫代碼,必然要懂架構(gòu)辐啄,最底層是持久層采章,中間為業(yè)務(wù)邏輯層,最上面是表示層壶辜。但是我不明白的是為什么中間的業(yè)務(wù)邏輯層必須要寫成單例悯舟,而且持久層通過協(xié)議代理設(shè)置持久層的對(duì)象的委托屬性始終指向業(yè)務(wù)邏輯層的單例對(duì)象,將所下載完成的數(shù)據(jù)全部傳遞給了業(yè)務(wù)邏輯層砸民。整體的大概思路就是將中間的業(yè)務(wù)邏輯層設(shè)置成單例類抵怎,然后將網(wǎng)絡(luò)請(qǐng)求類也就是持久層的數(shù)據(jù)全部存儲(chǔ)到中間的業(yè)務(wù)邏輯層,暫且叫做網(wǎng)絡(luò)請(qǐng)求管理類吧岭参!將網(wǎng)絡(luò)請(qǐng)求類的所有數(shù)據(jù)(通常就是一個(gè)字典)和此時(shí)此刻的線程任務(wù)名稱標(biāo)識(shí)符傳遞到網(wǎng)絡(luò)請(qǐng)求管理類反惕。當(dāng)我發(fā)現(xiàn)我所看的內(nèi)容完全超出我的理解范疇的時(shí)候!我能做的就是找到我已經(jīng)知道的信息⊙莺睿現(xiàn)在先列一下發(fā)生改變的地方:1姿染、協(xié)議里的回調(diào)方法發(fā)生了改變,輸入?yún)?shù)不僅僅包括下載完成處理后的數(shù)據(jù)秒际,也包括調(diào)用次協(xié)議代理方法的線程請(qǐng)求任務(wù)的名稱悬赏。2、網(wǎng)絡(luò)請(qǐng)求類的初始化函數(shù)發(fā)生了改變程癌,初始化函數(shù)增添了一個(gè)輸入?yún)?shù):- (instancetype)initWithDelegate:(id) delegate; 3舷嗡、網(wǎng)絡(luò)請(qǐng)求類不再是一個(gè)單例,4嵌莉、原本實(shí)例化一個(gè)網(wǎng)絡(luò)請(qǐng)求類的對(duì)象是在控制器里實(shí)現(xiàn)进萄,而現(xiàn)在則變成了在網(wǎng)絡(luò)請(qǐng)求管理類開辟網(wǎng)絡(luò)請(qǐng)求類的對(duì)象空間。5锐峭、原本是把下載完成的數(shù)據(jù)通過協(xié)議代理的回調(diào)方法傳遞到控制器中鼠,而現(xiàn)在則是通過協(xié)議代理的回調(diào)方法傳遞到網(wǎng)絡(luò)請(qǐng)求管理類,繼而結(jié)合傳遞到網(wǎng)絡(luò)請(qǐng)求管理類的線程任務(wù)名稱以發(fā)送通知的形式發(fā)送到對(duì)應(yīng)的控制器里沿癞。
現(xiàn)在需要再整理一下那個(gè)網(wǎng)絡(luò)請(qǐng)求管理類里面究竟都有哪些東西:1援雇、首先這是一個(gè)單例,肯定有創(chuàng)建網(wǎng)絡(luò)請(qǐng)求管理類單例對(duì)象的類方法椎扬,static,開辟內(nèi)存空間的方法惫搏,改寫父類的init初始化方法 具温,在改寫的初始化方法里為全局的網(wǎng)絡(luò)請(qǐng)求類對(duì)象開辟了內(nèi)存空間,同時(shí)將網(wǎng)絡(luò)請(qǐng)求類對(duì)象的委托屬性指向了網(wǎng)絡(luò)請(qǐng)求管理類對(duì)象(即self) 2筐赔、有通過網(wǎng)路請(qǐng)求管理類直接調(diào)用的網(wǎng)絡(luò)請(qǐng)求任務(wù)方法铣猩,相當(dāng)于一個(gè)中介 3、在.m文件里創(chuàng)建了一個(gè)網(wǎng)絡(luò)請(qǐng)求類對(duì)象的全局變量茴丰。4达皿、引入了網(wǎng)絡(luò)請(qǐng)求類中用于傳值的協(xié)議 5、實(shí)現(xiàn)了.h文件中聲明的添加網(wǎng)絡(luò)請(qǐng)求線程任務(wù)到隊(duì)列中的方法贿肩,其實(shí)這個(gè)方法本質(zhì)上在網(wǎng)絡(luò)請(qǐng)求類就已經(jīng)實(shí)現(xiàn)÷鸵現(xiàn)在只不過多封裝了一次,相當(dāng)于原本在控制器里通過網(wǎng)絡(luò)請(qǐng)求類的對(duì)象去調(diào)用添加網(wǎng)絡(luò)請(qǐng)求任務(wù)到隊(duì)列中的這個(gè)方法汰规,而現(xiàn)在雖然同樣是在控制器里調(diào)用添加網(wǎng)絡(luò)請(qǐng)求任務(wù)到隊(duì)列的方法汤功,只不過現(xiàn)在這個(gè)對(duì)象已經(jīng)不是網(wǎng)絡(luò)請(qǐng)求類的對(duì)象,而是變成網(wǎng)絡(luò)請(qǐng)求管理類的對(duì)象了控轿,所以需要重寫一下調(diào)用方法冤竹。5、控制器里面不在設(shè)置代理委托茬射,而是注冊(cè)通知,當(dāng)然當(dāng)網(wǎng)絡(luò)請(qǐng)求類將下載完成的數(shù)據(jù)和線程網(wǎng)絡(luò)請(qǐng)求任務(wù)的標(biāo)識(shí)符(線程任務(wù)名字)通過回調(diào)方法傳遞到網(wǎng)絡(luò)請(qǐng)求管理類的方法時(shí)冒签,進(jìn)一步操作就是通過switch根據(jù)傳過來的線程任務(wù)名字判斷到底通知的名字是什么在抛!傳值成功!
那么現(xiàn)在問題來了萧恕,我確實(shí)知道網(wǎng)絡(luò)請(qǐng)求類的是持久層刚梭,就是干事情的那一個(gè)人,網(wǎng)絡(luò)請(qǐng)求管理類只是一個(gè)中介而已票唆,也就是網(wǎng)絡(luò)請(qǐng)求類就相當(dāng)于具備一個(gè)功能就是的當(dāng)控制器想要?jiǎng)?chuàng)建網(wǎng)絡(luò)數(shù)據(jù)請(qǐng)求時(shí)朴读,通過網(wǎng)絡(luò)請(qǐng)求類的實(shí)例化對(duì)象調(diào)用創(chuàng)建網(wǎng)絡(luò)請(qǐng)求的方法,再在網(wǎng)絡(luò)請(qǐng)求管理類通過網(wǎng)絡(luò)請(qǐng)求類的實(shí)例化對(duì)象調(diào)用最本質(zhì)的那個(gè)添加網(wǎng)絡(luò)請(qǐng)求任務(wù)到網(wǎng)絡(luò)請(qǐng)求類的實(shí)例化的對(duì)象的隊(duì)列屬性里走趋。也就是說那個(gè)通過網(wǎng)絡(luò)請(qǐng)求管理類對(duì)象調(diào)用的開始網(wǎng)絡(luò)請(qǐng)求任務(wù)的方法其實(shí)是一個(gè)偽方法衅金!最根本的方法還是那個(gè)在網(wǎng)絡(luò)請(qǐng)求管理類里通過網(wǎng)絡(luò)請(qǐng)求類的對(duì)象調(diào)用的創(chuàng)建網(wǎng)絡(luò)請(qǐng)求的方法!但我不明白的是:為什么現(xiàn)在的網(wǎng)絡(luò)請(qǐng)求類變成了非單例簿煌,而網(wǎng)絡(luò)請(qǐng)求管理類又變成了單例呢氮唯?到底什么時(shí)候用單例,什么時(shí)候又不用單例姨伟,簡直愁死我了惩琉!我能目前能想到就是單例嘛!自然只能開辟一次內(nèi)存空間夺荒,所以當(dāng)我們需要覆蓋單例對(duì)象的屬性的時(shí)候瞒渠,自然選擇使用單例良蒸。可是呀可是伍玖,我難道只是因?yàn)橐粋€(gè)勉強(qiáng)的理由就是更改和刷新屬性就創(chuàng)建單例對(duì)象么诚啃?當(dāng)然不是!我將網(wǎng)路請(qǐng)求管理類設(shè)置成單例的根本原因還是在于:方便管理私沮,那么如何理解這個(gè)方便管理呢始赎?首先分析一下這個(gè)網(wǎng)路請(qǐng)求管理類的作用到底是啥呀!當(dāng)然你會(huì)說這還用問嗎仔燕?就是一個(gè)中介嘛造垛!控制器通過網(wǎng)絡(luò)請(qǐng)求管理類調(diào)用網(wǎng)絡(luò)請(qǐng)求類的的創(chuàng)建網(wǎng)絡(luò)請(qǐng)求任務(wù)的方法,而且網(wǎng)絡(luò)請(qǐng)求管理類通過協(xié)議代理的回調(diào)方法接收從網(wǎng)絡(luò)請(qǐng)求類接收過來的數(shù)據(jù)晰搀。那么問題來了五辽,如果不把網(wǎng)絡(luò)請(qǐng)求管理類設(shè)置成單例會(huì)發(fā)生什么事呢?自然我們知道網(wǎng)絡(luò)請(qǐng)求管理類設(shè)置了一個(gè)網(wǎng)絡(luò)請(qǐng)求類對(duì)象的全局變量外恕。那么這就必須先考慮一個(gè)問題就是:屬性和全局變量的區(qū)別是什么杆逗?又在什么時(shí)候使用屬性?又在什么時(shí)候使用全局變量鳞疲?全局變量沒有像屬性那樣的內(nèi)存管理修飾符罪郊,那么全局變量的內(nèi)存管理又怎么計(jì)算呢?先說區(qū)別吧尚洽?全局變量的使用是通過下劃線加屬性名稱進(jìn)行調(diào)用悔橄,不能使用self方法,因?yàn)閟elf方法就相當(dāng)于變量不僅是全局的腺毫,更是已創(chuàng)建了get和set方法癣疟。屬性比較全局變量的優(yōu)勢(shì)就是在于不僅是全局變量,更是已經(jīng)初始化默認(rèn)了set和get方法潮酒!那么如果你不需要set和get方法,也就是說不需要再這個(gè)類以外給這個(gè)變量賦值或取值睛挚,那么自然就是使用全局變量,而不是屬性啦急黎!因?yàn)橹灰獙懥藢傩跃湍J(rèn)這個(gè)變量是能夠在除這個(gè)類本身以外被調(diào)用到扎狱!而且可以進(jìn)行存儲(chǔ)賦值和get取值!那么現(xiàn)在網(wǎng)絡(luò)請(qǐng)求管理類里面有一個(gè)關(guān)于網(wǎng)絡(luò)請(qǐng)求類的全局對(duì)象變量叁熔。通過這個(gè)全局對(duì)象變量來調(diào)用網(wǎng)絡(luò)請(qǐng)求類里面創(chuàng)建網(wǎng)絡(luò)請(qǐng)求的方法委乌。但是這與為什么網(wǎng)絡(luò)請(qǐng)求管理類是個(gè)單例并沒有關(guān)系呀?但是當(dāng)我看到網(wǎng)絡(luò)請(qǐng)求管理類的單例對(duì)象的初始化方法后荣回,我好像明白了什么遭贸!同樣在改寫父類的初始化方法的時(shí)候,我們發(fā)現(xiàn)這里面為網(wǎng)絡(luò)請(qǐng)求類的對(duì)象的全局變量開辟了內(nèi)存空間心软。要知道壕吹,既然是對(duì)象型的全局變量著蛙,就必須開辟內(nèi)存空間才能使用呀?可是每當(dāng)我把話題說到這兒耳贬,我總是忘不了:這種邏輯對(duì)于那種字典或數(shù)組來說踏堡,確實(shí)每次創(chuàng)建這種對(duì)象類型的全局變量時(shí),總是需要為字典呀咒劲!數(shù)組呀顷蟆!自定義類的對(duì)象呀!開辟空間腐魂,但是心中總有一個(gè)聲音就是帐偎,字符串也是一個(gè)對(duì)象,對(duì)吧蛔屹?那為什么我創(chuàng)建字符串或長整形類型的數(shù)據(jù)的時(shí)候從來沒有開辟內(nèi)存空間這種印象呢削樊?每次都是直接就使用了!原因就是字符串和長整型這些類型的對(duì)象的空間一直存在于常量區(qū)兔毒,也就是說漫贞,字符串的內(nèi)存空間一直存在,字符串對(duì)象里的內(nèi)容不就是哪些已經(jīng)存在的固定字符存在的嘛育叁!把字符串對(duì)象的內(nèi)容比作英語迅脐,那么那片內(nèi)存空間就是那26個(gè)字母。所以它不同于字典和數(shù)組那樣內(nèi)容有很多的不確定性擂红。所以不用象字典和數(shù)組對(duì)象以及自定義類的對(duì)象那樣需要開辟內(nèi)存空間了仪际!現(xiàn)在的問題就是,我們明白給網(wǎng)絡(luò)請(qǐng)求類對(duì)象全局變量開辟內(nèi)存空間的方法寫在網(wǎng)絡(luò)請(qǐng)求管理類的對(duì)象創(chuàng)建時(shí)的初始化方法里是希望只有在創(chuàng)建一個(gè)新的網(wǎng)絡(luò)請(qǐng)求管理類對(duì)象的時(shí)才實(shí)例化一個(gè)網(wǎng)絡(luò)請(qǐng)求類對(duì)象昵骤,可是問題就在于多次創(chuàng)建網(wǎng)絡(luò)請(qǐng)求類對(duì)象不行么?到底行不行肯适?我應(yīng)該怎么去判斷变秦?
假設(shè)法?假設(shè)網(wǎng)路請(qǐng)求管理類不是一個(gè)單例類框舔,那么每當(dāng)一個(gè)控制器調(diào)通過網(wǎng)絡(luò)請(qǐng)求管理類的對(duì)象調(diào)用創(chuàng)建網(wǎng)絡(luò)請(qǐng)求任務(wù)的方法的時(shí)候蹦玫,都會(huì)實(shí)例化一個(gè)網(wǎng)絡(luò)請(qǐng)求管理類的對(duì)象。然后網(wǎng)絡(luò)請(qǐng)求管理類對(duì)象的初始化又會(huì)創(chuàng)建一個(gè)網(wǎng)絡(luò)請(qǐng)求類的對(duì)象刘绣。要知道創(chuàng)建一個(gè)對(duì)象就會(huì)開辟一次內(nèi)存空間呀樱溉!就算不開率這樣是否會(huì)出現(xiàn)神秘差錯(cuò),就算可以完美地實(shí)現(xiàn)功能纬凤,這些內(nèi)存空間的開辟就是一個(gè)很大的浪費(fèi)呀福贞!因?yàn)槟阒灰粍?chuàng)建網(wǎng)絡(luò)請(qǐng)求,就創(chuàng)建了一個(gè)對(duì)象停士,如果說創(chuàng)建對(duì)象的目的僅僅是想要臨時(shí)調(diào)用到所創(chuàng)建對(duì)象里面的方法挖帘,為什么不把這個(gè)對(duì)象設(shè)置成一個(gè)單例對(duì)象完丽,這要就可以完美地避免在控制器里,只要一調(diào)用創(chuàng)建網(wǎng)絡(luò)請(qǐng)求就開辟至少兩個(gè)內(nèi)存空間拇舀。所以總結(jié)一點(diǎn)逻族,就是當(dāng)控制器創(chuàng)建一個(gè)對(duì)象只是為了調(diào)用對(duì)象里實(shí)現(xiàn)的方法時(shí),那么通常都是把這個(gè)對(duì)象設(shè)置成一個(gè)單例對(duì)象〗颈溃現(xiàn)在想明白了如果在控制器里想多次調(diào)用其它類的方法通常都是用類方法或者說創(chuàng)建單例對(duì)象來調(diào)用了吧聘鳞!因?yàn)槭?nèi)存。那么還有最后一個(gè)問題就是要拂,為什么原本沒有創(chuàng)建網(wǎng)絡(luò)請(qǐng)求管理類的時(shí)候抠璃,網(wǎng)絡(luò)請(qǐng)求類是一個(gè)單例,當(dāng)網(wǎng)絡(luò)請(qǐng)求管理類被創(chuàng)建的時(shí)候宇弛,網(wǎng)絡(luò)請(qǐng)求管理類作為一個(gè)單例類可以理解鸡典,畢竟是控制器會(huì)調(diào)用到這個(gè)網(wǎng)絡(luò)請(qǐng)求管理類的方法。因此必須采用單例嘛枪芒!只是不明白現(xiàn)在那個(gè)網(wǎng)絡(luò)請(qǐng)求類從單例變成了非單例彻况,我想知道的是,如果不將網(wǎng)絡(luò)請(qǐng)求類從單例類變成非單例類會(huì)發(fā)生什么事呢舅踪?按理我這網(wǎng)絡(luò)請(qǐng)求管理類是一個(gè)單例類纽甘,每當(dāng)控制器調(diào)用網(wǎng)絡(luò)請(qǐng)求管理類的方法時(shí)。網(wǎng)絡(luò)請(qǐng)求管理類也會(huì)創(chuàng)建一個(gè)網(wǎng)絡(luò)請(qǐng)求類的對(duì)象來調(diào)用網(wǎng)絡(luò)請(qǐng)求類里面的方法抽碌。既然是網(wǎng)絡(luò)請(qǐng)求管理類是一個(gè)單例類悍赢,那么就不可能多次創(chuàng)建網(wǎng)絡(luò)請(qǐng)求類對(duì)象!問題就是弄清楚我們當(dāng)初沒有創(chuàng)建網(wǎng)絡(luò)請(qǐng)求管理類的時(shí)候货徙,將網(wǎng)絡(luò)請(qǐng)求類設(shè)置成單例左权。其目的不僅僅有利于控制器里面調(diào)用網(wǎng)絡(luò)請(qǐng)求類里面的方法的時(shí)候不會(huì)開辟太多的內(nèi)存空間。而且還有一個(gè)原因就是網(wǎng)絡(luò)請(qǐng)求類的對(duì)象里的對(duì)列屬性只能是一個(gè)痴颊!將所有線程請(qǐng)求任務(wù)全部添加到這一個(gè)隊(duì)列中赏迟!當(dāng)然給對(duì)象的隊(duì)列屬性賦值的程序只會(huì)存在改寫網(wǎng)絡(luò)請(qǐng)求類的重寫初始化方法里。現(xiàn)在同樣將隊(duì)列屬性賦值的過程寫在了網(wǎng)絡(luò)請(qǐng)求類對(duì)象的初始化方法里蠢棱!而這個(gè)網(wǎng)絡(luò)請(qǐng)求類的對(duì)象的初始化方法只會(huì)被執(zhí)行一次锌杀,所以當(dāng)然不需要網(wǎng)絡(luò)請(qǐng)求類是一個(gè)單例了。答案還是很勉強(qiáng)泻仙,最合理的解釋我想還是因?yàn)閱卫旧泶嬖诘膬r(jià)值是因?yàn)樾枰啻蝿?chuàng)建對(duì)象來調(diào)用對(duì)象里的方法糕再,而現(xiàn)在的問題就是我根本不需要多次創(chuàng)建對(duì)象就可以調(diào)用到方法呀!舉個(gè)例子吧玉转?就是控制器1調(diào)用網(wǎng)絡(luò)請(qǐng)求的方法時(shí)需要?jiǎng)?chuàng)建一個(gè)網(wǎng)絡(luò)請(qǐng)求類對(duì)象突想,當(dāng)控制器2又需要調(diào)用網(wǎng)絡(luò)請(qǐng)求的方法時(shí)又需要?jiǎng)?chuàng)建一個(gè)網(wǎng)絡(luò)請(qǐng)求類對(duì)象,一般在這種情況下,將網(wǎng)絡(luò)請(qǐng)求類對(duì)象設(shè)置為單例對(duì)象蒿柳。這樣就可以避免內(nèi)存浪費(fèi)饶套,重復(fù)開辟內(nèi)存空間了!但是現(xiàn)在是在網(wǎng)絡(luò)請(qǐng)求管理類調(diào)用創(chuàng)建網(wǎng)絡(luò)請(qǐng)求的方法垒探,也就是需要網(wǎng)絡(luò)請(qǐng)求類的對(duì)象才能調(diào)用的方法妓蛮!但是,在網(wǎng)絡(luò)請(qǐng)求管理類調(diào)用創(chuàng)建網(wǎng)絡(luò)請(qǐng)求類的方法圾叼,就相當(dāng)于是只有一個(gè)控制器來多次調(diào)用網(wǎng)絡(luò)請(qǐng)求類的方法蛤克,這樣就無須多次創(chuàng)建網(wǎng)絡(luò)請(qǐng)求類的對(duì)象呀!既然都只是創(chuàng)建一次網(wǎng)絡(luò)請(qǐng)求類的對(duì)象夷蚊,那還有啥將網(wǎng)絡(luò)請(qǐng)求類變成單例類的必要呢构挤?其實(shí)本質(zhì)上來講,網(wǎng)絡(luò)請(qǐng)求管理類對(duì)于網(wǎng)絡(luò)請(qǐng)求類就相當(dāng)于網(wǎng)絡(luò)請(qǐng)求類對(duì)于NSOpertionQueue隊(duì)列類惕鼓!
ASI網(wǎng)絡(luò)請(qǐng)求封裝筋现?
將ASI導(dǎo)入工程就是一件比較麻煩的事情,需要導(dǎo)入6個(gè)框架和1個(gè)路徑設(shè)置箱歧。ASI作為AFN出來之前廣為人知的網(wǎng)絡(luò)請(qǐng)求庫矾飞,封裝ASI變得尤為重要。現(xiàn)在談?wù)勎覍?duì)于ASI封裝的理解呀邢,到底是以謳歌怎樣的邏輯洒沦,
首先我們必須知道的就是ASI里面有三個(gè)很重要的類?
分別是:ASIHTTPRequest/ASIFormDataRequest/ASINetworkQueue這三個(gè)類价淌。
這三個(gè)類的關(guān)系和功能到底是什么呢申眼?
ASIHTTPRequest繼承于NSOperation,ASINetworkQueue繼承于NSOperationQueue,而ASIFormDataRequest繼承于ASIHTTPRequest蝉衣,主要用于網(wǎng)絡(luò)的Post請(qǐng)求臂港,當(dāng)然也具備父類get請(qǐng)求的功能侥锦。
對(duì)這個(gè)ASI庫進(jìn)行二次封裝的出發(fā)點(diǎn)是什么屡萤?
經(jīng)常會(huì)出現(xiàn)的情況就是蝙搔,在一個(gè)View Contorller里,不止一次使用到網(wǎng)絡(luò)請(qǐng)求剪验,如果不進(jìn)行封裝就會(huì)兩次使用到網(wǎng)絡(luò)請(qǐng)求,如果網(wǎng)絡(luò)請(qǐng)求到的數(shù)據(jù)是以協(xié)議回調(diào)方法的方式來返回前联,勢(shì)必造成控制器里的代碼非常冗雜功戚。因此封裝特別有必要,想要達(dá)到的想過僅僅就是一個(gè)調(diào)用天劍網(wǎng)絡(luò)請(qǐng)求任務(wù)和返回網(wǎng)絡(luò)請(qǐng)求結(jié)果的兩句必不可少的代碼似嗤。本質(zhì)就是:View展示的數(shù)據(jù)不止來源于一個(gè)接口啸臀。控制器不下載數(shù)據(jù)也不處理數(shù)據(jù),需要的僅僅是將已經(jīng)下載完成的且分析處理后存到數(shù)據(jù)模型里并用可變數(shù)組接收后的一個(gè)數(shù)組或字典傳遞給控制器乘粒,這就是封裝的意義所在豌注。
問題1:通過協(xié)議代理的方法來回調(diào)下載完成并處理好的數(shù)據(jù)可能導(dǎo)致程序的崩潰?
原因就在于下載請(qǐng)求類在請(qǐng)求數(shù)據(jù)結(jié)束后直接通過協(xié)議代理的回調(diào)方法將下載完成并處理好的值返回到了控制器灯萍。假如控制器1跳轉(zhuǎn)到控制器2又馬上銷毀控制器2轧铁,就會(huì)發(fā)生應(yīng)為網(wǎng)絡(luò)請(qǐng)求類的單例對(duì)象的委托屬性指向了一個(gè)空的控制器對(duì)象而發(fā)生崩潰。解決方法就是在將數(shù)據(jù)通過回調(diào)方法傳回控制器之前首先判斷網(wǎng)絡(luò)請(qǐng)求類對(duì)象的委托屬性值是否為空和協(xié)議代理的回調(diào)方法是否已經(jīng)存在旦棉。
問題2:當(dāng)我從控制器1跳轉(zhuǎn)到控制器2齿风,將控制器2的網(wǎng)絡(luò)請(qǐng)求任務(wù)添加到網(wǎng)絡(luò)請(qǐng)求類的隊(duì)列屬性中,進(jìn)行分線程下載绑洛,再下載還為完成就銷毀控制器2回到控制器1救斑,會(huì)發(fā)現(xiàn)本該本該回調(diào)到控制器2的數(shù)據(jù)現(xiàn)在傳遞到了控制器1,為什么真屯?
原因就是網(wǎng)絡(luò)請(qǐng)求類是一個(gè)單例類脸候,單例類的委托屬性可以隨時(shí)被修改,當(dāng)初這個(gè)單例類的委托屬性指向的是控制器2的對(duì)象绑蔫,而現(xiàn)在則指向了控制器1运沦,所以控制器2里面添加的網(wǎng)絡(luò)請(qǐng)求的任務(wù)在網(wǎng)絡(luò)請(qǐng)求類分線程下載處理完成后開始調(diào)用協(xié)議方法傳值×澜常回調(diào)哪一個(gè)傳值方法就是根據(jù)網(wǎng)絡(luò)請(qǐng)求類對(duì)象的委托屬性指向哪一個(gè)控制器來決定茶袒,現(xiàn)在單例類對(duì)象的委托屬性已經(jīng)變成的控制器1,自然調(diào)用控制器1里面的回調(diào)方法凉馆,所以自然把控制器2里面的值傳遞到了控制器1里面薪寓。
問題3:為什么網(wǎng)絡(luò)請(qǐng)求類是一個(gè)單例類?
要知道我們封裝網(wǎng)絡(luò)請(qǐng)求類的本質(zhì)就在于將所有的網(wǎng)絡(luò)請(qǐng)求任務(wù)添加到一個(gè)并行隊(duì)列中去管理澜共,而這個(gè)并行隊(duì)列是以一種屬性的方式存在于網(wǎng)絡(luò)請(qǐng)求類向叉,如果不把網(wǎng)絡(luò)請(qǐng)求類設(shè)置成單例類,那么并行隊(duì)列屬性將會(huì)被多次創(chuàng)建嗦董,這就無法達(dá)到通過并行隊(duì)列管理所有添加的網(wǎng)絡(luò)請(qǐng)求任務(wù)的目的母谎。而且沒添加一個(gè)網(wǎng)絡(luò)請(qǐng)求任務(wù)就需要?jiǎng)?chuàng)建一個(gè)網(wǎng)絡(luò)請(qǐng)求對(duì)象,就會(huì)創(chuàng)建一次隊(duì)列京革,這樣會(huì)造成多大的內(nèi)存消耗呀奇唤!
問題4:如何解決網(wǎng)絡(luò)請(qǐng)求類的委托屬性被修改的問題?
重新架構(gòu)封裝結(jié)構(gòu)匹摇,最底層是持久層也就時(shí)網(wǎng)絡(luò)請(qǐng)求類咬扇,中間為業(yè)務(wù)邏輯層也就是網(wǎng)絡(luò)請(qǐng)求管理類,最上面是表示層也就說我們說的控制器1和控制器2廊勃。中間業(yè)務(wù)邏輯層也就是網(wǎng)絡(luò)請(qǐng)求管理類現(xiàn)在寫成了單例類懈贺,網(wǎng)絡(luò)請(qǐng)求類繼承與普通的NSObject,網(wǎng)絡(luò)數(shù)據(jù)下載完成后首先通過協(xié)議代理的回調(diào)方法將數(shù)據(jù)全部傳遞到網(wǎng)絡(luò)請(qǐng)求管理類,繼而繼而結(jié)合傳遞到網(wǎng)絡(luò)請(qǐng)求管理類的線程任務(wù)名稱以發(fā)送通知的形式發(fā)送到對(duì)應(yīng)的控制器里梭灿。
也就是說画侣,現(xiàn)在控制器表示層不再設(shè)置代理委托,而是注冊(cè)通知堡妒,當(dāng)然當(dāng)網(wǎng)絡(luò)請(qǐng)求類將下載完成的數(shù)據(jù)和線程網(wǎng)絡(luò)請(qǐng)求任務(wù)的標(biāo)識(shí)符(線程任務(wù)名字)通過回調(diào)方法傳遞到網(wǎng)絡(luò)請(qǐng)求管理類的方法時(shí)配乱,進(jìn)一步操作就是通過switch根據(jù)傳過來的線程任務(wù)名字判斷到底通知的名字是什么!傳值成功涕蚤!