天天看點

Objective-c 知識總結 -- 繼承

知識盲點:

  • OOP 面向對象程式設計(Object Oriented Programming,OOP)
  • Unified Modeling Language (UML)
  • Inheritance 繼承
  • Syntax 文法
  • 重構(Refactoring):Moving and simplifying code this way is called refactoring, a subject that is quite trendy in the OOP community.(移動或簡化代碼稱為重構)When you refactor, you move code around to improve the architecture, as we did here to eliminate duplicate code, without changing the code’s behavior or results.(不改變代碼的最終效果)
  • polymorphism 多态

知識點:

  • 為什麼要使用繼承?
  • 繼承在Objective-C中的文法表示?
  • 為什麼要使用繼承?
  • 假設有兩個類 圓、方形

圓(.h):

Objective-c 知識總結 -- 繼承

方形(.h):

Objective-c 知識總結 -- 繼承

觀察發現,它們屬性和方法聲明是相同的,都有 填充色(fillcolor)、尺寸+位置(bounds)、繪制方法;

如此相同的聲明,除了類名不同,其它都一樣?那麼可否把它們的聲明封裝(抽象)起來,但類名(具體細節)又可以不一樣?

先儲存這種疑問,來看看(.m)檔案。

圓(.m):

Objective-c 知識總結 -- 繼承
Objective-c 知識總結 -- 繼承

方形(.m):

Objective-c 知識總結 -- 繼承
Objective-c 知識總結 -- 繼承

觀察發現,圓和方形的 setFillColor: 和 setBounds: 方法的實作是一樣的,唯一的不同就是它們的繪制方法 draw: ;

通過觀察分析可以得知,如果要把圓、方形封裝起來,那麼就要保證具體的實作可以有不一樣(draw:)方法;簡單來說就是,封裝(抽象)不對細節進行限制,隻對聲明進行限制,就是隻告訴你叫什麼,不告訴你是什麼,要确定是什麼,根據不同的類型(圓或者方形)來确定。

結:我們都知道的,圓和方形都是幾何圖形,而面向對象程式設計,就是對現實世界的抽象,而圓和方形的抽象就是幾何圖形;換種方式描述就是,幾何圖形是圓、方形的父集(父親),圓、方形是幾何的子集(子女)。

抽象過程:

Objective-c 知識總結 -- 繼承
Objective-c 知識總結 -- 繼承

繼承 :Inheritance in OOP means that a class acquires features from another class, its parent or superclass.(一個類(子類)的特性(屬性+方法+協定......)來源于另一個類(父類))

  • 繼承在Objective-C中的文法表示?

首先,通過上面的分析,圓、方形的特性可以由幾何圖形來提供,也就是說圓、方形是可以繼承于幾何圖形的。

Objective-c 知識總結 -- 繼承

分析代碼:

@interface 子類 : 父類

// 新特性

@end

@interface 和 @end 是一對,不能拆開前者表明繼承的開始,後者表明繼承的結束;

子類,就是新建立的類的類名(Circle / Rectangle);

父類,要繼承的類(Shape);

//新特性:可以定義自己的特性

上面 代碼的意思:Circle/Rectangle繼承于Shape(擁有Shape的特性);

Shape(.h):

Objective-c 知識總結 -- 繼承

Shape(.m):

Objective-c 知識總結 -- 繼承
Objective-c 知識總結 -- 繼承

代碼分析:

@interface Shape : NSObject ,NSObject是 Cocoa Touch 架構的根類,所有類的父類;

{...},是聲明執行個體變量;

@implementaion 和 @end 是一對,表明對方法的實作;

觀察 Shape 的(.m)檔案可以發現,隻有 draw 方法是空的,因為我們清楚圓和方形的繪制方式是不同的(相當于等待子類自己去實作),而顔色填充和尺寸位置的表現方式是一樣的;

圓的繪制方法:

Objective-c 知識總結 -- 繼承

方形的繪制方法:

Objective-c 知識總結 -- 繼承

疑問:

  • 一個類可以繼承多個父類嗎?
  • 子類可以直接使用父類的特性?
  • 子類重新實作了繪制方法,那麼編譯器會優先使用父類的方法還是子類的呢?
  • 子類可以修改父類的特性嗎?

一些繼承的術語:

  • superclass(超類):the class you’re inheriting from.(你所繼承的類,幾何圖形)
  • Parent class(父類),superclass 的另一種說法
  • subclass (子類):the class doing the inheriting(做繼承這個行為的類,圓、方形)
  • Child class (孩子類):subclass 的另一種說法
  • override (重寫):an inherited method when you want to change its implementation(重新實作繼承而來的方法)

疑問解答:

1.Objective-C不能實作多繼承,就是說(class : class1,class2...)是不允許的;

2.父類的執行個體變量能否被子類使用,取決于執行個體變量的權限修飾符

Objective-c 知識總結 -- 繼承

預設是@protected,子類可以繼承父類的執行個體變量,但是是否可以通路,就看權限修飾符;

如果是使用屬性@property進行聲明的,就要檢視相應的屬性修飾符;

3.方法排程優先級:

When code sends a message, the Objective-C method dispatcher searches for the method in the current class. If the dispatcher doesn’t find the method in the class of the object receiving the message, it looks at the object’s superclasses.(當一個類發送消息的時候,排程器會首先從目前類中的方法清單中查找相應的消息方法,如果發現目前沒有找到,就會進入到目前類的父類中進行查找如果有就執行,如果沒有就繼續向父類查找直到找到 NSObject 類還是沒有的話,就直接報錯。)

例子:

假設給圓這個類的執行個體設定顔色如下:

[Circle setFillColor:kRedColor];

未繼承Shape

Objective-c 知識總結 -- 繼承

繼承了Shape

Objective-c 知識總結 -- 繼承

4.子類可以添加新的執行個體變量

假設建立一個新類:RoundedRectangle(圓角矩形)

首先它是幾何圖形,也是矩形(方形),但是比矩形多了一個圓角;是以它可以直接繼承幾何圖形,也可以繼承矩形;

Objective-c 知識總結 -- 繼承

建立一個 RoundedRectangle 對象執行個體,它的執行個體變量在記憶體的分布是:

Objective-c 知識總結 -- 繼承

内容分析:

“isa”:The NSObject instance variable is called isa because inheritance sets up an “is a” relationship between the subclass and the superclass; that is, a Rectangle is a Shape, and a Circleis a Shape. Code that uses a Shape can also use a Rectangle or Circle instead.(isa意指 “是一個” ,如:圓是一個幾何圖形,矩形是一個幾何圖形,表明一種包含關系);isa 是 NSObject 的執行個體變量;

“fillcolor bounds”:Shape 的執行個體變量,因為 RoundedRectangle 繼承了 Shape 是以有這兩個特性,而 fillcolor 是先于 bounds 被定義的,是以它處于上方;

“radius”:圓角是 RoundedRectangle 這個類特有的特性,因為最後被定義是以處于最後的位置;

注:每一個執行個體變量都有一個隐藏的執行個體(元類的執行個體) self

完整的圖是:

Objective-c 知識總結 -- 繼承

内容分析:

所有的執行個體變量是配置設定在一塊記憶體區域中的,而且是有序的、每一個執行個體變量的記憶體大小是已經固定的;

self 就是指向記憶體區域的首位址,隻要根據各個執行個體變量的記憶體大小進行移位就可以正常通路到每一個執行個體變量;(The compiler works its magic by using a “base plus offset” (首位址+偏移量)mechanism. Given the base address of an object—that is, the memory location of the first byte of the first instance variable—the compiler can find all other instance variables by adding an offset to that address.)

???問題:This does lead to problems over time. These offsets are now hard-coded into the program generated by the compiler. Even if Apple’s engineers wanted to add another instance variable to NSObject, they couldn’t, because that would change all of the instance variable offsets. This is called the fragile base class problem(脆弱的基類問題). Apple has fixed this problem with the new 64-bit Objective-C runtime introduced with Leopard, which uses indirection for determining ivar locations.

從兩張圖可以知道,當一個類的執行個體化後,它的執行個體對象在記憶體的位置(位址)是固定的,而且大小也是固定的,也就是 self 每一次的偏移量也是固定的;

那麼問題來了,假設我現在又想增加一個執行個體變量呢,如果是添加在 radius 的後面,記憶體的位址沒有發生變化,如果添加在 fillcolor / bounds / radius 的中間呢?那麼記憶體位址就發生了改變, self 的每一交偏移量就發生了改變;

是以在後來蘋果使用了間接的手段對 ivar (執行個體變量)進行記憶體測定,進而杜絕執行個體對象在初始化化後執行個體變量頻繁修改所引起的記憶體變化;

5.修改(重寫)父類的方法(特性)

When classes such as Circle and Rectangle implement their own draw methods, we say that they have overridden the draw method.

在文章的開始時,就有 Circle / Rectangle 兩個類,它們都是 Shape 的子類,而且它們都實作了自己的 draw 方法,而這種行為就是重寫(重新實作 draw 方法);

注:When a draw message is sent to a circle object, the method dispatcher runs the overridden method—Circle’s implementation of draw. Any implementation of draw defined by a superclass, such as Shape, is completely ignored.(由于排程優先級的存在,排程會先從子類開始到根類,而子類一旦有相應的消息方法,那麼就會直接排程而不會再進行深一層的查找(繼承鍊),會直接忽略父類的相同方法)

???問題:假設現在要把所有建立的圓執行個體對象的紅色填充修改為綠色填充?

第一種就是,每一個執行個體對象都調用 [ Circle setFillColor:kGreenColor ];直接進行設定(實際上是調用了父類的顔色填充方法,因為父類的顔色填充方法沒有顔色判斷功能,隻是單純的顔色填充,是以導緻每一個執行個體對象都要自己去設定顔色,而且 Circlr 還無法保證設定是否符合要求);

第二種就是, Circle 類自己寫一個設定顔色的方法,隻要不是綠色的都改成綠色,再進行顔色填充;(重寫 父類方法)

看代碼:

Objective-c 知識總結 -- 繼承

代碼分析:

"super setFillColor":這句代碼就是使用父類的填充顔色方法;當然自己重新寫也可以;

重寫的方法(setFillColor:)的排程過程:

Objective-c 知識總結 -- 繼承
注:如果重寫了父類的方法,建議還是調用 [ super setFillColor:c ];這樣可以保證父類做完它應該做的事,避免不必要的錯誤。