iOS 面向對象與類

来源:https://www.cnblogs.com/hubert-style/p/18036401
-Advertisement-
Play Games

至於未來會怎樣,要走下去才知道反正路還很長,天總會亮。 1. 面向對象 1.1 什麼是面向對象(OOP) 面向對象 Object Oriented Programming。在軟體開發中,我們雖然用的是面向對象的語言,但我相信絕大多數入門或者工作經驗不長的同學敲出來的代碼依然是大段的面向過程的思想,我 ...


至於未來會怎樣,要走下去才知道反正路還很長,天總會亮。

1. 面向對象

1.1 什麼是面向對象(OOP)

面向對象 Object Oriented Programming。在軟體開發中,我們雖然用的是面向對象的語言,但我相信絕大多數入門或者工作經驗不長的同學敲出來的代碼依然是大段的面向過程的思想,我們只是把面向對象來當做 OC 語言的一個特性而已,具體是什麼估計自己也說不明白到底是什麼。那麼到底該怎麼去理解面向對象編程呢?

面向對象是一種程式設計的範型,同時也是一種程式開發的方法。面向對象是將現實世界中的事物抽象成對象,現實世界中的關係抽象成類、繼承,幫助人們實現對現實世界的抽象與數字建模,用更利於人的理解方式,對複雜的系統進行分析、設計與編程。

現代的程式開發幾乎都是以面向對象為基礎。而在面向對象廣泛流行之前,軟體行業中使用最廣泛的設計模式是面向過程方式。面向過程的操作是以程式的基本功能實現為主,開發過程中只針對問題本身的實現,並沒有很好的模塊化設計,所以在代碼維護的時候較為麻煩。而面向對象,採用的更多的是進行子模塊化的設計,每一個模塊都需要單獨的存在,並且可以被重覆利用。所以面向對象開發更像是一個具備標準模式的編程開發,每一個單獨設計的模塊都可以單獨存在,需要時候只要通過簡單的組裝就可以使用。但是,面向對象的底層還是面向過程,這兩種程式設計思想是可以互相依存的,也是貫穿我們整個程式開發周期的思想指導,我們在設計程式、開發程式中應該牢記“面向對象”、“面向過程”。面向對象中類和對象是最基本、最重要的組成單元。下麵會講到該怎麼理解類和對象。

1.2 面向對象的三大特征

繼承:繼承是子類繼承父類非私有數據結構和方法的機制,是類之間的一種關係。它是面向對象語言特有的特征,面向過程的語言不具有繼承特性,而 OC 是單繼承。繼承提供了類的規範等級結構,使公共的特性能夠共用,提高的軟體的重用性。類的繼承性使所建的軟體具有開發性、可擴充性,簡化了對象、類的創建工作量,提高了代碼的重用性。

封裝:在面向對象的語言中,對象、類、方法都是一種封裝,對象是封裝的最基本單位。

類的封裝體現在每個類都有 .h 和 .m 兩個文件,將定義與實現分開,.h 聲明(用戶可見的外部介面),.m實現(用戶不可見的內部實現)。

方法的封裝,是最常見的,每個方法中封裝了一個小的功能,這是單一職責的很好體現,第三方框架和代碼也是一種封裝。

封裝使程式的結構更加清晰,將實現的信息封裝隱藏,用的時候直接調用封裝好的方法或類,提高效率。此外,可以減少程式間的相互依賴。

多態:不同對象以自己的方式響應相同的消息的能力叫做多態。假設有一個類包含一個方法,由這個類派生出兩個子類或者兩個對象,其各自實現各自的方法,也就是不同的對象以自己的方式響應了相同的消息。多態增強了軟體的靈活性和重用性。

1.3 對象的理解

