語法淺析--從C語言到Objective-C

2021-02-19 Cocoa開發者社區
點擊上方「CocoaChina」即可關注

Objective—C(以下簡稱OC)作為C語言的超集,其基本特性就是C語言可以直接在OC中無縫結合。隨著開發時間越來越長,對C語言的熟悉往往給筆者帶來很多便利,所以這裡一部分做個分析,一部分也是作為一些小經驗的分享。新手寫文,捉襟見肘,總結的不好,或是有更好的方法,還請諸位不吝指出。

本文的示例代碼以Command Line Tools的方式做了一個簡單的demo,上傳到了Github上,如果您感興趣,也可以下載下來自己調試。

程式語言

對於語言的定義多種多樣,但是總讓人覺得差強人意。所以筆者索性自己總結了下,為計算機語言歸納出了三個要素:

1.語素 簡單說來,語素就是符號與文字單元。從早起的二進位0和1,到彙編語言的彙編指令,到C語言的ASCII碼字符集,再到swift的Unicode字符集。當然了,單純的符號是無意義的,更為狹義的語素會經過語言語法的劃分,如關鍵字、標識符、運算符、注釋符等等。就定義而言,語素規範是由語法規定的,而就功能而言,語素明確了我們「用什麼寫」。

2.語法 語法並不是某種顯式的符號,而是編寫或羅列語素的規則。從單個語素的含義,到語素間的排列規則,再到大段代碼的執行順序。語法負責讓一整篇無意義的符號按照一定的規則表達出含義,我們可以將其稱之為語義。一般來說,學語言初期,主要在學的就是語法了。就定義而言,語法是語義的表達框架,就功能而言,語法則明確了我們「如何去寫」。

3.語義 語義,可以歸納為思維的描述,引申的話,也可以歸結為邏輯,畢竟邏輯就是思維的規律。無論是平常交流用的語言也好,與計算機交互使用的計算機語言也罷,究其本質,都是為了表達、溝通與理解。就定義而言,語義是語言使用者思維的具化體現,就功能而言,語義其實就是在表達「想寫什麼」。

雖然從描述思維的角度來說,語義可以是無窮的,但無窮依舊有其局限性。這種局限性可以追溯到設計語言之初,從語言的特性與適用場所窺得端倪。

比如C語言中,選擇ASCII字符集和其字符組合作為語素,用if(...)作為語法中的判斷邏輯表達式,用if(x)return 1;來表達具體的語義。但當我們希望描述一個變量擁有自己的函數時,就顯得無比吃力。我們或許可以用很多句話來模擬,但是想要一句話解決問題就不可能了。究其原因,是因為C語言本身不支持這樣的描述方式。

從層次上來說,支持什麼樣的描述方式往往是由語法來確定的,但究其原因,語法作為表達框架,其集合的邊界是取決於我們語義的寬度的。其實說的簡單點,只是因為設計語言的時候,我們想說的東西還沒有甚至沒敢這麼複雜。

深層次的語言設計初衷就不再探討了,這些理由基本都囊括在自己的語言特性裡,有人說想深學一門語言,就要學它的歷史。從某個角度來說,這也是為了更方便我們去理解,設計語言的人,想要用這個語言來表達什麼而已。

順便討論下日常語言和計算機語言的區別,對於日常語言和來說計算機語言,最大的區別在於受體。日常語言的表達對象是具有相似思維能力的活生生的人,語言從思維形成,到表述,到傳達,到接收時轉化為自己所理解的信息,往往會進行多層次多維度的歸納和演繹。

計算機語言面對的是擁有著既定規則和一定計算能力的機器,所以需要我們的表達是精確到位的。所以,對應的語法也需要精確,「你不是一個人」這樣的歧義句,放在日常語言裡或許只博得一笑,可放在計算機語言裡,問題就嚴重了。

語素、語法、語義作為語言的三要素,未必是最精確和科學的,不過對於計算機語言來說,這也算是一種歸納並定性的思路,可以讓我們對一門語言有另一個層面的認識與特性區分。不推薦強行的教條劃分,頂多在自頂向下或是自底向上分析語言的時候,有個模型做借鑑吧。

聲明、實現與調用

說完了假大空的理論,我們回到C語言。 C語言定義了關鍵字、標識符、運算符、注釋符這樣的基本語素,定義了if、for等等的邏輯語句表達之後,最核心的元素分為兩種:數據與函數。對於C語言而言,代碼的實際構成都可以歸結為對數據和函數做以下三件事:聲明、實現與調用。讓我們從代碼上來做一些簡單的區分:

int var;//變量聲明,格式為 「數據類型 標識符;"

int func(int);//函數聲明,格式為「數據類型 標識符(數據類型,...);」

int func(int x){//函數實現,格式為「數據類型 標識符(數據類型 形參標識符,...){ ... return ...}」

var = x; //變量賦值,利用賦值運算符「=」

printf("%d",var); //變量調用,直接使用變量的標識符

return x?func(x-1):0;//函數調用,格式為「函數名標識符(傳入參數)」

}


相信你看到上面這幾句代碼後面的擁擠的注釋已經心慌慌了,不必緊張,這只是語法的一個列舉。C語言語法自成一本書,筆者不會妄想到用一篇博客講完的。如果你有一定的C語言開發經驗,你就會發現上面這段代碼寫的毫無意義,也就最後一句的遞歸調用可以裝裝逼。

首先,函數有一個很特別的地方,那就是它的調用,比如func()。這樣的表達是被視作一個數據來佔位的,在實際含義中表示它的返回值。這個特性將函數與數據從語法上放在了平行的位置,雖然看著不起眼,但這是個很棒的特性。讓我們可以用同樣的方式去對待計算過程和一個具體的值。 我們可以以此做兩種假設:

把變量當做函數:變量取值本身也是個過程,我們調用一個變量,其實也是通過一定過程取得了一個返回值。 int i = 1;與int i(){return 1;}在功能上是否很相似呢?

把函數當做變量:任何一個函數在調用後一定對應著一個確定的返回值,哪怕是一個void類型,也是與數據類型所對應的。所以哪怕是func(func(func(func(1)))),最終依舊是個int型的數據而已。

當然了,從實現角度來說,變量與函數的差別很大。但在編寫代碼的時候,尤其是將函數當做一個變量,會減少我們所需要關注的地方。我們完全可以聲明一個int whoCare(int,int),當做一個變量調用。最後再去考慮它的實現過程。

當我們把函數的調用視作一個數據佔位符的時候,我們會發現整個語法變得簡單了不少。除了聲明和實現的格式,語句就剩下各種情況下的數據運算了,然後嵌套,調用,隨意重複n++次。

那麼回到我們的聲明和實現,整個語法格式其實是走一步算一步的。



大概就是上圖這個樣子了。語法是個坑,一邊學一邊用成長起來比較快。大部分實踐經驗證明,學語法基本都是從寫什麼不可以開始的,找個報錯功能靠譜,比如Xcode這樣的IDE軟體開始碼代碼吧,什麼時候靜態編譯不報錯了,語法關也就過的差不多了。

OOP的OC

語言設計從來不是一件輕鬆的事情,每一個規則的設計都取捨於性能和易用性,一方面要求嚴謹,一方面要求靈活。從C語言中發展出來的面向對象語言就有好幾種,它們唯一的共同特徵大概就是都可以直接運行C語言代碼了(笑= =)。

從個人情感角度來說,筆者對於OC的這門語言的熱愛大概只有Python能夠分一杯羹了。無關於性能、環境與語法,其鍾愛點僅僅源於方括號[],讓整篇代碼看起來整齊美觀。

為了拓展面向對象的功能,一方面,OC在C語言中採納了很多固有特性來模擬對象特性。另一方面,則另起爐灶,設計了另外一套關鍵字語法系統,而且設計的頗具美感。第一眼看上去,語法或許特殊到再也見不到類似的,但是細細揣摩上去,反倒是因為這種不同讓我們在同一個片段裡混用C語言語法與OC語法,也絕對不會混淆。

