1. 程式人生 > >Objective-C 2.0 基礎要點歸納

Objective-C 2.0 基礎要點歸納

場景 問題 sub article 可選 特質 stop instance argc

本文的閱讀基本條件:

  • 具備C/C++基礎知識,了解面向對象特征
  • 閱讀過《Objective-C 2.0 程序設計(第二版)》、《Objective-C 程序設計 第6版》或相關基礎OC書籍

知識要點匯總模式: 提出問題。給出詳細解釋(Q&A)
PS:會以擴展方式延伸介紹一些知識點

問題列表
Q1. Objective-C特點以及和C++的差別?
Q2. 屬性的特點。和實例變量的差別,使用註意事項?
Q3. 類的繼承。協議和分類的概念和使用,以及須要註意的問題?

Q1. Objective-C特點以及和C++的差別?

Objective-C特點

Objective-C(OC)是C的超集,基於C語言加入了面向對象特性和消息轉發機制的動態語言(繼承了smalltalk語言面向對象的經典思想)。

除編譯器外還須要用執行時(runtime)系統來動態創建類和對象進行消息發送和轉發,而runtime系統是一個用C語言編寫動態鏈接庫(libobjc.A.dylib)。 該庫提供了OC語言所需的各種動態特性的支持。

OC採用消息傳遞(Messaging)即向對象傳遞消息的方式而非方法調用。如: [receiver message],編譯器並不立即執行對象的message方法,而是向receiver對象發送一條message消息,編譯器將其轉化為 obj_msgSend (runtime和對象模型詳細實現參見Foundation框架要點歸納

)及其相關函數調用,在程序執行時會依據obj_msgSend 查找詳細方法實現。

簡單的說,即OC程序在編譯時並未詳細確定方法實現,而是在執行時借助runtime系統實現方法的動態綁定,通過obj_msgSend實現查找方法的類別。進而調用方法實現,通過runtime系統實現面向對象的多態特性(不同的類有同樣的方法名)。

C++ 和 OC的差別

C++ 也是一種對C實現面向對象特性的擴展,能夠從面向對象的三個特性(封裝、繼承/派生、多態)角度簡單分析C++和OC的不同

封裝: OC和C++都有自己風格的類結構語法定義方式。OC成員變量默覺得protected。屬性是OC的一個特性,用於封裝對象中的數據。C++默覺得private。 OC能夠通過分類對原始類進行擴充,在不破壞類封裝特性的基礎上對類進行方法加入,但不能添加成員變量(屬性能夠)。

繼承: C++能夠多繼承。即同一時候繼承多個父類。 OC僅僅能單繼承,但通過協議有效添加了繼承的靈活性和多樣性,彌補了單繼承的缺點。

多態: 即同樣類具有同名函數(方法)。但能夠通過類別來區分實現相應的函數(方法)。C++和OC不同的是其對象採用函數調用方式,且在編譯階段實現類和函數的綁定(虛函數的動態綁定機制除外),這點和OC具有明顯的差別,OC是在執行時通過runtime系統來查找詳細類別方法並實現。

並且,C++能夠實現函數重載功能,OC沒有該功能。

Q2. 屬性的特點,和實例變量的差別,使用要點?

屬性的特點

屬性(property)是OC一個特性,用於封裝對象中的數據。OC通過定義實例變量來存儲對象所需的數據。並通過存取方法(access method。setter,getter)來訪問。

上述概念的成型且經由“屬性”這一特性而成為OC 2.0 一部分。

使用“屬性”代替定義傳統的實例變量和存取方法讓編譯器自己主動合成存取方法有利於提高程序性能([email protected]用的實例變量和存取方法)。而引入了點語法(dot syntax)更提高程序可讀性(”.”語法實際上編譯成消息傳遞模型[receiver message])。

屬性關鍵字(特質)

