runtime

ps:積少成多纵寝,積善成德

引言


曾經(jīng)的我并不知道運(yùn)行時(shí)這東西拢锹,因?yàn)槟莻€(gè)時(shí)候開(kāi)發(fā)并不是經(jīng)常用到這東西,我相信很多初出茅廬的開(kāi)發(fā)者也和我一樣鳞滨,每天雖然看似很忙,但是干的都是碼農(nóng)的活蟆淀。但是我也覺(jué)得這個(gè)過(guò)程是每個(gè)人必須經(jīng)歷的拯啦,你也不用灰心喪氣。好了熔任,不扯多了...今天我們一起來(lái)探討一下運(yùn)行時(shí)褒链。

簡(jiǎn)介


不知道你是否記得在C語(yǔ)言中當(dāng)我們是怎么調(diào)用一個(gè)函數(shù)的?我們都說(shuō)oc是一門(mén)動(dòng)態(tài)的語(yǔ)言笋敞,所以它總是想辦法把一些決定工作從編譯連接推遲到運(yùn)行時(shí)。也就是說(shuō)只有編譯器是不夠的荠瘪,還需要一個(gè)運(yùn)行時(shí)系統(tǒng) (runtime system)來(lái)執(zhí)行編譯后的代碼夯巷。這就是 Objective-C Runtime 系統(tǒng)存在的意義,它是整個(gè)Objc運(yùn)行框架的一塊基石哀墓。

Runtime其實(shí)有兩個(gè)版本:“modern”和 “l(fā)egacy”趁餐。我們現(xiàn)在用的 Objective-C 2.0采用的是現(xiàn)行(Modern)版的Runtime系統(tǒng),只能運(yùn)行在 iOS 和 OS X 10.5 之后的64位程序中篮绰。而OSX較老的32位程序仍采用 Objective-C 1中的(早期)Legacy 版本的 Runtime系統(tǒng)后雷。這兩個(gè)版本最大的區(qū)別在于當(dāng)你更改一個(gè)類(lèi)的實(shí)例變量的布局時(shí),在早期版本中你需要重新編譯它的子類(lèi),而現(xiàn)行版就不需要臀突。

Runtime基本是用C和匯編寫(xiě)的勉抓,可見(jiàn)蘋(píng)果為了動(dòng)態(tài)系統(tǒng)的高效而作出的努力。你可以在這里下到蘋(píng)果維護(hù)的開(kāi)源代碼候学。蘋(píng)果和GNU各自維護(hù)一個(gè)開(kāi)源的runtime版本藕筋,這兩個(gè)版本之間都在努力的保持一致。

交互


Objc 從三種不同的層級(jí)上與 Runtime 系統(tǒng)進(jìn)行交互梳码,分別是通過(guò) Objective-C 源代碼隐圾,通過(guò) Foundation 框架的NSObject類(lèi)定義的方法,通過(guò)對(duì) runtime 函數(shù)的直接調(diào)用掰茶。

Objective-C源代碼

大部分情況下你就只管寫(xiě)你的Objc代碼就行暇藏,runtime 系統(tǒng)自動(dòng)在幕后辛勤勞作著。

NSObject的方法

Cocoa 中大多數(shù)類(lèi)都繼承于NSObject類(lèi)濒蒋,也就自然繼承了它的方法盐碱。最特殊的例外是NSProxy,它是個(gè)抽象超類(lèi)啊胶,它實(shí)現(xiàn)了一些消息轉(zhuǎn)發(fā)有關(guān)的方法甸各,可以通過(guò)繼承它來(lái)實(shí)現(xiàn)一個(gè)其他類(lèi)的替身類(lèi)或是虛擬出一個(gè)不存在的類(lèi),說(shuō)白了就是領(lǐng)導(dǎo)把自己展現(xiàn)給大家風(fēng)光無(wú)限焰坪,但是把活兒都交給幕后小弟去干趣倾。

有的NSObject中的方法起到了抽象接口的作用,比如description方法需要你重載它并為你定義的類(lèi)提供描述內(nèi)容某饰。NSObject還有些方法能在運(yùn)行時(shí)獲得類(lèi)的信息儒恋,并檢查一些特性,比如class返回對(duì)象的類(lèi)黔漂;isKindOfClass:和isMemberOfClass:則檢查對(duì)象是否在指定的類(lèi)繼承體系中诫尽;respondsToSelector:檢查對(duì)象能否響應(yīng)指定的消息;conformsToProtocol:檢查對(duì)象是否實(shí)現(xiàn)了指定協(xié)議類(lèi)的方法炬守;methodForSelector:則返回指定方法實(shí)現(xiàn)的地址牧嫉。

Runtime的函數(shù)

Runtime 系統(tǒng)是一個(gè)由一系列函數(shù)和數(shù)據(jù)結(jié)構(gòu)組成,具有公共接口的動(dòng)態(tài)共享庫(kù)减途。頭文件存放于/usr/include/objc目錄下酣藻。許多函數(shù)允許你用純C代碼來(lái)重復(fù)實(shí)現(xiàn) Objc 中同樣的功能。雖然有一些方法構(gòu)成了NSObject類(lèi)的基礎(chǔ)鳍置,但是你在寫(xiě) Objc 代碼時(shí)一般不會(huì)直接用到這些函數(shù)的辽剧,除非是寫(xiě)一些 Objc 與其他語(yǔ)言的橋接或是底層的debug工作。在Objective-C Runtime Reference中有對(duì) Runtime 函數(shù)的詳細(xì)文檔税产。

Runtime術(shù)語(yǔ)


還記得引言中的objc_msgSend:方法吧怕轿,它的真身是這樣的:

id objc_msgSend (idself, SEL op, ... );

下面將會(huì)逐漸展開(kāi)介紹一些術(shù)語(yǔ)偷崩,其實(shí)它們都對(duì)應(yīng)著數(shù)據(jù)結(jié)構(gòu)。

SEL

objc_msgSend函數(shù)第二個(gè)參數(shù)類(lèi)型為SEL撞羽,它是selector在Objc中的表示類(lèi)型(Swift中是Selector類(lèi))阐斜。selector是方法選擇器,可以理解為區(qū)分方法的 ID放吩,而這個(gè) ID 的數(shù)據(jù)結(jié)構(gòu)是SEL:

typedef struct objc_selector *SEL;

其實(shí)它就是個(gè)映射到方法的C字符串智听,你可以用 Objc 編譯器命令@selector()或者 Runtime 系統(tǒng)的sel_registerName函數(shù)來(lái)獲得一個(gè)SEL類(lèi)型的方法選擇器。

不同類(lèi)中相同名字的方法所對(duì)應(yīng)的方法選擇器是相同的渡紫,即使方法名字相同而變量類(lèi)型不同也會(huì)導(dǎo)致它們具有相同的方法選擇器到推,于是 Objc 中方法命名有時(shí)會(huì)帶上參數(shù)類(lèi)型(NSNumber一堆抽象工廠方法拿走不謝),Cocoa 中有好多長(zhǎng)長(zhǎng)的方法哦惕澎。

id

objc_msgSend第一個(gè)參數(shù)類(lèi)型為id莉测,大家對(duì)它都不陌生,它是一個(gè)指向類(lèi)實(shí)例的指針:

typedef struct objc_object *id;

那objc_object又是啥呢:

struct objc_object { Class isa; };

objc_object結(jié)構(gòu)體包含一個(gè)isa指針唧喉,根據(jù)isa指針就可以順藤摸瓜找到對(duì)象所屬的類(lèi)捣卤。

PS:isa指針不總是指向?qū)嵗龑?duì)象所屬的類(lèi),不能依靠它來(lái)確定類(lèi)型八孝,而是應(yīng)該用class方法來(lái)確定實(shí)例對(duì)象的類(lèi)董朝。因?yàn)镵VO的實(shí)現(xiàn)機(jī)理就是將被觀察對(duì)象的isa指針指向一個(gè)中間類(lèi)而不是真實(shí)的類(lèi),這是一種叫做isa-swizzling的技術(shù)干跛,詳見(jiàn)官方文檔

Class

之所以說(shuō)isa是指針是因?yàn)镃lass其實(shí)是一個(gè)指向objc_class結(jié)構(gòu)體的指針:

typedef struct objc_class *Class;

而objc_class就是我們摸到的那個(gè)瓜子姜,里面的東西多著呢:

struct objc_class {

Class isa? OBJC_ISA_AVAILABILITY;

#if !__OBJC2__

Class super_class? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;

const char*name? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;

long version? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;

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;

#endif

} OBJC2_UNAVAILABLE;

