Cocoa改造前篇 - 說在前面的

更好的閱讀體驗請點擊 原文

從面相對象說起

面向?qū)ο蟮某绦蛟O(shè)計(Object-Oriented Programming搀罢,簡記為OOP)這個概念大家都有所耳聞妒蛇,目前(2017.12),在Tiobe世界語言排行榜上排前十的語言中抄瓦,C語言和Assembly language(匯編)外的八種語言均原生支持面向?qū)ο蟮某绦蛟O(shè)計捣郊。

怎么判斷一種編程語言是否支持OOP呢箱季?看看這門語言是否支持類(class)、對象(object)、封裝(encapsulation)、繼承(inheritance)等功能和特性怪与,支持這些就可以進(jìn)行面向?qū)ο缶幊獭D肙bjective-C(OC)來說缅疟,類就是Class分别,對象就是instance,萬物的基類是NSObject存淫,這些東西在C語言里并不存在耘斩,是OC使用C語言的結(jié)構(gòu)體(struct)抽象出來的產(chǎn)物。

我們從Objective-C的名字上也能看出一些端倪纫雁,直譯過來是對象化的C語言煌往,當(dāng)然不僅是OC,排行榜前十中的C++同樣是C語言的一個超集轧邪;C#和Java同樣屬于類C語言刽脖,把面向?qū)ο笞龅母訌氐祝籔HP雖然是腳本語言忌愚,其解釋器是使用C語言寫的曲管;而我們常說的Python,其全稱則是CPython硕糊,也是用C語言實現(xiàn)的解釋器院水,當(dāng)然Python解釋器也有Java和C#實現(xiàn)的版本腊徙。

為什么C語言,比其他語言顯得更底層呢檬某?接觸過的朋友相信都有很深的體會撬腾,C語言的程序,是在和圖靈機(jī)硬件打交道恢恼,變量民傻、數(shù)組、結(jié)構(gòu)體场斑,聲明在堆內(nèi)存就要為其分配內(nèi)存空間大小漓踢,分配了內(nèi)存,就要手動回收漏隐;數(shù)組還要區(qū)分靜態(tài)和動態(tài)喧半,每塊數(shù)據(jù)占幾個字節(jié),躺在內(nèi)存的什么位置青责,一切都按編程人員的安排挺据。所以有人說C語言就是一個高級匯編,想起來確實有一分道理(笑)爽柒。但在智能手機(jī)吴菠、移動計算機(jī)計算能力大大提升的今天,計算資源早已不是通用編程首先考慮的問題浩村,相比于C語言強(qiáng)迫編程人員從機(jī)器的角度設(shè)計程序,抽象程度更高的OOP才更接近人腦的思維方式占哟,才更適合提高軟件工程師的編程效率心墅。

即使如此,仍有一部分人至今站在OOP的對立面榨乎,從代碼復(fù)雜度怎燥、建模能力要求等方面提出異議,堅持寫C++蜜暑、Python铐姚、PHP的時候不構(gòu)造類,寫純過程的程序肛捍。但其實隐绵,這些自稱為原C黨的朋友,并不能說自己沒有使用OOP拙毫,因為這些語言中變量依许,跟C語言中的變量,有本質(zhì)的不同缀蹄。

就用字符串數(shù)組來舉例子峭跳,C語言是沒有string類型的膘婶,只有字符數(shù)組,用\0來標(biāo)記字符串結(jié)束蛀醉;而其他語言中的string則是早已封裝好的字符串類(Class)悬襟,用起來跟整型無異。

C語言中字符串和數(shù)字變量聲明

char name[] = "Tom\0";
int age = 12;

Python中字符串和數(shù)字變量聲明

name = "Tom"
age = 12

C++中字符串和數(shù)字變量聲明

string name = "Tom";
int age = 12;

我們在Python和C++中使用字符串拯刁,早已不是在直接與設(shè)備內(nèi)存打交道脊岳,而C語言中的“字符串”還停留在只是內(nèi)存中的一段連續(xù)空間的階段。

再來看一看數(shù)組筛璧,C++雖然也支持C的數(shù)組逸绎,但我想對比的其實是C++標(biāo)準(zhǔn)庫中的向量(Vector),以及Python中的鏈表(List)夭谤,這些高級容器同樣是基于OOP理念設(shè)計的類棺牧,仍只有C語言的數(shù)組內(nèi)容直接映射在內(nèi)存上。

所以即使你不構(gòu)造Class朗儒,在C++颊乘、Python、PHP中仍在使用對象和實例的OOP特性醉锄,即使開發(fā)的是線性程序乏悄。

徹底的OOP

經(jīng)常會看到有人抱怨Java把面向?qū)ο蟮睦砟钭龅奶^頭,C#作為Java的仿制品恳不,也同樣逃脫不了被詬病的現(xiàn)實檩小,但其穩(wěn)定性也是有口皆碑。然而真正把OOP理念實現(xiàn)的徹頭徹尾徹徹底底的烟勋,反而是最早的OOP語言之一的Smarttalk规求,讓我先看一段Samrttalk的代碼

