Objective-C 基礎教程第三章,面向對象編程基礎知
Objective-C 基礎教程第三章,面向對象編程基礎知
0x00 前言
書中的這章節主要是對零基礎的人介紹面向對象編程的基礎知識,一般學過高級程式語言的基本都會涉及到面向對象編程的知識,所以可以略過。
術語:OOP的解釋
面向對象編程(Object-Oriented Programming
)的首字母縮寫為:OOP
,這是一種編程技術,最初是為了編寫模擬程式
而開發
的。OOP很快就俘虜了其他種類軟體(比如涉及圖形用戶介面的軟體)開發者的心。很快OOP就成為了業內一個非常重要的流行詞。它被譽為具有魔力的顏色子彈,可以使編程工作變得簡單而愉悅。
在討論OOP之前,先來看看OOP的一個關鍵概念:間接(indirection)
0x01 間接(indirection)
間接是一種概念,為什麼要用間接從書本上的幾個例子中,我大概體會到應該就是為了可變性,比如說我在程式碼裡面有個循環語句,然後有個printf每次輸出1-xxx的值,那麼我要改循環次數的時候就需要每次將printf裡面的xxx改成循環的次數,如果用變數來代替的話我只需要改一次循環次數即可。
還有就是用文件間接的方式,比如我要輸出一堆數據,都需要提前定義一個列表或者數組,那麼需求有變動的時候我就要每次改這些變數裡面的值,但是如果我程式從文件裡面讀取這些數據,我只需要修改下文件裡面的內容即可,所以可變動性就特別好。
0x02 面向對象編程中使用間接
在書中看完間接的知識後, 對間接的概念大致有了個了解。在面向對象編程中(OOP),間接可以說是他的核心。
OOP使用間接來獲取數據,就像我們在之前的例子中使用變數、文件和參數所做的那樣。OOP真正的革命性
在於它使用間接來調用程式碼!
不是直接調用某個函數,而是用間接調用!
只要理解了這一點,你就算掌握了OOP的內涵了。其他一切都是通過間接產生的引申效果。
面向過程編程
首先來看兩個例子,分別是面向過程編程和面向對象編程的程式碼,書中說到:過程式編程建立在函數之上,數據為函數服務。
//*********************************************************************
//利用純C語言和過程式編程方式繪製幾何體的形狀。
//
//《Object-C 基礎教程》 03.08 Shapes-Procedural
//**********************************************************************
#import <Foundation/Foundation.h>
//----------------------------------------變數聲明----------------------------------
//幾何體形狀類型
typedef enum{
kCircle, //圓圈
kRectangle,//矩形
kEgg, //雞蛋
}ShapeType;
//幾何體顏色類型
typedef enum{
kRedColor, //紅色
kGreenColor,//綠色
kBlueColor, //藍色
}ShapeColor;
//幾何體輪廓結構體
typedef struct{
int x,y,width,height;
}ShapeRect;
//幾何體結構體
typedef struct{
ShapeType type;
ShapeColor fillColor;
ShapeRect bounds;
}Shape;
void drawShapes(Shape shapes[],int num)
//-------------------------------------------------------------------------------
//入口點程式碼
int main(int argc, const char * argv[]) {
@autoreleasepool {
Shape shapes[3];
//圓形數據賦值
ShapeRect rect0 = {0,0,10,30}; //x,y坐標與寬和高數據
shapes[0].type = kCircle; //幾何體類型
shapes[0].fillColor = kRedColor;//幾何體顏色
shapes[0].bounds = rect0;
//矩形數據賦值
ShapeRect rect1 = {30,40,50,60};
shapes[1].type = kRectangle;
shapes[1].fillColor = kGreenColor;
shapes[1].bounds = rect1;
//雞蛋數據賦值
ShapeRect rect2 = {15,18,37,29};
shapes[2].type = kEgg;
shapes[2].fillColor = kBlueColor;
shapes[2].bounds = rect2;
//繪製幾何體
drawShapes(shapes,3);
}
return 0;
}
//面向過程方式drawShapes
void drawShapes(Shapes shapes[],int count)
{
for(int i=0;i<count;i++)
{
switch(shapes[i].type){
case kCircle:
drawCircle(shapes[i].bounds,shapes[i].fillColor);
break;
case kRectangle:
drawRectangle(shapes[i].bounds,shapes[i].fillColor);
break;
case kEgg:
drawEgg(shapes[i].bounds,shapes[i].fillColor);
break;
case kTriangle:
drawkTriangle(shapes[i].bounds,shapes[i].fillColor);
break;
}
}
}
從書中的例子可以看出,面向過程編程中,如果我們要讓程式擴展一下,比如不僅能繪製各種形狀,還必須計算這些形狀的面積,並判斷滑鼠游標是否位於這些形狀中。在這種情況下,就必須修改每個對形狀執行操作的函數,修改過去正常工作的程式碼很可能引入新的錯誤,所以在這點上面向過程編程極其不方便,就需要用到OOP(面向對象編程)概念了。
面向對象編程
面向對象
編程則以程式的數據為中心
,函數為數據服務
。在OOP中,不再重點關注程式中的函數,而是專註於數據。
在OOP中,數據通過間接方式引用程式碼
,什麼意思呢?
就是程式碼可以對數據進行操作,不是向面向過程那樣,通知drawRectangle
函數繪製一個根據這種形狀
的圖形,而是要求形狀
繪製自身。(藉助間接的強大功能,這些數據能夠知道如何查找相應的函數來進行繪製)。
對象是什麼?
就是和C語言中的struct一樣,神奇的是它能夠通過函數指針查找與之相關的程式碼。
如下圖:展示了4種Shape對象:兩個正方形、一個圓形和一個橢圓形。(每個對象都能查找相應的函數並實現其繪圖功能)
每個對象都有自己的draw
函數,知道如何繪製自身的形狀。
(我們直接將之前的面向過程程式碼,改成面向對象的程式碼,只需要改動一個函數就行。)
void drawShapes(id shapes[],int count)
{
for(int i=0;i<count;i++)
{
id shape = shapes[i];
[shape draw];
}
}
改完了,程式碼是不是變的超級簡潔啊!
解釋下這段程式碼的意思,其中id屬於OC中的泛型
,他有點類似Java中的Object
,就是用id可以引用任何類型的對象。
對象是一種包含程式碼的struct結構體
,所以id實際上是一個指向結構體的指針
。
[shape draw]
,這一句程式碼的意思是通知shape對象發送draw消息
或者向shape發送draw消息
,至於形狀如何實際繪製自身的圖形,取決於shape的實現。
思考🤔?
向對象發送消息後,如何調用所需要的程式碼呢?就是如何才能觸發比如draw這個函數呢?
解答:
這就需要叫做類
的幕後幫手來協助完成這個任務,請看如下圖:
可以從圖中看出,其實他就是利用了指針來進行間接,方面我們擴展類的程式碼,當我們需要給圓形添加一些新功能,一些新屬性時候就可以直接在類的程式碼裡面進行編寫,我們也不需要修改之前對象調用的程式碼,這樣確實也不容易導致錯誤。
0x03 OC面向對象 術語
- 類(class) 其實就是一個結構體,用來描述對象的類型。
- 對象(object) 包含值和(this指針)指向其類的隱藏指針的結構體。
- 實例(instance)是「對象」的另外一種稱呼。
- 消息(message)是對象可以執行的操作,用於通知對象去做什麼。
- 方法(method)為了響應消息而運行的程式碼。(個人理解應該是private的類函數)
- 方法調度(method dispatcher)是Object-C的一種機制,用於推測執行什麼方法以響應某個特定的消息。
- 介面(interface)是類為對象提供的特性描述。例如,Circle類的介面聲明了Circle類可以接受draw消息。
- 實現(implementation)是使介面能正常工作的程式碼。
0x04 OC語言中的OOP
根據書本中的相關內容學習了OOP的一些基礎概念後,終於可以來體驗OC中的OOP了。
@interface
創建某個特定類的對象之前,Object-C編譯器需要一些有關該類的資訊,尤其是對象的數據成員(也就是編譯器需要根據結構體的大小,結構體的成員,函數等等)進行一些列的初始化工作,好方便轉換成彙編程式碼?應該是這樣理解。所以(我們可以使用@interface
指令把這些資訊傳遞給編譯器)。ps:說明這編譯器不太智慧?為啥Windows下C++都不用搞這麼麻煩! 可能是因為C++用了Class關鍵字來傳遞的。
//**********************************************************
/* Circle類介面 *
/***********************************************************/
@interface Circle : NSObject
{
ShapeColor fillColor;
ShapeRect bounds;
}
- (void) setFillColor: (ShapeColor) fillColor;
- (void) setBounds: (ShapeRect) bounds;
- (void) draw;
@end
根據書本中的例子,我們對上面程式碼進行詳細的分析,上面程式碼的語法都比較陌生,從@interface
開始,在第二章中說過只要出現@符號,就可以把它看成是對C語言的擴展
,然後上面也說過了@interface是用來把類資訊傳遞給編譯器的。
接著最尾部是@end
關鍵字,應該就是告訴編譯器@end以上的都是這個類的資訊。
接著@interface後面跟著的是類名,代表告訴編譯器這是Circle類的介面
,然後:NSObject
代表的是繼承自NSObject
類。
然後{...}
裡面的部分是告訴編譯器Circle對象
所需要的數據成員,其中這裡面的成員被稱為Circle類的實例變數
。
最後幾行程式碼有點類似C語言中的函數原型,不過略微有點區別,主要就是多了-
、()
、:
這些奇怪的符號。
這是OC中的方法聲明語法,其解釋如下。
- -(void) 其中這個減號代表的是
OC方法的聲明
,主要用來區分C語言原型函數,還有減號代表的是對象的方法加號代表的是類的方法。 setFillColor
、setBounds
、draw
這些代表的是方法名,不過前面兩個和最後一個有區別就是多了:
。:
,這個在OC中叫中綴符
語法,方法的名稱以及其參數都是合在一起的,所以最後一個沒有參數的函數就沒有這東西。- 帶中綴符號的方法調用時候可以這樣調用,
[circle setFillColor: kRedColor];
,帶有兩個中綴符的時候就[testThing setStringValue:@"hello there" color:kBlueColor];
。 - 參數的類型是在()中指定的。
@implementation
說過了帶@
就是C語言的擴展,@interface
用於定義類的介面,通常介面被稱為API
,Application Programming Interface
。而真正能使對象運行的程式碼都在@implementation
中實現
。
//**********************************************************
/* Circle類介面實現 *
/***********************************************************/
@implementation Circle
- (void) setFillColor:(ShapeColor) c
{
fillColor = c;
}
- (void) setBounds:(ShapeRect) b
{
bounds = b;
}
@end
這是OC中的類聲明語法,解釋如下:
-
@implementatation
英語翻譯為實現,如其名就是告訴編譯器接下來這些程式碼是用於實Circle類的。 -
- (void) setFillColor:(ShapeColor) c
這程式碼與@interface
處基本差不多,只是他結尾沒有;
,代表不是聲明語句,而且參數名是可以改變的。 -
fillColor = c
,類似C++中的this->fillColor = c
,在OC中默認的隱藏this指針為self
,也就是self->fillColor = c
。
instantiation(實例化對象)
我們學會了如何聲明類介面,並且實現類程式碼後,最後當然是要去調用該類的對象去實際操作,那麼在OC中如何實例化對象呢?
在OC中很方便可以把類當成對象去發送消息,所以我們實例化一個對象的時候,其實只要發送一個new消息就行,比如Circle circle = [Circle new];
,然後調用之前演示過了用[circle setFillColor: kRedColor]
,tips:(由於對象的局部變數只在對象的實例中有效,因此我們稱它們為實例變數,通常簡寫成ivar
。)
0x05 第三章小節
本章是面向對象的概念和定義,我寫的也比較多。首先是介紹了間接的這個概念,然後又從面向過程編程開始逐步過渡到面向對象編程,之後也用了大量的例子來進行實驗,最後過渡到OC中的面向對象編程,其中學習到了很多關鍵字比如,@interface
介面聲明,@implementation
類介面實現,:
中綴符,[xx xx]通知對象發送xx消息
,instantiation實例化對象可以通過發送new 消息給類來進行創建一個新的對象,接著第四章開始學習繼承。