什麼是對象?世界萬物皆對象,所有看到的看不到的都是對象,把對象引入到編程中,那就是面向對象編程(Object Oriented Programming,OOP,面向對象程式設計),在百度百科中有關於 OOP 詳細的介紹。簡單來說就是它將對象作為控制項的基本元素, 利用對象和對象之間的相互作用來設計程式,說白了,一款軟體的運行就是控制項之間的相互作用。說了那麼多,那到底啥是對象,對象是你,是我,是萬物,我認為在程式中能 alloc 出來的都是對象。就拿人來說,你是人,我也是人,為啥咱倆不一樣,要是放到程式里來說,是因為 alloc 分配的記憶體地址不一樣。人有頭髮、眼睛、手鼻等,這些在 OC 裡面稱之為屬性,人能跑、跳、投,等動作這些在 OC 裡面稱之為方法。其實,你在做項目的時候已經在用對象這個概念了,只不過你不知道罷了。舉個簡單的例子:點擊 tableview 的 cell 讓 cell 裡面的控制項變換顏色,我相信大家都能實現這個效果,是怎麼做的呢?肯定是定位點擊的哪個 cell,拿到當前 cell,那這個 cell 不就是對象,找到 cell 中的控制項,控制項不就是 cell 的屬性嗎?顏色也是控制項的屬性,控制項即是對象也是屬性,控制項間的相互作用完成了這個功能,說到底也就是操作的對象。

1.4 類的理解

面向對象編程中,具體的事物是對象,將具有相同或相似性質的對象的屬性或方法抽象出來便是類,類是對象的抽象化,對象便是類的具體實現。估計這不好理解,再拿網路請求數據來講,數據都有相同或相似的數據,將這段數據的相同點抽離出來便是我們所建的 model 類,比如 person 類,屬性有:name、height、weight 等,他們具有相同的屬性將屬性他們通過一個方法將數據進行轉化,那這個方法不就是封裝嗎?一般都是在 .h 文件中聲明方法,在 .m 中去實現,方法一般都是隱藏內部實現,預留一個穩定外部介面。面向對象程式設計中的方法可分為兩種,一為上述的實體(對象)方法,二為類方法,主要的差異在於實體方法需要有一對象去引發,而類別方法可以由類別名稱調用。

2. Objective-C 類

2.1 類概念

如同所有其他的面向對象語言,類是 Objective-C 用來封裝數據,以及操作數據的行為的基礎結構。對象就是類的運行期間實例,它包含了類聲明的實例變數自己的記憶體拷貝,以及類成員的指針。Objective-C 的類規格說明包含了兩個部分:定義(interface)與實現(implementation)。定義(interface)部分包含了類聲明和實例(成員)變數的定義,以及類相關的方法。實現(implementation)部分包含了方法的實現,以及定義私有(private)變數及方法。類的定義文件遵循 C 語言之慣例以 .h 為尾碼,實現文件以 .m 為尾碼。

2.2 類定義(Interface)

2.2.1 定義

定義文件遵循 C 語言之慣例以 .h 為尾碼。定義部分,清楚定義了類的名稱、數據成員和方法。 以關鍵字 @interface 作為開始,@end 作為結束。

下麵定義一個叫做 XBCar 的類的語法,這個類繼承自 NSObject 基礎類。類名之後的(用冒號分隔的)是父類的名字。類的實例(成員)變數聲明在被大括弧包含的代碼塊中。實例變數塊後面就是類聲明的方法的列表。每個實例變數和方法聲明都以分號結尾。

@interface XBCar : NSObject {
    // 成員變數
    float maxVelocity;
    double money;
  	// 實例變數
    NSString *name;
}

+ (void)class_method; // 類方法
- (void)instance_method1; // 實例方法
- (void)instance_method2:(int)p1;

@end

​ 類定義具體內容包括:

  1. 類的聲明,類聲明總是由 @interface 編譯選項開始,由 @end 編譯選項結束;
  2. 實例(成員)變數的定義(用大括弧包含的);
  3. 類相關方法的定義:類方法和實例方法;

2.2.2 實例變數和成員變數的理解

實例(Instance)是針對類(class)而言的。實例是指類的聲明; 由此推理,實例變數(Instance Variable) 是指由類聲明的對象。成員變數就是基本類型聲明的變數。

嚴格來說 @interface{} 里定義的實例(成員)變數,它是這個類內部真正的全局變數。然而這個 instance variable 是不對外公開的,因此我們還需要一個對外公開的東西來調用,就是屬性,關鍵字 @property。它其實是告訴大家,我這個類里,有一個變數的 seter/geter 方法。比如,@property NSString* string;就是說,本類里有一個 string/setString 供你們調用。屬性具體的使用後面會講到,這裡就不展開了。