可以看到運(yùn)行時(shí)一個(gè)類(lèi)還關(guān)聯(lián)了它的超類(lèi)指針,類(lèi)名楼入,成員變量哥捕,方法,緩存嘉熊,還有附屬的協(xié)議遥赚。

PS:OBJC2_UNAVAILABLE之類(lèi)的宏定義是蘋(píng)果在? Objc? 中對(duì)系統(tǒng)運(yùn)行版本進(jìn)行約束的黑魔法,為的是兼容非Objective-C 2.0的遺留邏輯阐肤,但我們?nèi)阅軓闹蝎@得一些有價(jià)值的信息凫佛,有興趣的可以查看源代碼。

Objective-C 2.0 的頭文件雖然沒(méi)暴露出objc_class結(jié)構(gòu)體更詳細(xì)的設(shè)計(jì)孕惜,我們依然可以從Objective-C 1.0 的定義中小窺端倪:

在objc_class結(jié)構(gòu)體中:ivars是objc_ivar_list指針愧薛;methodLists是指向objc_method_list指針的指針。也就是說(shuō)可以動(dòng)態(tài)修改*methodLists的值來(lái)添加成員方法诊赊,這也是Category實(shí)現(xiàn)的原理厚满,同樣解釋了Category不能添加屬性的原因府瞄。關(guān)于二級(jí)指針碧磅,可以參考這篇文章碘箍。而最新版的 Runtime 源碼對(duì)這一塊的描述已經(jīng)有很大變化,可以參考下美團(tuán)技術(shù)團(tuán)隊(duì)的深入理解Objective-C:Category鲸郊。

PS:任性的話(huà)可以在Category中添加@dynamic的屬性丰榴,并利用運(yùn)行期動(dòng)態(tài)提供存取方法或干脆動(dòng)態(tài)轉(zhuǎn)發(fā);或者干脆使用關(guān)聯(lián)度對(duì)象(AssociatedObject)

其中objc_ivar_list和objc_method_list分別是成員變量列表和方法列表:

struct objc_ivar_list {

int ivar_count? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;

#ifdef __LP64__

int space? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;

#endif

/* variable length structure */

struct objc_ivar ivar_list[1]? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;

}? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;

struct objc_method_list {

struct objc_method_list *obsolete? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;

int method_count? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;

#ifdef __LP64__

int space? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;

#endif

/* variable length structure */

struct objc_method method_list[1]? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;

}

如果你C語(yǔ)言不是特別好秆撮,可以直接理解為objc_ivar_list結(jié)構(gòu)體存儲(chǔ)著objc_ivar數(shù)組列表四濒,而objc_ivar結(jié)構(gòu)體存儲(chǔ)了類(lèi)的單個(gè)成員變量的信息;同理objc_method_list結(jié)構(gòu)體存儲(chǔ)著objc_method數(shù)組列表职辨,而objc_method結(jié)構(gòu)體存儲(chǔ)了類(lèi)的某個(gè)方法的信息盗蟆。

最后要提到的還有一個(gè)objc_cache,顧名思義它是緩存舒裤,它在objc_class的作用很重要喳资,在后面會(huì)講到。

不知道你是否注意到了objc_class中也有一個(gè)isa對(duì)象腾供,這是因?yàn)橐粋€(gè) ObjC 類(lèi)本身同時(shí)也是一個(gè)對(duì)象仆邓,為了處理類(lèi)和對(duì)象的關(guān)系,runtime 庫(kù)創(chuàng)建了一種叫做元類(lèi) (Meta Class) 的東西伴鳖,類(lèi)對(duì)象所屬類(lèi)型就叫做元類(lèi)节值,它用來(lái)表述類(lèi)對(duì)象本身所具備的元數(shù)據(jù)。類(lèi)方法就定義于此處榜聂,因?yàn)檫@些方法可以理解成類(lèi)對(duì)象的實(shí)例方法搞疗。每個(gè)類(lèi)僅有一個(gè)類(lèi)對(duì)象,而每個(gè)類(lèi)對(duì)象僅有一個(gè)與之相關(guān)的元類(lèi)峻汉。當(dāng)你發(fā)出一個(gè)類(lèi)似[NSObject alloc]的消息時(shí)贴汪,你事實(shí)上是把這個(gè)消息發(fā)給了一個(gè)類(lèi)對(duì)象 (Class Object) ,這個(gè)類(lèi)對(duì)象必須是一個(gè)元類(lèi)的實(shí)例休吠,而這個(gè)元類(lèi)同時(shí)也是一個(gè)根元類(lèi) (root meta class) 的實(shí)例扳埂。所有的元類(lèi)最終都指向根元類(lèi)為其超類(lèi)。所有的元類(lèi)的方法列表都有能夠響應(yīng)消息的類(lèi)方法瘤礁。所以當(dāng)[NSObject alloc]這條消息發(fā)給類(lèi)對(duì)象的時(shí)候阳懂,objc_msgSend()會(huì)去它的元類(lèi)里面去查找能夠響應(yīng)消息的方法,如果找到了柜思,然后對(duì)這個(gè)類(lèi)對(duì)象執(zhí)行方法調(diào)用岩调。


