include程序員
include <> :用於對系統文件的引用,編譯器會在系統文件目錄下去查找該文件。web
#include "xx.h":用於對用戶自定義的文件的引用,編譯器首先會去用戶目錄下查找,而後去安裝目錄,最後去系統目錄查找。
注:使用include要注意重複引用的問題:服務器
class A,class B都引用了class C,class D若引用class A與class B,就會報重複引用的錯誤。網絡
功能與include基本相同,不過它避免了重複引用的問題。因此在OC中咱們基本用的都是import。session
@class
@class就是告訴編譯器有這個類存在,可是類是如何實現的不用告訴編譯器.若.m文件用到了這個類,仍是要在.m文件彙總import這個類的。
既然這樣,爲何不直接在頭文件中import呢,舉個例子:
class A引用了class B,class B引用了class C…. , class A,B,C…的頭文件又import了不少文件,那麼 import了A的話,編譯器就須要編譯大量的文件,編譯時間就會增長。併發
難道頭文件中都是用@class嗎?固然不是,有時也是須要#import的,那麼何時該用什麼呢?
(1)通常若是有繼承關係的用#import,如B是A的子類那麼在B中聲明A時用#import;異步
(2) 另外就是若是有循環依賴關係,如:A->B,B->A這樣相互依賴時,若是在兩個文件的頭文件中用#import分別聲明對方,那麼就會出現頭文件循環利用的錯誤,這時在頭文件中用@class聲明就不會出錯;async
(3)還有就是自定義代理的時候,若是在頭文件中想聲明代理的話如@interface SecondViewController:UIViewController時應用#import否則的話會出錯誤,注意XXXXDelegate是自定義的。編輯器
區別:模塊化
當咱們在代碼中使用兩次#include的時候會報錯:由於#include至關於拷貝頭文件中的聲明內容,因此會報重複定義的錯誤
可是使用兩次#import的話,不會報錯,因此他能夠解決重複導入的問題,他會作一次判斷,若是已經導入一次就不導入了
2、#import與@class的區別
1.import會包含這個類的全部信息,包括實體變量和方法,而@class只是告訴編譯器,其後面聲明的名稱是類的名稱,至於這些類是如何定義的,暫時不用考慮,後面會再告訴你。
2.在頭文件中, 通常只須要知道被引用的類的名稱就能夠了。 不須要知道其內部的實體變量和方法,因此在頭文件中通常使用@class來聲明這個名稱是類的名稱。 而在實現類裏面,由於會用到這個引用類的內部的實體變量和方法,因此須要使用#import來包含這個被引用類的頭文件。
3.在編譯效率方面考慮,若是你有100個頭文件都#import了同一個頭文件,或者這些文件是依次引用的,如A–>B, B–>C, C–>D這樣的引用關係。當最開始的那個頭文件有變化的話,後面全部引用它的類都須要從新編譯,若是你的類有不少的話,這將耗費大量的時間。而是用@class則不會。
4.若是有循環依賴關係,如:A–>B, B–>A這樣的相互依賴關係,若是使用#import來相互包含,那麼就會出現編譯錯誤,若是使用@class在兩個類的頭文件中相互聲明,則不會有編譯錯誤出現。
因此,通常來講,@class是放在interface中的,只是爲了在interface中引用這個類,把這個類做爲一個類型來用的。 在實現這個接口的實現類中,若是須要引用這個類的實體變量或者方法之類的,仍是須要import在@class中聲明的類進來.
舉個例子說明一下。
在ClassA.h中
若是
只是 ClassB 那就沒有include ClassB.h。僅須要在須要用到ClassB的.m文件中 #import ClassB.h
那麼何時能夠用呢?
若是ClassA.h中僅須要聲明一個ClassB的指針,那麼就能夠在ClassA.h中聲明
@ClassB
…
ClassB *pointer;
假設,有兩個類:ClassA和ClassB,兩個之間相互使用到,即構成了circular dependency(循環依賴)。若是在頭文件裏面只用#import把對方的頭文件包含進來(構成circular inclusions,循環包含),則編譯器會報錯:
Expected specifier-qualifier-list before ‘ClassA’
或者
Expected specifier-qualifier-list before ‘ClassB’
爲了不循環包含,在ClassA.h文件裏面用@class classB把classB包含進來,一樣,在ClassB.h文件裏面用@class ClassA把ClassA包含進來。@class指令只是告訴編譯器,這是個類,保留個空間來存放指針就能夠了。
接下來,極可能在ClassA.m和ClassB.m中會有訪問包含進來對象的成員的狀況,這時必須讓編譯器知道更多信息,好比那個類有些什麼方法能夠調用,就必須用#import,再次把用到的類包含進來,告訴編譯器所須要的額外信息。
不然,編譯器會警告:
warning: receiver ‘ClassA’ is a forward class and corresponding @interface may not exist
還有另外一種狀況,使用有Categories的類,要在.h頭文件裏用#import把Categories包含進來。
總之,使用原則是:
頭文件裏面只#import超類 消息文件裏面#import須要發消息過去的類 其餘地方就用@class轉向聲明
import會包含這個類的全部信息,包括實體變量和方法(.h文件中),而@class只是告訴編譯器,其後面聲明的名稱是類的名稱,至於這些類是如何定義的,後面會再告訴你。
在頭文件中, 通常只須要知道被引用的類的名稱就能夠了。 不須要知道其內部的實體變量和方法,因此在頭文件中通常使用@class來聲明這個名稱是類的名稱。 而在實現類裏面,由於會用到這個引用類的內部的實體變量和方法,因此須要使用#import來包含這個被引用類的頭文件。
一.通俗的說:
copy, mutableCopy
@interface A {
B *b;
}
淺拷貝只是拷貝對象自己,不會對裏面的子對象進一步拷貝
深拷貝會對子對象以及子對象的子對象進一步拷貝
二.深刻理解:
淺拷貝並不複製數據,只複製指向數據的指針,所以是多個指針指向同一份數據。 深拷貝會複製原始數據,每一個指針指向一份獨立的數據。經過下面的代碼, 能夠清楚地看出它們的區別:
struct Test{
char ptr;
};
void shallow_copy(Test &src, Test &dest){
dest.ptr = src.ptr;
}
void deep_copy(Test &src, Test &dest){
dest.ptr = (char)malloc(strlen(src.ptr) + 1);
memcpy(dest.ptr, src.ptr);
}
淺拷貝在構造和刪除對象時容易產生問題,所以使用時要十分當心。如無必要, 儘可能不用淺拷貝。當咱們要傳遞複雜結構的信息,而又不想產生另外一份數據時, 可使用淺拷貝,好比引用傳參。淺拷貝特別須要注意的就是析構時的問題, 當多個指針指向同一分內存時,刪除這些指針將致使屢次釋放同一內存而出錯。
實際狀況下是不多使用淺拷貝的,而智能指針是淺拷貝概念的加強。 好比智能指針能夠維護一個引用計數來代表指向某塊內存的指針數量, 只有當引用計數減至0時,才真正釋放內存。
大部分時候,咱們用的是深拷貝,特別是當拷貝的結構不大的時候。
1)在ARC中,在有可能出現循環引用的時候,每每要經過讓其中一端使用weak來解決,好比:delegate代理屬性
2)自身已經對它進行一次強引用,沒有必要再強引用一次,此時也會使用weak,自定義IBOutlet控件屬性通常也使用weak;固然,也可使用strong。在下文也有論述:《IBOutlet連出來的視圖屬性爲何能夠被設置成weak?》
二.不一樣點:
1)weak 此特質代表該屬性定義了一種「非擁有關係」 (nonowning relationship)。爲這種屬性設置新值時,設置方法既不保留新值,也不釋放舊值。此特質同assign相似, 然而在屬性所指的對象遭到摧毀時,屬性值也會清空(nil out)。 而 assign 的「設置方法」只會執行鍼對「純量類型」 (scalar type,例如 CGFloat 或 NSlnteger 等)的簡單賦值操做。
2)assigin 能夠用非OC對象,而weak必須用於OC對象
@property 的本質是什麼?
@property = ivar + getter + setter;
下面解釋下:
「屬性」 (property)有兩大概念:ivar(實例變量)、存取方法(access method = getter + setter)。
「屬性」 (property)做爲 Objective-C 的一項特性,主要的做用就在於封裝對象中的數據。 Objective-C 對象一般會把其所須要的數據保存爲各類實例變量。實例變量通常經過「存取方法」(access method)來訪問。其中,「獲取方法」 (getter)用於讀取變量值,而「設置方法」 (setter)用於寫入變量值。這個概念已經定型,而且經由「屬性」這一特性而成爲Objective-C 2.0的一部分。 而在正規的 Objective-C 編碼風格中,存取方法有着嚴格的命名規範。 正由於有了這種嚴格的命名規範,因此 Objective-C 這門語言才能根據名稱自動建立出存取方法。其實也能夠把屬性當作一種關鍵字,其表示:
編譯器會自動寫出一套存取方法,用以訪問給定類型中具備給定名稱的變量。 因此你也能夠這麼說:
@property = getter + setter;
例以下面這個類:
@interface Person : NSObject
@property NSString firstName;
@property NSString lastName;
@end
上述代碼寫出來的類與下面這種寫法等效:
@interface Person : NSObject
(NSString )firstName;
(void)setFirstName:(NSString )firstName;
(NSString )lastName;
(void)setLastName:(NSString )lastName; @end ivar、getter、setter 是如何生成並添加到這個類中的?
「自動合成」( autosynthesis)
完成屬性定義後,編譯器會自動編寫訪問這些屬性所需的方法,此過程叫作「自動合成」( autosynthesis)。須要強調的是,這個過程由編譯 器在編譯期執行,因此編輯器裏看不到這些「合成方法」(synthesized method)的源代碼。除了生成方法代碼 getter、setter 以外,編譯器還要自動向類中添加適當類型的實例變量,而且在屬性名前面加下劃線,以此做爲實例變量的名字。在前例中,會生成兩個實例變量,其名稱分別爲 _firstName與_lastName。也能夠在類的實現代碼裏經過 @synthesize語法來指定實例變量的名字.
@implementation Person
@synthesize firstName = _myFirstName;
@synthesize lastName = myLastName;
@end
我爲了搞清屬性是怎麼實現的,曾經反編譯過相關的代碼,大體生成了五個東西:
1)OBJCIVAR$類名$屬性名稱 :該屬性的「偏移量」 (offset),這個偏移量是「硬編碼」 (hardcode),表示該變量距離存放對象的內存區域的起始地址有多遠。
2)setter與getter方法對應的實現函數
3)ivar_list :成員變量列表
4)method_list :方法列表
5)prop_list :屬性列表
也就是說咱們每次在增長一個屬性,系統都會在ivar_list中添加一個成員變量的描述,在method_list中增長setter與getter方法的描述,在屬性列表中增長一個屬性的描述,而後計算該屬性在對象中的偏移量,而後給出setter與getter方法對應的實現,在setter方法中從偏移量的位置開始賦值,在getter方法中從偏移量開始取值,爲了可以讀取正確字節數,系統對象偏移量的指針類型進行了類型強轉.
補充:
Objective-C運行時定義了幾種重要的類型。
Class:定義Objective-C類
Ivar:定義對象的實例變量,包括類型和名字。
Protocol:定義正式協議。
objc_property_t:定義屬性。叫這個名字多是爲了防止和Objective-C 1.0中的用戶類型衝突,那時候尚未屬性。
Method:定義對象方法或類方法。這個類型提供了方法的名字(就是選擇器)、參數數量和類型,以及返回值(這些信息合起來稱爲方法的簽名),還有一個指向代碼的函數指針(也就是方法的實現)。
SEL:定義選擇器。選擇器是方法名的惟一標識符。
IMP:定義方法實現。這只是一個指向某個函數的指針,該函數接受一個對象、一個選擇器和一個可變長參數列表(varargs),返回一個對象
dispatch_queue_t queue = dispatch_get_main_queue(); for (int i =0;i<10;i++){ dispatch_async(queue,^{ [self longtimeOperation:i]; }); }