2.3 類實現(Implementation)

實現文件以 .m 為尾碼。實現區塊則包含了公開方法的實現,以及定義私有(private)變數及方法。 以關鍵字 @implementation 作為區塊起頭,@end結尾。

@implementation XBCar {
    int private; // 私有成員變數
}

+ (void)class_method {
    
}

- (void)instance_method1 {
    
}
- (void)instance_method2:(int)p1{
    
}

值得一提的是不只 Interface 區塊可定義實例(成員)變數,Implementation 區塊也可以定義實例(成員)變數,兩者的差別在於訪問許可權的不同,Interface 區塊內的實體變數預設許可權為 protected,implementation 區塊的實例(成員)變數則預設為 private,故在 implementation 區塊定義私有成員更匹配面向對象之封裝原則,因為如此類別之私有信息就不需曝露於公開 interface(.h文件)中。在程式開發中我們會發現在實現文件(.m)中,有如下這樣的代碼:

@interface XBCar () // Class Extension

@end

那麼問題來了,為什麼 .h 文件和 .m 文件里各有1個 @interface 它們分別有什麼用呢?

定義文件(.h 文件)裡面的 @interface,不用說,是典型的頭文件,用來定義(聲明)類的。

實現文件(.m 文件)裡面的 @interface,在 OC 里叫作 Class Extension,是 .h文件中 @interface 聲明類的補充擴展。但是 .m 文件里的 @interface,對外是不開放的,只在 .m 文件里可見。

2.4 創建對象

Objective-C 創建對象需通過 alloc 以及 init 兩個消息。alloc 的作用是分配記憶體,init 則是初始化對象。 init 與 alloc 都是定義在 NSObject 里的方法,父對象收到這兩個消息並做出正確回應後,新對象才創建完畢。

XBCar *car = [[XBCar alloc] init];

Objective-C 還可以通過 new 關鍵字來創建對象。

XBCar *blueCar = [XBCar new];

那麼 alloc/init 和 new 有什麼區別呢?首先功能上他兩幾乎是一致的,都是分配記憶體並完成初始化。差別在於,採用 new 的方式創建對象只能採用預設的 init 方法完成初始化,而通過 alloc 的方式可以採用其他定製的初始化方法完成初始化。另外 alloc 分配記憶體的時候使用了 zone。它是給對象分配記憶體的時候,把關聯的對象分配到一個相鄰的記憶體區域內,以便於調用時消耗很少的代價,提升了程式處理速度。

2.5 方法聲明

1. 方法聲明語法.png

當你想調用一個方法,你傳遞消息到對應的對象。這裡消息就是方法標識符,以及傳遞給方法的參數信息。發送給對象的所有消息都會動態分發,這樣有利於實現 Objective-C 類的多態行為。也就是說,如果子類定義了跟父類的具有相同標識符的方法,那麼子類首先收到消息,然後可以有選擇的把消息轉發(也可以不轉發)給他的父類。

消息被中括弧( [ 和 ] )包括。中括弧中間,接收消息的對象在左邊,消息(包括消息需要的任何參數)在右邊。例如,給 myArray 變數傳遞消息insertObject:atIndex: 消息,你需要使用如下的語法:

[myArray insertObject:anObj atIndex:0];

為了避免聲明過多的本地變數保存臨時結果,Objective-C 允許你使用嵌套消息。每個嵌套消息的返回值可以作為其他消息的參數或者目標。例如,你可以用任何獲取這種值的消息來代替前面例子裡面的任何變數。所以,如果你有另外一個對象叫做 myAppObject 擁有方法,可以訪問數組對象,以及插入對象到一個數組,你可以把前面的例子寫成如下的樣子:

[[myAppObject getArray] insertObject:[myAppObject getObjectToInsert] atIndex:0];

雖然前面的例子都是傳遞消息給某個類的實例,但是你也可以傳遞消息給類本身。當給類發消息,你指定的方法必須被定義為類方法,而不是實例方法。你可以認為類方法跟 C++ 類裡面的靜態成員有點像(但是不是完全相同的)。

類方法的典型用途是用做創建新的類實例的工廠方法,或者是訪問類相關的共用信息的途徑。類方法聲明的語法跟實例方法的幾乎完全一樣,只有一點小差別。與實例方法使用減號作為方法類型標識符不同,類方法使用加號( + )。

