迷惑新手的IOS开发问题
十個很不錯的迷惑新手的Cocoa,OC開發問題總結,適合大家進行注意和學習。
在你繼續深入學習之前,請停下腳步弄清這些問題。如果你是新手,這個教程不要希望一次能看的非常透徹,學一定階段反回來再看看又會有新的體會的。對于完全看不懂的同學,也不要強求自己。找來《計算機組成原理》《數據結構》《操作系統》《編譯原理》《計算機網絡》之類的大學課本看看吧。這些基礎不可能在這個論壇學到的,至少不可能系統的學到。?
?
1. language background?
?
首先c, c++語言背景,必須。?
很多人問 “沒有任何語言基礎,我不想學c直接學objective-c”?
這里簡單幾句,objc 90%代碼是c、眾多開源代碼是c,c++。你不學好c在unix世界里只能是個二流開發者!也許說得過于嚴厲,不過自己斟酌把。??
?
接著English必須。?
需要英語!需要英語!需要英語!蘋果不會把它們文檔都寫成中文的。“什么,有人翻譯?” 等有人閑著翻譯出來了的時候,大家都已經賣了很多軟件了。你也是跟著人家屁股后面做開發。?
?
2. Runtime(運行時)?
?
Objective-c是動態語言,??很多新手或者開發人員常常被Runtime這個東西所迷惑。而恰恰這是一個非常重要的概念。 為什么重要呢!?我可以這么問:“如果讓你(設計、)實現一個計算機語言,你要如何下手?” 很少程序員這么思考過。但是這么一問,就會強迫你從更高層次思考(1)以前的問題了。 注意我這句話‘設計’括起來了,稍微次要點,關鍵是實現。?
?
我把實現分成3鐘不同的層次:?
?
????2. 我們希望靈活,于是需要開發面向對象的語言,例如c++。 c++在c的基礎上增加了類的部分。但這到底意味著什么呢?我們再寫它的編譯器要如何考慮呢?其實,就是讓編譯器多繞個彎,在嚴格的c編譯器上增加一層類處理的機制,把一個函數限制在它處在的class環境里,每次請求一個函數調用,先找到它的對象, 其類型,返回值,參數等等,確定了這些后再jmp跳轉到需要的函數。這樣很多程序增加了靈活性同樣一個函數調用會根據請求參數和類的環境返回完全不同的結果。增加類機制后,就模擬了現實世界的抽象模式,不同的對象有不同的屬性和方法。同樣的方法,不同的類有不同的行為! 這里大家就可以看到作為一個編譯器開發者都做了哪些進一步的思考。但是。。。還是死板, 我們仍然叫c++是static language。??
?
????3. 希望更加靈活! 于是我們完全把上面哪個類的實現部分抽象出來,做成一套完整運行階段的檢測環境。這次再寫編譯器甚至保留部分代碼里的sytax名稱,名稱錯誤檢測,runtime環境注冊所以全局的類,函數,變量等等信息等等,我們可以無限的為這個層增加必要的功能。調用函數時候,會先從這個運行時環境里檢測所以可能的參數再做jmp跳轉。這,就是runtime。編譯器開發起來比上面更加彎彎繞。但是這個層極大增加了程序的靈活性。??例如當調用一個函數時候,前2種語言,很有可能一個jmp到了一個非法地址導致程序crash, 但是在這個層次里面,runtime就過濾掉了這些可能性。 這就是為什么dynamic langauge更加強壯。 因為編譯器和runtime環境開發人員已經幫你處理了這些問題。 ?
?
?
好了上面說著這么多,我們再返回來看objective-c.??現在你是不是能理解這樣的語句了呢??
?
| 復制代碼 |
?
看似很簡單的語句,但是為了讓語言實現這個能力,語言開發者要付出很多努力實現runtime環境。這里運行時環境處理了弱類型、函數存在檢查工作。runtime會檢測注冊列表里是否存在對應的函數,類型是否正確,最后確定下來正確的函數地址,再進行保存寄存器狀態,壓棧,函數調用等等實際的操作。?
?
| 復制代碼 |
?
用c,c++完成這個功能還是比較非常麻煩的,但是動態語言處理卻非常簡單并且這些語句讓objc語言更加intuitive。?
?
現在說一下runtime的負面影響:?
1. 關于執行效率問題。 “靜態語言執行效率要比動態語言高”,這句沒錯。因為一部分cpu計算損耗在了runtime過程中。而靜態語言生成的機器指令更簡潔。正因為知道這個原因,所以開發語言的人付出很大一部分努力為了保持runtime小巧上。所以objecitve-c是c的超集+一個小巧的runtime環境。 但是,換句話說,從算法角度考慮,這點復雜度不算差別的,Big O notation結果不會有差別。( It’s not log(n) vs n2 )?
2. 另外一個就是安全性。動態語言由于運行時環境的需求,會保留一些源碼級別的程序結構。這樣就給破解帶來的方便之門。一個現成的說明就是,java,大家都知道java運行在jre上面。這就是典型的runtime例子。它的執行文件.class全部可以反編譯回近似源代碼。所以這里的額外提示就是如果你需要寫和安全有關的代碼,離objc遠點,直接用c去。?
?
簡單理解:“Runtime is everything between your each function call.”?
?
但是大家要明白,第二點我提到runtime并不只是因為它帶來了這些簡便的語言特性。而是這些簡單的語言特性,在實際運用中需要你從完全不同的角度考慮和解決問題。只是計算1+1,很多語言都是一樣的,但是隨著問題的復雜,項目的增長,靜態語言和動態語言就會演化出完全不同的風景。?
?
3. thread?
?
"thread synchronization another notorious trouble!"??
?
記不記得上學時候學得操作系統這門課,里面都會有專門一章介紹任務調度和生產者消費者的問題。 這就是為了今后使用進程、線程開發打基礎。概念很簡單,但是心知肚明的人很少。難點在synchronization(同步),因為1. There is no 100% deadlock detection algorithm. If there is, no deadlock at all. 2. 往往這類錯誤很隱晦,靜態分析很難找到。 3. 抽象度較高需要經驗去把握。???
?
總體來說,我見到的在這方面的問題可以分為一下幾點:?
???
1. 不知道多線程開發的幾個基點,看別人代碼越看越糊涂的。一會NSThread、一會Grand Central Dispatch、block等等。。。Apple封裝了很多線程的api, down to core多線程的結構基本是:?
?
??
?
可以看到在多線程開發中你可以選擇這上面這4種不同的方式。?
?
Mach是核心的操作系統部分。其實這個我也不是非常熟悉,至少我還沒有讀到過直接使用mach做多線程的代碼。?
?
pthread(POSIX Threads)是傳統的多線程標準,靈活、輕巧,但是需要理論基礎,開發復雜。需要注意一點,根據apple文檔提示,在Cocoa下使用pthread需要先啟動至少一個NSThread,確定進入多線程環境后才可以。?
?
NSThread是Mac OS 10.0后發布的多線程API較為高層,但是缺乏靈活性,而且和pthread相比效率低下。?
?
Grand Central Dispatch 是10.6后引入的開源多線程庫,它介于pthread和NSThread之間。比NSThread更靈活、小巧,并且不需要像pthread一樣考慮很多lock的問題。同時objective-c 2.0發布的新語法特性之一blocks,也正是根據Grand Central Dispatch需求推出的。?
?
所以在你寫多線程代碼或者閱讀多線程代碼時候,心理要先明確使用哪種技術。?
?
??? 2. thread和Reference Counting內存管理造成的問題。?
??線程里面的方法都要放到NSAutoreleasePool里面嗎這類問題很常見,主要原因是 NSAutoreleasePool 到底是干什么用得不明白。 NSAutoreleasePool跟thread其實關系并不顯著,它提供一個臨時內存管理空間,好比一個沙箱,確保不會有不當的內存分配泄露出來,在這個空間內新分配的對象要向這個pool做一下注冊告訴:“pool,我新分配一塊空間了”。當pool drain掉或者release,它里面分配過的內存同樣釋放掉??梢姾蛅hread沒有很大關系。但是,我們閱讀代碼的時候經常會看到,新開線程的函數內總是以NSAutoreleasePool開始結束。這又是為什么呢!? 因為thread內恰好是最適合需要它的地方! 線程函數應該計算量大,時間長(supposed to be heavy)。在線程里面可能會有大量對象生成,這時使用autoreleasepool管理更簡潔。所以這里的答案是,不一定非要在線程里放NSAutoreleasePool,相對的在cocoa環境下任意地方都可以使用NSAutoreleasePool。如果你在線程內不使用NSAutoreleasePool,要記得在內部alloc和relase配對出現保證沒有內存泄露。?
?
????3. 線程安全?
???每個程序都有一個主線程(main thread),它負責處理事件相應,和UI更新。?
?
更新UI問題。很多新手會因為這個問題,導致程序崩潰或出現各種問題。而且逛論壇會看到所以人都會這么告訴你:“不要在后臺線程更新你的UI”。其實這個說法不嚴密,在多線程環境里處理這個問題需要謹慎,而且要了解線程安全特性。?
?
首先我們需要把“UI更新”這個詞做一個說明,它可以有2個層次理解,首先是繪制,其次是顯示。這里繪制是可以在任何線程里進行,但是要向屏幕顯示出來就需要在主線程操作了。我舉個例子說明一下,例如現在我們有一個NSImageView,里面設置了一個NSImage, 這時我想給NSImage加個變色濾鏡,這個過程就可以理解為繪制。那么我完全可以再另外一個線程做這個比較費時的操作,濾鏡增加完畢再通知NSImageView顯示一下。另一個例子就是,Twitter客戶端會把每一條微博顯示成一個cell,但是速度很快,這就是因為它先對cell做了offscreen的渲染,然后再拿出來顯示。?
?
所以通過這點我們也可以得到進一步的認識,合理設計view的更新是非常重要的部分。很多新手寫得代碼片段沒錯,只是因為放錯了地方就導致整個程序出現各種問題。?
?
根據蘋果線程安全摘要說明,再其它線程更新view需要使用lockFocusIfCanDraw和unlockFocus鎖定,確保不會出現安全問題。?
?
另外還要知道常用容器的線程安全情況。immutable的容器是線程安全的,而mutable容器則不是。例如NSArray和NSMutableArray。?
?
?????
????5. Asynchronous(異步) vs. Synchronous(同步)?
???? 引用 我在一個view要顯示多張web圖片,我想問一下,我是應該采用異步一個一個下載的方式,還是應該采用多線程同時下載的方式,還是2個都用,那種方式好呢? ?
?
實際上單獨用這2個方法都不好。并不是簡單的用了更多線程就提高速度。這個問題同時涉及客戶端和服務器的情況。?
?
處理這種類型的程序,比較好的結構應該是:非主線程建立一個隊列(相當于Asynchronous任務),隊列里同時啟動n個下載任務(相當于Synchronous任務)。這里的n在2~8左右就夠了。這個結構下可以認為隊列里面每n個任務之間是異步關系,但是這n個任務之間又是同步關系,每次同時下載2~8張圖片。這樣處理基本可以滿足速度要求和各類服務器限制。?
???
(非常抱歉,之前質疑問問題的人理解錯誤。其實是我自己暈了,問得問題很好。)?
?
4. runloop?
現在說說runloop為何會成為cocoa開發中迷惑的點。因為很多新手沒有從動態角度看它。 首先回想一下第2點介紹的runtime的概念。 接著我出一個題思考一下。?
?
現在我有一個程序片段如下:?
| 復制代碼 |
現在要求,做某些設計,使得當這個線程運行的同時,還可以從其它線程里往它里面隨意增加或去掉不同的計算任務。 這,就是NSRunloop的最原始的開發初衷。讓一個線程的計算任務更加靈活。 這個功能在c, c++里也許可以做到但是非常難,最主要的是因為語言能力的限制,以前的程序員很少這么去思考。?
?
好,現在我們對上面代碼做一個非常簡單的進化:?
?
| 復制代碼 |
注意,這里沒有做線程安全處理,記住Mutable container is not thread safe.?
這個簡單的擴展,讓我們看到了如何利用runtime能力讓線程靈活起來。當我們從另外線程向targetQueue和actionQueue同時加入對象和方法時候,這個線程函數就有了執行一個額外代碼的能力。?
?
但,有人會問,哪里有runloop? 那個是 nsrunloop? 看不出來啊。?
| 復制代碼 |
一個線程內這個結構就叫線程的runloop,?? 它和NSRunloop這個類雖然名字很像,但完全不是一個東西。以前在使用靜態語言開始時候,程序員沒有什么迷惑,因為沒有NSRunloop這個東西。 我接著來說,這個NSRunloop是如何來得。?
?
第二段擴展代碼里面確實沒有NSRunloop這個玩意兒,我們接著做第3次改進。 這次我們的目的是把其中動態部分抽象出來。?
?
| 復制代碼 |
?
?
| 復制代碼 |
?
| 復制代碼 |
?
走到這里,我們就算是基本把Runloop結構抽象出來了。例如我有一個MyNSThread實例,myThread1。我可以給這個實例的線程添加需要的任務,而myThread1內部的MyNSRunloop對象會管理好這些任務。?
?
| 復制代碼 |
?
當你看懂了上面的代碼也許會感嘆,‘原來是這么回事啊!為什么把這么簡單的功能搞這么復雜呢?’ 其實就是這么回事,把Runloop抽象出來可以使得線程任務管理更加loose coupling,給設計模式提供更大的空間。這樣第三方開發者不需要過深入的涉及線程內部代碼而輕松管理線程任務。另外請注意,這里MyNSRunloop, MyNSTimer等類是我寫得一個模擬情況,真實的NSRunloop實現肯定不是這么簡單。這里只為了說明一個思想。這種思想貫穿整個cocoa framework從界面更新到event管理。?
?
5. delegate, protocol?
這個會列出來因為,我感覺問它的數量僅此于內存管理部分,它們用得很頻繁,并且它們是多鐘設計模式的重要組成部分。?
?
?
6. event responder?
?
?
使用過Xcode的開發者都知道Interface Builder這個開發組件,在Xcode4版本以后該組件已經和xcode整合到一起。它是蘋果軟件開發中非常重要的部分。ib為開發者減輕了很大一部分界面設計工作。但是其中有一個東西讓新接觸ib的開發者一頭霧水,那就是First Responder, 它是什么東西,為何它會有那么多Actions。這節我會詳細介紹如何理解Responder和Cocoa下的事件響應鏈。?
?
First Responder在IB屬性為Placeholders,這意味著它屬于一個虛擬實例。就好比TextField里面的string placeholder一樣,只是臨時顯示一下。真正的first responder會被其它對象代替。實際上,任何派生自NSResponder類的對象都可以替代First Responder。而First Responder里面的所有Actions就是NSResponder提供的接口函數,當然你也可以定義自己的響應函數。?
?
MacOS在系統內部會維護一個稱為“The Responder Chain”的鏈表。該列表內容為responder對象實例,它們會對各種系統事件做出響應。最上面的哪個對象就叫做first responder,它是最先接收到系統事件的對象。如果該對象不處理該事件,系統會將這個事件向下傳遞,直到找到響應事件的對象,我們可以理解為該事件被該這個對象截取了。?
?
The Responder Chain基本結構如下圖所示:?
?
?
?
在理解了上面的概念之后,我希望使用一個例子讓大家對responder有更加具體的認識。大家都知道NSTextField這個控件,它是最常見的控件之一。它最基本功能是顯示一個字符串,如果啟用可選,那么用戶可以選中文本,拷貝文本,如果開啟編輯選項,還可以運行用戶編輯文本,等等基本操作。?
?
下面展示給大家的例子是創建一個我們自己創建的簡單textfield叫LXTextField。它不屬于NSTextField而是派生自NSView,具有功能顯示字符串,全選字符串,響應用戶cmd+c的拷貝操作,三個基本功能。注意NSView派生自NSResponder。?
?
?
| 復制代碼 |
?
運行實例,可以看到隨著LXTextField收到系統發送的becomeFirstResponder消息,LXTextField變成responder chain中的frist responder, 這時候可以理解為IB里的哪個First Responder虛擬實例被該LXTextField取代。這時候mainMenu上哪些菜單項,例如:全選(cmd+a), 拷貝(cmd+a)等事件都會最先發給當前這個LXTextField。一旦你的LXTextField實現了NSResponder的哪些默認函數,那么該對象就會截取系統事件。當然這些事件具體如何實現還是需要你自己寫代碼實現。例如這里的 - (IBAction)copy:(id)sender; 顯然我手動實現了textfield的copy能力。?
?
注意上述代碼中我實現了一個空函數- (void)keyDown:(NSEvent *)theEvent 這意味著我們希望LXTextField截取鍵盤事件而不再傳遞給responder chain后續對象。當然,如果我們希望LXTextField響應特定鍵盤事件,而其他事件繼續傳給其他響應對象,我們可以編寫如下代碼。?
?
| 復制代碼 |
?
7. mem-ory management?
這個也許是問得最多的問題了吧。所有這些問題往往來源于3個地方,1、不了解底層機制;2、沒有吃透規則; 3、不了解常用container的Reference Counting特性,或著說沒有下功夫去看對應文檔。?
?
1. 底層機制?
大家是否知道從舊時代的RC到ARC機制到底意味著什么呢? 為什么ARC從開發速度,到執行速度和穩定性都要優于rc??
?
開發速度不言而喻,你少寫很多release代碼,甚至很少去操心這部分。?
?
執行速度呢?這個還要從runtime說起,還記得我在第2點說得一句話么:“Runtime is everything between your each function call.”???
?
RC是一個古老的內存管理哲學:? 誰分配誰釋放。?通過counting來確定一個資源有幾個使用者。道理很簡單,但是往往簡單的東西人卻會犯錯。從來沒有一個程序員可以充滿信心的說,我寫得代碼從來沒有過內存泄露。這樣來看,我們就更需要讓程序可以自己處理這個管理機制,這就需要把這個機制放到runtime里。?
?
所以RC->ARC就是把內存管理部分從普通開發者的函數中移到了函數外的runtime中。因為runtime的開發原型簡單,邏輯層次更高,所以做這個開發和管理出錯的概率更小。實際上編譯器開發人員對這部分經過無數次測試,所以可以說用arc幾乎不會出錯。另外由于編譯的額外優化,使得這個部分比程序員自己寫得代碼要快速很多。而且對于一些通用的開發模式,例如autorelease對象,arc有更優秀的算法保證autoreleasepool里的對象更少。 ?
?
所以RC->ARC就是把內存釋放語句從普通開發者的代碼中去掉,然后交給編譯器讓它在編譯階段自動增加該有的釋放語句。 因為是編譯器分析,所以出錯的概率更小。實際上編譯器開發人員對這部分經過無數次測試,所以可以說用arc幾乎不會出錯。另外由于交給編譯器處理,使得這個部分比程序員自己寫得代碼要快速很多。?
?
ARC另一個特點就是,強制程序員遵守owner規則。例如, 舊時很多程序員開發,喜歡在需要用的時候臨時建立一個windowController,窗口關閉后controller自己release掉。但是這個思路在arc下面是行不通的。因為這種模式打破了owner規則(一個實例需要至少有1個擁有者)一個臨時建立的windowController如果沒有owner,那么它會在當前函數退出前就被arc釋放掉。有些程序員認為這是arc非常討厭的地方,當然也有一些程序員認為這樣更規范。?
?
?
2. RC規則?
首先說一下rc是什么,r-Reference參照,引用 c-counting計數, rc就是引用計數。俗話說就是記錄使用者的數量。 例如現在我有一個房間空著,大家可以進去隨意使用,但是你進門前,需要給門口的計數牌子+1, 出門時候-1?!∵@時候這個門口的牌子就是該房間里的人數。一但這個牌子變為0我就可以把房間關閉。?
?
這個規則可以讓NSObject決定是不是要釋放內存。當一個對象alloc時候,系統分配其一塊內存并且object自動計數retainCount=1 這時候每當[object retain]一次retainCount+1(這里雖然簡寫也是rc不過是巧合或者當時開發人員故意選的retain這個詞吧)每次[object release]時候retainCount-1 當retainCount==0時候object就真正把這快內存還給系統。?
?
3. 常用container的Reference Counting特性?
這個規則很簡單把。但是這塊確實讓新手最頭疼的地方?!栴}出在,新手總想去驗證rc規則,又總是發現和自己的期望不符合。? ?
無數次看到有人寫下如下句子?
| 復制代碼 |
?
| 復制代碼 |
?
當然了,我也做過類似的動作,那種希望一切盡在掌握中的心態。但是你會看到其他人告訴這么做完全沒有意義。rc does not work this way.?? 也許這樣的暴力釋放會起作用,但是retainCount并不是用來做這個的。每個數字意味著有其它對象引用該資源,這樣的暴力釋放很容易導致程序崩潰。這個數字也許并不是你心目中的哪個。因為你很難跟蹤到底哪些對象引用的該資源。你用代碼建立的資源不光只有你的代碼才會用到,你調用的各種Framework,Framework調用的Framework,都有可能改變這個資源的retainCount.??所以去驗證rc規則不是明智之舉。?
?
你能做的就是理解規則,使用規則,讀文檔了解container的引用特性?;蛘吒纱嘁频絘rc上面,讓runtime環境處理這些問題。?
?
引用 我有一個NSMutableArray里面保存了1000個NSString對象,我在release的時候需要循環釋放1000個string么?還是只需要release NSMutableArray。 ?
?
就像上面提到的,如果你了解container的引用特性,這個問題自然就解決了?!癗SMutableArray在添加、插入objects時會做retain操作?!?通過這一句話就分析出,用戶不否需要幫助NSMutableArray釋放1000個string。回憶上面提到的管理哲學,“誰分配誰釋放” 編寫NSMutableArray的程序員非常熟悉這個規則,NSMutableArray內部retain了,NSMutableArray自然要負責release。但是NSMutableArray才不會管你在外面什么地方引用了這1000個string,它只管理好內部的rc就夠了。所以如果你在NSMutableArray外面對1000個string retain了,你自然需要release。相應的,你作為創建這個NSMutableArray的程序員,你只管release這個NSMutableArray就可以了。?
?
最后說一下不用arc的情況。目前情況來看,有不少第三方的庫并未支持arc,所以如果你的舊項目使用了這些庫,請檢查是否作者發布了新版本,或者你需要自己修正支持arc。?
?
8. class heritage and category?
?
在這節里說明2個比較重要的問題,第一是subclass和category的區別。第二是新手最容易忽略的學習步驟。?
?
Objective-C的Object-oriented programming特性提供subclass和category這2個比較非常重要的部分。subclass應該反復被各種編程書籍介紹過。它是oop繼承特性的關鍵語法,它給類添加了延續并且多樣化自己的方法??梢哉f沒有繼承就沒有oop這玩意。而category相對于subclass就不那么出名了。其實category思想出世于smalltalk,所以它不能算是一個新生事物。?
?
先說一下這2個特性最主要的區別。簡單可以這么理解,subclass體現了類的上下級關系,而category是類間的平級關系。?
?
??
?
如上圖所示,左側是subclass,可以看到class, subclass1, subclass2是遞進關系。同時下面的子類完全繼承父類的方法,并且可以覆蓋父類的方法。subclass2擁有function1,function2,function3三個函數方法。function1的執行代碼來自subclass1, function2的執行代碼來自于subclass2。?
?
右側是category??梢钥吹?#xff0c;無論如何擴展類的category,最終就只有一個類class。category可以說是類的不同方法的小集合,它把一個類的方法劃分成不同的區塊。請注意觀察,每個category塊內的方法名稱都沒有重復的。這正是category的重要要求。?
?
經過上面簡單解釋了解了這2點的基本區別,現在深入說一下category。?
?
在Objective-c語言設計之初一個主要的哲學觀點就是盡量讓一個程序員維護龐大的代碼集。(對于龐大的項目‘原則’和‘協議’是非常重要的東西。甚至編寫良好的文件名都是非常重要的開發技巧)根據結構化程序設計的經驗出發,把一個大塊代碼劃分成一些小塊的代碼更便于程序員管理。于是objc借用了smalltalk的categories概念。允許程序員把一系列功能相近的方法組織到一個單獨的文件內,使得這些代碼更容易識別。?
?
更進一步的,和c,c++這種靜態語言相比。objc把class categories功能集成到了run-time里面。因此,objc的categories允許程序員為已經存在的類添加新的方法而不需要重新編譯舊的類。一旦一個category加入,它可以訪問該類所有方法和實例變量,包括私有變量。?
?
category不僅可以為原有class添加方法,而且如果category方法與類內某個方法具有同樣的method signature,那么category里的方法將會替換類的原有方法。這是category的替換特性。利用這個特性,category還可以用來修復一些bugs。例如已經發布的Framework出現漏洞,如果不便于重新發布新版本,可以使用category替換特性修復漏洞。另外,由于category有run-time級別的集成度,所以使得cocoa程序安全性有所下降。許多黑客就是利用這個特性(和posting技術2)劫持函數、破解軟件,或者為軟件增加新功能。一個很典型的例子就是,我發布的QQ表情管理器。?
?
值得注意的一點是,由于一個類的categories之間是平級關系。所以如果不同categories擁有相同的方法,這個調用結果是未知的。所以:?
?
引用 Category methods should not override existing methods (class or instance).??
Two different categories implementing the same method results in undefined behavior?
?
?
Objc中Categories有其局限的部分,就是你不能為原有的class添加變量,只能添加方法。當然方法里可以添加局部變量。在這個局限基礎上就有其它語言做了進一步改進,例如TOM語言就為Categories增加了添加類變量的能力。?
?
總上所屬,如果你開發時候遇到無論如何都需要為類添加變量的情況,最好的選擇就是subclass。相反如果你只希望增加一些函數簇。Categories是最好的選擇。?
?
并且我個人給大家的建議是,每個Cocoa程序員都應該收集整理自己的一套NS類函數的Categories擴展庫。這對你今后程序開發效率和掌控情況都有很大提高。?
?
?
9. drawing Issues?
?
大家都知道,MacOS是一個非常注重UI的系統。所以在MacOS編程里繪制是一個非常重要的部分。第10部分,我會從2點介紹MacOS下繪制編程。首先是繪制技術分類;其次是繪制代碼結構。?
?
從繪制技術分類上看,Cocoa程序員能接觸的幾種繪制技術列表如下:?
?
1. Cocoa Drawing(NS-prefix)?
2. Core Graphics(CG-prefix, called Quazrtz 2D)?
3. Core Animation?
4. Core Image?
5. OpenGL??
?
在這里我不打算給大家介紹每一種都是如何繪制具體的圖像。只是介紹一下,它們大概長什么樣子,并且有什么優勢和限制。?
?
###Cocoa Drawing?
Cocoa Drawing應該是學習Cocoa程序開發最先接觸的繪制技術。也是目前大多數MacOS程序所使用的繪制技術,其底層使用Quazrtz 2D(Core Graphics)。蘋果對應文檔為 [Cocoa Drawing Guide]( https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CocoaDrawingGuide/Introduction/Introduction.html)。Cocoa Drawing并沒有統一的繪制函數,所有繪制函數分散在幾個主要的NS類的下面。例如, NSImage, NSBezierPath, NSString, NSAttributedString, NSColor, NSShadow,NSGradient …?
?
所以很簡單,當你看到如下代碼就可以判斷,使用的是Cocoa Drawing方法?
?
| 復制代碼 |
?
這種代碼多出現在NSView的drawRect函數內。Cocoa Drawing 的渲染上下文是 NSGraphicsContext,我不斷的看到很多新手把 NSGraphicsContext 和 CoreGraphics 的 CGContextRef 搞混。雖然它們很像并且也確實是有關系的,不過如果你不了解當繪制時候的 render context 很多時候將得到一個空白頁面的結果。?
?
###Core Graphics?
Core Graphics 是 Cocoa Drawing layer 的底層技術,在 iOS 開發中非常普遍,因為 iOS 系統中并不存在 Cocoa layer 所以網上可以找到的多是 Core Graphics 繪制代碼段子,這給那些不了解 Mac 開發的新手來說造成了很大困擾。Cocoa 是 Mac OS 下的 application framework 而 iOS 下的 application framework 則是 UIKit.framework又叫 Cocoa Touch,它們分享部分代碼基礎但又不完全一樣。例如,Cocoa Touch 下的 UIView 的渲染上下文會使用 UIGraphicsGetCurrentContext() 取得,它得到的是一個 CGContextRef 指針,而在 NSView 里多用 [NSGraphicsContext currentContext] 取得渲染上下文。它得到的是一個 NSGraphicsContext 對象。當然 NSView 里也可以通過 CGContextRef ctx = [[NSGraphicsContext currentContext] graphicsPort]; 來取得一個 Core Graphics 渲染上下文。 可見 Mac OS 下的開發更為靈活一些。因為 iOS 中的 UIKit 開發初期就瞄準了顯卡硬件加速,所有 UIView 都是默認 layer-backed 的。iOS 開發者必須使用 Core Graphics 和 Core Animation 這幾個相對底層的繪制技術。?
?
請看下面等價代碼,作用是繪制一個白色矩形。但是分別使用 Core Graphics 和 Cocoa Drawing:?
?
| 復制代碼 |
?
可以看出,這是2種*風格完全不同*的繪制技術。Cocoa Drawing 是分散式的繪制函數,而 Core Graphics 是傳統的類似 OpenGL 的集成式的繪制方式。其實 Cocoa Drawing 下層是 Core Graphics, Core Graphics 的下層是 OpenGL。?
?
?
###Core Animation?
如果說 Core Graphics 和 Cocoa Drawing 是通用的 UI 繪制框架的話,那么 CA 顯然是界面動畫繪制的高級技術。?
Core Animation 的對應 Cocoa Animation 部分應該是 NSAnimation 和 NSViewAnimation,但這2個差距比較大。NSAnimation 出現與 OS X 10.4,Core Animation 是 10.5 后出現的。NSViewAnimation 功能和使用相對簡單。?
?
簡單來說,Core Animation 的作用對象是 CALayer, NSAnimation 的作用對象是 NSView。
轉載于:https://my.oschina.net/u/1027869/blog/124398
總結
以上是生活随笔為你收集整理的迷惑新手的IOS开发问题的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Python 变量作用域问题 函数名.变
- 下一篇: 中石油(某些代码)