理論上,這裡是應該對比一下C++的,不過筆者對C++學習甚淺,還是不要妄加論斷的好。這裡要說的是,OC也加入了與C++混編的功能,不過因為對象機制不同,不同語言下的面向對象語法是不能相互替換的。一想到一篇代碼裡會存在兩種類,一個對象= new CPPClass,一個= [OCClass new],望而生畏。

OC語法區別於C語言的主要地方都是在對象層面,對於基礎的語法,基本上可以照抄照搬,倒也省心省事。

所以從基本語素說起,我們就可以直接看OC的對象與方法了。順便提一句OC的文件結構。在C語言裡面,.c文件是靠#include來聲明包含的,在OC裡面,則會分為.h和.m文件。

如果你像我一樣對引用生出好奇心的話,會發現是沒有對.m的引用的,但是文件裡的實現代碼卻會被執行,答案很簡單,編譯器用另外一種方式替我們做了這件事。至於具體如何做到,這裡先不討論(筆者也順便再研究研究= =)。要說明的是,儘可能不要在OC中寫#import "....m",即便這樣是完全可以的。

第一步,我們來建立一個新的對象好了。為了方便,我稍稍違反一下默認的文件結構,將聲明和實現代碼都搬到一個.h文件裡,請不要在寫代碼的時候這樣做。

//BCObject.h

@import Foundation;

#pragma mark - BCObject.h

@interface BCObject : NSObject

@end

#pragma mark - BCObject.m

@implementation BCObject

@end


與C語言的聲明與實現相對應,在OC中,一個類也需要做這兩件事,並且賦予了專門的關鍵字。在這裡叫關鍵字或許不太合適,你也可以稱為編譯器標記,畢竟從實現角度來說,它們和之前的關鍵字的功能還不太一樣。在OC中,@符號的功能頗多,在這裡,是與後面定義好的字符共同作為一個標記出現,供編譯器進行轉義。比如@import是庫引用,@interface是類聲明,而@implementation則是類實現,當然了,聲明和實現還需要伴隨@end這樣的結束記號。

OC的面向對象語法基本都是在自己的編譯器標記中才可以使用的,它等於和編譯器約定,在@...@end之間的代碼,是使用OC編寫的。也可以反過來做假設,如果不在聲明和實現的段落中,則該語句與對象無關,那麼面向對象的語法也就想當然的無法成立了。 不過,這只是針對聲明和實現,如果僅僅是調用,OC和C語法的混搭,真的是爽歪歪~

@implementation BCObject

+(NSString *)hello{

return @"hello world";

}

@end

void say(NSString *str){

NSLog(@"%@",str);

}


然後,我們就可以使用say([BCObject hello]);這樣的語句來實現"hello world"了。

如果你仔細觀察,你會發現我並沒有為+(NSString *)hello這個方法做聲明,只是和C語言中一樣,把實現放在了調用的代碼段前。

這裡也順便說明一個問題,在面向對象中,往往會有私有方法和實例變量這樣的說法。在OC中,最簡單的方法是把私有的東西放在.m方法裡,因為不存在文件包含,所以也就無法調用了。

而像我們剛才這樣,直接調用@implenmentation的方法,就會發現他是沒有語法上的防禦力的,僅僅是靠文件結構的約定俗成而已。這也是為什麼不讓大家引用.m文件的原因之一。

不過,如果是私有實例變量的話,OC卻是提供了對應的標記來聲明私有。

@import Foundation;

@interface BCObject : NSObject{

@public

NSString *hello;

@private

NSString *bye;

}

@end

@implementation BCObject

-(instancetype)init{

self = [super init];

if(self){

hello = @"hello";

bye = @"bye";

}

return self;

}

-(NSString *)bye{

return bye;

}

@end

//在main.m中調用

int main(int argc, const char * argv[]) {

@autoreleasepool {

say([BCObject hello]);

BCObject *obj = [BCObject new];

say(obj -> hello);

say(obj.bye);

return 0;

}

}


在C語言中,我們可以用->來尋址結構體指針中的元素數據,而在OC中,我們則可以通過這個符號來尋址對象內的實例變量。有沒有聯想到什麼呢?對= =因為OC的對象的其實就是個結構體指針,這個運算是通用的。只不過,我們可以通過@private和@public,當然也會有@protect來限制訪問權限。