property有一些具有特殊用途的關鍵字(特質),一般分為三類:原子性。存取器控制,內存管理
(1)原子性
atomic(默認):僅僅同意一個線程訪問實例變量。線程安全效率低下
nonatomic: 能夠被多線程訪問,效率高
(2) 讀寫權限
readwrite(默認):[email protected],編譯器自己主動生成setter和getter方法
readonly :[email protected],編譯器才會合成獲取方法,僅僅有 getter沒有setter
(3)內存管理
顯示內存管理策略
assign(默認):用於值類型,如 int,float,NSInteger 等表示單純的復制
retain: 在setter方法中。須要對傳入對象進行引用計數+1的操作,即對對象具有全部權,該對象不會被釋放
strong:和retain意思同樣。並產生同樣代碼,但語意上更能體現對象擁有
weak:setter方法中對傳入對象不進行引用計數+1的操作,即對傳入的對象沒有全部權。當對象引用計數為0時,對象被釋放,聲明實例變量指向nil
unsafe_unretained: 和assign同樣,可是它適用於“對象類型”,非擁有關系,當目標對象被清除時,該屬性之不被清空(nil, 和weak有差別), 訪問會造成崩潰。
copy: 和strong相似。但差別在於對對象副本擁有全部權而非對象本身。經常使用於NS String * 類型。用於保護其封裝性。

property使用要點

  1. [email protected], 則使用屬性時能夠通過 _name(編譯器隱藏了 @synthesize name = _name;) 或者self.name , [self name]訪問,點運算符和調用默認getter方法效果一樣。
  2. 但假設自己定義了getter或者setter方法,則在方法實現中不能夠使用 self ,須要用_name對實例變量進行訪問。否則 會進入死循環, 以下的Demo代碼中有體現
  3. [email protected],則能夠直接使用變量 name

淺復制(淺拷貝)和深復制(深拷貝)

概念解釋:
淺復制: 對於對象中的每一層(對象成員中包括的對象)復制都是指針復制(引用計數角度,每層對象引用計數+1)
深復制:至少有一個對象復制是對象內容復制從引用計數角度出發,除了原對象,其它指針復制的對象引用計數都+1)

方法關聯:
retain:始終採取淺復制。引用計數+1
copy: 對於不可變對象,copy 採用的是淺復制。引用計數+1(編譯器進行的優化)
對於可變對象copy採用的是深復制,引用計數器不變
mutableCopy: 可變和不可變對象都採用深復制

屬性修飾詞關聯:
retain , strong 都是對象引用計數+1
copy 是拷貝對象副本,引用計數+1
對於常量類型 assign
NSString類 copy
id對象 strong
關聯對象 weak
非系統內存管理 unsafed_retained

// ------AClass.h------
#import <Foundation/Foundation.h>
@interface AClass : NSObject
// property
@property (nonatomic, strong) NSString *aName;
// print
- (void)print;
// self-defined getter & setter
// 主要和 自己定義getter方法作比較。通過獲取的限制條件
- (NSString *)aName;
@end

// ------AClass.m------
@implementation AClass

[email protected] aName = _aName; // 系統隱藏
[email protected] aName;

// print
- (void)print
{
    // 調用實例變量
    NSLog(@"內部Print方法直接調用實例變量%@", _aName);
    // 調用自己定義getter函數
    NSLog(@"內部Print方法通過存取方法獲取屬性%@", self.aName);
    NSLog(@"內部Print方法通過存取方法獲取屬性%@", [self aName]);
}

// getter
- (NSString *)aName
{
    if ([_aName isEqualToString:@"King"]) { // self.aName 會進入死循環
        return _aName;
    }
    else {
        return @"Not King~";
    }
}
@end