Transcript show: 'Hello world'

這是Smarttalk版本的Hello world程序,Transcript是Squeak(這是Smalltalk語言的一種版本實現(xiàn))環(huán)境里卵惦,把信息顯示到屏幕上的一個對象阻肿。這段代碼是用冒號給這個對象發(fā)送了一個消息(Message),如果給這段代碼加上一對中括號沮尿,是不是像極了Ojective-C丛塌,沒錯,因為OC就是參考Smarttalk設(shè)計的Runtime畜疾。

同樣赴邻,Samrttalk也支持中括號的寫法,我們可以把上面的一段代碼段落庸疾,賦值給一個變量:

t := [ Transcript show: 'Hello world']

這個t變量乍楚,其實是一個閉包(BlockClosure)對象,相同的概念在C++ 11標(biāo)準(zhǔn)里才出現(xiàn)届慈,相比之下Smarttalk的設(shè)計理念真的很前衛(wèi)徒溪。而OC作為Smarttalk的追隨者忿偷,更是擁有NSOperation類來實現(xiàn)閉包,相比之下臊泌,block并不是基于OOP的設(shè)計鲤桥。

C++的blcok和ObjC的NSOperation,這里block寫法OC同樣支持

void hello = ^ {
    NSLog(@"hello world");
};

hello();

NSBlockOperation* block = [NSBlockOperation blockOperationWithBlock:^{
    // 做一些操作
}];
[[NSOperationQueue mainQueue] addOperation:block];

要注意的是NSBlockOperation是在OC支持block以后才出現(xiàn)的類渠概,在此之前要使用NSOpertaion茶凳,我們需要繼承NSOpertaion類,并重寫這個類的-(void)main方法播揪,這無疑是一件十分繁瑣的事贮喧。

一切皆對象

OC作為Smarttalk的追隨者,在OOP的理念上是要強(qiáng)于C++猪狈、Python和PHP的箱沦,interfaceimplementation雇庙、getter谓形、setter的接口設(shè)計,和Java疆前、C#相互參考寒跳,水平相近,但仍比Smarttalk和Ruby略遜一籌竹椒。

熟悉Cocoa框架的朋友都知道童太,UI繪制框架CoreGraphic中仍然要使用大量的CG開頭的C語言函數(shù),點胸完、線康愤、面的容器,依舊是CGPoint舶吗,CGSize,CGRect這些C語言結(jié)構(gòu)體择膝;數(shù)字變量依然是int誓琼、NSInteger、NSNumber(數(shù)字類)混著用肴捉,相互轉(zhuǎn)換忙的不亦樂乎腹侣。當(dāng)然這一切在OC支持字面量特性(Literals)以后有了好轉(zhuǎn):

//通過@符號直接把普通變量轉(zhuǎn)換為數(shù)字對象
NSNumber *myIntegerNumber = @8;
//轉(zhuǎn)回來
NSInteger customNumber = [myIntegerNumber integerValue];

相比之下,Smarttalk和Ruby做的更徹底齿穗,更好用傲隶,下面是用Smarttalk重復(fù)輸出十次Hello world的代碼,給數(shù)字10發(fā)timesRepeat消息窃页,重復(fù)消息參數(shù)中的閉包:

10 timesRepeat: [Transcript show: 'Hello world']

為什么整數(shù)類要設(shè)計這么方法呢跺株?因為Smarttalk中并沒有循環(huán)語法复濒,甚至其他語言常見的條件語句if/else在Smarttalk中都是不存在的,而都是使用OOP的理念實現(xiàn)乒省,有興趣了解更多關(guān)于Smarttalk的內(nèi)容巧颈,請來這里

給對象發(fā)消息是更符合人類思維模式的設(shè)計

這里我們從繼承Smarttalk理念的Ruby說起袖扛,雖然其使用點語法替代了冒號砸泛,但仍能看出Ruby中的數(shù)字類型,就是數(shù)字對象蛆封。

//將數(shù)字對象102轉(zhuǎn)換成字符串對象
102.to_s

用Smarttalk實現(xiàn)則是

102 printString

相比之下Python則像是一個作者對OOP還處于感性認(rèn)知階段設(shè)計出來的語言唇礁,所以會設(shè)計出len()、map()惨篱、fliter()這種C語言函數(shù)風(fēng)格的接口盏筐,例如我在OC中我們獲取數(shù)組的長度使用count屬性,使用點語法或者中括號消息都可以獲榷噬摺(關(guān)于OC中的點語法和中括號語法我們后面再聊)

NSArray* a = @[@(1),@(2),@(3)];
a.count;
[a count];

這很面向?qū)ο蠡希驗槲覀円@取數(shù)量的主體數(shù)組實例a,發(fā)消息讓他返回長度很符合人類的思維邏輯绣夺。同樣的我們看看Ruby吏奸,也是一樣的操作

a = [1,2,3]
a.length
a.size

