扯會淡
? ? ?? 這周項目有點忙冠王,又要新版本的提測向挖,又要老版本適配iOS 11,確實沒有什么成段的時間可以靜下心來好好看看書哪替,只能周日的早上迎著”朝陽“看看書(其實是陰天栋荸,冷死,杭州這幾天天天下雨凭舶,I hate rainy days)晌块,最近可能會對于項目中適配iOS 11的東西,寫一篇文章(看心情吧帅霜,哈哈)匆背。
?????? 今天就先對《Effective Objective-C 2.0》的前5條做一個記錄和總結(jié)。話說這本書好像有很多人推薦身冀,看了一會覺得確實對于代碼的細節(jié)會有一個深刻的反思钝尸,我覺得是本可以細細咀嚼的書括享。
第一條:了解Objective-C語言的起源
?????? OC是基于C編寫的,這個應該大家都知道珍促,所以O(shè)C支持c铃辖,c++的混編,我個人覺得一個真正厲害的OC程序員踢星,應該對于三者混編應該有自己獨到的理解(輕噴)澳叉。但是OC跟c++,Java等面向?qū)ο蟮恼Z言有一個很大的不同沐悦,就是消息結(jié)構(gòu)機制成洗,而不是函數(shù)調(diào)用機制。
???????? //Message (OC):
???????? Object *obj = [Object new];
???????? [obj performWith:parameter and:parameter1];
???????? //Function Calling (c++)
???????? Object *obje = new Object;
???????? obj->perform(parameter,parameter1);
?????? 這在程序的編譯和執(zhí)行時有很大的區(qū)別藏否,最關(guān)鍵的一點區(qū)別是:使用消息結(jié)構(gòu)的語言瓶殃,在運行的時候,怎么執(zhí)行是由運行環(huán)境決定的副签,就是在編譯的時候遥椿,根本不關(guān)心接收消息的對象是何類型;而使用函數(shù)調(diào)用的語言淆储,則是由編譯器來決定冠场,在編譯的時候就已經(jīng)明確的知道這個函數(shù)是誰來執(zhí)行。
?????? OC的重要工作都依賴于OC獨有的一個運行期組件(Runtime Component)本砰。運行期組件本質(zhì)上就是一種與開發(fā)者寫的代碼鏈接的動態(tài)庫(dynamic library)碴裙,其功能就是把開發(fā)者的所有代碼粘合起來。
?????? 想要理解OC的內(nèi)存模型以及“引用計數(shù)”(reference counting)機制点额,首先需要明白一個前提:Objective-C語言中的指針是用來指示對象的舔株。就是對象多占內(nèi)存總是分配在堆中,而指向這個對象的指針是分配在棧中还棱。OC將堆內(nèi)存管理抽象出來了载慈,不需要用malloc和free來分配或釋放對象所占內(nèi)存。OC運行期環(huán)境把這一部分工作抽象為一套內(nèi)存管理架構(gòu)珍手,名為“引用計數(shù)”办铡。
?????? 在OC代碼中,有時會遇到不含*的變量琳要,也就是基本數(shù)據(jù)類型料扰,它們使用的是可能是“棧空間”焙蹭。比如我們設(shè)置frame,經(jīng)常用到的CGRect嫂伞,它是個c結(jié)構(gòu)體孔厉,
struct CGRect {
??? CGPoint origin拯钻;
??? CGSize size;
}撰豺;
typedef struct CGRect CGRect粪般;
?????? OC的整個系統(tǒng)框架,可以看到很多這種結(jié)構(gòu)體污桦,因為相比創(chuàng)建對象來說亩歹,節(jié)省了分配及釋放堆內(nèi)存的額外開銷。如果只需保存基本數(shù)據(jù)類型凡橱,那么通常使用這種c結(jié)構(gòu)體小作。
第二條:在類的頭文件中盡量少引入其他頭文件
?????? 關(guān)于這一點,我想所有的程序員應該多多少少都有體會稼钩,如果在類A的頭文件中引用類B的頭文件顾稀,又在類B的頭文件中引用類A的頭文件,就會造成循環(huán)引用坝撑。對于這一點OC有一個專門的引用指令#import相比#include指令静秆,雖然不會導致死循環(huán),但這卻意味著兩個類中有一個類無法被正確編譯巡李。
?????? 所以正確的做法應該是向前聲明抚笔,就是只在頭文件中聲明有另一個類,并不需要知道另一個類的全部細節(jié)侨拦。就是在類A的頭文件中使用@class B殊橙;而在實現(xiàn)文件中正確的引入類B#import “B.h”
?????? //A.h
?????? #import <Foundation/Foundation.h>
?????? @class B;
?????? @interface A : NSObject
?????? @property (nonatomic, strong) B *b;
??????? //...
??????? @end
???????? //A.m
???????? #import "A.h"
???????? #import "B.h"
???????? @implementation A
???????? //...
???????? @end
?????? 有時無法使用向前聲明,比如要聲明某個類遵循一項協(xié)議阳谍。這種情況下蛀柴,盡量把“該類遵循某協(xié)議”的這條聲明移至“class-continuation分類”中。如果不行的話矫夯,就把協(xié)議單獨放在一個頭文件中鸽疾,然后將其引入。
?????? ”class-continuation“分類就是.m文件中的@interface那一塊训貌。
第三條:多用字面量語法制肮,少用與之等價的方法
???????? 什么是字面量語法?
NSString *string = @"hello world!";???????? //字符串字面量
NSNumber *intNumber = @1;?????????????????? //字面數(shù)值
NSNumber *floatNumber = @2.5f;
NSNumber *doubleNumber = @3.14159;
NSNumber *boolNumber = @YES;
NSNumber *charNumber = @'a';
int i = 5;
folat f = 5.3f;
NSArray *array = @[@"cat",@"dog",@"pig"];?????? //字面量數(shù)組
NSString *dog = array[1];
NSDictionary *dic = @{@"firstName":@"tao",@"lastName":@"bingzhi",@"age":@25};
?????? 以上都是一些字面量的寫法递沪,那么有什么好處呢豺鼻?很直白的一點是,代碼清晰明了易讀款慨,沒有多余的語法儒飒。
?????? 同時在聲明數(shù)組和字典時,如果有一個元素是nil檩奠,這樣還會報錯桩了,而如果用數(shù)組或字典的類初始方法創(chuàng)建的話“arrayWithObjects:”會一次處理各個參數(shù)附帽,知道發(fā)現(xiàn)nil停止,不至于莫名其妙創(chuàng)建好數(shù)組之后發(fā)現(xiàn)元素少了井誉。
?????? 當然也有一定的局限性蕉扮,除了字符串之外,所創(chuàng)建出來的對象必須屬于Foundation框架才行颗圣。如果自定義了這些類的子類喳钟,則無法用字面量語法創(chuàng)建其對象。還有就是字面量創(chuàng)建出來的字符串在岂、數(shù)組和字典都是不可變的(immutable)奔则。如果想變成可變的,需要mutaleCopy以下洁段。
NSMutableArray *mutableArr = [@[@"hh",@"ss",@"tt"] mutableCopy];
第四條:多用類型常量应狱,少用#define預處理指令
?????? 寫代碼時常常需要定義常量,那么怎么定義常量才是正確且合適的祠丝,常量定義的位置又有什么講究呢疾呻?
?????? 如果我們用#define來預處理一個寬度,可能會這么寫
#define kIconWidth 30;
???????? 這雖然能解決写半,但是這樣定義出來的常量沒有類型岸蜗,代碼不易讀,可以使用下面這行代碼:
static const CGFloat kIconWidth = 30;
?????? 同時還要注意常量名稱叠蝇,常用的:若常量局限于某實現(xiàn)文件內(nèi)璃岳,則在前面加子母“k”;若常量在類之外可見悔捶,則通常以類名為前綴铃慷。
?????? 定義常量的位置也很重要,若不打算公開這個常量蜕该,則應將其定義在使用該常量的實現(xiàn)文件里犁柜。
?????? 這里需要理解一下const和static兩個關(guān)鍵字的意思,
?????? const:如果試圖修改由const修飾符所聲明的變量堂淡,那么編譯器就會報錯馋缅,不能被更新;
?????? static:就是限制了變量的可用范圍绢淀,如果是在實現(xiàn)文件中用static聲明了一個變量萤悴,那么變量的可用域就是該類所生成的目標文件,如果是在方法內(nèi)用static聲明皆的,那么可用域就是這個代碼塊覆履。
?????? 最主要的一點還是,用這種方法聲明常量帶有類型信息。
?????? 有的時候你需要公開一個常量内狗,就是無需知道這個常量名背后的意義怪嫌,只是需要用到這個常量名,例如通知名稱的公開定義柳沙。這種時候,可以將常量放在“全局符號表“中拌倍,此時應該這么聲明:
?????? //.h
?????? extern NSString *const WLLoginControllerNotification;
?????? //.m
? ? ? NSString *const WLLoginControllerNotification = @"WLLoginControllerNotification"
;
? ? ? 個人更多的還是在pch文件中赂鲤,直接寫一個通知名static const NSString * WLLoginControllerNotification = @“WLLoginControllerNotification”;
第五條:用枚舉表示狀態(tài)、選項柱恤、狀態(tài)碼
?????? 在日常開發(fā)中数初,枚舉是很好用的一個東西,可以用來表示狀態(tài)梗顺,傳遞給方法的選項以及狀態(tài)碼等值泡孩,需要主要的是,新的C++11標準修訂了枚舉的一項改動:可以指明用何種“底層數(shù)據(jù)類型”來保存枚舉類型的變量寺谤。這樣做的好處是可以向前聲明枚舉變量仑鸥,編譯器在用到枚舉的時候,就知道該給枚舉變量分配多少內(nèi)存空間了变屁。
?????? 如果把傳遞給某個方法的選項表示為枚舉類型眼俊,而多個選項又可以同時用,那么就將各選項值定義為2的冪粟关,以便通過換位或操作將其組合起來疮胖。
?????? 這里要注意一點:如果想要通過換位或操作的話,定義枚舉是就不能用NS_ENUM來定義闷板,而需要使用另一種方式定義澎灸,NS_OPTIONS,以便省去類型轉(zhuǎn)換過程遮晚。
?????? 最后一個小的tip性昭,在處理枚舉類型的switch語句中不要事先default分支,這樣的話鹏漆,加入新枚舉之后巩梢,編譯器會提示開發(fā)者:switch語句并未處理所有枚舉。
總結(jié)
?????? 今天狀態(tài)不好艺玲,寫的時候一直是很趕的情緒括蝠,不知道為什么,就沒有靜下心饭聚,很急忌警,很抱歉。可能起太早了法绵,天氣又陰沉沉的箕速,還沒吃早飯,情緒不太對朋譬。sorry sorry盐茎。
?????? 最后的最后,還是要說徙赢,換季了字柠,大家不要感冒,出門上班可以帶件外套狡赐。希望大家身體健康窑业,吃嘛嘛香。
Better Late Than Never!
努力是為了當機會來臨時不會錯失機會枕屉。
???????????????? 共勉常柄!