NSObject方法
Cocoa中大多數(shù)對象是繼承自NSObjectl類的子類(唯一例外的是NSProxy類)
NSObject類定義了一個description實例方法,返回一個描述類內(nèi)容的字符串。這主要是用于調(diào)試,從這個方法通過GDB命令打印對象并返回打印字符串。
還有一些類方法,它要求一個對象來確定其類;isKindOfClass:方法和isMemberOfClass根據(jù)對象來確定其類;respondsToSelector:表明一個對象是否可以接受一個特定的消息;conformsToProtocol:表明一個對象是否要求實現(xiàn)一個由協(xié)議定義的特定的方法;methodForSelector:提供一個方法實現(xiàn)地址,這個方法給一個對象自我反省能力。
消息傳遞
本章介紹了消息傳遞是如何轉(zhuǎn)換為objc_msgsend函數(shù)調(diào)用,它說明了如何可以利用objc_msgsend,如果你需要使用它,如何可以規(guī)避動態(tài)綁定。
objc_msgSend函數(shù)
在Objective - C中,消息沒有綁定到方法實現(xiàn),直到Runtime時, 編譯器才將消息傳遞綁定到方法的實現(xiàn)。
通過調(diào)用objc_msgSend插入一個消息功能。該函數(shù)所提到的參數(shù)主要有兩個selector和method(receiver指向Method)
objc_msgSend(receiver, selector)
在所有Runtime 以char *定義的API都被視為UTF-8編碼
在消息傳遞的任何參數(shù)都交給objc_msgSend來處理
objc_msgSend(receiver, selector, arg1, arg2, ...)
消息傳遞函數(shù)動態(tài)綁定所需的一切事情
它首先通過IMP指針查找的程序的實現(xiàn)(方法實現(xiàn))緊接著查找選擇器(selector),由于同樣的方法可以用不同的類來實現(xiàn),它能夠精確定位取決于接收器的類。
然后調(diào)用該程序,通過選擇器來接收對象(一個指向接收數(shù)據(jù)的指針),緊接著接收傳入的參數(shù)以及特定的方法,
最后,它將程序返回的值作為自身的返回值
消息傳遞的關(guān)鍵在于編譯器為每個類和對象生成結(jié)構(gòu)。每一類結(jié)構(gòu)都包含了這2個基本要素:
一個指向父類。。
一個指向類調(diào)度表。這個表將方法的選擇器列表與其對應(yīng)類的方法地址關(guān)聯(lián),通過setOrigin方法實現(xiàn)selector選擇器與method實現(xiàn)的地址相關(guān)聯(lián)起來,顯示的選擇器與顯示的方法關(guān)聯(lián) ,等等。當一個新的對象創(chuàng)建時,并且實例變量被初始化時,將分配給他們內(nèi)存,對象的變量是一個指向class結(jié)構(gòu)的指針,這個指針被稱為isa,能通過這個指針訪問他自身的類以及訪問它所有父類
isa指針等效”objc_object"結(jié)構(gòu)體,繼承自NSObject的或NSProxy時,所有對象都自動有一個isa變量。
類和對象結(jié)構(gòu)消息傳遞框架如圖1-1所示