下麵的例子演示了一個類方法如何作為類的工廠方法。在這裡,arrayWithCapacity 是 NSMutableArray 類的類方法,為類的新實例分配內容並初始化,然後返回給你。

NSMutableArray* myArray = nil; // nil 基本上等同於 NULL
// 創建一個新的數組,並把它賦值給 myArray 變數
myArray = [NSMutableArray arrayWithCapacity:0];

2.6 屬性

屬性(property)用於封裝對象中的數據,iOS 開發中最常用最方便的變數聲明方式,允許我們用點語法來訪問對象的實例變數。

2.6.1 屬性的實質是什麼

@property 是聲明屬性的語法。可以快速為實例變數創建存取器。允許我們通過點語法使用存取器。

	屬性(@property) = 實例變數定義 + setter方法 + getter方法。

當我們聲明一個屬性屬性 name 的時候,在編譯階段,編譯器會自動給對象添加一個實例變數 name 和它的存取方法 - (void)setName:(NSString *)name- (NSString *)name。這個過程由於是在編譯階段自動合成的,所以我們在編輯階段是看不到的。添加實例變數是有一個前提的,就是對象還沒有同名的成員變數,就是如果已經有 _name 了,就不再添加了。我們可以用運行時驗證一下:

- (void)propertyTest {
    unsigned int count = 0;
    Ivar *varList = class_copyIvarList([self class], &count);
    for (unsigned int i = 0; i < count; i++) {
        const char *varName = ivar_getName(varList[i]);
        printf("成員變數----%s\n", varName);
    }
    
    Method *methodList = class_copyMethodList([self class], &count);
    for (unsigned int i = 0; i < count; i++) {
        SEL methodName = method_getName(methodList[i]);
        NSLog(@"方法----%@",NSStringFromSelector(methodName));
    }
}

列印的日誌:
成員變數----_name
2021-07-20 16:55:59.492166+0800 001 - Class[772:380998] 方法----propertyTest
2021-07-20 16:55:59.492321+0800 001 - Class[772:380998] 方法----name
2021-07-20 16:55:59.492551+0800 001 - Class[772:380998] 方法----setName:
2021-07-20 16:55:59.492664+0800 001 - Class[772:380998] 方法----viewDidLoad

2.6.2 @synthesize

@synthesize 屬於編譯器指令,用來告訴編譯器要做什麼。

@property (strong,nonatomic) NSString *name;
@synthesize name = myname;
@dynamic name;

@synthesize 關鍵字主要有兩個作用,在 ARC 下已經很少用了。

  • 在 MRC 下,@synthesize name,用在實現文件中告訴編譯器自動實現實例變數 name 的存取(訪問器 getter/setter)方法。不過在 ARC 下就不必了,無論你是否 @synthesize name,編譯器都會自動合成 name 的存取方法。
  • 如果你聲明的屬性是 name,系統自動給你添加的成員變數是 _name,如果你對這個變數名字不滿,可以這樣 @synthesize name = myname;自己給個名字。這樣系統給添加的成員變數就是 myname,而不是_name,但是變數的存取方法沒有變化。不過我建議最好不要這麼辦,因為都按照約定成俗的方式來命名變數,代碼的可讀性較高,大家都理解,所以我建議大家最好不要用這個關鍵字。

2.6.3 @dynamic

@dynamic 關鍵字主要是告訴編譯器不用為我們自動合成變數的存取方法, 我們會自己實現。即使我們沒有實現,編譯器也不會警告,因為它相信在運行階段會實現。如果我們沒有實現還調用了,就會報這個錯誤 '-[ViewController setName:]: unrecognized selector sent to instance 0x10040af10'

2.6.4 屬性的特性(關鍵字)

1)屬性特性概述

原子性:

  • atomic(預設):atomic 意為操作是原子的,意味著只有一個線程訪問實例變數(生成的 setter 和 getter 方法是一個原子操作)。atomic 是線程安全的,至少在當前的存取器上是安全的。它是一個預設的特性,但是很少使用,因為比較影響效率;
  • nonatomic:意為操作是非原子的,可以被多個線程訪問。它的效率比atomic 快。但不能保證在多線程環境下的安全性,開發中常用,所以我們在寫代碼的時候要儘量避非免線程安全的代碼出現;

