[GeekBand]Objective-C編程語言第一周筆記

1. Objective-C簡介

Objective-C語言簡介

  • Objective-C在C語言基礎上做了面向對象擴展。
  • 1983年由Brad Cox和Tom Love發明,后成為NeXT的主力語言,后被蘋果收購,成為蘋果開發平臺的主力語言。
  • 與Cocoa和Cocoa Touch框架高度集成,支持開發Mac OS X、iOS應用。
  • 在蘋果開發平臺上,通過LLVM編譯器架構,支持與Swift語言雙向互操作。
    在iOS開發平臺上支持的語言有Swift、Objective-C、C/C++,主要使用前兩者。
    ?

如何掌握高級編程語言

底層思維:向下,如何把握機器底層從微觀理解對象構造

  • 語言轉換
  • 編譯轉換
  • 內存模型
  • 運行時機制

抽象思維:向上,如何將我們的周圍世界抽象為程序代碼

  • 面向對象
  • 組件封裝
  • 設計模式
  • 架構模式

「時空人」三位一體分析法

  • 對時間分析——發生在什么時候?
    • Compile-time VS Run-time
  • 對空間分析——變量放在哪里?
    • Stack VS Heap
  • 人物分析——代碼哪里來的?
    • Programmer VS Compiler/Runtime/Framework

兩種開發方式

  • Clang或GCC命令行
    • clang -fobjc-arc HelloWorld.m -o HelloWorld
    • gcc -fobjc-arc HelloWorld.m -o HelloWorld,推薦用clang
    • -fobjc-arc:支持ARC(Automatic Reference Counting)
    • 適合調試、研究、微觀探查
  • -o:輸出文件名
  • 更多可以參考-help
//引入頭文件,可以避免多次引入重復頭文件,推薦使用。
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        //類似于prinft,但多了日期時間等信息。
        NSLog(@"Hello World~");
    }
    return 0;
}
  • Objective-C的字符串前要加@
  • Xcode項目
    • 構建正規工程項目
    • 使用大型框架,追求設計質量與代碼組織

Objective-C編譯過程

  • GCC: GCC Front End -> GCC Optimizer -> GCC Code Generator
  • LLVM-GCC: GCC Front End -> LLVM Optimizer -> LLVM Code Generator
  • LLVM-Clang: Clang Front End -> LLVM Optimizer -> LLVM Code Generator
  • Objective-C直接生成機器碼
  • LLVM:Low Level Virtual Machine
  • LLVM-Clang:目前iOS上一般使用的方式,針對iOS優化更好。

學習資源

課程總結

  • 了解Objective-C語言和編譯架構
  • 了解Objective-C開發工具
  • 了解機器思維和抽象思維
  • 駕馭工具,而不是被工具駕馭
  • 掌握英文學習資源很重要

2. 類與對象

類型系統

  • 引用類型 Reference Type
    • 類 Class
    • 指針 Pointer
    • 塊 Block
  • 值類型 Value Type
    • 基礎數值類型
    • 結構 Struct
    • 枚舉 Enum
  • 類型裝飾
    • 協議 Protocol
    • 類別 Category
    • 擴展 Extension
  • C語言中的數據類型均可用在Objective-C中

類 VS 結構

  • 類型與實例
    • 類與對象
    • 結構與值
  • 類——引用類型
    • 位于棧上的指針(引用)
    • 位于堆上的實體對象
  • 結構——值類型
    • 實例直接位于棧中
  • 空間分析
    • 運行時內存圖——「胸中有溝壑」

類定義

RPoint.h

@interface RPoint: NSObject

@property int x; //屬性,狀態,這里默認初始化為0
@property int y; 

- (void) print; //方法,行為

@end
  • - (void)print-表示實例方法,+ 表示類方法,其后的括號內為返回值類型。

RPoint.m

#import <Foundation/Foundation.h>
#import "RPoint.h"

@implementation RPoint

- (void) print {
    NSLog(@"[%d, %d]", self.x, self.y);
}

@end

SPoint.h

typedef struct {
    int x;
    int y;
}SPoint;
對象的空間分析

棧上存儲指針(引用),堆上存儲真正的對象,

值的空間分析

實例(值)內存直接存儲在??臻g

棧 VS 堆

棧:存儲值類型

  • 無ARC負擔,由系統自動管理,以執行函數為單位
  • 空間大小編譯時確定(參數+局部變量)
  • 函數執行時,系統自動分配一個Stack
  • 函數執行結束,系統立即自動回收Stack
  • 函數之間通過拷貝值傳遞
  • 具有局部性,大小有限額,超出會Stack Overflow