上圖實(shí)線是super_class指針,虛線是isa指針赡盘。 有趣的是根元類(lèi)的超類(lèi)是NSObject号枕,而isa指向了自己,而NSObject的超類(lèi)為nil陨享,也就是它沒(méi)有超類(lèi)葱淳。

Method

Method是一種代表類(lèi)中的某個(gè)方法的類(lèi)型钝腺。

typedef struct objc_method *Method;

而objc_method在上面的方法列表中提到過(guò),它存儲(chǔ)了方法名赞厕,方法類(lèi)型和方法實(shí)現(xiàn):

struct objc_method {

SEL method_name? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;

char* method_types? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;

IMP method_imp? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;

}? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;

1.方法名類(lèi)型為SEL艳狐,前面提到過(guò)相同名字的方法即使在不同類(lèi)中定義,它們的方法選擇器也相同皿桑。

2.方法類(lèi)型method_types是個(gè)char指針毫目,其實(shí)存儲(chǔ)著方法的參數(shù)類(lèi)型和返回值類(lèi)型。

3.method_imp指向了方法的實(shí)現(xiàn)诲侮,本質(zhì)上是一個(gè)函數(shù)指針镀虐,后面會(huì)詳細(xì)講到。

Ivar

Ivar是一種代表類(lèi)中實(shí)例變量的類(lèi)型沟绪。

typedef struct objc_ivar *Ivar;

而objc_ivar在上面的成員變量列表中也提到過(guò):

struct objc_ivar {

char* ivar_name? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;

char* ivar_type? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;

int ivar_offset? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;

#ifdef __LP64__

int space? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;

#endif

}? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;

可以根據(jù)實(shí)例查找其在類(lèi)中的名字粉私,也就是“反射”:

-(NSString*)nameWithInstance:(id)instance {

unsigned int numIvars =0;

NSString*key=nil;

Ivar * ivars = class_copyIvarList([selfclass], &numIvars);

for(int i =0; i < numIvars; i++) {

Ivar thisIvar = ivars[i];

constchar* type = ivar_getTypeEncoding(thisIvar);

NSString* stringType =? [NSStringstringWithCString:type encoding:NSUTF8StringEncoding];

if(![stringType hasPrefix:@"@"]) {

continue;

}

if((object_getIvar(self, thisIvar) == instance)) {//此處若 crash 不要慌!


key = [NSStringstringWithUTF8String:ivar_getName(thisIvar)];

break;

}

}

free(ivars);

returnkey;

}

class_copyIvarList函數(shù)獲取的不僅有實(shí)例變量近零,還有屬性诺核。但會(huì)在原本的屬性名前加上一個(gè)下劃線。

IMP

IMP在objc.h中的定義是:

typedef id(*IMP)(id, SEL, ...);

它就是一個(gè)函數(shù)指針久信,這是由編譯器生成的窖杀。當(dāng)你發(fā)起一個(gè) ObjC 消息之后,最終它會(huì)執(zhí)行的那段代碼裙士,就是由這個(gè)函數(shù)指針指定的入客。而IMP這個(gè)函數(shù)指針就指向了這個(gè)方法的實(shí)現(xiàn)。既然得到了執(zhí)行某個(gè)實(shí)例某個(gè)方法的入口腿椎,我們就可以繞開(kāi)消息傳遞階段桌硫,直接執(zhí)行方法,這在后面會(huì)提到啃炸。