然而當(dāng)我使用第一次寫Python代碼的時候,我經(jīng)歷了很多人都遇到過的情況陶耍,不知道字符串或者數(shù)組如何獲取長度奋蔚。因為Python中string和list都沒有l(wèi)ength、size烈钞、count泊碑、len等屬性和方法,然后我們發(fā)現(xiàn)Python提供了一個len()方法獲取序列長度毯欣,這個方法接受一切的對象作為參數(shù)。

a = [1,2,3]
len(a)

s = "123"
len(s)

針對這個問題酗钞,有一部分人認(rèn)為不是問題腹忽,他們說做OOP不要太教條主義,len在前在后能有很大差別么砚作?我想說真的是有的窘奏,這個看似簡單的前后問題,其實影響了實際的編程體驗葫录,就是是否基于對象思考問題的體驗着裹。

一方面,len()方法像一個憑空存在的方法米同,不依賴于任何類和對象骇扇,也不是依附于某個模塊摔竿,知道它存在,才會去使用它匠题,同樣的還有Python中的type()拯坟、map()方法等。另一方面韭山,這一類方法到底可以用于什么類型的對象郁季,開發(fā)者心里也沒底,必須對照接口標(biāo)明的參數(shù)類型使用钱磅。

這一切無疑不利于程序開發(fā)的思維連貫性梦裂,有朋友可能覺得我說的言過其實,我這里舉一個例子大家體會一下何為思維連貫性盖淡。

需求是將一段英文字符串的單詞逆序年柠,How are you處理成you are How

我們用OC實現(xiàn)如下:

#import <Foundation/Foundation.h>
NSString* reverse(NSString* text) {
    NSArray *words = [text componentsSeparatedByString:@" "];
    NSArray *reversed = [[words reverseObjectEnumerator] allObjects];
    return [reversed componentsJoinedByString:@" "];
}

Python實現(xiàn)為

def reverse(text):
    a = text.split(' ')
    a.reverse()
    return ' '.join(a)

Ruby的實現(xiàn)為

def reverse(string)
  return string.split.reverse.join(' ')
end

觀察出來區(qū)別了了吧褪迟,重要的不是Ruby只用了一行代碼冗恨,而是Ruby相比于OC和Python,省去了很多中間變量味赃,別看只是一點點節(jié)省掀抹,其實省去我們實際開發(fā)中很大一部分無用工作。當(dāng)然心俗,OC可以通過括號多層嵌套連貫起來寫傲武,也能達(dá)到同樣的效果,但我們并不推薦這樣做城榛,因為OC的方法名偏長揪利,如果縮進(jìn)不當(dāng),會讓代碼更難理解狠持。

相比之下疟位,Python的接口設(shè)計更滑稽一些,首先在Ruby中

array.reverse!
array.reverse

是兩個不同的方法喘垂,前者只逆轉(zhuǎn)array献汗,沒有返回值。后者則返回一個新的逆轉(zhuǎn)數(shù)組對象王污,Python沒有類似設(shè)計。

其次Ruby和OC都將join方法設(shè)計在array類里楚午,唯獨Python將其設(shè)為字符串類型的方法昭齐,導(dǎo)致了Python沒法連貫地將中間參數(shù)略去。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末矾柜,一起剝皮案震驚了整個濱河市阱驾,隨后出現(xiàn)的幾起案子就谜,更是在濱河造成了極大的恐慌,老刑警劉巖里覆,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件丧荐,死亡現(xiàn)場離奇詭異,居然都是意外死亡喧枷,警方通過查閱死者的電腦和手機(jī)虹统,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來隧甚,“玉大人车荔,你說我怎么就攤上這事∑莅猓” “怎么了忧便?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長帽借。 經(jīng)常有香客問我珠增,道長,這世上最難降的妖魔是什么砍艾? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任蒂教,我火速辦了婚禮,結(jié)果婚禮上辐董,老公的妹妹穿的比我還像新娘悴品。我一直安慰自己,他們只是感情好简烘,可當(dāng)我...
    茶點故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布苔严。 她就那樣靜靜地躺著,像睡著了一般孤澎。 火紅的嫁衣襯著肌膚如雪届氢。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天覆旭,我揣著相機(jī)與錄音退子,去河邊找鬼。 笑死型将,一個胖子當(dāng)著我的面吹牛寂祥,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播七兜,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼丸凭,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起惜犀,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤铛碑,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后虽界,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體汽烦,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年莉御,在試婚紗的時候發(fā)現(xiàn)自己被綠了撇吞。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡颈将,死狀恐怖梢夯,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情晴圾,我是刑警寧澤颂砸,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站死姚,受9級特大地震影響人乓,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜都毒,卻給世界環(huán)境...
    茶點故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一色罚、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧账劲,春花似錦戳护、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至榛瓮,卻和暖如春铺董,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背禀晓。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工精续, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人粹懒。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓重付,卻偏偏與公主長得像,于是被迫代替她去往敵國和親凫乖。 傳聞我的和親對象是個殘疾皇子堪夭,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,722評論 2 345

推薦閱讀更多精彩內(nèi)容