讀寫許可權(存取器控制):

  • readwrite(預設):readwrite 是預設值,表示該屬性同時擁有 getter 和 setter;
  • readonly: readonly 表示只有 getter 沒有 setter;
  • 有時候為了語意更明確可能需要自定義訪問器的名字;

記憶體管理語義:

  • retain(MRC)/strong(ARC)

    記憶體管理語義:強引用

    系統預設:ARC 情況下是預設

    作用:只能修飾對象。對象會更改引用計數,那麼每次被引用,引用計數都會+1,釋放後都會-1;即使對象本身被釋放了,只要還有對象在引用,就會持有不會造成什麼問題;只有當引用計數為0時,就被dealloc析構函數回收記憶體了。對應變數許可權修飾符為__strong。

  • weak

    記憶體管理語義:弱引用

    系統預設:否

    作用:只能修飾對象。不改變對象的引用計數,當其指向對象被銷毀時,它會自動置為nil;變數許可權修飾符為__weak,常用於容易造成迴圈引用的地方不改變對象的引用計數。

  • assign

    記憶體管理語義:賦值

    系統預設:MRC 情況下是

    作用:主要用於修飾值類型,如 int、float、double 和 CGPoint、CGFloat 等表示單純的複製。還包括不存在所有權關係的對象,比如常見的 delegate。值類型變數的記憶體由編譯器自動管理;修飾對象屬性時,其指向一個對象後,不改變該對象的引用計數。即只引用已創建的對象,而不持有對象;assign 修飾的屬性不持有對象,當其指向對象在別處釋放後,該指針變為懸掛指針也叫野指針。對應的變數許可權修飾符為 __unsafe_unretained

  • unsafe_unretained

    用來修飾屬性的時候,和 assing 修飾對象的時候是一模一樣的。為屬性設置新值的時候。唯一的區別就是當屬性所指的對象釋放的時候,屬性不會被置為 nil,這就會產生野指針,所以是不安全的。對應的變數許可權修飾符為 __unsafe_unretained

  • copy

    記憶體管理語義:複製/拷貝

    系統預設:否

    作用:只能修飾對象。一般情況下屬性會持有該對象的一份拷貝,建立一個索引計數為1的新對象,然後釋放舊對象。copy 一般用在修飾有可變對應類型的不可變對象上,如 NSString, NSArray, NSDictionary

2)strong 和 copy 修飾屬性的區別

不可變字元串:

@property (nonatomic,strong)NSString* strongedString;
@property (nonatomic,copy)NSString* copyedString;

// 當tempString為不可變字元串時候
NSString *tempString = @"Text";
self.strongedString = tempString;
self.copyedString = tempString;
NSLog(@"tempString : %@ %p %p",tempString, tempString, &tempString);
NSLog(@"strongedString : %@ %p %p",_strongedString, _strongedString, &_strongedString);
NSLog(@"copyedString : %@ %p %p",_copyedString, _copyedString, &_copyedString);

tempString = @"Change OK";
NSLog(@"tempString : %@ %p %p",tempString, tempString, &tempString);
NSLog(@"strongedString : %@ %p %p",_strongedString, _strongedString, &_strongedString);
NSLog(@"copyedString : %@ %p %p",_copyedString, _copyedString, &_copyedString);

// 列印結果:
BaseGrammar[40394:185339] tempString : Text 0x10d858e68 0x7ffee23adbf8
BaseGrammar[40394:185339] strongedString : Text 0x10d858e68 0x7ff634530440
BaseGrammar[40394:185339] copyedString : Text 0x10d858e68 0x7ff634530448
// 改變後:
BaseGrammar[40394:185339] tempString : Change OK 0x10d858ee8 0x7ffee23adbf8
BaseGrammar[40394:185339] strongedString : Text 0x10d858e68 0x7ff634530440
BaseGrammar[40394:185339] copyedString : Text 0x10d858e68 0x7ff634530448

