1)根類:由於類 NSObject 是層次結構的最頂層,所以稱爲根類。html
2)分類/類別(category):容許以模塊的方式向現有類定義添加新的方法(默認不能添加實例變量)。擴展本身或他人之前實現的類,使它適合本身的須要。程序員
分類的名稱括在類名以後的一對圓括號「( )」中。objective-c
@interface QCStudent (Print) @end @implementation QCStudent (Print) @end
分類文件名使用符號「+」來分隔類和分類的名字(Xcode 會自動生成)。編程
QCStudent+Print.m QCStudent+Print.h
分類用它能夠將類的定義模塊化到相關方法的組或分類中。它還提供了擴展示有類定義的簡便方式,而且沒必要訪問類的源代碼,也無需建立子類。設計模式
分類能夠覆寫該類中的另外一個方法,可是一般認爲這種作法是拙劣的設計習慣。一個類能夠擁有多個分類。使用分類添加新方法來擴展類不只會影響這個類,同時也會影響它的全部子類。分類爲現有類添加新方法可能對你有用,但它們可能和該類的原始設計或意圖不一致。對象/分類命名對必須是惟一的。數組
iOS 開發中,分類默認不容許添加屬性。可是若是在本身開發的框架中,但願在分類中動態添加屬性,能夠經過 OC 運行時的關聯對象功能添加,詳見 iOS - OC Category 分類。安全
3)類的擴展:有一種特殊的狀況是建立一個未命名的分類,而且括號「( )」之間不指定名字。多線程
這種特殊的語法定義稱爲類的擴展。定義一個像這樣的未命名的分類時,能夠經過定義額外的實例變量和屬性來擴展類,這在有命名的分類中是不容許的。框架
@interface QCStudent () @end @implementation QCStudent @end
未命名的分類中聲明的方法須要在主實現區域實現,而不是在分類的的實現區域。模塊化
未命名的分類的方法都是私有的。若是須要寫一個類,並且數據和方法僅供這個類自己使用,未命名分類比較合適。
4)抽象類:有時建立類只是爲了更容易建立子類。所以,這些類名爲抽象類,或等價的稱爲抽象超類。在該類中定義方法和實例變量,但不但願任何人從這個類建立實例。
5)類與類之間的關係:
@class
聲明。一個類中包含有 #import
另外一個類的 .h
頭文件。.h
頭文件中用 @class
聲明一個類,在 .m
文件中使用到該類時再在 .m
文件中包含該類的 .h
頭文件。#import:文件包含。這種方式會包含被引用類的全部信息,包括被引用類的變量和方法; @class :類的聲明。這種方式只是告訴編譯器另外一個類的聲明,具體這個類裏邊有什麼信息,這裏不須要知道,等實現文件中具體用到時, 纔會真正的去查看引用類的信息,由於編譯器不須要引入和處理整個文件,只需知道是一個類名,在 .h 文件中使用 @class 指令提升了效率。
6)類的加載:
1>、類加載時自動調用方法:+ (void)load
;
+ (void)load { NSLog(@"%@",@"Student ------------- load"); }
2>、類首次使用時自動調用方法:+ (void)initialize
;
+ (void)initialize { NSLog(@"%@",@"Student ------------- initialize"); }
3>、使用 %@ 打印對象時會自動調用方法:- (NSString *)description
;
// description 覆寫 - (NSString *)description { return [NSString stringWithFormat:@"age : %d, name : %@", self.age, self.name]; }
7)是在子類中使用的實例變量,必須先在接口部分聲明,而不是在實現部分聲明。在實現部分聲明和合成的實例變量是私有的,子類中不能直接訪問,須要明肯定義或合成取值方法,才能訪問實例變量的值。
8)類前綴:
使用 Objective-C 開發 iOS 程序時,最好在每一個類名前面加一個前綴,用來標識這個類。
類前綴的設置
Xcode 6 以前:
在建立項目時設置。
Xcode 6 以後:
建立完項目後設置。
設置完後,再建立新的文件時會自動添加上設置的類前綴。
1)類的獨特存在就是一個實例(對象),對實例執行的操做稱爲方法。
2)合成對象:能夠定義一個類包含其它類的一個或多個對象,這個新類的對象就是所謂的合成對象,由於它是由其它對象組成的。
3)實例初始化
1>、初始化方式:
alloc :方法保證對象的全部實例變量都變成初始狀態。 建立對象。 init :方法用於初始化類的實例變量。 初始化對象。 new :能夠將 alloc 和 init 的結合起來。 建立並初始化對象。
2>、構造方法:實例初始化常見的編程習慣是類中全部初始化方法都以 init 開頭。若是但願在類對象初始化時作一些事情,能夠經過重載 init 方法達到這個目的。下面是重載 init 方法的一個標準模板。
不帶參數: - (instancetype)init { self = [super init]; if (self) { // 初始化代碼 } return self; } 帶參數: - (instancetype)initWithAge:(int)age andNo:(int)no { self = [super init]; if (self) { _age = age; _no = no; } return self; } 類方法: + (instancetype *)studentWithAge:(int)age { Student *stu = [[Student alloc] init]; stu.age = age; return stu; }
執行父類的初始化方法,使得繼承的實例變量可以正常的初始化。若是父類初始化成功,返回的值將是非空的。self 用來指明對象是當前方法的接收者。必須將父類 init 方法的執行結果賦值給 self,由於初始化過程改變了對象在內存中的位置(意味着引用將要改變)。
特殊類型 instancetype 代表從 init 方法返回的類型與它的初始化類(也就是初始化消息的接收者)相同。 init 被定義爲返回 instancetype 類型,這是編寫可能被繼承的類 init 方法的通常規則。當編譯器碰見 instancetype 做爲返回類型,它就知道返回的類型是發送消息的對象。
4)消息:請求一個類或實例來執行某個操做時,就是在向它發送一條消息,消息的接受者稱爲接收者。
[類/實例 方法];
實例方法(動態方法):對類的實例執行一些操做。
建立方法名時,參數名其實是可選的,參數名能夠省略。如:- (int)set :(int)name :(int)age;
方法(函數)不返回任何值時,無需在方法的末尾執行一條 return 語句。或者也能夠執行一條不帶任何指定值的 return 語句:return;
。
6)重寫(覆蓋):在子類中新建一個與父類中的方法同名的方法。子類中的新方法必須具備相同的返回類型,而且參數的數目和覆寫的方法相同。
if (self = [super init])
的判斷。7)重載:在類中,相同名字不一樣參數的方法的寫法有一個專門的術語來描述,叫作重載。
8)懶加載
通常重寫 getter 方法實現對象的懶加載。
@property (strong, nonatomic) NSArray *shops; - (NSArray *)shops { // 加載數據 if (_shops == nil) { NSString *filePath = [[NSBundle mainBundle] pathForResource:@"shops" ofType:@"plist"]; _shops = [NSArray arrayWithContentsOfFile: filePath]; } return _shops; }
1)數據封裝:將實例變量隱藏起來的這種作法實際上涉及一個關鍵概念 --「數據封裝」。
2)實例變量的定義做用域
@public 全局均可以訪問,實例對象可使用符號 「->」 直接訪問實例變量。 @protected 只能在類內部和子類中訪問 (訪問器方法 默認) 。 @private 只能在類內部訪問 (合成取值方法 默認)。 @package 經常使用於框架類的實例變量,同一包內能用,跨包就不能訪問。
3)訪問器方法(accessor):取值方法和設值方法一般稱爲訪問器方法。一般實例變量聲明時如下畫線( _ )字符開頭,此實例變量默認爲保護(@protected
)的。在類內部和子類中均可以訪問。
設值方法(setter):設置實例變量值的方法一般總稱爲設值方法。定義時在實例變量名前加上 set。如:
// ARC - (void)setAge:(NSNumber *)age { _age = age; } // MRC - (void)setAge:(NSNumber *)age { if (_age) { [_age release]; } _age = [age retain]; }
取值方法(getter):用於檢索實例變量值的方法叫作取值方法。定義時直接使用實例變量名。如:
- (NSNumber *)age { return _age; }
4)合成取值方法:一般實例變量聲明時不如下畫線( _ )字符開頭,以字母開頭,而且此實例變量是私有(@private
)的。只能在類內部訪問。
在接口部分中使用 @property 指令標識屬性,聲明實例變量的 setter 和 getter 方法。 - 如:@property int numerator, denominator;
在實現部分中使用 @synthesize 指令標識屬性,實現實例變量的 setter 和 getter 方法。 - 如:@synthesize numerator, denominator;
1>、若是使用了 @property 指令,就不須要在實現部分聲明相應的實例變量。固然也能夠再聲明相應的實例變量,可是那不是必需要作的,編譯器會有一些提示。
2>、@property 的修飾
在不寫任何修飾時,Xcode 會自動生成標準的 setter 和 getter 方法,寫修飾時 Xcode 會自動生成帶內存管理的 setter 方法,標準 getter 方法。
參數分類:
讀寫屬性:readwrite/readonly setter 處理:assign/retain/copy 原子性:atomic/nonatomic 方法名:setter = method / getter = method 引用型:strong/weak 可選性:nonnull/nullable/null_unspecified/null_resettable // Xcode 7 新增特性 readwrite:可讀寫,生成 setter 和 getter 方法。默認。 readonly :只讀,只生成 getter 方法。 assign :修飾普通類型,在 setter 方法中直接賦值。默認。簡單賦值,不更改引用計數。 如:@property (nonatomic, assign)int age; retain :修飾 OC 對象,在 setter 方法中 release 舊值,retain 新值。釋放舊的對象,將舊對象的值賦予輸入對象,再提升輸入對象的引用計數爲 1。 如:@property (nonatomic, retain)Dog *dog; copy :修飾 NSString 類型,在 setter 方法中 release 舊值,copy 新值。創建了一個相同的對象,地址不一樣(retain:指針拷貝 copy:內容拷貝)。 如:@property (nonatomic, copy)NSString *name; atomic :原子性,默認。是 OC 使用的一種線程保護技術,防止在寫入未完成的時候被另一個線程讀取,形成數據錯誤。 給 setter 和 getter 方法加鎖,保證多線程安全。 nonatomic:非原子性,禁止多線程,變量保護,提升性能。不給 setter 和 getter 方法加鎖,執行相對快點。 setter = method:指定 setter 方法的方法名。 如:@property (nonatomic, setter = setIsRich)BOOL rich; 將 rich 的 setter 方法重命名爲 setIsRich 。 getter = method:指定 getter 方法的方法名。 如:@property (nonatomic, getter = isRich)BOOL rich; 將 rich 的 getter 方法重命名爲 isRich 。 strong :強引用,在 OC 中對象默認都是 strong。(ARC 下的)和(MRC)retain 同樣 (默認)。 viewController 對根視圖是強引用,view addSubviews 方法是向數組中添加子視圖,數組會對子視圖強引用。 weak :弱引用,weak 的做用,一旦沒有強引用,會被當即釋放。(ARC 下的)和(MRC)assign 同樣。 蘋果從 StoryBoard 拖線默認是 weak。weak 當指向的內存釋放掉後自動 nil 化,防止野指針。 nonnull :不可爲空 nullable :能夠爲空 null_unspecified:不肯定是否能夠爲空(極少狀況) null_resettable :set 方法能夠爲 nil,get 方法不可返回 nil,只能用在屬性的聲明中。
5)點運算符(點語法):訪問的是方法(setter/getter 方法),不是實例變量。
合成取值方法中可使用點運算符訪問屬性,也能夠對自定義的方法使用點運算符,如語句 myFraction.print
,並未考慮編碼風格是否良好。
點運算符一般用在屬性上,用於設置或取得實例變量的值。作其它工做的方法一般不是由點運算符執行的,而是使用傳統的方括號形式的消息表達式做爲首選的語法。
6)尖運算符(->):當實例變量定義爲 @public
類型時,實例對象可使用符號 「->」 直接訪問實例變量。 如:car -> _speed = 80; int a = car -> _speed;
局部對象變量:默認初始值爲 nil 。
靜態變量:在局部變量聲明前加上關鍵字 static ,可使局部變量保留屢次調用一個方法所得的值。靜態變量的初始值爲 0 。
外部變量:在方法外定義的變量不只是全局變量,並且是外部變量。
使用外部變量時,必須遵循下面這條重要原則:變量必須定義在源文件中的某個位置。即在全部的方法和函數以外定義變量,而且前面不加關鍵字 extern 。在全部的函數以外聲明變量,在聲明前面加上關鍵字 extern 。
處理外部變量時,變量能夠在許多地方聲明爲 extern ,可是隻能定義一次。
1)繼承的概念做用於整個繼承鏈。
類的每一個實例(對象)都擁有本身的實例變量,即便這些實例變量是繼承來的。
繼承一般用於擴展一個類。不能經過繼承刪除或減小方法。
爲何須要建立子類:(1)但願繼承一個類的函數,也需加入一些新的方法和/或實例變量。(2)但願建立一個類的特別版本。(3)但願經過覆寫一個或多個方法 來改變類的默認行爲。
2)多態:使不一樣的類共享相同方法名稱的能力稱爲多態。可以使來自不一樣類的對象定義相同名稱的方法。
動態綁定:在運行時而不是編譯時肯定對象須要調用的方法,能使程序直到執行時才肯定實際要調用的對象方法。
id 類型的對象先斷定對象所屬的類(動態類型),而後在運行時肯定須要動態調用的方法,而不是在編譯的時候(動態綁定)。
爲何還要關心靜態類型:(1)它能更好的在程序編譯階段而不是運行時指出錯誤。(2)它能提升程序的可讀性。
若是使用動態類型來調用一個方法,須要注意一下規則:若是在多個類中實現名稱相同的方法,那麼每一個方法都必須符合各個參數的類型和返回值類型,這樣編譯器才能爲消息表達式生成正確的代碼。
處理動態類型的方法:
如下總結了 NSObject 類所支持的一些基本方法,其中,class-object 是一個類對象(一般是由 class 方法產生的),selector 是一個 SEL 類型的值(一般是由 @selector
指令產生的)。
- (BOOL)isKindOfClass:class-object // 對象是否是 class-object 或其子類的成員 - (BOOL)isMemberOfClass:class-object // 對象是否是 class-object 的成員 + (BOOL)isSubclassOfClass:class-object // 某個類是不是指定類的子類 - (BOOL)respondsToSelector:selector // 對象是否可以響應 selector 所指定的方法 + (BOOL)instancesRespondToSelector:selector // 指定的類實例是否能響應 selector - (id)performSelector:selector // 應用 selector 指定的方法 - (id)performSelector:selector withObject:object // 應用 selector 指定的方法,傳遞參數 object - (id)performSelector:selector withObject:object1 withObject:object2 // 應用 selector 指定的方法,傳遞參數 object1 和 object2 [Square class] // 從名爲 Square 的類中得到類對象 [mySquare class] // 知道對象 mySquare 所屬的類 @selector(alloc) // 爲名爲 alloc 的方法生成一個 SEL 類型的值。
4)消除 performSelector: 方法警告
#pragma clang diagnostic push #pragma clang diagnostic ignored "-Warc-performSelector-leaks" // performSelector: 方法 #pragma clang diagnostic pop
1)協議:是多個類共享的一個方法列表。協議中列出的方法沒有相應的實現,計劃由其餘人來實現。協議中列出的方法,有些是能夠選擇實現,有些是必須實現。
1>、若是你定義了本身的協議,那麼沒必要由本身實現它。可是,這就告訴其餘程序員,若是要採用這項協議,則必須實現這些方法。這些方法能夠從超類繼承。
協議不引用任何類,它是無類的。
分類也能夠採用一項協議。
2>、定義一個協議很簡單:只要使用 @protocol 指令,後面跟上你給出的協議名稱。定義一項協議時,能夠擴展示有協議的定義。
@protocol PlayerDelegate <NSObject> - (void)end; @end
3>、協議的修飾
@optional:該指令以後列出的全部方法都是可選的。 @required:該指令以後列出的全部方都是必須實現的,默認。因爲 OC 是弱語法,雖然字面上是必須,但編譯器並無強求實現。
4>、協議的聲明
@protocol protocol-name
5>、協議的檢查
// 檢查一個對象是否遵照某項協議。 - (BOOL)conformsToProtocol:(Protocol *)aProtocol; // 用於獲取一個協議名稱,併產生一個 Protocol 對象,conformsToProtocol: 方法指望這個對象做爲它的參數。 @protocol(Drawing) // 檢查對象是否可以響應 selector 所指定的方法。 - (BOOL)respondsToSelector:selector // 爲名爲 alloc 的方法生成一個 SEL 類型的值。 @selector(alloc)
2)非正式協議:其實是一個分類,列出了一組方法但並無實現它們。非正式協議一般是爲根類定義的,有時,非正式協議也稱爲抽象協議。
聲明非正式協議的類本身並不實現這些方法,而且選擇實現這些方法的子類須要在它的接口部分從新聲明這些方法,同時還要實現這些方法中的一個或多個。
指令 @optional 添加到 OC 2.0 語言中,用於取代非正式協議的使用。
3)代理:協議也是一種兩個類之間的接口定義。定義了協議的類能夠看做是將協議定義的方法代理給了實現它們的類。
爲何 Objective-C 的方法調用要用方括號 [obj foo],而不是別的語言經常使用的點 obj.foo ?
首先要說的是,Objective-C 的歷史至關久遠,若是你查 wiki 的話,你會發現:Objective-C 和 C++ 這兩種語言的發行年份都是 1983 年。在設計之初,兩者都是做爲 C 語言的面向對象的接班人,但願成爲事實上的標準。最後結果你們都知道了,C++ 最終勝利了,而 Objective-C 在以後的幾十年中,基本上變成了蘋果本身家玩的玩具。不過最終,因爲 iPhone 的出現,Objective-C 迎來了第二春,在 TOBIE 語言排行榜上,從 20 名開外一路上升,排名曾經超越過 C++,達到了第三名(下圖),可是隨着 Swift 的出現,Objective-C 的排名則一路下滑。
Objective-C 在設計之初參考了很多 Smalltalk 的設計,而消息發送則是向 Smalltalk 學來的。Objective-C 當時採用了方括號的形式來表示發送消息,爲何沒有選擇用點呢?我我的以爲是,當時市面上並無別的面嚮對象語言的設計參考,而 Objective-C 「發明」了方括號的形式來給對象發消息,而 C++ 則「發明」了用點的方式來 「發消息」。有人可能會爭論說 C++ 的「點」並非真正的發消息,可是其實兩者都是表示「調用對象所屬的成員函數」。
另外,有讀者評論說使用方括號的形式是爲了向下兼容 C 語言,我並不以爲中括號是惟一選擇,C++ 不也兼容了 C 語言麼?Swift 不也能夠調用 C 函數麼?
最終,實際上是 C++ 的「發明」顯得更舒服一些,因此後來的各類語言都借鑑了 C++ 的這種設計,也包括 Objective-C 在內。Objective-C 2.0 版本中,引入了 dot syntax,即:
a = obj.foo 等價於 a = [obj foo] obj.foo = 1 則等價於 [obj setFoo:1]
Objective-C 其實在設計之中確實是比較特立獨行的,除了方括號的函數調用方式外,還包括比較長的,可讀性很強的函數命名風格。
我我的並不討厭 Objective-C 的這種設計,可是從 Swift 語言的設計來看,蘋果也開始放棄一些 Objective-C 的特色了,好比就去掉了方括號這種函數調用方式。
因此,回到咱們的問題,我我的認爲,答案就是:Objective-C 在 1983 年設計的時候,並無什麼有效的效仿對象,因而就發明了一種有特色的函數調用方式,如今看起來,這種方式比點操做符仍是略遜一籌。
大多數語言一旦被設計好,就很難被再次修改,應該說 Objective-C 發明在 30 年前,仍是很是優秀的,它的面向對象化設計得很是純粹,比 C++ 要全面得多,也比 C++ 要簡單得多。