你會(huì)發(fā)現(xiàn)IMP指向的方法與objc_msgSend函數(shù)類(lèi)型相同铆隘,參數(shù)都包含id和SEL類(lèi)型。每個(gè)方法名都對(duì)應(yīng)一個(gè)SEL類(lèi)型的方法選擇器南用,而每個(gè)實(shí)例對(duì)象中的SEL對(duì)應(yīng)的方法實(shí)現(xiàn)肯定是唯一的膀钠,通過(guò)一組id和SEL參數(shù)就能確定唯一的方法實(shí)現(xiàn)地址;反之亦然裹虫。

Cache

在runtime.h中Cache的定義如下:

typedef struct objc_cache *Cache;

還記得之前objc_class結(jié)構(gòu)體中有一個(gè)struct objc_cache *cache吧肿嘲,它到底是緩存啥的呢,先看看objc_cache的實(shí)現(xiàn)

struct objc_cache {

unsignedi nt mask/* total = mask + 1 */OBJC2_UNAVAILABLE;

unsigned int occupied? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;

Method buckets[1]? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;

};

Cache為方法調(diào)用的性能進(jìn)行優(yōu)化筑公,通俗地講雳窟,每當(dāng)實(shí)例對(duì)象接收到一個(gè)消息時(shí),它不會(huì)直接在isa指向的類(lèi)的方法列表中遍歷查找能夠響應(yīng)消息的方法匣屡,因?yàn)檫@樣效率太低了封救,而是優(yōu)先在Cache中查找际长。Runtime 系統(tǒng)會(huì)把被調(diào)用的方法存到Cache中(理論上講一個(gè)方法如果被調(diào)用,那么它有可能今后還會(huì)被調(diào)用)兴泥,下次查找的時(shí)候效率更高。這根計(jì)算機(jī)組成原理中學(xué)過(guò)的 CPU 繞過(guò)主存先訪問(wèn)Cache的道理挺像虾宇,而我猜蘋(píng)果為提高Cache命中率應(yīng)該也做了努力吧搓彻。

Property

@property標(biāo)記了類(lèi)中的屬性,這個(gè)不必多說(shuō)大家都很熟悉嘱朽,它是一個(gè)指向objc_property結(jié)構(gòu)體的指針:

typedef struct objc_property *Property;

typedef struct objc_property *objc_property_t;//這個(gè)更常用

可以通過(guò)class_copyPropertyList和protocol_copyPropertyList方法來(lái)獲取類(lèi)和協(xié)議中的屬性:

objc_property_t*class_copyPropertyList(Class cls,unsignedint*outCount)

objc_property_t*protocol_copyPropertyList(Protocol *proto,unsignedint*outCount)

返回類(lèi)型為指向指針的指針旭贬,哈哈,因?yàn)閷傩粤斜硎莻€(gè)數(shù)組搪泳,每個(gè)元素內(nèi)容都是一個(gè)objc_property_t指針稀轨,而這兩個(gè)函數(shù)返回的值是指向這個(gè)數(shù)組的指針。

舉個(gè)栗子岸军,先聲明一個(gè)類(lèi):

@interfaceLender :NSObject {

floatalone;

}

@propertyfloatalone;

@end

你可以用下面的代碼獲取屬性列表:

id LenderClass = objc_getClass("Lender");

unsigned int outCount;

objc_property_t *properties = class_copyPropertyList(LenderClass,&outCount);

你可以用property_getName函數(shù)來(lái)查找屬性名稱(chēng):

const char *property_getName(objc_property_t property)

你可以用class_getProperty和protocol_getProperty通過(guò)給出的名稱(chēng)來(lái)在類(lèi)和協(xié)議中獲取屬性的引用:

objc_property_t class_getProperty(Class cls, const char *name)

objc_property_t protocol_getProperty(Protocol *proto, const char *name, BOOL isRequiredProperty, BOOL isInstanceProperty)

你可以用property_getAttributes函數(shù)來(lái)發(fā)掘?qū)傩缘拿Q(chēng)和@encode類(lèi)型字符串:

const char *property_getAttributes(objc_property_t property)

把上面的代碼放一起奋刽,你就能從一個(gè)類(lèi)中獲取它的屬性啦:

id LenderClass = objc_getClass("Lender");

unsigned int outCount, i;

objc_property_t*properties = class_copyPropertyList(LenderClass, &outCount);

for(i =0; i < outCount; i++){

objc_property_t property = properties[i];

fprintf(stdout,"%s %s\n", property_getName(property), property_getAttributes(property));

}

對(duì)比下class_copyIvarList函數(shù),使用class_copyPropertyList函數(shù)只能獲取類(lèi)的屬性艰赞,而不包含成員變量佣谐。但此時(shí)獲取的屬性名是不帶下劃線的。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末方妖,一起剝皮案震驚了整個(gè)濱河市狭魂,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌党觅,老刑警劉巖雌澄,帶你破解...
    沈念sama閱讀 212,884評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異杯瞻,居然都是意外死亡镐牺,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)魁莉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)任柜,“玉大人,你說(shuō)我怎么就攤上這事沛厨≈娴兀” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 158,369評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵逆皮,是天一觀的道長(zhǎng)宅粥。 經(jīng)常有香客問(wèn)我,道長(zhǎng)电谣,這世上最難降的妖魔是什么秽梅? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,799評(píng)論 1 285
  • 正文 為了忘掉前任抹蚀,我火速辦了婚禮,結(jié)果婚禮上企垦,老公的妹妹穿的比我還像新娘环壤。我一直安慰自己,他們只是感情好钞诡,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,910評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布郑现。 她就那樣靜靜地躺著,像睡著了一般荧降。 火紅的嫁衣襯著肌膚如雪接箫。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 50,096評(píng)論 1 291
  • 那天朵诫,我揣著相機(jī)與錄音辛友,去河邊找鬼。 笑死剪返,一個(gè)胖子當(dāng)著我的面吹牛废累,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播脱盲,決...
    沈念sama閱讀 39,159評(píng)論 3 411
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼九默,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了宾毒?” 一聲冷哼從身側(cè)響起驼修,我...
    開(kāi)封第一講書(shū)人閱讀 37,917評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎诈铛,沒(méi)想到半個(gè)月后乙各,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,360評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡幢竹,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,673評(píng)論 2 327
  • 正文 我和宋清朗相戀三年耳峦,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片焕毫。...
    茶點(diǎn)故事閱讀 38,814評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蹲坷,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出邑飒,到底是詐尸還是另有隱情循签,我是刑警寧澤,帶...
    沈念sama閱讀 34,509評(píng)論 4 334
  • 正文 年R本政府宣布疙咸,位于F島的核電站县匠,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜乞旦,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,156評(píng)論 3 317
  • 文/蒙蒙 一贼穆、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧兰粉,春花似錦故痊、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,882評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至客峭,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間抡柿,已是汗流浹背舔琅。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,123評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留洲劣,地道東北人备蚓。 一個(gè)月前我還...
    沈念sama閱讀 46,641評(píng)論 2 362
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像囱稽,于是被迫代替她去往敵國(guó)和親郊尝。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,728評(píng)論 2 351

推薦閱讀更多精彩內(nèi)容

  • 我們常常會(huì)聽(tīng)說(shuō) Objective-C 是一門(mén)動(dòng)態(tài)語(yǔ)言战惊,那么這個(gè)「動(dòng)態(tài)」表現(xiàn)在哪呢流昏?我想最主要的表現(xiàn)就是 Obje...
    Ethan_Struggle閱讀 2,186評(píng)論 0 7
  • 轉(zhuǎn)載:http://yulingtianxia.com/blog/2014/11/05/objective-c-r...
    F麥子閱讀 729評(píng)論 0 2
  • 本文轉(zhuǎn)載自:http://yulingtianxia.com/blog/2014/11/05/objective-...
    ant_flex閱讀 753評(píng)論 0 1
  • 前言 runtime其實(shí)在我們?nèi)粘i_(kāi)發(fā)過(guò)程中很少使用到,尤其是像我現(xiàn)在比較初級(jí)的程序猿就更用不到了吞获。但是去面試很多...
    WolfTin閱讀 620評(píng)論 0 2
  • 本文詳細(xì)整理了 Cocoa 的 Runtime 系統(tǒng)的知識(shí)况凉,它使得 Objective-C 如虎添翼,具備了靈活的...
    lylaut閱讀 795評(píng)論 0 4