結論:當 tempString 為不可變字元串時

  • 不管是 strong 還是 copy 屬性的對象,其指向的地址都是同一個,即為tempString 指向的地址。
  • 如果我們換作 MRC 環境,列印 tempString 的引用計數的話,會看到其引用計數值是3,即 strong 操作和 copy 操作都使原字元串對象的引用計數值加了1。
  • 當 tempString 的值發生改變時,兩個對象的值也保持原來的值。

可變字元串:

// 可變字元串
NSMutableString *tempString = [NSMutableString stringWithFormat:@"Text"];
self.strongedString = tempString;
self.copyedString = tempString;
NSLog(@"tempString : %@ %p %p",tempString, tempString, &tempString);
NSLog(@"strongedString : %@ %p %p",_strongedString, _strongedString, &_strongedString);
NSLog(@"copyedString : %@ %p %p",_copyedString, _copyedString, &_copyedString);

// 改變tempString的值
[tempString appendString:@"Change OK"];
NSLog(@"tempString : %@ %p %p",tempString, tempString, &tempString);
NSLog(@"strongedString : %@ %p %p",_strongedString, _strongedString, &_strongedString);
NSLog(@"copyedString : %@ %p %p",_copyedString, _copyedString, &_copyedString);

// 列印結果:
BaseGrammar[42008:192582] tempString : Text 0x60000279c960 0x7ffee5983bf8
BaseGrammar[42008:192582] strongedString : Text 0x60000279c960 0x7fe20fe14380
BaseGrammar[42008:192582] copyedString : Text 0xb96aa7441ad7466e 0x7fe20fe14388
// 改變後:
BaseGrammar[42008:192582] tempString : TextChange OK 0x60000279c960 0x7ffee5983bf8
BaseGrammar[42008:192582] strongedString : TextChange OK 0x60000279c960 0x7fe20fe14380
BaseGrammar[42008:192582] copyedString : Text 0xb96aa7441ad7466e 0x7fe20fe14388
  

結論:當 tempString 為可變字元串時

  • 此時 copy 屬性字元串已不再指向 tempString 字元串對象,而是深拷貝了 tempString 字元串,並讓 copyedString 對象指向這個字元串。

  • strongString 與 tempString 是指向同一對象,所以 strongString 的值也會跟隨著改變(需要註意的是,此時 strongString 的類型實際上是NSMutableString,而不是 NSString);而 copyedString 是指向另一個對象的,所以並不會改變。

  • 在 MRC 環境下,列印兩者的引用計數,可以看到 tempString 對象的引用計數是2,而 copyedString 對象的引用計數是1。

由上面分析我們能得到一個結論:copy 對於不可變對象是淺拷貝,對於可變對象是深拷貝。在聲明 NSString 屬性時,到底是選擇 strong 還是 copy,可以根據實際情況來定。不過,一般我們將對象聲明為 NSString 時,都不希望它改變,所以大多數情況下,我們建議用 copy,以免因可變字元串的修改導致的一些非預期問題。即對於可變字元串以得到新的記憶體分配,而不只是原來的引用。

3)assign 和 weak 的區別

assign 的特點:

  • 修飾基本數據類型和原子類類型。
  • 修飾對象類型時,不改變其引用計數。
  • 會產生懸掛指針(野指針),用 assign 修飾的對象被釋放之後,assign指針仍指向原對象的地址記憶體,繼續使用 assign 指針訪問原對象,會產生懸掛指針導致記憶體泄漏。

weak 的特點:

  • 只能修飾對象。
  • 不改變修飾對象的引用計數。
  • 所指對象在被釋放之後會自動置為 nil。
4)深淺拷貝

淺拷貝:就是對記憶體地址的複製,讓目標對象指針和原對象指針指向同一片記憶體空間。淺拷貝增加了被拷貝對象的引用計數,並沒有產生新的記憶體分配空間。

深拷貝:就是讓對象指針和原對象指針指向兩片內容相同的記憶體空間。深拷貝沒有增加被拷貝對象的引用計數,產生新的記憶體分配控制項。

總結:

copy:對於可變對象為深拷貝,對於不可變對象為淺拷貝。拷貝出來的是不可變對象。

mutableCopy:始終是深拷貝。拷貝出來的是可變對象。

總的來說在 Objective-C 裡面只有一種情況是淺拷貝,那就是不可變對象的copy,其它的都是深拷貝(包括不可變對象mutableCopy、可變對象的的 copy 和mutableCopy)。

