《Effective Objective-C 2.0》:概念篇

蜂鳥.jpg

想必大家都知道這本書,iOS開發(fā)者推薦書籍之一。之前讀過這本書,但是并沒有認(rèn)真的整理,本文純屬筆者的學(xué)習(xí)筆記,如有侵犯還請(qǐng)見諒。

思維導(dǎo)圖文件

結(jié)構(gòu)圖.png

本篇涉及到的面試題

  • @prorperty的本質(zhì)是什么?ivar, getter, setter是如何添加到類中的?
  • @protocolCategory中如何使用@property
  • ARC下,如果不指定任何屬性關(guān)鍵字,默認(rèn)的關(guān)鍵字有哪些?
  • @synthesize@dynamic分別有什么作用?
  • @synthesize合成實(shí)例變量的規(guī)則是什么?假如property名為foo,存在一個(gè)名為_foo的實(shí)例變量,那么還會(huì)自動(dòng)合成新變量嗎?
  • 在有了自動(dòng)合成實(shí)例變量之后,@synthesize還有哪些使用場(chǎng)景?
  • 如何比較兩個(gè)對(duì)象是否相等?如何為自定義對(duì)象實(shí)現(xiàn)等同性比較?
  • objc中向一個(gè)對(duì)象發(fā)送消息[obj foo]objc_msgSend() 函數(shù)之間有什么關(guān)系?
  • 什么時(shí)候會(huì)出現(xiàn) unrecognized selector的異常?
  • 一個(gè)objc對(duì)象如何進(jìn)行內(nèi)存布局?
  • 一個(gè)objc對(duì)象的isa的指針指向什么?有什么作用?
    *objc向一個(gè)nil對(duì)象發(fā)送消息會(huì)發(fā)生什么?

第1條:了解Objective-C語言的起源

Objective-C是由 Smalltalk 演變而來的,后者被稱為消息型語言的鼻祖。

Objective-CJava 等面向?qū)ο笳Z言的區(qū)別:

  • OC : 消息結(jié)構(gòu)
  • Java、C++: 函數(shù)調(diào)用

消息結(jié)構(gòu)和函數(shù)調(diào)用的區(qū)別?

消息結(jié)構(gòu)的語言,其運(yùn)行時(shí)所應(yīng)執(zhí)行的代碼由運(yùn)行環(huán)境來決定;而使用函數(shù)調(diào)用的語言,則由編譯器決定。

什么是動(dòng)態(tài)綁定?

如果調(diào)用的函數(shù)是多態(tài)的,那么在運(yùn)行的時(shí)候通過“虛函數(shù)表”,查找需要執(zhí)行函數(shù)的哪個(gè)實(shí)現(xiàn)。而采用消息結(jié)構(gòu)語言,不論是不是多態(tài),總是在運(yùn)行時(shí)才去查找所要執(zhí)行的方法。編譯器不用關(guān)心接收對(duì)象的類型,接收消息的對(duì)象問題也要在運(yùn)行時(shí)處理,這就是動(dòng)態(tài)綁定。

什么是運(yùn)行時(shí)組件?

運(yùn)行時(shí)組件本質(zhì)上是一種與開發(fā)者所編寫的代碼相鏈接的“動(dòng)態(tài)庫(kù)(dynamic library)” ,將開發(fā)者編寫的代碼整合到一起。運(yùn)行時(shí)組件包含全部的內(nèi)存管理方法,OC全部的數(shù)據(jù)結(jié)構(gòu)和函數(shù)都在運(yùn)行時(shí)組件里面。它的優(yōu)點(diǎn)是:只要更新運(yùn)行時(shí)組件,就可以提升應(yīng)用性能,而其他語言則需要重新編譯應(yīng)用程序代碼。

第6條:理解“屬性”這一概念

什么是屬性?

”屬性“是 Objective-C 的一個(gè)特性,用于封裝對(duì)象中的數(shù)據(jù)。Objective-C對(duì)象通常會(huì)把其所需要的數(shù)據(jù)保存為各種實(shí)例變量。實(shí)例變量通過存取方法來訪問。通過“setter”方法寫入變量,“getter”方法讀取變量。

Java、C++Objective-C屬性對(duì)比


@interface EOCPerson : NSObject {
    @public
    NSString *_firstName;
    NSString *_lastName;
    @private
    NSString *_someInternalData;
}
@end

上述寫法是JavaC++寫法,但是這種寫法存在一些問題:對(duì)象布局在編譯期就固定,每個(gè)變量對(duì)應(yīng)一個(gè)“偏移量”,表示變量距離存放對(duì)象的內(nèi)存區(qū)域的起始地址有多遠(yuǎn)。
如果,添加一個(gè)變量_dateOfBrith_firstName之前,那么之前定義的變量的偏移量都會(huì)發(fā)生變化。各種編程語言都有解決方法,那么,Objective-C是如何解決這個(gè)問題的呢?

第一種方法
把實(shí)例變量當(dāng)成一種存儲(chǔ)偏移量所用的“特殊變量”,交由“類對(duì)象”保管。偏移量會(huì)在運(yùn)行期查找,存儲(chǔ)的偏移量隨著類的定義改變而改變,無論何時(shí)訪問偏移量,都能正確獲取。甚至可以在運(yùn)行期添加新的實(shí)例變量,這就是“應(yīng)用程序二進(jìn)制接口(Application Binary Interface,ABI)”。

ABI作用
ABI定義了生成代碼時(shí)所應(yīng)遵循的規(guī)范。有了“穩(wěn)固的”ABI,我們可以在“clas-continuation分類”或?qū)崿F(xiàn)文件中定義實(shí)例變量。我們可以將實(shí)例變量從Public區(qū)域移走,以保護(hù)與類實(shí)現(xiàn)有關(guān)的內(nèi)容信息。
PS:Swift3.0 ABI還沒有“穩(wěn)固”。

第二種方法
盡量不直接訪問實(shí)例變量,而是通過存取方法來訪問。

@property語法

@interface EOCPerson : NSObject
@property NSString *firstName;
@property NSString *lastName;
@end

@interface EOCPerson : NSObject
- (NSString *)firstName;
- (void)setFirstName:(NSString *)firstName;
- (NSString *)lastName;
- (void)setLastName:(NSString *)lastName;
@end

上述代碼對(duì)類的使用者來說是等效的。通過@property語法,編譯器自動(dòng)生成一套存取方法。

點(diǎn)語法

EOCPerson *person = [EOCPerson new];
person.firstName = @"Bob"; // <=> [person setFirstName:@"Bob"];
NSString *firstName = person.firstName; // <=> [person firstName];

注: 使用“點(diǎn)語法”和調(diào)用存取方法沒有毫差別。

通過上面學(xué)習(xí)知道屬性可以自動(dòng)生成存取方法,其實(shí)屬性功能還很多。自動(dòng)生成實(shí)例變量的名字,在屬性名前加下劃線前綴。也可以手動(dòng)指定實(shí)例變量的名字。通過@synthesize語法。

@implementation EOCPerson
@synthesize firstName = _firstName;
@synthesize lastName = _lastName;
@end

注:推薦默認(rèn),提高可讀性。

不想用自動(dòng)生成存取方法,如何自己實(shí)現(xiàn)呢?這就需要@dynamic語法,可以阻止編譯器自動(dòng)合成存取方法。

@interface EOCPerson : NSObject
@property NSString *firstName;
@property NSString *lastName;
@end

@implementation EOCPerson
@dynamic firstName, lastName;
@end

在編譯訪問屬性的代碼時(shí),即使編譯器發(fā)現(xiàn)沒有定義存取方法,也不會(huì)報(bào)錯(cuò),它相信這些方法能在運(yùn)行期找到。(運(yùn)行期找不到就會(huì)Crash,廢話)

屬性特質(zhì)

屬性特質(zhì)分為四種: 原子性,讀寫權(quán)限內(nèi)存管理語義,方法名

@property (nonatomic, readwrite, copy, getter=myFirstName) NSString *firstName;

原子性

  • atomic 原子性,使用同步鎖。
  • nonatomic 非原子性

兩者區(qū)別?
具備atomic 特質(zhì)的獲取方法會(huì)通過鎖定機(jī)制來確保其操作的原子性。就是為了防止多個(gè)線程同時(shí)讀取同一屬性。然而,在iOS程序中,所有屬性都聲明為“nonatomic”。這是因?yàn)閕OS使用同步鎖開銷較大,另外,不能真正實(shí)現(xiàn)“線程安全”。

讀/寫權(quán)限

  • readwrite 讀/寫
  • readonly 只讀

內(nèi)存管理語義

  • assign : 只針對(duì)“純量類型”。
  • strong :”擁有關(guān)系“
  • weak : “非擁有關(guān)系”
  • unsafe_unretained:與“assign”相似,但是適用于“對(duì)象類型”,“非擁有關(guān)系“類似于”weak“。
  • copy :“拷貝”

方法名

  • getter=<name> :用于指定”獲取方法“的方法名。
  • setter=<name> :用于指定”設(shè)置方法“的方法名,不常用。

第8條:理解”對(duì)象等同性“這一概念

比較對(duì)象時(shí),使用 == 操作符比較的是兩個(gè)指針本身,而不是指針指向的對(duì)象。

那么如何比較對(duì)象呢?對(duì)象相等的依據(jù)是什么?

在Objective-C中,NSObject協(xié)議有兩個(gè)用于判斷等同性的方法

- (BOOL)isEqual:(id)object;
- (NSUInteger)hash;

NSObject協(xié)議對(duì)兩個(gè)方法的默認(rèn)實(shí)現(xiàn)是:當(dāng)且僅當(dāng)內(nèi)存地址完全相同時(shí),這兩個(gè)對(duì)象才相等。

知道了如何比較兩個(gè)對(duì)象等同性,那么自定義的對(duì)象如何實(shí)現(xiàn)等同性呢?想必你已經(jīng)猜到了,那就是覆寫上面兩方法。覆寫的約定:對(duì)象相等,則其哈希碼相等,但是哈希碼相等的對(duì)象未必相等。

例子:

@interface EOCPerson : NSObject
@property (nonatomic, copy) NSString *firstName;
@property (nonatomic, copy) NSString *lastName;
@property (nonatomic, assign) NSUInteger age;
@end

實(shí)現(xiàn)isEqual:方法:

//覆寫父類方法
- (BOOL)isEqual:(id)object {
    
    //1. 如果指針相同,如果相同表示指向同一地址
    if (self == object) {
        return YES;
    }
    //2. 比較對(duì)象所屬的類
    if ([self class] != [object class]) {
        return NO;
    }
    
    //3. 檢測(cè)對(duì)象的所有屬性,只要有不相等的屬性,則判斷對(duì)象不相等
    EOCPerson *otherPerson = (EOCPerson *)object;
    
    if (![_firstName isEqualToString: otherPerson.firstName]) {
        return NO;
    }
    if (![_lastName isEqualToString: otherPerson.lastName]) {
        return NO;
    }
    if (_age != otherPerson.age) {
        return NO;
    }
    return YES;
}

實(shí)現(xiàn)hash方法:

- (NSUInteger)hash {
    NSUInteger firstNameHash = [_firstName hash];
    NSUInteger lastNameHash = [_lastName hash];
    NSUInteger ageHash = _age;
    return firstNameHash ^ lastNameHash ^ ageHash;
}

上述方法既能保持較高效率,又能使生成的哈希碼至少位于一定范圍之內(nèi),而不會(huì)過于頻繁重復(fù)。在編寫hash方法時(shí),要在減少碰撞頻度和降低運(yùn)算復(fù)雜度之間取舍。

特定類所具有的等同性判定方法

  • isEqualToString:
  • isEqualToArray:
  • isEqualToDictionary:

第11條:理解objc_msgSend的作用

之前提到Objective-C是 “消息結(jié)構(gòu)” 語言。那什么是消息?消息有“名稱”或“選擇子(selector)",可以接收參數(shù),而且可能還有返回值。