不過,在實際編寫中,這樣的聲明和取值實在太過繁瑣,雖然可以這麼用,但早已鮮有人問津。自OC2.0之後,我們有了@property用來解決類內實例變量對外的存取,我們將其翻譯為屬性,並形象的將它的存取方法稱為setter和getter。在這之後,訪問類內的實例變量就全部靠方法,而非指針了。

@interface BCObject : NSObject{

@public

NSString *hello;

@private

NSString *bye;

}

@property NSString *word;

@end

//在main.m中調用

int main(int argc, const char * argv[]) {

@autoreleasepool {

say([BCObject hello]);

BCObject *obj = [BCObject new];

obj.word = obj -> hello;

say(obj.word);

return 0;

}

}


回過頭來看OC的聲明和實現語法,數據的聲明和c語言完全一樣,只不過對象都是指針,所以總會多個"*"號。而在方法的聲明和實現中,為了區別於C語言的函數,OC使用「()」來聲明數據類型,使用「:」來標識傳入參數,並且創新的引入了參雜多個「:」的函數命名方式,讓整個句子看著更符合語言邏輯。

雖然這個特性對我等英語盲人來說意義並不大,但是[you doItWith:A and:B for:C]看起來比you.doIt(A,B,C)還是要舒服的多了。如果英語詞彙量充足的話,我們是可以有效的減少翻閱文檔的次數的。

因為格式特立獨行,所以想要混淆就變的困難。OC的語法看起來唬人,學起來倒也不算難,還是那句話,在xcode裡多寫代碼,什麼時候靜態編譯不報錯了,語法自然就過關了。

多說一點

除了聲明、實現、調用的格式之外,語法需要學習的地方還有很多。

在C語言中,我們要考慮變量的作用域,參數的值傳遞與引用傳遞,要考慮一個值的儲存方式等等。

在OC中,複雜的東西則更多,不過,隨著學習的遞進,我們就會發現這些東西其實不在語法範疇之內,比如模式,比如庫和框架,比如內存管理,再比如Runtime機制。

如果你已經有編程基礎的話,那麼玩轉一門新語言的語法並不是一件非常困難的事。

學語法,或許一周左右。

學習語言特性,或許把玩個把月也就很有成效了。

不過學語言真正困難的地方在於語言的環境:框架、適應場合、需要解決的問題、持續的更新與升級還有使用群體的約定俗成。如果非讓我加個期限的話,那會是,一萬年。

總結與吐槽

對於一個初學者來說,學習語法往往是個很彆扭的事情。因為大量的符號、關鍵字和標記需要逐個去記憶,而且還需要分門別類的明確這些符號分別是做什麼用的。

尤其是對於一個英語盲來說,將int、long、short、float、double、char、void歸為一類比獨立背下來這些單詞還要再難上一分。而好不容易過了詞彙關的我們,接下來面對的示例代碼,全都是些陌生的單詞,根本不知道在說什麼好不好。

記起來當初大神們為特性舉例子總喜歡拿幾個系統內置的生僻方法來舉例,語法理解不了不說,還要特地先去查這些方法是什麼,否則返回值根本莫名其妙啊= =。

正因為這樣的事情總是發生,所以總結一些語法特徵,可以多多少少幫我們點忙。比如()是函數,[]是方法,{}是一大句話,標識符都是大神們戲謔我們特地起的人名地名(吐槽...吐槽= =)。閱讀理解逼著我們挨個看單詞,可讀原始碼,是可以先讀符號的。這也是語法給我們帶來的一點小便利。

(來自bifidy的投稿)

喜歡我們的內容,可以點擊右上角「分享到朋友圈」,或「查看官方帳號」並關注我們。