// ------main.m------
int main(int argc, char const *argv[])
{
    @autoreleasepool {
        AClass *a = [[AClass alloc] init];
        a.aName = @"King";
        NSLog(@"通過點運算符實現屬性訪問: %@", a.aName);     
        // 調用存取方法的setter
        [a setAName:@"King2"];
        NSLog(@"通過存取方法實現屬性訪問: %@", [a aName]);    
        NSLog(@"----------------------------------------");    
        [a print];
        return 0;
    }
}
OutPut:
通過點運算符實現屬性訪問: King
通過存取方法實現屬性訪問: Not King~
----------------------------------------
內部Print方法直接調用實例變量King2
內部Print方法通過存取方法獲取屬性Not King~
內部Print方法通過存取方法獲取屬性Not King~`

Q3. 類的繼承,協議和分類的概念和使用。以及須要註意的問題?

知識點匯總

(Maybe you need it or learn something important)

  • 繼承] OC是面向對象語言,繼承方式和C++有明顯差別即單繼承,而沒有C++的多繼承。但多協議彌補了不能多繼承的缺陷。能夠通過子類繼承的方式
    a.添加新的方法/或實例變量,
    b.類的特別接口封裝。
    c. 覆寫 一個或者多個方法改變類的默認行為

  • [多態,動態類型和動態綁定]
    多態: 不同的類對象定義同樣名稱
    動態類型id類型: 直到執行時才確定所屬對象的類,通用對象類型,定義為:
    typedef struct objc_object *id;
    (id 變量不能使用點運算符)
    動態綁定:執行時才確定實際要調用的對象方法

  • [分類 - Category]
    提供一種簡單方式將類定義模塊化到相關方法的組或分類中。一般。假設主類為B,則分類頭文件和實現文件名分別為:B+subClass.h, B+subClass.m
    從調用角度,分類就是對原始類的一種擴展
    從程序角度。分類可讀性更強

分類文件格式
#import “SuperClassName.h”
@interface Fraction (MathOp)
@end

類擴展

假設創建一種未命名的分類。則稱為類的擴展,在有命名的分類中時不同意的。和有命名分類不同,未命名分類主要在主實現區實現,而非分離實現區域。未命名分類擴展的方法和屬性或者實例變量僅僅能由該類本身私有。

Category 使用場景:

a. 已經定義的類須要加入新的方法功能
b. 一個類中包括很多種不同類型方法,須要不同團隊實現,有利於任務分配

註意問題:

a. Category能夠訪問原始類方法。但不能加入變量,加入變量能夠通過創建子類(繼承)方式來實現
b. Category能夠重載原始方法,會導致不能訪問原來的方法,創建子類實現 重載 覆寫
c. 和普通接口有所差別,分類實現文件裏能夠不必實現全部聲明的方法

  • [協議]
    協議 即多個類共享一個方法列表,協議中列出的方法沒有相應的實現,通過文檔說明,使用協議中的方法能夠通過文檔說明進行實現。

一系列不屬於不論什麽類的方法列表,當中聲明的方法能夠被不論什麽類實現。

這樣的模式稱為代理模式。

在不同場景中實現不同模式。Apple採用了大量的代理模式來實現 MVC中 View和 Controller 的解耦。

最經常使用的是托付代理模式。 Cocoa框架中大量採用這樣的模式實現 數據和UI的分離: UIView產生的全部事件 都是通過托付的方式 交給 Controller 完畢

框架中後綴為Delegate都是 Protocol

@protocol NSCopying
- (id)copyWithZone:(NSZone *)zone;
@end
(能夠放在單獨的.h頭文件裏定義。也能夠放在相關類的h文件裏)
使用:
@interface AddressBook : NSObject< NSCopying, NSCoding >

PS:

  1. 不是必需在接口部分聲明協議的方法。但要在實現部分定義這些方法
    2. 能夠通過 例如以下代碼來檢查一個對象是否遵循某項協議
    id currentObject;
    if ([currentObject conformsToProtocol:@protocol(Drawing)] == YES) {
    }
    3. 也能夠使用 respondsToSelector:@selector() 來檢查是否實現了可選的方法
    代理(delegation)
    -(BOOL)respondsToSelector:selector 被廣泛用於實現托付方法定義
    4. id currentObject; 借助編譯器來檢查變量一致性
    5. 和類名一樣,協議名必須唯一
    6. 協議是能夠繼承的,具有繼承的屬性。假設協議B繼承了協議A,則實現協議B 須要實現A和B的全部方法

    • [代理] delegation
      協議是一種兩個類之間的接口定義,定義了協議的類能夠看作是將協議定義的方法代理給實現他們的類。

      (定義了協議的類稱為 代理)

測試程序

//******測試樣例: (涵蓋之前一些內容,算是整合一下思路)
//******MTProtocol.h 文件,為定義的協議
@protocol Printing
// 默認
@required
- (void)printProtocol;
@optional
- (void)printProtocolOptional;
@end

//******A.h 文件,包括A類定義。採用Printing協議
#import <Foundation/Foundation.h>
#import "MTProtocol.h"
// 接口處定義實例變量, 採用協議
@interface A : NSObject <Printing>
{
    int x;
}
@property (nonatomic, assign) int y;
- (void)initXY;
- (void)printXY;
@end

//******A.m 文件,包括A類實現,註意實例變量和屬性初始化的問題(這裏沒有重載init方法,所以不須要模版 self = [super init] 。。。)
#import "A.h"
@implementation A
- (void)initXY
{
    x = 1;
    self.y = 2;
}
- (void)printXY
{
    NSLog(@"This is Class A : %i, %i", x, self.y);
}
- (void)printProtocol
{
    NSLog(@"I am printProtocol Method form Printing!");
}
- (void)printProtocolOptional
{
    NSLog(@"I am printProtocolOptional Method form Printing!");
}
@end

//******A+Op.h A類的分類接口,用於擴展(感覺就是擴展。一方面通過子類。一方面在原類中進行分類擴展,或者 合成類 這個比較奇葩)
#import "A.h"
@interface A (Op)
- (void)printCategory;
@end

//******A+Op.m A類的分類實現
#import "A+Op.h"
@implementation A (Op)
- (void)printCategory
{
    NSLog(@"Op Category for Class A!");
}
@end

//******B.h B類接口,A 類子類。在B類中我們嘗試 未命名分類,即私有方法擴展
#import "A.h"
@interface B : A
- (void)initXY;
- (void)printXY;
@end

//******B.m B類實現,註意未命名分類
#import "B.h"
@interface B ()
- (void)printNanNameCategory;
@end
@implementation B
- (void)initXY
{
    x = 10;       // A類中接口處定義了實例變量,能夠被繼承
    super.y = 20; // 屬性或者實現部分聲明的變量為私有實例變量。通過合成取值方法獲取
}
- (void)printXY
{
    NSLog(@"This is Class B : %i, %i", x, self.y);
}
- (void)printNanNameCategory
{
    NSLog(@"I am Nan Name Category for B!");
}
@end


//******main.m 實現,註意未命名分類
#import "A+Op.h"
#import "B.h"
// ---------- main ----------

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        A *a = [[A alloc] init];
        B *b = [[B alloc] init];

        [a initXY];
        [b initXY];
        // 定義動態類型 id
        id tmp = a;
        [tmp printXY];
        [tmp printCategory];
        [tmp printProtocol];
        [tmp printProtocolOptional];

        tmp = b;
        [tmp printXY];

        [tmp printProtocolOptional];

        // 測試 tmp 是否是A類別
        //[tmp isKindOfClass:[A class]] ?

NSLog(@"Yes") : NSLog(@"No"); // 測試A 類是否包括printXY方法 //[A instancesRespondToSelector:@selector(printXY)] ? NSLog(@"Yes") : NSLog(@"No"); // 測試對象a 是否包括init方法 //[a respondsToSelector:@selector(init)] ? NSLog(@"Yes") : NSLog(@"No"); } }

結果分析
This is Class A : 1, 2
Op Category for Class A!
I am printProtocol Method form Printing!
I am printProtocolOptional Method form Printing!
This is Class B : 10, 20
I am printProtocolOptional Method form Printing!

參考資源

  • 《Effective Objective-C2.0》
  • 《Objective-C 2.0 程序設計(第二版)》/《Objective-C 程序設計 第6版》
  • Objective-C消息傳遞機制
  • 深入理解Objective-C的Runtime機制
  • Objective-C的對象模型與執行時
  • Objective-C 消息、Category和Protocol

Objective-C 2.0 基礎要點歸納