為什么說Objective-C是真正的動(dòng)態(tài)語言?

如果向某個(gè)對(duì)象傳遞消息,那就會(huì)使用動(dòng)態(tài)綁定機(jī)制來決定需要調(diào)用的方法。在底層,所有的方法都是C函數(shù),然而當(dāng)對(duì)象接收到消息之后,究竟該調(diào)用哪個(gè)方法則完全由運(yùn)行期決定,甚至可以在運(yùn)行期修改,這些特性使得OC成為一門真正的動(dòng)態(tài)語言。

給對(duì)象發(fā)消息:

id returnValue = [someObject messageName: parameter];
  • someObject : 接收者
  • messageName: 選擇子(selector)

selector和接收者合起來成為 “消息”。

實(shí)際上,編譯器調(diào)用下面函數(shù),是消息機(jī)制中的核心函數(shù)。此函數(shù)是 “參數(shù)個(gè)數(shù)可變的函數(shù)”。

void objc_msgSend(id self, SEL cmd, ...)

將代碼轉(zhuǎn)換成:

id returnValue = objc_msgSend(someObject, @selector(messageName:), parameter);

objc_msgSend函數(shù)的工作流程:

objc_msgSend函數(shù)會(huì)依據(jù)接收者與選擇子的類型來調(diào)用適當(dāng)?shù)姆椒?。首先,搜尋接收者?“方法列表(list of methods)” , 如果找到就跳轉(zhuǎn)執(zhí)行,否則沿著繼承體系向上查找,找到即執(zhí)行,最終未找到,就執(zhí)行 “消息轉(zhuǎn)發(fā)(message forwarding)" 操作。

注: 為了提高查找效率,提供了一個(gè)”快速映射表(fast map)“。用來緩存經(jīng)常用到的方法,這是方法的緩存機(jī)制。

邊界情況

  • objc_msgSend_stret : 返回結(jié)構(gòu)體
  • objc_msgSend_fpret : 返回浮點(diǎn)數(shù)
  • objc_msgSendSuper : 給超類發(fā)消息。例如 [super message: parameter]

第12條:理解消息轉(zhuǎn)發(fā)機(jī)制

對(duì)象在收到無法解讀的消息之后會(huì)發(fā)生什么?當(dāng)對(duì)象接收到無法解析的消息后,就會(huì)啟動(dòng) “消息轉(zhuǎn)發(fā)(message forwarding)” 機(jī)制。

消息轉(zhuǎn)發(fā)流程:

第一階段:先征求接收者所屬的類是否能動(dòng)態(tài)添加方法,以處理當(dāng)前這個(gè)“未知的選擇子”,這叫做“動(dòng)態(tài)方法解析”。

第二階段:”完整的消息轉(zhuǎn)發(fā)機(jī)制“。分為兩種情況:如果,接收者查看其它對(duì)象(備援的接收者)能否處理消息,如果能轉(zhuǎn)發(fā)處理。否則,啟動(dòng)完整消息轉(zhuǎn)發(fā)機(jī)制,運(yùn)行期系統(tǒng)就會(huì)把與消息有關(guān)的全部細(xì)節(jié)都封裝到NSInvocation對(duì)象中,在給接收者最后一次機(jī)會(huì),處理當(dāng)前消息。

動(dòng)態(tài)方法解析

+ (BOOL)resolveInstanceMethod:(SEL)selector;

selector :是未知的選擇子。該方法表示類能否新增一個(gè)實(shí)例方法處理選擇子。如果是類方法,則使用:resolveClassMethod:。

備援接收者

- (id)forwardingTargetForSelector:(SEL)selector;

是否能找到備援對(duì)象,找到返回,否則返回nil。

完整消息轉(zhuǎn)發(fā)

- (void)forwardInvocation:(NSInvocation *)invocation;

實(shí)現(xiàn)方式:在觸發(fā)前,先以某種方式改變消息內(nèi)容。比如,追加參數(shù)或者修改選擇子等。如果,最終無法處理消息,則會(huì)調(diào)用NSObject類中doesNotRecoginzeSelector:拋出異常。