堆:存儲引用類型對象

  • 分配由程序員手動請求(創建對象時)
  • 釋放由運行時ARC機制自動釋放(確定時)
  • 函數之間通過拷貝引用(指針)傳遞
  • 具有全局性,總體無大小限制(受制于系統內存整體大?。?/li>

main.m

#import <Foundation/Foundation.h>
#import "RPoint.h"
#import "SPoint.h"

void process(RPoint* rp3, SPoint sp3);

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        //Objective-C中所有的對象均以指針的形式存在,要加 * 號
        //中括號為發送消息(方法調用)
        //創建對象,alloc為向系統請求內存分配
        RPoint* rp1 = [[RPoint alloc] init];
        
        rp1.x = 10;
        rp1.y = 20;

        [rp1 print]; //[10, 20]

        //
        SPoint sp1;
        sp1.x = 10;
        sp2.y = 20;

        NSLog(@"------拷貝------")
        RPoint rp2 = rp1;
        rp2.x++;
        rp2.y++;

        //引用傳遞
        [rp1 print]; //[11, 21]
        [rp2 print]; //[11, 21]

        SPoint sp2 = sp1;
        sp2.x++;
        sp2.y++;

        //值傳遞
        NSLog(@"[%d, %d]", sp1.x, sp1.y); //[10, 20]
        NSLog(@"[%d, %d]", sp2.x, sp2.y); //[11, 21]

        NSLog(@"------傳參------");
        process(rp1, sp1);
        [rp1 print]; //[12, 22]
        NSLog(@"[%d, %d]", sp1.x, sp1.y); //[10, 20]
    }
    return 0;
}
//函數開始執行時自動創建一個新棧,與main函數的棧不同,結束時會自動銷毀
void process(RPoint* rp3, SPoint sp3) {
    rp3.x++;
    rp3.y++;
    
    sp3.x++;
    sp3.y++;

    [rp3 print]; //[12, 22]
    NSLog(@"[%d, %d]", sp3.x, sp3.y); //[11, 21]
    //函數執行結束后rp3和sp3將被銷毀
    //但rp3僅是指針,銷毀后指針所指向的對象并不受影響。
}

?

3. 數據成員:屬性與實例變量

類型成員 Type Member

數據成員 Data Member 描述對象狀態

  • 實例變量 Instance Variable
  • 屬性 Property

函數成員 Function Member 描述對象行為

  • 方法 Method
  • 初始化器 Init
  • 析構器 Dealloc

認識屬性

屬性表達實例狀態,描述類型對外接口。相比直接訪問實例變量,屬性可以做更多控制。

默認情況下,編譯器會為屬性定義propertyName自動合成:

  • 一個getter訪問器方法:propertyName
  • 一個setter訪問器方法:setPropertyName
  • 一個實例變量:_propertyName

可自定義訪問器方法,也可更改訪問器方法名、或實例變量名。

可以使用靜態全局變量(C語言)+ 類方法,模擬類型屬性。

屬性聲明:

@property NSString* firstName;
//--上述代碼將生成:--
NSString* _firstName;
- (NSString*) firstName {/**code**/}
- (void) setFirstName: (NSString*)newValue {/**code**/}
//----

//更改訪問器方法名
@property (getter = GivenName, setter = setGivenName:) NSString* lastName;
//更改實例變量名
@synthesize firstName = givenName;

調用方法:

//訪問器方法
[employee setFirstName: @"Tom"];//set
[employee firstName]; //get

//屬性表達式,推薦。
employee.firstName = @"Tom"; //set
NSLog(@"First Name: %@", employee.firstName);//get
模擬靜態(類)屬性

在.m文件中定義一個靜態變量

//靜態變量
static int _max = 100;

@implementation Employee {
    //...
}

在.h文件中定義方法

@interface Employee: NSObject

//...

+ (int) max;
+ (void) setMax: (int)newValue;

//...
@end

在.m文件中實現方法

@implementation Employee {
//...

//為靜態變量提供訪問器方法
+ (int) max {
    return _max;
}

+ (void) setMax: (int)newValue {
    _max = newValue;
}

//...
}

調用方法:

//Employee為類型,不是對象
[Employee setMax: 300];
Employee.max = 400;
NSLog(@"class variable is %d.", Employee.max);

實例變量

可以定義實例變量,而不定義屬性。只有實例變量,沒有類變量。

如果同時自定義了getter和setter訪問器方法,或者針對只讀屬性定義了getter訪問其方法,編譯器將不再合成實例變量。

在類外一律使用屬性來訪問,類內大多也通過self使用屬性訪問。只有一下情況使用實例變量來訪問:

  • 初始化器 init
  • 析構器 dealloc
  • 自定義訪問器方法
  • 實例變量只能在類內訪問,類外不可訪問。
  • 引用類型的屬性使用實例變量訪問時可能會?有內存管理的問題。

