Objective-C 基礎教程第三章,物件導向程式設計基礎知識

VxerLee暱稱已被使用發表於2022-02-24

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物件:兩個正方形、一個圓形和一個橢圓形。(每個物件都能查詢相應的函式並實現其繪圖功能)

image-20220110135518394

每個物件都有自己的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這個函式呢?

解答:

這就需要叫做的幕後幫手來協助完成這個任務,請看如下圖:

image-20220110163321331

可以從圖中看出,其實他就是利用了指標來進行間接,方面我們擴充套件類的程式碼,當我們需要給圓形新增一些新功能,一些新屬性時候就可以直接在類的程式碼裡面進行編寫,我們也不需要修改之前物件呼叫的程式碼,這樣確實也不容易導致錯誤。

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語言原型函式,還有減號代表的是物件的方法加號代表的是類的方法。
  • setFillColorsetBoundsdraw這些代表的是方法名,不過前面兩個和最後一個有區別就是多了:
  • :,這個在OC中叫中綴符語法,方法的名稱以及其引數都是合在一起的,所以最後一個沒有引數的函式就沒有這東西。
  • 帶中綴符號的方法呼叫時候可以這樣呼叫,[circle setFillColor: kRedColor];,帶有兩個中綴符的時候就[testThing setStringValue:@"hello there" color:kBlueColor];
  • 引數的型別是在()中指定的。

@implementation

說過了帶@就是C語言的擴充套件,@interface用於定義類的介面,通常介面被稱為APIApplication 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 訊息給類來進行建立一個新的物件,接著第四章開始學習繼承。

相關文章