在《Effective Objective-C》中給出了完整的例子演示動(dòng)態(tài)方法解析。需要注意的是,在CoreAnimation框架中,CALayer就是用這種方法實(shí)現(xiàn)的,可以隨意添加屬性,然后以鍵值對(duì)形式訪問。

第14條:理解”類對(duì)象“的用意

Class對(duì)象定義在運(yùn)行期程序庫(kù)的頭文件中:

typedef struct objc_class *Classs;
struct objc_class {
  Class isa;
  Class super_class;
  const chat *name;
  long version;
  long info;
  long instance_size;
  struct objc_ivar_list *ivars;
  struct objc_method_list **methodLists;
  struct objc_cache *cache;
  struct objc_protocol_list *protocols;
}
  1. isa:指向metaClass(元類)的指針。
  2. super_class:指向該類的父類,如果該類是根類(NSObject或NSProxy),則為NULL。
  3. version:記錄類的版本信息。主要用于對(duì)象的序列化,可以通過它識(shí)別出不同定義版本中實(shí)例變量布局的改變。
  4. cache:用于緩存常用的方法。當(dāng)接收對(duì)象收到消息時(shí),根據(jù)isa指針去查找相應(yīng)的對(duì)象。實(shí)際上,這個(gè)對(duì)象中有很多方法,只有一少部分經(jīng)常使用,很多方法不常使用或者根本用不上。這種情況下,如果每次接收到消息都去遍歷methodLists,性能較差,所以使用cache保存經(jīng)常使用的方法,在接收到消息后,首先查找cache,如果沒有則查找methodLists,提高查找效率。
    Objective-C Runtime:類與對(duì)象之前學(xué)習(xí)筆記。

super_class指針確立了繼承關(guān)系,而isa指針描述了實(shí)例所屬的類。通過他們可以實(shí)現(xiàn)“類型信息查詢”。

在類繼承體系中查詢類型信息

  • isMemberOfClass: 能夠判斷出對(duì)象是否為某個(gè)特定類的實(shí)例。
  • isKindOfClass: 判斷對(duì)象是否為某類或其派生類的實(shí)例。
NSMutableDictionary * dict = [NSMutableDictionary new];
[dict isMemberOfClass:[NSDictionary class]]; // NO
[dict isMemberOfClass:[NSMutableDictionary class]]; //YES
[dict isKindOfClass:[NSDictionary class]]; //YES
[dict isKindOfClass:[NSArray Class]]; // NO

像這種類型查詢通過isa指針獲取對(duì)象所屬的類,通過super_class指針在繼承體系中游走。

第21條:理解Objective-C錯(cuò)誤模型

Objective-C

  • 只有發(fā)生了使整個(gè)應(yīng)用程序崩潰的嚴(yán)重錯(cuò)誤事,才會(huì)使用異常。

  • 錯(cuò)誤不那么嚴(yán)重的情況下,可以指派”委托方法“來處理錯(cuò)誤,也可以把錯(cuò)誤信息放在NSError對(duì)象里,經(jīng)由”輸出參數(shù)“返回給調(diào)用者。

NSError對(duì)象里封裝了三條信息:

  • Error domain (錯(cuò)誤范圍,其類型為字符串)
  • Error code (錯(cuò)誤碼,其類型為整數(shù))
  • User info (用戶信息,其類型為字典)

NSError常見用法:

  1. 通過協(xié)議來傳遞錯(cuò)誤信息,最常見是NSURLConnection中。
  2. 經(jīng)由方法的”輸出參數(shù)“返回給調(diào)用者。例如:
  - (BOOL)doSomething:(NSError **)error;

使用范例:

 NSError *error = nil;
  BOOL ret = [self doSomething: &error];
  if (error) {
      //錯(cuò)誤信息
  }

自定義Error

//EOCError.h
extern NSString *const EOCErrorDomain;
typedef NS_ENUM(NSUInteger, EOCError) {
    EOCErrorUnknown         = -1,
    EOCErrorInternalInconsistency = 100,
    EOCErrorGeneralFault        = 105,
    EOCErrorBadInput        = 500,
};
//EOCError.m
NSString *const EOCErrorDomain = @"EOCErrorDomain";