3. 引用關鍵字

關鍵字說明:

  • #import 是 Objective-C 導入頭文件的關鍵字。確保一個頭文件只能被導入一次,這使你在遞歸包含中不會出現問題,所以 #import 比起#include 的好處是不會引起交叉編譯。

    • import<> 代表導入系統自帶的框架;

    • import"" 代表導入我們自己創建的頭文件;

  • #include 是C/C++導入頭文件的關鍵字。

  • @class 一般用於聲明某個字元串作為類名使用,它只是聲明瞭一個類名,沒有導入 .h 文件中的內容,不會引起交叉編譯問題。

import 和 @class 的區別總結:

  • Import 會包含這個類的所有信息,包括實體變數和方法(.h 文件中),而 @class 只是告訴編譯器,其後邊的聲明的名稱是類的名稱,至於類是如何定義的,後邊再說,這邊不關心。或者說 @class 創建了一個前向引用,就是在告訴編譯器,“相信我,以後你會知道這個類到底是什麼,但是現在,你只需要知道這些就好”,如果有迴圈依賴關係;
  • 在頭文件中,一般只需要知道被引用的類的名稱就可以了,不需要知道其內部的實體變數和方法,所以在頭文件中一般使用 @class 來聲明這個名稱是類的名稱,而在實現類裡邊,因為會用到這個引用類的內部的實體變數和方法,所以需要使用 #import 來包含這個被引用類的頭文件;