實例變量的生存周期

實例變量的存儲:跟隨對象實例存儲在堆上。

值類型實例變量直「內嵌」在對象實例中。跟隨對象實例內存釋放而被釋放。

引用類型實例變量通過指針「引用」堆上的引用類型實例,ARC針對引用進行計數管理,自動釋放引用計數為0的對象。

屬性的描述特性

屬性描述特性(Attribute)可以指定屬性不同環境下的不同功能。

  • 讀寫特性
    • 讀寫屬性 readwrite (默認)
    • 只讀屬性 readonly
  • 多線程特性
    • 原子性 atomic (默認)
    • 非原子性 nonatomic
  • 內存管理特性
    • ARC環境
      • 強引用 strong (默認)
      • 弱引用 weak 阻止循環引用
      • 拷貝屬性 copy 為屬性賦值時創建獨立拷貝
    • 其他情況
      • retain
      • assign
      • unsafe_unretained
@property (readonly, nonatomic) NSString* firstName;
  • 避免循環引用:使用weak屬性

4. 函數成員:方法

函數成員 Function Member 描述對象行為

  • 方法 Method
  • 初始化器 Init
  • 析構器 Dealloc

認識方法 Method

函數:代碼段上的可執行指令序列

  • 全局函數(C語言函數)
  • 成員函數(Objective-C方法)

方法是類的成員函數,表達實例行為或類型行為。

所有方法默認為公有方法。沒有private或protected方法。

動態消息分發:方法調用通過運行時動態消息分發實現,在對象上調用方法又稱「向對象發送消息」。

實例方法或類型方法

實例方法——表達實例行為,可以訪問

  • 實例成員(實例屬性、實例變量、實例方法)
  • 類型方法、靜態變量

類方法——表達類型行為,訪問權限:

  • 可以訪問:類型方法、靜態變量
  • 不能訪問:實例成員(實力屬性、實例變量、實例方法)

了解編譯器背后對實例方法和類方法的不同處理:self指針

//實例方法
- (void) print {
    NSLog(@"[%d, %d]", self.x, self.y);
}
//編譯器編譯后:
void print(BLNPoint* self) {
    NSLog(@"[%d, %d]", self.x, self.y);
}

//調用時:
[p1 print];
//編譯器編譯后:
print(p1);

//類型方法
+ (BLNPoint*) getOriginPoint {
    //...

    //在類型方法中,self 等同于類型,與實例方法中的self不同。
    [self process];
    //等同于
    [BLNPoint process];

    [self print];//錯誤
}
//編譯器編譯后:
BLNPoint* getOriginPoint() {
    //...
}

//調用時:
BLNPoint* origin = [BLNPoint getOriginPoint];
//編譯器編譯后:
BLNPoint* origin = getOriginPoint();

方法參數

  • 如果參數類型為值類型,則為傳值方式;如果參數類型為引用類型,則為傳指針方式。
  • 方法可以沒有參數,也可以沒有返回值。
  • 如果方法有參數,方法名約定包含第一個參數名,第二個參數開始需要顯示提供外部參數名。
  • 調用時,第一個參數名忽略,但后面的參數名必須顯式標明。
- (BOOL) isEqualToPoint: (BLNPoint*) point;
- (void) moveToX: (int)x toY: (int)y;

//調用
[p1 isEqualToPoint: p2];
[p1 moveToX: 100 toY: 200];
  • id可以表示所有的類型
id obj = [[BLNPoint alloc] init];
[obj moveToX: 50 toY: 60];
[obj print];
[obj setX: 70];
obj.x = 70;//obj聲明為id時不可以這樣用
  • 理解動態方法調用機制——消息分發表

5. 初始化器與析構器

認識初始化器與析構器

初始化器用于初始化對象實例或者類型,是一個特殊的函數。

  • 對象初始化器:- (id) init 可以重載多個
  • 類型初始化器:+ (void) initialize 只能有一個

析構器用于釋放對象擁有的資源,無返回值的函數。

  • 對象析構器 - (void) dealloc 只能有一個
  • 沒有類型析構器
- (id) init;
- (id) initWithName: (NSString *)name;
- (id) initWithName: (NSString *)name WithPages:  (int)pages;
- (void) dealloc;
+ (void) initialize;

對象初始化器

初始化對象實例時,init通常和alloc搭配使用。

alloc所做的事情——NSObject已實現:

  1. 在堆上分配合適大小的內存。
  2. 將屬性或者實例變量的內存置0。

init所做的事情——可以自定義:

  1. 調用父類初始化器 [super init] (前置調用)。
  2. 初始化當前對象實例變量(注意使用實例變量,不要使用屬性)。

new 相當于調用alloc/init的無參數版本。