錯(cuò)誤范圍應(yīng)該定義成NSString型的全局常量,而錯(cuò)誤碼則定義成枚舉類型。

第22條:理解NSCopying協(xié)議

在Objective-C中,如果要讓自定義的類支持拷貝操作,需要實(shí)現(xiàn)NSCoping協(xié)議,該協(xié)議唯一的方法:

- (id)copyWithZone:(NSZone *)zone;

NSZone 是歷史遺留下來,可以忽略

自定義類實(shí)現(xiàn)Copy功能示例:

#import <Foundation/Foundation.h>

@interface EOCPerson : NSObject
@property (nonatomic, copy) NSString *firstName;
@property (nonatomic, copy) NSString *lastName;
@property (nonatomic, assign) NSUInteger age;

- (instancetype)initWithFirstName:(NSString *)firstName lastName:(NSString *)lastName age:(NSUInteger)age;
@end

實(shí)現(xiàn)協(xié)議中規(guī)定的方法:

@implementation EOCPerson {
    NSMutableSet *_friends;
}

- (instancetype)initWithFirstName:(NSString *)firstName lastName:(NSString *)lastName age:(NSUInteger)age {
    self = [super init];
    if (self) {
        _firstName = firstName;
        _lastName = lastName;
        _age = age;
        _friends = [NSMutableSet new];
    }
    return self;
}
- (instancetype)copyWithZone:(NSZone *)zone {
    
    EOCPerson *copy = [[[self class] allocWithZone: zone] initWithFirstName:_firstName lastName:_lastName age:_age];
    copy->_friends =  [_friends mutableCopy];
    return copy;
}
@end 

由于friends只是EOCPerson的一個(gè)實(shí)例變量不是屬性,無法用點(diǎn)語法訪問,使用->

有時(shí)候我們實(shí)現(xiàn)類分為可變版本和不可變版本那么實(shí)現(xiàn)拷貝方法需要怎么實(shí)現(xiàn)呢?

實(shí)現(xiàn)可變版本復(fù)制需要使用:

- (id)mutableCopyWithZone:(NSZone *)zone;

通常情況下,為了方便可變版本與不可變版本之間的轉(zhuǎn)換,當(dāng)時(shí)用”copyWithZone:“時(shí)返回不可變版本,使用”mutableCopyZone:“返回可變版本。例如:

- [NSMutableArray copy] => NSArray 
- [NSArray mutableCopy] => NSMutableArray

深復(fù)制和淺復(fù)制
深復(fù)制: 在拷貝對(duì)象自身時(shí),將其底層數(shù)據(jù)也一并復(fù)制過去。
淺復(fù)制:只復(fù)制對(duì)象指針,底層數(shù)據(jù)還是原來一份。Foundation中所有的集合類型默認(rèn)情況下都執(zhí)行淺復(fù)制。

第29條:理解引用計(jì)數(shù)

什么是引用計(jì)數(shù)?
引用計(jì)數(shù)是Objective-C管理內(nèi)存的方式。在Java中,使用垃圾收集器管理內(nèi)存,在Objective-C中則使用引用計(jì)數(shù)管理內(nèi)存。

NSObject協(xié)議聲明了三個(gè)方法用于操作計(jì)數(shù)器:

  • retain :遞增引用計(jì)數(shù)
  • release : 遞減引用計(jì)數(shù)
  • autorelease :清理”自動(dòng)釋放池“時(shí),在遞減引用計(jì)數(shù)
    查看引用計(jì)數(shù)的方法:retainCount

自動(dòng)釋放池

在Objective-C的引用計(jì)數(shù)架構(gòu)中,自動(dòng)釋放池是一個(gè)重要特性。調(diào)用release會(huì)立即減少對(duì)象引用計(jì)數(shù),然而有時(shí)我們不調(diào)用它,改為調(diào)用autorelease,此方法會(huì)在稍后遞減對(duì)象引用計(jì)數(shù)。