使用總結:

  1. 如果不是 C/C++,儘量使用 #import;

  2. 能在實現文件中 #import,就不在頭文件中 #import;

  3. 能在頭文件中 @class 實現文件中 #import,就不再頭文件中 #import;


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 在資料庫設計中,存儲過程、觸發器、游標、視圖、自定義函數、欄位類型、欄位可空、統計欄位、邏輯刪除以及許可權系統和無限級類別設計都是重要的概念。下麵我將逐一解釋這些概念,並提供相關的設計建議。 存儲過程 (Stored Procedure) 定義:存儲過程是一組為了完成特定功能的SQL語句集,經編譯後存 ...
  • 要實現兩個資料庫之間的實時同步,需要給Oracle設置參數 ALTER DATABASE ADD SUPPLEMENTAL LOG DATA; -- 執行了12小時,等待資料庫中的其它事務都提交以後才執行完成 ALTER DATABASE ADD SUPPLEMENTAL LOG DATA (PRI ...
  • 北京白鯨開源科技有限公司(以下簡稱“白鯨開源”)今日宣佈,其旗艦產品WhaleStudio套件已與瀚高基礎軟體股份有限公司(以下簡稱“瀚高軟體”)旗下的IvorySQL資料庫管理系統V3.0完成深度相容性認證。此次合作標志著兩家領軍企業在數據管理領域的緊密聯合,為用戶提供更加穩定、高效的數據處理解決 ...
  • 本文分享自華為雲社區《GaussDB(DWS)存儲引擎:從CU入手優化HStore表》,作者: yd_261437590。 1. 前言 適用版本:【8.2.1(及以上)】 HStore同時擁有處理傳統TP場景的事務能力和強大的數據分析能力,但是強大的數據分析能力很可能被小CU問題給破壞,另外,將多個 ...
  • 序言 開年的第一篇文章,今天分享的是SwiftUI,SwiftUI出來好幾年,之前一直沒學習,所以現在才開始;如果大家還留在 iOS 開發,這們語言也是一個趨勢; 目前待業中.... 不得不說已逝的2023年,大家開始都抱著一解封,經濟都會向上轉好,可是現實不是我們想象那樣;目前我也在學習 Swif ...
  • 方案一 :可以用 if來替代 如下 原因:在Android Studio中使用JDK17以上版本,會出現switch語句報錯"Constant expression required"的問題,這是因為在JDK17中switch語句的條件表達式支持使用枚舉類型,而這個特性還沒有被支持。 方案2:換JD ...
  • 一、混淆的意義 混淆代碼並不是讓代碼無法被反編譯,而是將代碼中的類、方法、變數等信息進行重命名,把它們改成一些毫無意義的名字,同時也可以移除未被使用的類、方法、變數等。 所以直觀的看,通過混淆可以提高程式的安全性,增加逆向工程的難度,同時也有效縮減了apk的體積。總結如下: 1、將項目中的類、方法、 ...
  • 有時候下載sdk的時候報各種錯誤導致無法下載,如圖 那麼可以離線下載,到瀏覽器或IDM中下載圖中提示的url鏈接,將第一個下載的包即sources的解壓後放到SDK目錄下的sources目錄,並重命名為對應的api版本如android-25 第二個鏈接即platform的那個則解壓後放到SDK目錄中 ...
一周排行
    -Advertisement-
    Play Games
  • 基於.NET Framework 4.8 開發的深度學習模型部署測試平臺,提供了YOLO框架的主流系列模型,包括YOLOv8~v9,以及其系列下的Det、Seg、Pose、Obb、Cls等應用場景,同時支持圖像與視頻檢測。模型部署引擎使用的是OpenVINO™、TensorRT、ONNX runti... ...
  • 十年沉澱,重啟開發之路 十年前,我沉浸在開發的海洋中,每日與代碼為伍,與演算法共舞。那時的我,滿懷激情,對技術的追求近乎狂熱。然而,隨著歲月的流逝,生活的忙碌逐漸占據了我的大部分時間,讓我無暇顧及技術的沉澱與積累。 十年間,我經歷了職業生涯的起伏和變遷。從初出茅廬的菜鳥到逐漸嶄露頭角的開發者,我見證了 ...
  • C# 是一種簡單、現代、面向對象和類型安全的編程語言。.NET 是由 Microsoft 創建的開發平臺,平臺包含了語言規範、工具、運行,支持開發各種應用,如Web、移動、桌面等。.NET框架有多個實現,如.NET Framework、.NET Core(及後續的.NET 5+版本),以及社區版本M... ...
  • 前言 本文介紹瞭如何使用三菱提供的MX Component插件實現對三菱PLC軟元件數據的讀寫,記錄了使用電腦模擬,模擬PLC,直至完成測試的詳細流程,並重點介紹了在這個過程中的易錯點,供參考。 用到的軟體: 1. PLC開發編程環境GX Works2,GX Works2下載鏈接 https:// ...
  • 前言 整理這個官方翻譯的系列,原因是網上大部分的 tomcat 版本比較舊,此版本為 v11 最新的版本。 開源項目 從零手寫實現 tomcat minicat 別稱【嗅虎】心有猛虎,輕嗅薔薇。 系列文章 web server apache tomcat11-01-官方文檔入門介紹 web serv ...
  • 1、jQuery介紹 jQuery是什麼 jQuery是一個快速、簡潔的JavaScript框架,是繼Prototype之後又一個優秀的JavaScript代碼庫(或JavaScript框架)。jQuery設計的宗旨是“write Less,Do More”,即倡導寫更少的代碼,做更多的事情。它封裝 ...
  • 前言 之前的文章把js引擎(aardio封裝庫) 微軟開源的js引擎(ChakraCore))寫好了,這篇文章整點js代碼來測一下bug。測試網站:https://fanyi.youdao.com/index.html#/ 逆向思路 逆向思路可以看有道翻譯js逆向(MD5加密,AES加密)附完整源碼 ...
  • 引言 現代的操作系統(Windows,Linux,Mac OS)等都可以同時打開多個軟體(任務),這些軟體在我們的感知上是同時運行的,例如我們可以一邊瀏覽網頁,一邊聽音樂。而CPU執行代碼同一時間只能執行一條,但即使我們的電腦是單核CPU也可以同時運行多個任務,如下圖所示,這是因為我們的 CPU 的 ...
  • 掌握使用Python進行文本英文統計的基本方法,並瞭解如何進一步優化和擴展這些方法,以應對更複雜的文本分析任務。 ...
  • 背景 Redis多數據源常見的場景: 分區數據處理:當數據量增長時,單個Redis實例可能無法處理所有的數據。通過使用多個Redis數據源,可以將數據分區存儲在不同的實例中,使得數據處理更加高效。 多租戶應用程式:對於多租戶應用程式,每個租戶可以擁有自己的Redis數據源,以確保數據隔離和安全性。 ...