相關焦點

  • Objective-C 與 Runtime:為什麼是這樣?
    /(點擊尾部閱讀原文前往)筆者非常高興能為Objective-C寫寫自己的理解和總結,不僅僅因為是筆者是Objective-C多年的重度開發者,更是因為這是一門有獨特想法的,有創造性的,有優美語法的,有歷史地位的程式語言。
  • 【C語言】02.第一個C語言程序
    學習任何一門語言,首先要掌握的肯定是語法。學習C語言語法的目的:就是能夠利用C語言編寫程序,然後運行程序跟硬體(計算機、手機等硬體設備)進行交互。由於我們的最終目的是學習iOS開發,學習iOS開發的話必須在Mac系統下,因此我就在Mac系統環境下開發C語言程序,而不是在Windows環境下。
  • 深入理解Objective-C的Runtime機制
    Objective-C是基於C語言加入了面向對象特性和消息轉發機制的動態語言,這意味著它不僅需要一個編譯器,還需要Runtime系統來動態創建類和對象,進行消息發送和轉發。下面通過分析Apple開源的Runtime代碼(我使用的版本是objc4-646.tar)來深入理解Objective-C的Runtime機制。
  • 專升本c語言和二級c語言哪個難?
    c語言是計算機專業必考的科目,很多同學不知道專升本c語言和二級c語言哪個難?專升本c語言備考技巧有哪些?請看下文的介紹。專升本c語言和二級c語言哪個難?二級c語言要難一些,專升本c語言只考編程題,而且考的題目也不難,二級c語言考的比較系統,題型也更豐富,而且有時考的很細,要求知道更準確的c語言語法。零基礎,如何學c語言?
  • C語言,美麗的語言
    觀看一場精彩的比賽,我能領悟到一些道理:賽事的魅力就在於它本身 —— 運動員只是媒介。足球很簡單。一個球,44英尺,2個球門,草坪。沒有任何東西妨礙它向我們展現它那充滿生機的精神靈魂。我們看到了這些,感覺到了這些, 我們(至少是我)所以說它是一種美麗的體育運動。 當我瀏覽C語言代碼時,我也感覺到了相同的美。
  • C語言中「c=a+b」,這種結構合理嗎?
    C語言會同意一些"令人震驚"的結構,下面的結構是合法的嗎,我們來看看幾個例子。 c = a+++b; 以下代碼是合法的嗎,咋的一看不禁有這樣的疑問?
  • Swift和Objective-C混編
    方法的使用在Swift中,用點語法來調用Objective-C方法。Objective-C方法引入到Swift後,方法的第一部分,變成方法名,在括號前面。第一個參數在括號裡,沒有參數名。剩下的參數對應各自的參數名排在括號裡。方法的所有組成部分都是需要的,少了任何部分,調用地址就是不對的。
  • 程式語言:Objective-C和C++有何不同?
    Obj-c(圖:ios.25pp.com)  Objective-C,通常寫作ObjC和較少用的Objective C或Obj-C,是擴充C的面向對象程式語言。它主要使用於Mac OS X和GNUstep這兩個使用OpenStep標準的系統,而在NeXTSTEP和OpenStep中它更是基本語言。Objective-C可以在GCC以及Clang運作的系統上編寫和編譯,因為GCC與Clang含Objective-C的編譯器。  Objective-C是非常實際的語言。
  • Objective-C 裡的 eval
    它們接受特定語法的表達式,內置了數學運算甚至局部變量的支持,還能調用任意 Objective-C 方法,相當於在語言當中嵌入了另一個腳本語言。NSPredicate(謂詞)對許多 iOS 開發者來說不會陌生。最常見的場景就是用來過濾數組和正則表達式匹配,也可以配合 CoreData 查詢資料庫。
  • C語言從入門到放棄(3)
    程序的幾種基本結構程序設計模式    C語言中,常用的流程控制結構分為順序結構、選擇結構和循環結構。由這三種結構可組成各種複雜的程序。順序結構是三種結構中最簡單,最常見的程序結構。三種流程控制結構,使用特定的流程控制語句,從而實現程序的各種結構方式。
  • c語言程序設計自學教程
    如果您不甘落後,那麼請自製自控,自學c語言程序設計也是完全可能的。c語言十分依賴於計算機思維,而思維的培養不是一日之功,而是一個日積月累的過程一:準確把握語法語句概念1、編譯預處理不是C語言的一部分,不佔運行時間,不要加分號。
  • c語言——基本語法
    c語言由Dennis MacAlistair Ritchie創始,是普適性最強的一種電腦程式編輯語言,它不僅可以發揮出高級程式語言的功用,還具有彙編語言的優點。本期將簡潔地介紹c的基本語法。目錄1.輸入輸出2.選擇結構3.循環結構4.數組5.結構體6.子函數一、printf輸出格式:printf(格式控制,輸出表列);例如:printf("%d, %c\n", i, c);
  • C語言,C++,C ,Java之間的關係
    C語言,C++,C#,Java,這幾種語言,應該說是當前最流行,也是最基礎的計算機語言。是不是有些人看著會頭大,大腦會不叫混亂,一個計算機怎麼會有那麼的的語言呢?看著就頭大。現在,小編先來給大家說下計算機語言的發張,一臺計算機最本質的語言是機器語言,由01010101的代碼組成,CPU處理的也是由由010101的代碼組成的數據。但是,這種語言太簡單了,不好理解。就來個數字組成的語言,可以用來表達一句話,一個數字,圖像,字母......也許只有計算機可以理解,反正小編是不知道什麼意思。
  • 現代程式語言起點,C語言之環境搭建
    >3、C語言最適合的領域:C語言小巧靈活、語法簡單、適合做小工具嵌入式開發:命令終端中的命令小工具:命令工具作業系統:Linux(多個命令工具的組合)硬體開發:(、C語言的語法標準C11:C11(也被稱為C1X)指ISO標準ISO/IEC 9899:2011,是當前最新的C語言標準。
  • 「C語言從入門到入土」必備C語言基礎筆記整理
    一、C語言1、什麼是C語言?C語言是人寫機器看的一種語言。C語言是高級語言中的低級語言。C語言貼近硬體。C語言的入門學習比較簡單。彙編語言——>B語言——>C語言2、C語言的特性首先C語言就是你的女朋友。無論你讓它幹什麼,它絕對不會自己找到方法。
  • python與c語言的語法有哪些不一樣的
    在眾多程式語言之中,想必很多人都聽說過Python和C語言,在進行編程學習之前,大家都會問:python和c語言的區別有哪些?我該如何選擇?接下來我們來看看吧。python與C的區別如下:1、語言類型:Python是一種基於解釋器的語言,會逐行讀取代碼,將Python編譯為字節碼,由大型C程序解釋;C是一種編譯語言,完整的原始碼將直接編譯為機器代碼,由CPU直接執行。
  • C語言基礎(下)
    C語言的世界結構體類型什麼是結構體21; stu.gender = "男"; stu.mail = "2633218009@qq.com"; printf("%s\n", stu.name); printf("%s\n", stu.gender); printf("%s\n", stu.mail); printf("%d\n", stu.age);結構體數組語法定義
  • Swift學習: 從Objective-C到Swift
    文章組織脈絡:從Objective-C到Swift的語法差異。我們熟悉的Objective-C特性在Swift中如何展現。從Objective-C到Swift的進步改進。研究對比Swift在安全性,易用性上的提升,給我們帶來的新編程範式。
  • 2018年自考《C語言程序設計》試題七
    2018年自考《C語言程序設計》試題七二、 閱讀程序題閱讀程序對於初學者來說很重要,一方面可以鞏固所學的語法知識,另一方面通過閱讀別人寫好的程序來打開自己的思路,就所謂見多識廣。如果選擇有誤,就要認真分析原因,是概念方面的錯誤還是對程序邏輯理解不對,從而加深對語法規則的理解,提高程序設計能力。程序設計語言是開發程序的一個工具,學習語言的目的是為了編寫程序來解決實際問題,所以特別提倡通過實際上機來檢驗備選答案,增強動手能力。1.下面程序的輸出結果是____。
  • C語言快速入門系列(三)
    本節引言:在上一節中,對C語言的基本語法進行了學習,類比成學英語的話,我們現在只是會單詞而已,組成一個個句子還需要學習一些語法