幾年前初學(xué)iOS的時(shí)候悼院,iOS編程是我的啟蒙書,那時(shí)候看覺得很有難度袱耽,現(xiàn)在粗略重溫一遍杀餐。溫故而知新,發(fā)現(xiàn)里面還是有挺多干貨的朱巨。記錄一下史翘。
第一章
開發(fā)者中心的4個(gè)重要概念:
Developer Certificate:這份證書文件會通過Keychain Access(鑰匙串訪問)程序,加入讀者當(dāng)前使用的鑰匙串冀续。Xcode會使用這份證書為代碼簽名琼讽。
App ID:應(yīng)用程序標(biāo)識(application identifier)是一串能夠在App store中唯一標(biāo)識應(yīng)用的字符串。應(yīng)用程序標(biāo)識通常為這種形式:com.bignerdranch.awesomeApp洪唐,其中應(yīng)用名稱跟在公司名稱后面钻蹬。Provisioning Profile中的應(yīng)用程序標(biāo)識必須和應(yīng)用的程序包標(biāo)識(bundle identifier)匹配。針對開發(fā)的profile凭需,App ID可以包含通配符问欠,匹配任意的程序包標(biāo)識。
Device ID(UDID粒蜈,設(shè)備標(biāo)識):每一個(gè)iOS設(shè)備都有一個(gè)唯一的標(biāo)識顺献。
Provisioning Profile:需要在開發(fā)設(shè)備和計(jì)算機(jī)上保存Provisioning Profile文件。該文件對應(yīng)這些設(shè)置:1份開發(fā)者標(biāo)識和1組設(shè)備標(biāo)識(只有和這些標(biāo)識匹配的設(shè)備才能安裝)枯怖。Provisioning Profile文件的后綴名是.mobileprovision注整。
????????注意:Xcode在將應(yīng)用安裝至設(shè)備時(shí),會通過計(jì)算機(jī)上的某個(gè)Provisioning Profile獲得合適的證書(Certificate)嫁怀,并用這份證書為應(yīng)用的二進(jìn)制簽名设捐。接著,開發(fā)設(shè)備的UDID會和Provisioning Profile中的某個(gè)UDID匹配塘淑,應(yīng)用程序標(biāo)識(application identifier)會和程序包標(biāo)識(bundle identifier)匹配萝招。最后,Xcode會將簽名后的二進(jìn)制文件傳入開發(fā)設(shè)備存捺,并經(jīng)由設(shè)備上的同一個(gè)Provisioning Profile確認(rèn)后并最終啟動槐沼。
第二章
????????在Objective-C中,方法的唯一性取決于方法名捌治。因此岗钩,即使參數(shù)類型或返回類型不同,一個(gè)類也不能有兩個(gè)名稱相同的方法肖油。
????????NSLog支持一種額外的轉(zhuǎn)換說明:%@兼吓,對應(yīng)的實(shí)參類型是任何一種對象。程序在處理格式化字符串時(shí)森枪,如果遇到%@视搏,則不會直接替換為相應(yīng)位置的實(shí)參审孽。程序會先向?qū)?yīng)位置的實(shí)參發(fā)送description消息,得到description消息所返回的NSString對象浑娜,然后使用得到的NSString對象替換%@佑力。因?yàn)槌绦蚧叵?@所對應(yīng)的實(shí)參發(fā)送消息,所以這些實(shí)參必須是對象筋遭,又因?yàn)槊總€(gè)對象都實(shí)現(xiàn)了description方法打颤,所以任何對象都可以對應(yīng)%@。
? ? ? ? 數(shù)組對象只能保存指向Objective-C對象的指針漓滔,所以不能將基本類型的變量或C結(jié)構(gòu)加入到數(shù)組對象中编饺。
? ? ? ? #import和C語言中的#include作用相同,差別是#import不會重復(fù)導(dǎo)入同一個(gè)文件次和。
? ? ? ? isa:任何一個(gè)對象都有一個(gè)名為isa的實(shí)例變量反肋。在程序向某個(gè)類發(fā)送alloc消息并創(chuàng)建對象后,相應(yīng)的類會為新創(chuàng)建的對象的isa實(shí)例賦值踏施,將其指回自己,即創(chuàng)建該對象的類罕邀。所以任何一個(gè)對象都可以通過自己的isa指針畅形,知道自身的類型。(isa诉探,is a表示“是一個(gè)”的意思)日熬。Objective-C的很多特性都源自于isa指針。在運(yùn)行時(shí)肾胯,當(dāng)某個(gè)對象收到消息后竖席,會根據(jù)isa指針?biāo)赶虻念悾瑘?zhí)行和相應(yīng)的消息想匹配的方法敬肚。這種特性和多數(shù)編譯語言不同毕荐,在這些多數(shù)的編譯語言,需要執(zhí)行的方法在編譯時(shí)就決定了艳馒。而Objective-C的這種特性表明憎亚,需要執(zhí)行的方法只有在運(yùn)行時(shí)才能確定。
? ? ? ? self:self存在于方法中弄慰,是一個(gè)隱式局部變量第美。編寫方法時(shí)不需要聲明self,并且程序會自動為self賦值陆爽,指向收到消息的對象自身(大多數(shù)面向?qū)ο蟮恼Z言也有這個(gè)概念什往,有些將其稱為this)。通常情況下慌闭,self會用來向?qū)ο笞约喊l(fā)送消息别威。
? ? ? ? super:在覆蓋某個(gè)類的某個(gè)方法時(shí)躯舔,往往需要保留該方法在父類中的實(shí)現(xiàn),然后在其基礎(chǔ)上擴(kuò)充子類的實(shí)現(xiàn)兔港,為了能更方便地完成這項(xiàng)任務(wù)庸毫,Objective-C提供了名為super的編譯器指令。那么super是怎樣工作的呢衫樊?通常情況下飒赃,當(dāng)某個(gè)對象收到消息時(shí),系統(tǒng)會先從這個(gè)對象的類開始科侈,查詢和消息名相同的方法载佳。如果沒有找到,則會在這個(gè)對象的父類中繼續(xù)查找臀栈。該查詢過程會沿著繼承路線向上蔫慧,直到找到相應(yīng)的方法名為止(如果直到頂層結(jié)構(gòu)的頂端也沒能找到合適的方法 ,程序會拋出異常)权薯。向super發(fā)送消息姑躲,其實(shí)是向self發(fā)送消息,但是要求系統(tǒng)在查找方法時(shí)跳過當(dāng)前對象的類盟蚣。向父類開始查詢黍析。
? ? ? ? 異常和未知選擇器:Objective-C是一種動態(tài)類型語言,無法在編譯時(shí)(即構(gòu)建應(yīng)用時(shí))判斷某個(gè)對象是否能夠響應(yīng)特定的消息屎开。如果Xcode判斷應(yīng)用會向某個(gè)對象發(fā)送其無法響應(yīng)的消息阐枣,就會顯示相應(yīng)的警告,但是代碼仍然能夠編譯通過奄抽。出于某些原因(原因很多)蔼两,如果應(yīng)用最后還是向某個(gè)對象發(fā)送了其無法響應(yīng)的消息,那么程序在運(yùn)行時(shí)會拋出異常(exception)逞度,異常也稱為運(yùn)行時(shí)錯(cuò)誤(run-time error)额划。這是因?yàn)楫惓V粫趹?yīng)用運(yùn)行時(shí)才會出現(xiàn)。和運(yùn)行時(shí)錯(cuò)誤相對應(yīng)的是編譯時(shí)錯(cuò)誤第晰,編譯時(shí)錯(cuò)誤只會在構(gòu)建應(yīng)用或編譯代碼時(shí)出現(xiàn)锁孟。一個(gè)最常見的運(yùn)行時(shí)錯(cuò)誤是unreconized selector(未知選擇器),未知選擇器的意思是某個(gè)對象收到了其沒有實(shí)現(xiàn)的消息茁瘦。
第三章
堆:堆(heap)是指內(nèi)存中的一塊區(qū)域品抽,應(yīng)用中的所有對象都會保存在堆中。當(dāng)應(yīng)用向某各類發(fā)送alloc消息時(shí)甜熔,系統(tǒng)會從堆中分配一塊內(nèi)存圆恤,其大小足夠存放相應(yīng)的對象的全部實(shí)例變量。
? ? ? ? 比如可以以NSDate對象的實(shí)例來解析內(nèi)存分配的問題。該對象包含兩個(gè)實(shí)例變量:一個(gè)是double類型的變量(用于保存從某個(gè)固定時(shí)間點(diǎn)算起的時(shí)間差盆昙,單位為秒)羽历,另一個(gè)是繼承自NSObject的isa指針(所有的對象都會繼承)。一個(gè)double變量的大小是8個(gè)字節(jié)(byte)淡喜,一個(gè)isa指針的大小是4個(gè)字節(jié)秕磷。因此,NSDate類收到alloc消息后炼团,系統(tǒng)會在堆上為新的NSDate對象分配一個(gè)大小為12字節(jié)的內(nèi)存空間澎嚣。
? ? ? ? 一個(gè)對象永遠(yuǎn)不會直接保存另一個(gè)對象,所有的對象在堆中都是獨(dú)立存在的瘟芝。如果需要易桃,一個(gè)對象可以保存指向其他對象的引用,級可以將其他對象的地址賦給指針實(shí)例變量锌俱。
棧:棧是內(nèi)存中的另一塊區(qū)域晤郑,和堆是分開的。堆包含了大量無序的對象贸宏,需要通過指針來保存這些對象在堆中的地址造寝。棧,則會按先進(jìn)后出吭练,后進(jìn)先出的規(guī)則來保存一組幀(frame匹舞,為了方便讀者辨認(rèn),下面會將frame翻譯為棧幀)线脚。當(dāng)程序執(zhí)行某個(gè)方法(或函數(shù))時(shí),會從棧中分配一塊內(nèi)存空間叫榕,這塊空間成為棧幀浑侥。棧幀負(fù)責(zé)保存程序在方法中聲明的變量的值。在方法中聲明的變量為局部變量(local variable)晰绎。當(dāng)某個(gè)應(yīng)用啟動并運(yùn)行main函數(shù)時(shí)寓落,它的棧幀會被保存在棧的底部。當(dāng)main函數(shù)調(diào)用另一個(gè)方法時(shí)荞下,這個(gè)方法的棧幀會壓入棧的頂部伶选。被調(diào)用的方法還可以調(diào)用其他方法,依此類推尖昏,最終會在棧中形成一個(gè)塔狀的棧幀序列仰税。當(dāng)被調(diào)用的方法結(jié)束時(shí),程序會將其棧幀從棧中“彈出”并釋放抽诉。如果同一個(gè)方法被再次調(diào)用陨簇,則應(yīng)用會創(chuàng)建一個(gè)全新的棧幀,并將其壓入棧的頂部迹淌。
? ? ? ? __weak:使用__weak關(guān)鍵字河绽,可以將某個(gè)變量聲明為具有弱引用特性己单。如__weak Person *person;弱引用有一項(xiàng)很有用的功能耙饰,當(dāng)程序釋放了某個(gè)對象時(shí)纹笼,會自動地將指向該對象的具有弱引用特性的指針全部設(shè)置為nil。
? ? ? ? __unsafe__unretained:除了使用__weak苟跪,還可以使用__unsafe__unretained來聲明變量廷痘。具有__unsafe__unretained特性的指針不會改變其指向的對象的擁有方個(gè)數(shù)(這點(diǎn)與__weak相同)。但是當(dāng)程序釋放某個(gè)對象后削咆,不會自動的將指向該對象的具有__unsafe__unretained特性的指針設(shè)置為nil牍疏。這也是為什么具有__unsafe__unretained特性的變量是不安全的。(Objective-C之所以會提供__unsafe_unretained特性拨齐,主要是出于兼容性考慮:弱引用__weak是iOS5引入的新特性鳞陨,針對老版本的iOS開發(fā)的應(yīng)用,如果需要使用ARC瞻惋,并且保留原有的內(nèi)存管理邏輯厦滤,就必須使用__unsafe__unretained。
第四章
????????深入學(xué)習(xí):構(gòu)建階段歼狼、編譯器錯(cuò)誤和連接器錯(cuò)誤
? ? ? ? Xcode會分步驟構(gòu)建應(yīng)用掏导,這些步驟成為構(gòu)建階段(build phases),構(gòu)建階段需要完成的任務(wù)如下:
1羽峰、編譯源代碼(Compile Sources):該階段會編譯構(gòu)建目標(biāo)所需的源代碼趟咆,凡是加入項(xiàng)目的源代碼文件,默認(rèn)都會加入該構(gòu)建階段梅屉。
2值纱、連接二進(jìn)制文件(Link Binary With Libraries):Xcode會將編譯后的代碼和指定的框架(庫)連接起來,使代碼能夠使用相應(yīng)的框架中的類坯汤。
3虐唠、拷貝程序包資源(Copy Bundle Resources):完成代碼的編譯和連接后,Xcode會生成一個(gè)可執(zhí)行文件惰聂,并將其放入應(yīng)用程序包(應(yīng)用程序包其實(shí)是一個(gè)目錄)疆偿。接著,Xcode會將指定的文件加入程序包搓幌。這些資源是運(yùn)行應(yīng)用時(shí)要使用的數(shù)據(jù)文件杆故,例如Xib文件、圖片和聲音文件鼻种。凡是加入項(xiàng)目的非源文件反番,默認(rèn)都會加入該構(gòu)建階段。
? ? ? ? 使用Xcode構(gòu)建應(yīng)用時(shí),會在編譯源代碼的階段發(fā)現(xiàn)錯(cuò)誤罢缸。連接二進(jìn)制和庫的階段有時(shí)也會出錯(cuò)篙贸。
1、編譯源代碼分為兩步:預(yù)處理(preprocessing)和編譯枫疆。
預(yù)處理
????????預(yù)處理的作用是為每個(gè)實(shí)現(xiàn)文件(.m)創(chuàng)建一個(gè)中間文件(intermediate file)(.mi文件)爵川。中間文件和實(shí)現(xiàn)文件一樣,都是Objective-C代碼息楔,但是中間文件的體積可能會很大寝贡。
? ? ? ? 預(yù)處理器處理完實(shí)現(xiàn)文件中的所有預(yù)處理指令后,會生成一個(gè)中間文件值依。預(yù)處理指令是帶有前綴#的語句圃泡,例如#import、#define等愿险。預(yù)處理器在處理#import語句時(shí)颇蜡,會將語句替換成導(dǎo)入文件的內(nèi)容。
? ? ? ? 我自己理解的預(yù)處理過程:預(yù)處理器先處理.m文件中帶#前綴的預(yù)處理語句辆亏,會導(dǎo)入相應(yīng)的文件內(nèi)容风秤。然后.m文件會生成類型為.mi的中間文件。
編譯
? ? ? ? 完成預(yù)處理步驟后扮叨,Xcode會在編譯之前生成的中間文件缤弦,將Objective-C代碼轉(zhuǎn)換成機(jī)器碼(機(jī)器碼的意思應(yīng)該就是機(jī)器可是識別的代碼格式)。新生成的機(jī)器碼會被保存在目標(biāo)文件.o中彻磁,一個(gè)中間文件就對應(yīng)著一個(gè)目標(biāo)文件碍沐。
? ? ? ? 開發(fā)應(yīng)用時(shí),大部分錯(cuò)誤會發(fā)生在這個(gè)將中間文件轉(zhuǎn)換成機(jī)器碼的編譯階段衷蜓。當(dāng)編譯器“看不懂”代碼時(shí)抢韭,就會報(bào)錯(cuò)。這類在編譯階段產(chǎn)生的錯(cuò)誤成為編譯時(shí)錯(cuò)誤(compile-time errors)或語法錯(cuò)誤(syntax errors)恍箭。常見的編譯時(shí)錯(cuò)誤有放錯(cuò)分號;的位置、方括號[]或花括號{}不匹配瞧省、拼寫錯(cuò)誤或字母大小寫錯(cuò)誤等扯夭。
連接
? ? ? ? 目標(biāo)文件.o包含了實(shí)現(xiàn)文件.m中實(shí)現(xiàn)的方法的機(jī)器碼。但是某個(gè)實(shí)現(xiàn)文件可能會用到其他實(shí)現(xiàn)文件中的代碼鞍匾。例如WhereamiViewController.m會使用startUpdatingLocation方法交洗,而此方法的機(jī)器碼存在于CLLocationManager.m的目標(biāo)文件中。
? ? ? ? 編譯器不是將startUpdateingLocation方法的代碼拷貝至WhereamiViewController.m的目標(biāo)文件中橡淑,而是設(shè)置一個(gè)連接构拳,指向CLLocationManager.m的目標(biāo)文件。連接二進(jìn)制文件和庫就是處理這類連接的階段,簡稱連接階段置森。
? ? ? ? 框架是一組類的集合斗埂,而類由頭文件.h和實(shí)現(xiàn)文件.m兩個(gè)文件定義≠旌#框架的特別之處是呛凶,框架的實(shí)現(xiàn)文件是已經(jīng)預(yù)編譯好的(即框架中的.m文件已經(jīng)經(jīng)過了預(yù)處理和編譯的過程),并且會將處理后的目標(biāo)文件分成一個(gè)或多個(gè)庫文件(所以我們看到Objective-C的框架是沒有.m文件的行贪,相應(yīng)的實(shí)現(xiàn)文件已經(jīng)轉(zhuǎn)成機(jī)器碼)漾稀。如凡是用Core Location框架的代碼的類,編譯器都會將其目標(biāo)文件中放置相應(yīng)的連接建瘫,指向Core Location庫崭捍。
? ? ? ? 如果編譯器無法處理某個(gè)連接(例如無法找到包含相應(yīng)代碼的目標(biāo)文件,或者目標(biāo)文件沒有包含被引用的代碼)啰脚,就會產(chǎn)生連接器錯(cuò)誤(linker error)殷蛇。因?yàn)檫B接器錯(cuò)誤會包含陌生的術(shù)語,并且錯(cuò)誤不是源自某行代碼拣播,所以對于新手而言晾咪,這類錯(cuò)誤更難看懂。(比如我們有時(shí)候集成一些第三方的插件贮配,如友盟推送谍倦,如果沒有在Lind Binary With Libraries導(dǎo)入規(guī)定的框架或庫,就會產(chǎn)生連接器錯(cuò)誤)
第五章
? ? ? ? 響應(yīng)對象(UIResponder)負(fù)責(zé)接收和響應(yīng)和處理與其相關(guān)的事件泪勒。UIView是UIResponder的子類昼蛀。所以UIView也可以接收事件。在應(yīng)用的響應(yīng)對象中圆存,會有一個(gè)對象成為第一響應(yīng)者叼旋。同一時(shí)刻,只能有一個(gè)響應(yīng)者成為第一響應(yīng)者沦辙。第一響應(yīng)者會處理那些與屏幕位置無關(guān)的事件夫植。例如:輕按事件會被發(fā)送給某個(gè)被單擊的視圖對象,但是搖動事件和屏幕位置無關(guān)油讯,所以會被發(fā)送給第一響應(yīng)者對象详民。
? ? ? ? #import “”和<>的區(qū)別:在導(dǎo)入文件時(shí),如果是自己創(chuàng)建的文件陌兑,就要用雙引號沈跨,如果是框架的頭文件,就要用尖括號兔综。編譯器在處理尖括號時(shí)饿凛,只會在系統(tǒng)庫里查找相應(yīng)的文件狞玛。編譯器在處理雙引號的導(dǎo)入時(shí),會先在項(xiàng)目目錄中查找涧窒,如果沒找到心肪,才會在系統(tǒng)庫里查找。
第六章
? ? ? ? 每個(gè)視圖都會維護(hù)一張圖片杀狡,即視圖的外觀蒙畴。如UIButton對象的圖片是帶居中文字的圓角四方形。UILabel對象的圖片是單純的文字呜象。當(dāng)視圖的數(shù)據(jù)(例如UIButton的title屬性膳凝、UILabel的text屬性)發(fā)生變化時(shí),視圖會重畫這張圖片恭陡,從而使視圖的外觀能夠匹配數(shù)據(jù)的變化蹬音。
? ? ? ? 應(yīng)用在重畫屏幕(應(yīng)用在處理完當(dāng)前事件后重畫屏幕)時(shí),UIWindow對象會先將其圖片畫在屏幕上休玩。接著著淆,UIWindow對象的所有子視圖會將各自的圖片畫在屏幕上,然后這些子視圖的所有子視圖會畫出各自的圖片拴疤,以此類推永部。創(chuàng)建用戶界面的過程可以總結(jié)為:為每個(gè)視圖創(chuàng)建相應(yīng)的圖片,然后將這些視圖加入視圖層次結(jié)構(gòu)呐矾。
? ? ? ? 負(fù)責(zé)繪制視圖的方法是drawRect:方法苔埋,默認(rèn)情況下,這個(gè)方法不做任何事情蜒犯。UIView的子類可以覆蓋drawRect:方法來完成自定義的繪圖任務(wù)组橄。覆蓋drawRect:方法后,需要使用特定的繪圖方法或函數(shù)罚随,才能使UIView的子類創(chuàng)建相應(yīng)的圖片玉工。這些繪圖方法或函數(shù)屬于Core Graphics框架。覆蓋drawRect:之后的第一件事情就是獲取繪圖上下文淘菩。繪圖上下文作用是1遵班、維護(hù)各種繪圖狀態(tài)(如當(dāng)前的繪圖顏色、線條粗細(xì))2潮改、執(zhí)行繪圖操作费奸。應(yīng)用在向某個(gè)視圖發(fā)送drawRect:方法前,會先自動創(chuàng)建一個(gè)繪圖上下文进陡,然后將其設(shè)置為當(dāng)前上下文。
? ? ? ? frame包括視圖大小和相對于父識圖的位置微服。bounds與的位置和大小與父視圖無關(guān)趾疚,是其在真實(shí)坐標(biāo)系中的位置缨历。通常情況下,bounds的origin會是(0糙麦,0)辛孵,size和frame的size相同。
夜的第七章
????????UIViewController的指定初始化方法是initWithNibNane:bundle:方法赡磅。如果這個(gè)方法的兩個(gè)入?yún)⒍紓鱪il魄缚,那么會找跟控制器相同名稱的xib文件、并在mainBundle中查找焚廊。其實(shí)init方法就相當(dāng)于initWithNibName:bundle:兩個(gè)入?yún)鱪il冶匹。?
? ? ? ? 當(dāng)某個(gè)控制器由于內(nèi)存警告而卸載其視圖時(shí),會向自己發(fā)送viewDidUnload方法咆瘟。不過現(xiàn)在這個(gè)方法已經(jīng)被棄用了嚼隘。
? ? ? ? 創(chuàng)建一個(gè)UITabBarController,然后賦給self.window.rootViewController袒餐。設(shè)置tabbarController的viewControllers數(shù)組飞蛹,數(shù)組里面裝的都是已經(jīng)初始化的UIViewController。這時(shí)候每個(gè)UIViewController都有一個(gè)tabBarItem屬性灸眼,通過tabBarItem屬性設(shè)置title和image卧檐。
? ? ? ? UIApplicationMain函數(shù)做了什么?1焰宣、創(chuàng)建一個(gè)運(yùn)行循環(huán)霉囚,讓程序可以不死并持續(xù)做事情。2宛徊、創(chuàng)建一個(gè)AppDelegate對象佛嬉,該對象時(shí)應(yīng)用的代理對象, 在特定的時(shí)候闸天,特定的代理方法會回調(diào)暖呕。
? ? ? ? 對于Retina屏幕:如果一個(gè)點(diǎn)是1像素x1像素,那么使用1@的圖片苞氮,如果一個(gè)點(diǎn)是2像素x2像素湾揽,那么使用2@,如果一個(gè)點(diǎn)是3像素x3像素笼吟,那么使用3@库物。
第八章
????????如果程序?qū)⒛硞€(gè)對象注冊成為觀察器,就必須在釋放該對象的時(shí)候?qū)⑵湟瞥鐾ǜ嬷行拇铩T颍喝绻绦蜥尫帕四硞€(gè)已經(jīng)注冊成為觀察器的對象戚揭,但是沒有將其移出通告中心。當(dāng)通告中心轉(zhuǎn)發(fā)該對象曾經(jīng)關(guān)心的事情時(shí)撵枢,會向已經(jīng)不存在的該對象發(fā)送通知民晒,導(dǎo)致程序崩潰精居。
第十一章
? ? ? ? UISlipViewController只適合于iPad使用。
? ? ? ? UINavigationController里面有UINavigationBar。但是棧幀上的Controller有navigationItem。navigationItem里面有barButtonItem(left和right)伴郁,還有一個(gè)titleView茵乱。
? ? ? ? UIToolbar和UINavigationBar的原理很相似,都可以加入U(xiǎn)IBarButtonItem,但是UINavigationBar只可以在左右放入兩個(gè),而UIToolbar可以放入一組。
? ? ? ? UIImagePickerController维雇,需要設(shè)置sourceType和delegate,sourceType包括UIImagePickerControllerSourceTypeCamera搜贤、UIImagePickerControllerSourceTypePhotoLibriry谆沃、UIImagePickerControllerSourceTypeAlbum。但是需要先判斷isSourceTypeAvailable仪芒。然后以模態(tài)的形式彈出來唁影。還有一個(gè)屬性是mediaTypes數(shù)組,可以用來限制用戶選擇媒體的類型掂名。媒體類型有兩種据沈,kUTTypeImage、kUTTypeMovie饺蔑。auvalableMediaTypesForSourceType:方法可以檢查是否能拍攝視頻锌介。UIImagePickerController會將拍攝的視頻存入臨時(shí)目錄(tmp),(臨時(shí)目錄是不安全的猾警,隨時(shí)可能被清理)孔祸。可以用NSFileManager存入caches目錄或者使用UISaveVideoAtPathToSavePhotoAlbum([mediaURL path],nil,nil,nil);[NSFileManager defaultManager] removeItemAtPath:[mediaURL path] error:nil];
? ? ? ? 如果選擇完圖片跳回來发皿,有可能之前的控制器的頁面會由于內(nèi)存警告而釋放了ImageView崔慧,從而導(dǎo)致無法設(shè)置。所以需要一個(gè)圖片保存的類穴墅。
第十二章
? ? ? ? 有很多種途徑可以生成無重復(fù)字符串惶室。本節(jié)使用一種Cocoa Touch提供的一種機(jī)制,這種機(jī)制可以生成唯一標(biāo)識(UUID玄货,也稱GUID)皇钞。類型為CFUUIDRef的指針?biāo)赶虻腃結(jié)構(gòu)代表UUID,UUID是基于當(dāng)前時(shí)間松捉、計(jì)數(shù)器和硬件標(biāo)識(通常為以太網(wǎng)卡的MAC地址)等數(shù)據(jù)計(jì)算生成夹界。
// 創(chuàng)建一個(gè)CFUUID,CFUUIDCreate函數(shù)知道如何創(chuàng)建無重復(fù)的字符串
CFUUIDRef newUniqueID = CFUUIDCreate(kCFAllocatorDefault)隘世;
? ? ? ? CFUUIDRef的類型名稱有前綴CF可柿,這是因?yàn)樗醋杂贑ore Foundation框架(后綴Ref代表它是指針也拜。Core Foundation是一套C語言API),提供多種C結(jié)構(gòu)和C函數(shù)趾痘。當(dāng)要?jiǎng)?chuàng)建Core Foundation框架中的特定結(jié)構(gòu)時(shí),必須調(diào)用相應(yīng)的C函數(shù)蔓钟,這些函數(shù)的函數(shù)名是有規(guī)律的永票,一般都會以某種結(jié)構(gòu)的類型名稱為起始,然后為英文單詞Create(例如上面的CFUUIDCreate)滥沫。此外侣集,這些函數(shù)的第一個(gè)實(shí)參類型都是CFAllocatorRef,用于指定內(nèi)存的分配方式兰绣。通常情況下都會傳入kCFAllocatorDefault世分,要求系統(tǒng)自行決定內(nèi)存分配方式。
? ? ? ? CFUUIDRef指向C結(jié)構(gòu)缀辩,使用成員變量來保存UUID臭埋。如果使用字符串來代表該結(jié)構(gòu),則示例如下:28153B74-4D6A-12F-9D61-155EA4C32167
? ? ? ? 通過C函數(shù)CFUUIDCreateString臀玄,可以根據(jù)指定的CFUUIDRef創(chuàng)建相應(yīng)的字符串對象瓢阴。
// 根據(jù)指定的CFUUIDRef創(chuàng)建相應(yīng)的字符串對象
CFStringRef newUniqueIDString = CFUUIDCreateString(kCFAllocatorDefault,newUniqueID)健无;
? ? ? ? Core Foundation中的很多結(jié)構(gòu)都包含Objective-C類的版本荣恐,并且兩者可以自由轉(zhuǎn)換(toll-free-bridging)。例如累贤,對應(yīng)CFStringRef的Objective-C類是NSString叠穆,對應(yīng)CFArrayRef的Objective-C類是NSArray。這些支持自由轉(zhuǎn)換的結(jié)構(gòu)和類實(shí)例之所以能夠自由轉(zhuǎn)換臼膏,是因?yàn)閮烧咴趦?nèi)存中的存儲結(jié)構(gòu)完全相同硼被。
NSString *key = (__bridge NSStrign *)newUinqueIDString;
????????這段代碼使用了__bridge關(guān)鍵字。要理解__bridge的作用讶请,必須先了解Core Foundation的C結(jié)構(gòu)的內(nèi)存管理機(jī)制祷嘶。
Core Foundation與toll-free-bridging:
? ? ? ? 當(dāng)程序丟失了某個(gè)指向Objective-C對象的指針時(shí)(指針變量被釋放,或者指向了其他對象)夺溢,ARC就會起作用论巍,將相應(yīng)的對象標(biāo)記為失去了一個(gè)擁有方。但是ARC對Core Foundation的C結(jié)構(gòu)無效风响。因此嘉汰,在程序失去了某個(gè)指向Core Foundation的C結(jié)構(gòu)的指針前,必須調(diào)用一個(gè)特定的函數(shù)状勤,將相應(yīng)的C結(jié)構(gòu)的擁有方減1.這個(gè)特定的函數(shù)是CFRelease鞋怀。
? ? ? ? 如果在失去指針錢沒有調(diào)用CFRelease双泪,那么相應(yīng)的C結(jié)構(gòu)的擁有方數(shù)會保持不變,從而導(dǎo)致內(nèi)存泄漏密似。
? ? ? ? CFRelease(newUniqueIDString)焙矛;CFRelease(newUniqueID);
針對Core ?Foundation的C結(jié)構(gòu)(如下簡寫為結(jié)構(gòu))残腌,有如下若干內(nèi)存管理規(guī)則:
1村斟、指針變量可以擁有其指向的結(jié)構(gòu),前提是該結(jié)構(gòu)必須由函數(shù)名包含Create或Copy的函數(shù)創(chuàng)建的抛猫。如NSString *key = (__bridge NSStrign *)newUinqueIDString; ---- key指針變量可以擁有newUniqueIDString這個(gè)結(jié)構(gòu)蟆盹,且這個(gè)結(jié)構(gòu)是由Create函數(shù)創(chuàng)建的。
2闺金、如果某個(gè)指針擁有一個(gè)結(jié)構(gòu)逾滥,就必須在程序失去該指針前調(diào)用CFRelease函數(shù),將相應(yīng)結(jié)構(gòu)的擁有方個(gè)數(shù)減少1败匹。失去指針是指程序修改了某個(gè)指針變量寨昙,將其指向了其他對象或結(jié)構(gòu)(包括nil),或者是程序釋放了指向某個(gè)對象的指針變量哎壳。
3毅待、一旦某個(gè)指針調(diào)用了CFRelease函數(shù),就不要再嘗試訪問該指針归榕。
? ? ? ? 下面介紹一下__bridge關(guān)鍵字尸红。因?yàn)锳RC對Core Foundation的C結(jié)構(gòu)無效,所以當(dāng)代碼將某個(gè)Core Foundation的C結(jié)構(gòu)轉(zhuǎn)換成Objective-C對象時(shí)刹泄,ARC將無法正確處理相應(yīng)的內(nèi)存管理問題外里。轉(zhuǎn)換運(yùn)算符仲的(__bridge)關(guān)鍵字的作用是要求ARC忽略針對該指針的內(nèi)存管理規(guī)則。因此特石,當(dāng)ARC在處理下面這行代碼的時(shí)候盅蝗,不會將key變量設(shè)置為NSString對象的擁有方。NSStrign *key = (__bridge NSString *)newUinqueIDStrign姆蘸;對于賦值后的指針變量key墩莫,ARC可以正確地發(fā)揮作用。
第十三章
? ? ? ? 區(qū)別是iPad還是iPhone:[[UIDevice currentDevice] userInterfaceIdiom]; iPad為userInterfaceIdiomPad逞敷、iPhone為userInterfaceIdiomPhone狂秦。
? ? ? ? UIPopoverController,只能在iPad上使用推捐。不過已經(jīng)被棄用了×盐剩現(xiàn)在用UIPopoverPresentationController,可以在iPhone和iPad上使用】安荆可以做到彈出帶三角形的小控制器和視圖的效果痊乾。
第十四章
? ? ? ? 固化(archive)是iOS SDK提供的一種保存和讀取對象的機(jī)制,使用非常廣泛椭更。當(dāng)固化某個(gè)對象時(shí)哪审,會將該對象的所有實(shí)例變量存入指定的文件。當(dāng)應(yīng)用解固(unarchive)某個(gè)對象時(shí)虑瀑,會從指定的文件讀取相應(yīng)的數(shù)據(jù)协饲,然后根據(jù)數(shù)據(jù)還原對象。為了能夠固化和解固某個(gè)對象缴川,相應(yīng)的對象必須遵守NSCoding協(xié)議,并且實(shí)現(xiàn)兩個(gè)必須方法:encodeWithCoder:和initWithCoder:
? ? ? ? 當(dāng)需要編碼某個(gè)對象時(shí)描馅,會向這個(gè)對象發(fā)送encodeWithCoder:消息把夸。收到消息的對象需要編碼自己的實(shí)例變量,所以也會向這些實(shí)例變量發(fā)送encodeWithCoder:消息铭污。因此恋日,對象的編碼(固化)過程是一個(gè)遞歸過程,編碼中的對象會再編碼其他對象嘹狞。
? ? ? ? 當(dāng)應(yīng)用需要根據(jù)編碼后的數(shù)據(jù)初始化某個(gè)對象時(shí)岂膳,會向該對象發(fā)送initWithCoder:消息。initWithCoder:應(yīng)該還原之前通過encodeWithCoder:編碼(固化)的所有對象磅网,然后將這些對象賦給相應(yīng)的實(shí)例變量谈截。
? ? ? ? 有人會問,問什么要這么寫涧偷?原因很簡單簸喂,小數(shù)據(jù)量的持久化都用NSUserDefaults來實(shí)現(xiàn),但是NSUserDefaults只能保存NSString,燎潮、NSNumber,喻鳄、NSDate、 NSArray确封、NSDictionary這些數(shù)據(jù)類型除呵,但大多時(shí)候,我們想要把某個(gè)對象持久化保存起來爪喘,所以就需要用到NSCoding協(xié)議這樣來實(shí)現(xiàn)颜曾。比如我們需要持久化保存某個(gè)Model對象。然后我們再結(jié)合NSKeyedArchiver和NSKeyedUnArchiver或者把對象存到應(yīng)用沙盒中腥放。
? ? ? ? xib文件也是基于固化機(jī)制的泛啸。當(dāng)讀者在Xcode中將某個(gè)視圖拖拽至畫布時(shí),Xcode會創(chuàng)建相應(yīng)的對象。保存xib文件時(shí)候址,Xcode會將這些視圖固化至指定的文件(UIView遵守NSCoding協(xié)議)吕粹。當(dāng)應(yīng)用需要載入xib文件時(shí),就會解固xib文件中的視圖岗仑。
應(yīng)用沙盒
應(yīng)用沙盒包括如下幾個(gè)目錄:
1匹耕、應(yīng)用程序包(application bundle):包含所有的資源文件和可執(zhí)行文件,是只讀目錄荠雕。(NSBundle mainBundle)稳其。
2、Documents/目錄:存放應(yīng)用運(yùn)行時(shí)生成的需要保存的數(shù)據(jù)炸卑。iTunes和iCloud會在同步設(shè)備時(shí)備份該目錄既鞠。當(dāng)設(shè)備發(fā)生故障時(shí),可以從iTunes和iCloud恢復(fù)該目錄中的文件盖文。例如嘱蛋,游戲存檔可以保存在此。
3五续、Library/Preferences/目錄:存放所有的偏好設(shè)置洒敏。iOS中的設(shè)置(Settings)應(yīng)用也會在該目錄中查找本應(yīng)用的設(shè)置信息。使用NSUserDefaults類疙驾,可以通過plist文件保存數(shù)據(jù)在此凶伙。iTunes和iCloud會在同步設(shè)備時(shí)備份該目錄。
4它碎、Library/Caches/目錄:存放應(yīng)用運(yùn)行時(shí)生成的需要保存的數(shù)據(jù)函荣。但是iTunes和iCloud不會備份這個(gè)目錄。不備份數(shù)據(jù)緩存的原因是因?yàn)檫@個(gè)文件夾的數(shù)據(jù)可能很大扳肛,從而延長同步需要的時(shí)間偏竟。如果數(shù)據(jù)源是從別處(如web服務(wù)器)獲取的。就可以保存到本目錄敞峭。當(dāng)用戶需要恢復(fù)設(shè)備時(shí)踊谋,應(yīng)用只需再次去別處(如web服務(wù)器)獲取即可。
5旋讹、tmp/文件夾:存放應(yīng)用運(yùn)行時(shí)所需的臨時(shí)數(shù)據(jù)殖蚕。當(dāng)應(yīng)用不再需要使用tmp/目錄中的文件,就應(yīng)該改刪除這些文件沉迹。iOS系統(tǒng)會不定時(shí)清理這個(gè)目錄中的文件睦疫。iTunes和iCloud不會備份此目錄。
通過NSKeyedArchiver和NSKeyedUnarchiver來把對象數(shù)據(jù)保存和讀缺夼弧:
archiveRootObject:toFile:方法會先創(chuàng)建一個(gè)NSKeyedArchiver對象蛤育,然后向跟對象(這里是p1)發(fā)送encodeWithCoder:消息,并將之前創(chuàng)建的NSKeyedArchiver對象作為實(shí)參傳入(NSKeyedArchiver是NSCoder的子類)。p1的encodeWithCoder:方法會向其包含的所有屬性發(fā)送encodeWithCoder:消息瓦糕。并傳入一個(gè)NSKeyedArchiver對象底洗。當(dāng)所有的對象都完成編碼后,NSKeyedArchiver對象就會將數(shù)據(jù)寫入指定的文件中咕娄。(我們可以通過打印documents文件夾路徑亥揖,然后按Command+Shift+G來進(jìn)入沙盒查看)
? ? ? ? 當(dāng)iOS系統(tǒng)認(rèn)為當(dāng)前可用的內(nèi)存過低時(shí),會根據(jù)需要終止掛起狀態(tài)的應(yīng)用圣勒,并且不會發(fā)出警告费变。當(dāng)系統(tǒng)有足夠多的空域內(nèi)存時(shí),出于掛起狀態(tài)的應(yīng)用可以一直保留該狀態(tài)圣贸。當(dāng)出于掛起狀態(tài)的應(yīng)用即將被系統(tǒng)終止時(shí)挚歧,并不會收到相應(yīng)的通知,系統(tǒng)會直接將其從內(nèi)存中移除(終止后的應(yīng)用吁峻,其圖標(biāo)可能還會保留在Dock中昼激,按下圖標(biāo)會重啟應(yīng)用)
? ? ? ? 隱式變量“_cmd”表示當(dāng)前方法的選擇器。NSStringFromSelector()傳入_cmd可以為指定的選擇器生成相應(yīng)的字符串
? ? ? ? 如果要讀寫二進(jìn)制數(shù)據(jù)锡搜,可以使用NSData。如果要讀寫文本數(shù)據(jù)瞧掺,可以使用NSString的實(shí)例方法initWithContentsOfFile:讀方法和writeToFile:atomically:encoding:error:寫方法耕餐。和NSString類似,NSDictionary辟狈、NSArray也有initWithContentsOfFile:讀方法和writeToFile:寫方法肠缔。
????????深入學(xué)習(xí):應(yīng)用程序包:Xcode在構(gòu)建iOS應(yīng)用時(shí),需要完成的工作是創(chuàng)建應(yīng)用程序包(application bundle)哼转。應(yīng)用程序包會包含應(yīng)用的可執(zhí)行文件和執(zhí)行應(yīng)用所需的全部資源明未。這些資源包括xib文件、圖片和音頻文件等壹蔓。將某個(gè)文件加入到項(xiàng)目中趟妥,Xcode會自動判斷是否應(yīng)該將該文件加入應(yīng)用程序包,然后再進(jìn)行分類佣蓉。打開應(yīng)用的沙盒披摄,可以看到應(yīng)用沙盒中包括以下目錄:應(yīng)用程序包、Documents勇凭、tmp疚膊、Library。[NSBundle mainBundle]可以獲取應(yīng)用程序包虾标,pathForResource:ofType:可以獲取某個(gè)文件的全路徑寓盗。
第十五章
UITableViewCell的兩種注冊方式:
1、只在tableView: cellForRowAtIndexPath:方法里面這樣寫:static NSString *cellIdentifier = @"cell";
? ? UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
? ? if (!cell) {
? ? ? ? cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:cellIdentifier];
? ? }
? ? return cell;
2、UINib *nib = [UINib nibWithNibName:@"UITableViewCell" bundle:nil];
? ? [self.tableView registerNib:nib forCellReuseIdentifier:@"cell"];
tableView: ?cellForRowAtIndexPath:方法里面:
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
? ? return cell;
說明:創(chuàng)建某個(gè)UINib對象時(shí)傀蚌,必須指定一個(gè)XIB文件基显。新創(chuàng)建的UINib對象會載入該文件,并始終保留相應(yīng)的數(shù)據(jù)喳张。當(dāng)UINib對象收到instantiateWithOwner:options:消息后续镇,就會解析指定的XIB文件:解固所有的固化對象,并恢復(fù)所有的關(guān)聯(lián)销部∶剑【NSBunlde mainBundle】loadNibName:owner:options:方法,實(shí)際上該消息會在內(nèi)部使用UINib:先創(chuàng)建一個(gè)UINib對象舅桩,然后根據(jù)傳入的實(shí)參酱虎,向UINib對象發(fā)送instantiateWithOwner:options:消息。loadNibName:owner:options:和instantiateWithOwner:options:都會返回一個(gè)NSArray對象擂涛。
第二十二章+第二十三章(動畫相關(guān))
? ? ? ? CALayer和CAAnimation來進(jìn)行動畫读串。這兩個(gè)是Core Animation的兩個(gè)主要類。而Core Animation在QuanzCore框架中撒妈。
? ? ? ? 每一個(gè)UIView都有一個(gè)layer的隱式層屬性恢暖,負(fù)責(zé)圖片和顯示的效果。UIView和CALayer的區(qū)別是UIView繼承自UIResponder狰右,是可以進(jìn)行事件的響應(yīng)的杰捂。CALayer的代理就是包含它的UIView對象。也就是說棋蚌,UIView是用類封裝起來的一個(gè)抽象可視對象嫁佳,并且可以處理觸摸事件,而和繪圖有關(guān)的功能谷暮,放在CALayer中蒿往。
? ? ? ? CALayer是沒有frame屬相的,但是仍然可以向其發(fā)送setFrame:消息湿弦,這時(shí)候會根據(jù)frame的position和bounds來對layer設(shè)置瓤漏。CALayer有一個(gè)anchorPoint屬性,默認(rèn)為(0.5颊埃,0.5)赌蔑。CALayer有一個(gè)contents屬性,指向一個(gè)CGImage對象竟秫。zPosition屬性決定同級的layer的上下層位置娃惯。zPosition越大,越上層肥败。CALayer的很多屬性都是隱式可動畫的趾浅,如position愕提、transform、bounds皿哨、contents浅侨。
? ? ? ? 當(dāng)某個(gè)UIView對象收到setNeedDisplay消息后,會將該消息轉(zhuǎn)發(fā)給自己的CALayer對象证膨。當(dāng)應(yīng)用處理完當(dāng)前的運(yùn)行循環(huán)后如输,會將所有標(biāo)記為“需要重新顯示”的層準(zhǔn)備一個(gè)CGContext對象。CALayer對象在調(diào)用繪圖函數(shù)時(shí)央勒,凡是基于CGContext對象的繪圖操作不见,生成的像素最終都會體現(xiàn)在這個(gè)層的位圖上。
? ? ? ? 動畫對象的作用是崔步,在指定的時(shí)間內(nèi)稳吮,持續(xù)驅(qū)動CALayer屬性的變化。
? ? ? ? CAAnimationGroup可以保存一組動畫對象井濒,其包含的動畫對象會并發(fā)執(zhí)行
? ? ? ? CATransation可以為CALayer對象提供移出移入屏幕的動畫效果灶似。UINavigationController的推入推出的動畫效果就是通過CATransation來來實(shí)現(xiàn)的。
? ? ? ? CALayer的transform屬相作用是包含一個(gè)變形的3D矩陣瑞你。通過應(yīng)用不同的矩陣酪惭,可以完成旋轉(zhuǎn)rotation、縮放scale者甲、變形春感、傾斜自身frame。
? ? ? ? 速度控制函數(shù):CAMediaTimingFunction过牙。設(shè)置動畫對象的速度。CABasicAnimation *basic = [CABasicAnimation animationWithKeyPath:@"transform.rotaion"];
? ? CAMediaTimingFunction *func = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
? ? basic.timingFunction = func;當(dāng)然纺铭,也可以自定義function來控制速度寇钉。
? ? ? ? 控制CAAnimation可以通過delegate的方式。
CAKeyframeAnimation *keyFrame = [CAKeyframeAnimation animationWithKeyPath:@"transform"];
? ? CATransform3D forward = CATransform3DMakeScale(1.3, 1.3, 1.0);
? ? CATransform3D back = CATransform3DMakeScale(0.7, 0.7, 1.0);
? ? CATransform3D forward2 = CATransform3DMakeScale(1.2, 1.2, 1.0);
? ? CATransform3D back2 = CATransform3DMakeScale(0.9, 0.9, 1.0);
? ? [keyFrame setValues:[NSArray arrayWithObjects:
? ? ? ? ? ? ? ? ? ? ? ? [NSValue valueWithCATransform3D:forward],
? ? ? ? ? ? ? ? ? ? ? ? [NSValue valueWithCATransform3D:back],
? ? ? ? ? ? ? ? ? ? ? ? [NSValue valueWithCATransform3D:forward2],
? ? ? ? ? ? ? ? ? ? ? ? [NSValue valueWithCATransform3D:back2]
? ? ? ? ? ? ? ? ? ? ? ? , nil]];
? ? [keyFrame setDuration:1.0];
? ? [self.view.layer addAnimation:keyFrame forKey:@"keyFrame"];
要做出炫酷的動畫效果舶赔,關(guān)鍵是給出合適的鍵路徑扫倡,并設(shè)置釋放的速度控制函數(shù)。
第二十四章
? ? ? ? UIStroyboard和XIB很相似竟纳,但是不同點(diǎn)是撵溃,可以用Stroyboard建立多個(gè)視圖之間的關(guān)系。
? ? ? ? Storyboard的優(yōu)點(diǎn):1锥累、可以很快開發(fā)出原型缘挑,向客戶同事展示界面流程;2桶略、可以替代部分簡單的代碼语淘;
? ? ? ? Storyboard的缺點(diǎn):1诲宇、不利于團(tuán)隊(duì)協(xié)同開發(fā);2惶翻、代碼管理工具不容易管理姑蓝;3、會讓簡單的開發(fā)復(fù)雜化吕粗;4纺荧、降低開發(fā)的靈活性。
第二十五章:Block
? ? ? ? block對象是存在于棧中的颅筋,需要使用copy屬性讓他保存在堆中宙暇。