循環(huán)引用

循環(huán)引用是對(duì)象間存在相互引用的情況,對(duì)象的引用計(jì)數(shù)永遠(yuǎn)不會(huì)降到0,造成內(nèi)存泄露。解決方法:通過”弱引用“解決這個(gè)問題,或者外界命令某個(gè)對(duì)象不再?gòu)?qiáng)引用另一個(gè)對(duì)象。這兩種方法都可以打破循環(huán)引用。

第30條:以ARC簡(jiǎn)化引用計(jì)數(shù)

什么是自動(dòng)引用計(jì)數(shù)?

顧名思義,就是自動(dòng)管理引用計(jì)數(shù)。ARC的是基于核心的內(nèi)存管理語義構(gòu)建的。

在ARC中,我們不能直接操作內(nèi)存管理方法,如下方法:

  • retain
  • release
  • autorelease
  • dealloc

如果無意中調(diào)用,結(jié)果當(dāng)然是編譯錯(cuò)誤。

需要注意的是:ARC只負(fù)責(zé)管理Objective-C對(duì)象的內(nèi)存。CoreFoundation對(duì)象不歸ARC管理,開發(fā)者必須適時(shí)調(diào)用CFRetain / CFRelease。

第37條:理解”塊“這一概念

基本概念

塊和函數(shù)類似,只不過是直接定義在另一個(gè)函數(shù)里面的,和定義它的那個(gè)函數(shù)共享同一范圍的東西。塊用“^”符號(hào)來實(shí)現(xiàn),后面跟著花括號(hào),括號(hào)里面是塊的實(shí)現(xiàn)代碼。
示例:

^{
      //block implementation here
}

塊其實(shí)就是值,而且自由其相關(guān)類型。有Objective-C其他值和對(duì)象一樣,可以把塊賦給其他變量,然后像使用其他變量一樣使用它。

塊的語法結(jié)構(gòu):

return_type (^block_name)(parameter)

塊的強(qiáng)大之處:在聲明它的范圍值,所有變量都可以為其所捕獲。

在默認(rèn)情況下,為塊所捕獲的變量,是不可以在塊里面修改的。 需要添加__block修飾符。

注意:self也是對(duì)象,因而塊在捕獲它時(shí)也會(huì)將其保留,如果self的那個(gè)對(duì)象同時(shí)也保留了塊,那么這樣情況通常會(huì)導(dǎo)致“循環(huán)引用”。

塊的底層實(shí)現(xiàn),在我之前整理的筆記中重識(shí)Objective-C:Block底層實(shí)現(xiàn),這里就不重復(fù)了。

第47條:熟悉系統(tǒng)架構(gòu)

什么是框架?
將一系列代碼封裝為動(dòng)態(tài)庫(kù)(dynamic library),并在其中放入描述其接口的頭文件,這樣做出來的東西稱為框架。

常用框架:

  • Foundation :像NSObject,NSArray,NSDictionary等類都在其中,F(xiàn)oundation框架中的類,使用NS做前綴。

  • CoreFoundation: 確切的說它不是Objective-C框架,但是在Foundation框架中的許多功能,在它中可以找到對(duì)應(yīng)的C語言的API。

  • CFNetwork :提供C語言級(jí)別的網(wǎng)絡(luò)通訊能力。

  • CoreAudio :可以用來操作設(shè)備上的音頻硬件。

  • AVFoundation : 可以用來回放和錄制音頻和視頻。

  • CoreData :提供數(shù)據(jù)保存功能。

  • CoreText : 此框架提供的C語言接口可以高效執(zhí)行文字排版及渲染操作。

請(qǐng)注意: 用純C寫成的框架與用Objective-C寫成的框架一樣重要。

小結(jié)

以上是《Effective Objective-C 2.0》概念部分的全部?jī)?nèi)容,后續(xù)內(nèi)容稍后陸續(xù)整理出來。溫故而知新,重讀此書收獲頗豐。

哎吆,我去,原來是這個(gè)意思啊,之前咋沒想到呢!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容