(1)當一個消息被發(fā)送到一個對象時,消息傳遞函數(shù)遵循該對象的isa指針,該指針指向調(diào)度表中查找方法選擇器的類結(jié)構(gòu)。如果它找不到選擇器,消息傳遞通過objc_msgsend指向父類的調(diào)度表并試圖查找選擇器。連續(xù)的失敗造成objc_msgsend攀登類層次結(jié)構(gòu)直至NSObject類。一旦它(函數(shù)調(diào)用表中輸入的方法)找到了選擇器,將其傳遞給接收對象的數(shù)據(jù)結(jié)構(gòu)。
(2)這是在Runtime被選擇實現(xiàn)的方法,或者用面向?qū)ο缶幊痰男性捳f,即這個方法是被動態(tài)的綁定到消息。
(3)為了加快消息傳遞處理,通過Runtime系統(tǒng)的緩存,使用它們的選擇器和方法地址,每一個類都有單獨的緩存,它可以包含繼承的方法選擇器以及類中定義的方法。在搜索調(diào)度表之前,消息傳遞程序首先檢查接收對象類的緩存(在理論上,一個被使用的方法可能會被再次使用)。如果該方法選擇器在緩存中,則消息僅略慢于函數(shù)調(diào)用。當一個程序運行足夠長的時間直至可以“活躍”它的緩存時,幾乎所有的消息都會發(fā)送到緩存的方法。在高速緩存的動態(tài)變化下,以適應(yīng)新消息的程序運行。
獲得方法地址
規(guī)避動態(tài)綁定的唯一方法是獲取一個方法的地址,將其稱為一個函數(shù)。在特殊的適當情況下,特定的方法將被執(zhí)行多次,每次執(zhí)行該方法時,都要避免架空的消息
在一個NSObject類中定義的方法,methodForSelector:,你可以要求一個指針來實現(xiàn)該方法,然后用指針來調(diào)用程序。methodForSelector:返回必須小心地轉(zhuǎn)換為適當?shù)暮瘮?shù)類型。返回的參數(shù)類型都應(yīng)經(jīng)過一定的計算
下面的例子展示了如何實現(xiàn)setFilled程序
void (*setter)(id, SEL, BOOL);
int i;
setter = (void (*)(id, SEL, BOOL))[target
methodForSelector:@selector(setFilled:)];
for ( i = 0 ; i < 1000 ; i++ )
setter(targetList[i], @selector(setFilled:), YES);
參數(shù)傳遞給程序接收的對象(self)和方法選擇器(_cmd)。這幾個參數(shù)都隱藏在方法語法中,但是當該方法被稱為函數(shù)時,必須進行顯式的調(diào)用。
使用methodForSelector:規(guī)避動態(tài)綁定可以節(jié)省通信所需要的大部分時間。然而,存儲過程將是顯著的,只有特定的信息重復多次,如在上面的循環(huán)顯示,這樣存儲才有更意義
注意,methodForSelector:由Cocoa Runtime系統(tǒng)提供的;它不是一個功能而是Objective-C語言本身。
動態(tài)方法解析
在有些情況下,你可能要動態(tài)地提供方法的實現(xiàn)。例如,Objective - C聲明屬性特征
@dynamic propertyName;
它告訴編譯器,將動態(tài)提供與該屬性相關(guān)聯(lián)的方法。
你可以通過實現(xiàn)方法resolveInstanceMethod:和resolveClassMethod:
動態(tài)提供了一個實例和類方法分別指定selector實現(xiàn)。
一個Objective - C方法僅僅是一個C函數(shù),至少需要兩個參數(shù)self和_cmd。
void dynamicMethodIMP(id self, SEL _cmd) {
// implementation ....
}
您可以添加一個函數(shù)到一個類把它作為一個方法來使用函數(shù)class_addMethod。
@implementation MyClass
+ (BOOL)resolveInstanceMethod:(SEL)aSEL
{
if (aSEL == @selector(resolveThisMethodDynamically)) {
class_addMethod([self class], aSEL, (IMP) dynamicMethodIMP, "v@:");
return YES;
}
return [super resolveInstanceMethod:aSEL];
}
@end
轉(zhuǎn)發(fā)方法(如消息轉(zhuǎn)發(fā)描述)和動態(tài)方法的解析,在很大程度上。一個類有機會解決動態(tài)的轉(zhuǎn)發(fā)機制開始之前的方法,如果respondsToSelector:或instancesRespondToSelector:被調(diào)用時,動態(tài)方法是有機會的首先為selector提供一個IMP。如果要實現(xiàn)resolveInstanceMethod:但要實際上selector選擇器可以通過轉(zhuǎn)發(fā)機制轉(zhuǎn)發(fā),還沒有選擇器時返回NO。
消息轉(zhuǎn)發(fā)
當我們發(fā)送一條消息給一個對象時,這個過程消息不經(jīng)過處理的,而且不知道其錯誤。在宣布錯誤之前,Runtime系統(tǒng)給接收對象提供了一個機會來處理這個消息
轉(zhuǎn)發(fā)
如果你發(fā)送一條消息給一個對象,而消息是沒有經(jīng)過處理的,在宣布一個錯誤之前,運行時系統(tǒng)會對對象發(fā)送向forwardInvocation: message
方法發(fā)送消息并且使用NSInvocation對象作為其唯一參數(shù)(NSInvocation對象裝入傳遞的原始消息和參數(shù))
為了查看轉(zhuǎn)發(fā)的范圍和意圖,有以下幾種情況:假設(shè)你正在設(shè)計一個可以響應(yīng)消息的對象,并且你希望它的響應(yīng)包括另一個對象的響應(yīng)。你可以輕而易舉地將一個negotiate信息傳遞給對方
進一步,假設(shè)你想讓你的對象相應(yīng)一個negotiate消息到達準確在另一個類的實現(xiàn)中相應(yīng)。完成這一個方法可以使你的類繼承到另一個類的方法。然后,它可能不會按照這種方式來達成此過程。可能有很好的方式,你的類和其他類,實現(xiàn)negotiate的繼承層次結(jié)構(gòu)的不同分支。
即使你的類不能繼承negotiate的方法,你仍然可以“借用”它通過實施一個版本的方法,簡單地傳遞給一個實例的其他類
- (id)negotiate
{
if ( [someOtherObject respondsTo:@selector(negotiate)] )
return [someOtherObject negotiate];
return self;
}
這樣做的事情可能會有點麻煩,特別是如果有一些消息,你希望你的對象傳遞到另一個對象。你必須實現(xiàn)一個方法來覆蓋每個你想借用其他類的方法。此外,在你不知道的情況下,在你寫代碼的時候,你可能想要向前的全部信息是不可能的。這個集合可能依賴于運行時的事件,它可能在未來實現(xiàn)會改變新的方法和類。
第二次改變機會是由forwardInvocation: message 方法提供:這個方法提供了一個特設(shè)的解決方案,動態(tài)的而不是靜態(tài)的解決方案。它是這樣工作的:當一個對象不能響應(yīng)消息造成它沒有一個方法在消息選擇器匹配時,Runtime系統(tǒng)就會通知對象發(fā)送一個消息forwardInvocation。每一個對象從NSObject類的方法都繼承了一個forwardinvocation方法:。然而,NSObject的版本的方法只是簡單調(diào)用doesNotRecognizeSelector:。通過重寫NSObject的方法來實現(xiàn),你可以利用在這個forwardInvocation: message 方法機會,提供轉(zhuǎn)發(fā)消息到其他對象
為了轉(zhuǎn)發(fā)消息, forwardInvocation: method 需要做的是:
確定信息應(yīng)該在哪里去了
把它發(fā)送給它的原始參數(shù)。
消息可以發(fā)送到invokeWithTarget: method:
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
if ([someOtherObject respondsToSelector:
[anInvocation selector]])
[anInvocation invokeWithTarget:someOtherObject];
else
[super forwardInvocation:anInvocation];
}
forwardInvocation: method獲取處理消息,如果他們不是在接收器中調(diào)用一個現(xiàn)有的方法。例如,如果你希望你的對象將消息轉(zhuǎn)發(fā)到另一個對象,它就不能有一個negotiate的方法。如果是這樣,該消
轉(zhuǎn)發(fā)和多重繼承
在這個例子中,Warrior類的一個實例將一個negotiate信息轉(zhuǎn)發(fā)給一個Diplomat類的實例。Warrior發(fā)出negotiate給Diplomat問它想要做什么。它對negotiate的消息作出反應(yīng),并為所有的實際目的,它確實作出回應(yīng)(盡管這是一個真正的在做Diplomat工作)。
轉(zhuǎn)發(fā)消息的對象,因此從繼承層次結(jié)構(gòu)的2個分支中國“繼承”方法,它自己的分支以及響應(yīng)的對象的分支。在上面的例子中,仿佛Warrior類繼承的Diplomat以及它自己的父類。
轉(zhuǎn)發(fā)提供了通常想要從多個繼承的功能。然而,兩者之間有一個重要的區(qū)別:多繼承將不同的功能組合在一個單獨的對象中。它趨向于大、多方面的對象。轉(zhuǎn)發(fā),另一方面,分配單獨的責任,不同的對象。它將問題分解為較小的對象,但將這些對象關(guān)聯(lián)在一個對消息發(fā)送者透明的方式中
轉(zhuǎn)發(fā)和繼承
雖然轉(zhuǎn)發(fā)模仿繼承NSObject類,但是不要把這兩個方法混淆。方法:和respondstoselector isKindOfClass:方法看起來只能繼承層次,從不在處理轉(zhuǎn)發(fā)鏈。例如,如果一個Warrior對象被問到它是否會對negotiate信息作出反應(yīng),
if ( [aWarrior respondsToSelector:@selector(negotiate)] )
...
答案是NO的,即使它可以接收negotiate的消息,沒有錯誤,并響應(yīng)他們,在一種意義上,通過轉(zhuǎn)發(fā)給一名Diplomat。(參見圖5-1。)
在許多情況下,沒有正確的答案。但它可能不是。如果您使用轉(zhuǎn)發(fā)來設(shè)置代理對象或擴展一個類的功能,顯而易見轉(zhuǎn)發(fā)機制很可能是繼承。如果你希望你的對象作為如果他們真正繼承的對象來轉(zhuǎn)發(fā)消息,你就需要重新實現(xiàn)respondstoselector:和isKindOfClass: methods方法包括你的轉(zhuǎn)發(fā)算法
- (BOOL)respondsToSelector:(SEL)aSelector
{
if ( [super respondsToSelector:aSelector] )
return YES;
else {
/* Here, test whether the aSelector message can *
* be forwarded to another object and whether that *
* object can respond to it. Return YES if it can. */
}
return NO;
}
instancesRespondToSelector:該方法用的也是轉(zhuǎn)發(fā)算法
conformsToProtocol:如果使用該方法也應(yīng)該被添加到列表,將任何遠程信息的作為接收對象
methodSignatureForSelector:返回最終被轉(zhuǎn)發(fā)的消息做出準確的描述,通常用在一個對象能夠?qū)⑾⑥D(zhuǎn)發(fā)給它的代理
- (NSMethodSignature*)methodSignatureForSelector:(SEL)selector
{
NSMethodSignature* signature = [super methodSignatureForSelector:selector];
if (!signature) {
signature = [surrogate methodSignatureForSelector:selector];
}
return signature;
}
Runtime源碼分析
Working with Classes
Adding Classes
Instantiating Classes
Working with Instances
Obtaining Class Definitions
Working with Instance Variables
Associative References
Sending Messages
Working with Methods
Working with Libraries
Working with Selectors
Working with Protocols
Working with Properties
Using Objective-C Language Features
類與對象基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)
本文介紹了Objective-C 2.0 Runtime庫支持的功能和數(shù)據(jù)結(jié)構(gòu)。該功能在可以在/usr/lib/libobjc.A.dylib共享庫的找到具體實現(xiàn)。這個共享庫提供了Objective-C語言的動態(tài)特性的支持,應(yīng)用于Objective-C
該文檔引用主要用于在實際開發(fā)中Objective-C和其他語言之間進行橋接,或低級別的調(diào)試。你通常不需要使用Objective-C Runtime 庫通過編程直接作用在OC
OS X實現(xiàn)Objective-C Runtime庫對于MAC來說是獨一無二的。在其他平臺,GNU編譯器提供不同執(zhí)行情況但是相似的API集合。本文檔只涉及OS X 的實現(xiàn)。
低級別的Objective-C運行時API是在OS X 10.5版本大幅更新。許多功能和所有現(xiàn)有的數(shù)據(jù)結(jié)構(gòu)都用新的函數(shù)來代替。舊的結(jié)構(gòu)和功能中不推薦使用32位、64位模式。API的限制值為32位整型數(shù)即使在64位模式的類數(shù),協(xié)議數(shù),每個類的方法,每個類的Ivars列表,該類方法的參數(shù),sizeof(所有參數(shù)的個數(shù))每個方法和類的版本號。此外,新的Objective-C ABI(這里沒有描述)進一步對32位sizeof(...)進行約束
Data Types
Class定義的數(shù)據(jù)結(jié)構(gòu)
Class:一個不透明的類型,表示一個Objective-C類。
typedef struct objc_class *Class;
數(shù)據(jù)結(jié)構(gòu)
struct objc_class {
Class isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class super_class OBJC2_UNAVAILABLE; // 父類
const char *name OBJC2_UNAVAILABLE; // 類名
long version OBJC2_UNAVAILABLE; // 類的版本信息,默認為0
long info OBJC2_UNAVAILABLE; // 類信息,供運行期使用的一些位標識
long instance_size OBJC2_UNAVAILABLE; // 該類的實例變量大小
struct objc_ivar_list *ivars OBJC2_UNAVAILABLE; // 該類的成員(實例)變量鏈表
struct objc_method_list **methodLists OBJC2_UNAVAILABLE; // 方法定義的鏈表
struct objc_cache *cache OBJC2_UNAVAILABLE; // 方法緩存
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE; // 協(xié)議鏈表
#endif
} OBJC2_UNAVAILABLE;
isa:需要注意的是在Objective-C中,所有的類自身也是一個對象,這個對象的Class里面也有一個isa指針,它指向metaClass(元類),我們會在后面介紹它。
super_class:指向該類的父類,如果該類已經(jīng)是最頂層的根類(如NSObject或NSProxy),則super_class為NULL。
cache:用于緩存最近使用的方法。一個接收者對象接收到一個消息時,它會根據(jù)isa指針去查找能夠響應(yīng)這個消息的對象。在實際使用中,這個對象只有一部分方法是常用的,很多方法其實很少用或者根本用不上。這種情況下,如果每次消息來時,我們都是methodLists中遍歷一遍,性能勢必很差。這時,cache就派上用場了。在我們每次調(diào)用過一個方法后,這個方法就會被緩存到cache列表中,下次調(diào)用的時候runtime就會優(yōu)先去cache中查找,如果cache沒有,才去methodLists中查找方法。這樣,對于那些經(jīng)常用到的方法的調(diào)用,但提高了調(diào)用的效率。
version:我們可以使用這個字段來提供類的版本信息。這對于對象的序列化非常有用,它可是讓我們識別出不同類定義版本中實例變量布局的改變。
注意:OBJC2_UNAVAILABLE是一個Apple對Objc系統(tǒng)運行版本進行約束的宏定義,主要為了兼容非Objective-C 2.0的遺留版本,但我們?nèi)阅軓闹蝎@取一些有用信息
objc_method_description數(shù)據(jù)結(jié)構(gòu)
objc_method_description:定義一個Objetive-C方法
struct objc_method_description { SEL name; char *types; };
字段:
name
在運行時的方法名稱
types
該方法參數(shù)的類型
objc_method_list數(shù)據(jù)結(jié)構(gòu)
objc_method_list:包含一切方法的定義
定義
struct objc_method_list { struct objc_method_list *obsolete; int method_count; struct objc_method method_list[1]; }
字段
obsolete
保留字段,供將來使用
method_count
在方法列表數(shù)組中指定方法的數(shù)量
method_list
一個指向objc_method數(shù)據(jù)結(jié)構(gòu)數(shù)組
objc_cache數(shù)據(jù)結(jié)構(gòu)
objc_cache:作用于方法調(diào)用的性能優(yōu)化(存儲近期使用的方法緩存結(jié)構(gòu))。其中包含最近使用的方法指針
定義
struct objc_cache { unsigned int mask; unsigned int occupied; Method buckets[1]; };
字段
mask
一個整數(shù),指定分配緩存buckets(塊),這里的buckets塊總數(shù)(減一)。在方法查找,Objective-C Runtime使用該字段通過索引來確定開始線性搜索的buckets數(shù)組。使用邏輯和操作(索引=(掩碼和選擇器))對該方法的selector(選擇器)指針進行屏蔽。這是一個簡單的哈希算法。
occupied
一個整數(shù),指定占用的緩存buckets(塊)總數(shù)。
buckets
一個指向Method data structures(方法數(shù)據(jù)結(jié)構(gòu))數(shù)組。這個數(shù)組可以包含不超過mask + 1 項。注意,指針可以為空,表示緩存buckets(塊)是空著的,并且被占據(jù)的buckets(塊)可能不是連續(xù)的。這個數(shù)組可能會隨著時間的推移而增長。
Discussion
為了限制經(jīng)常訪問的方法而把使用過的方法存入Cache
,當需要執(zhí)行某個方法的時候,通過線性搜索(這個線性搜索算法應(yīng)該使用的是哈希算法,在buckets字段描述有提到,所以效率才有所提高)Cache,這一操作可以大大減緩方法的查找---Objective-C Runtime 函數(shù)存儲指針(* Cache)指向objc_cache數(shù)據(jù)結(jié)構(gòu)
objc_protocol_list數(shù)據(jù)結(jié)構(gòu)
objc_protocol_list:表示協(xié)議列表
struct objc_protocol_list { struct objc_protocol_list *next; int count; Protocol *list[1]; };
字段
next
一個指向另一個objc_protocol_list數(shù)據(jù)結(jié)構(gòu)指針
count
在此協(xié)議列表中的協(xié)議的數(shù)目
list
表示一個指向Class數(shù)據(jù)結(jié)構(gòu)的協(xié)議數(shù)組
objc_method_list數(shù)據(jù)結(jié)構(gòu)
一個正式的協(xié)議是一個類定義,它聲明了一組方法,該方法是一個類必須實現(xiàn)的。這樣的類定義不包含實例變量。一個類定義可以保證實現(xiàn)任意數(shù)量的形式化協(xié)議。
objc_property_attribute_t數(shù)據(jù)結(jié)構(gòu)
typedef struct { const char *name; const char *value; } objc_property_attribute_t;
字段
name
屬性的名稱
value
屬性的值,通常為NULL
Instance Data Types
這些數(shù)據(jù)結(jié)構(gòu)類型表示Object、Classes、superClasses
id 指向一個class的實例指針
objc_object 表示class的實例
objc_super 指定實例的superclass(基類、父類)
objc_object
objc_object:標識類的實例
objc_property_attribute_t數(shù)據(jù)結(jié)構(gòu)
struct objc_object { Class isa; };
字段
isa
指向該對象的類而定義的一個實例指針
Discussion:
objc_super
objc_super:指定實例的父類(superclass)
objc_super數(shù)據(jù)結(jié)構(gòu)
struct objc_super { id receiver; Class class; };
字段
指向id標識的指針。特指一個類的實例
Discussion
編譯器產(chǎn)生一個object_super數(shù)據(jù)結(jié)構(gòu),當它遇到super關(guān)鍵字實例時,作為接收器的信息,它特指父類,應(yīng)該把消息通知發(fā)送給類定義。
class
一個指向Class數(shù)據(jù)結(jié)構(gòu)的指針。特指實例的父類信息
搜集的知識點小結(jié)
NS_ASSUME_NONNULL_BEGIN & NS_ASSUME_NONNULL_END
如果需要每個屬性或每個方法都去指定nonnull和nullable,是一件非常繁瑣的事。蘋果為了減輕我們的工作量,專門提供了兩個宏:NS_ASSUME_NONNULL_BEGIN和NS_ASSUME_NONNULL_END。在這兩個宏之間的代碼,所有簡單指針對象都被假定為 nonnull,因此我們只需要去指定那些nullable的指針。
typedef定義的類型的nullability特性通常依賴于上下文,即使是在Audited Regions中,也不能假定它為nonnull。
復雜的指針類型(如id )必須顯示去指定是nonnull還是nullable。例如,指定一個指向nullable對象的nonnull指針,可以使用”nullable id nonnull”。
我們經(jīng)常使用的NSError **通常是被假定為一個指向nullable NSError對象的nullable指針。
nullable 和 nonnull 。從字面上我們可以猜到,nullable表示對象可以是NULL或nil,而nonnull表示對象不應(yīng)該為空。當我們不遵循這一規(guī)則時,編譯器就會給出警告。
可以使用const關(guān)鍵字的地方都可以使用nullable和nonnull,不過這兩個關(guān)鍵字僅限于使用在指針類型上
- (nullable id)itemWithName:(NSString * nonnull)name
在屬性聲明中,也增加了兩個相應(yīng)的特性,因此上例中的items屬性可以如下聲明:
@property (nonatomic, copy, nonnull) NSArray * items;
當然也可以用以下這種方式:
@property (nonatomic, copy) NSArray * __nonnull items;
推薦使用nonnull這種方式,這樣可以讓屬性聲明看起來更清晰。
__kindof
這修飾符還是很實用的,解決了一個長期以來的小痛點,拿原來的 UITableView 的這個方法來說:
(id)dequeueReusableCellWithIdentifier:(NSString *)identifier;
使用時前面基本會使用 UITableViewCell 子類型的指針來接收返回值,所以這個 API 為了讓開發(fā)者不必每次都蛋疼的寫顯式強轉(zhuǎn),把返回值定義成了 id 類型,而這個 API 實際上的意思是返回一個 UITableViewCell 或 UITableViewCell 子類的實例(__kindof UITableViewCell *)dequeueReusableCellWithIdentifier:(NSString *)identifier;
既明確表明了返回值,又讓使用者不必寫強轉(zhuǎn)。再舉個帶泛型的例子,UIView 的 subviews 屬性被修改成了:
@property (nonatomic, readonly, copy) NSArray<__kindof UIView *> *subviews;
這樣,寫下面的代碼時就沒有任何警告了:
UIButton *button = view.subviews.lastObject;
attribute((always_inline))
我們知道一般的函數(shù)調(diào)用都會通過call的方式來調(diào)用,這樣讓攻擊很容易對一個函數(shù)做手腳,如果是以inline的方式編譯的會,會把該函數(shù)的code拷貝到每次調(diào)用該函數(shù)的地方。而static會讓生成的二進制文件中沒有清晰的符號表,讓逆向的人很難弄清楚邏輯。
attribute((always_inline)) 的意思是強制內(nèi)聯(lián),所有加了attribute((always_inline)) 的函數(shù)再被調(diào)用時不會被編譯成函數(shù)調(diào)用而是直接擴展到調(diào)用函數(shù)體內(nèi),比如定義了函數(shù) attribute((always_inline)) void a()
和
void b(){
a();
}
b 調(diào)用 a 函數(shù)的匯編代碼不會是跳轉(zhuǎn)到a執(zhí)行,而是 a 函數(shù)的代碼直接在 b 內(nèi)成為 b 的一部分。
define inline attribute__((always_inline))
inline 代替attribute__((always_inline))
內(nèi)聲明a的時候可以直接寫成inline void a() 這樣比較方便于attribute__((always_inline))
undef
這是預(yù)編譯指令,和#define搭配使用,意思是取消之前的宏定義。
#define PROC_ADD
void main(void)
{
#ifdef PROC_ADD
// Do this code here then undefined it to run the code in the else
// processing work
#undef PROC_ADD
#else
// now that PROC_ADD has been undefined run this code
// processing work
#endif
}
@private
實例變量只能被聲明它的類訪問.
@protected
實例變量能被聲明它的類和子類訪問,所有沒有顯式制定范圍的實例變量都是.
@public
實例變量可以被在任何地方訪問.
@package
@private outside.使用modern運行時,一個@package實例變量在實現(xiàn)這個類的可執(zhí)行文件鏡像中實際上是@public的,但是在外面就是
@private
Objective-C中的@package與C語言中變量和函數(shù)的private_extern類似。任何在實現(xiàn)類的鏡像之外的代碼想使用這個實例變量都會引發(fā)link error,這個類型最常用于框架類的實例變量,使用@private太限制,使用@protected或者@public又太開放.
YYModel
特性
高性能: 模型轉(zhuǎn)換性能接近手寫解析代碼。
自動類型轉(zhuǎn)換: 對象類型可以自動轉(zhuǎn)換,詳情見下方表格。
類型安全: 轉(zhuǎn)換過程中,所有的數(shù)據(jù)類型都會被檢測一遍,以保證類型安全,避免崩潰問題。
無侵入性: 模型無需繼承自其他基類。
輕量: 該框架只有 5 個文件 (包括.h文件)。
文檔和單元測試: 文檔覆蓋率100%, 代碼覆蓋率99.6%。