- (id) init {
    self = [super init];
    if (self) {
        //...
    }
    return self;
}
- (id) initWithName: (NSString *)name WithPages:  (int)pages {
    self = [super init];
    if (self) {
        //初始化實例對象,使用實例變量,不要使用屬性self.name
        _name = [name copy];
        _pages = pages;
    }
    return self;
}


//調用:

Book* book = [Book new];
//相當于
Book* book = [[Book alloc] init];

類初始化器

類初始化器initialize負責類型級別的初始化。

initialize在每個類使用之前被系統自動調用,且每個進程周期中,只被調用一次。

子類的initialize會自動調用父類的initialize(前置調用)。

+ (void) initialize {
    if (self == [Book class]) {
        //...
    }
}

對象析構器

對象析構器dealloc負責釋放對象擁有的動態資源:

  • 自動實現:1. ARC將對象屬性引用計數減持
  • 手動實現:2. 釋放不受ARC管理的動態內存,如malloc分配的內存
  • 手動實現:3. 關閉非內存資源,如文件句柄、網絡端口...

dealloc由ARC根據對象引用計數規則,在釋放對象內存前自動調用,無法手工調用。

子類的dealloc會自動調用父類的dealloc(后置調用)。

6. 繼承與多態

認識面向對象

封裝 Encapsulation

隱藏對象內部實現細節,對外僅提供公共接口訪問。

繼承 Inheritance

一個類型在另外類型基礎上進行的擴展實現。

多態 Polymorphism

不同類型針對同一行為接口的不同實現方式。

認識繼承 Inheritance

繼承:每一個類只能有一個基類,子類自動繼承基類的:

  • 實例變量
  • 屬性
  • 實例方法
  • 類方法

了解所有類的根類:NSObject

繼承的兩層含義:

  • 成員復用:子類復用基類成員
  • 類型抽象:將子類當做父類來使用(IS-A關系準則Circle is a Shape)
  • Objective-C只支持單繼承,一個類只能有一個基類

  • 如果希望實例變量在類外可以訪問,可以放在接口文件中,必須放在大括號內并加上@public,一般情況下不推薦

@interface Shape: NSObject {
    @public int _data;
}

//調用時
shape->_data++;

認識運行時多態 Polymorphism

多態:子類在父類統一行為接口下,表現不同的實現方式。

對比重寫與重載

  • 子類重寫父類同名同參數方法:子類只可以重寫override父類方法
  • 方法名相同、參數不同:Objective-C不支持方法的重載。

在子類的代碼中,可以使用super來調用基類的實現。

  • self具有多態性,可以指向不同子類
  • super,沒有多態性,僅指向當前父類
  • 屬性的setter和getter方法也可以被重寫

理解self的多態性:

//Shape類
- (void) draw {
    NSLog(@"Shape object draw");
}
- (void) move {
    NSLog(@"Shape object move");
    //這種情況下:self指代rectangle
    //所以會調用rectangle的draw方法
    [self draw];
}

//------------------------------

//Rectangle類繼承自Shape
- (void) draw {
    NSLog(@"Rectangle object draw");
}
/**
//可以理解為Rectangle有一個隱藏的move方法
//和Shape類中的一模一樣
- (void) move {
    NSLog(@"Shape object move");
    //這種情況下:self指代rectangle
    //所以會調用rectangle的draw方法
    [self draw];
}
**/

//----------------------------

//調用時

//Shape是聲明類型,Rectangle是實際類型
//所以這兩行的執行結果是相同的
Shape* rectangle = [[Rectangle alloc] init];
//Rectangle* rectangle = [[Rectangle alloc] init];

[rectangle draw];//Rectangle object draw

//這里會輸出:
//Shape object move
//Rectangle object draw
[rectangle move];

繼承中的init和dealloc

初始化器 init

  • 子類自動繼承基類的初始化器
  • 子類也可以重寫基類初始化器,此時子類初始化器必須首先調用基類的一個初始化器(手工調用)。

析構器 dealloc

  • 子類可以選擇繼承基類析構器,或者重寫基類析構器。
  • 子類析構器執行完畢后,會自動調用基類析構器(后置調用,且在開啟ARC后不支持手工調用)
  • 子類析構器自動具有多態性

Tips: 盡量避免在父類init和dealloc中調用子類重寫的方法。

- (id) init {
    self = [super init];
    if (self) {
        //...
    }
    return self;
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,763評論 6 539
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,238評論 3 428
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 177,823評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,604評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,339評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,713評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,712評論 3 445
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,893評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,448評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,201評論 3 357
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,397評論 1 372
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,944評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,631評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,033評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,321評論 1 293
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,128評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,347評論 2 377

推薦閱讀更多精彩內容