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

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 消息給類來進行創建一個新的對象,接著第四章開始學習繼承。

Tags: