Runtime API

OC是運行時語言阔拳,只有在程序運行時,才會去確定對象的類型类嗤,并調(diào)用類與對象相應(yīng)的方法糊肠。平時編寫的OC代碼, 在程序運行過程中, 其實最終都是轉(zhuǎn)成了runtime的C語言代碼, runtime算是OC的幕后工作者。利用runtime機制讓我們可以在程序運行時動態(tài)修改類遗锣、對象中的所有屬性货裹、方法,就算是私有方法以及私有屬性都是可以動態(tài)修改的精偿。

runtime是屬于OC的底層, 可以進行一些非常底層的操作弧圆。在程序運行過程中, 動態(tài)創(chuàng)建一個類, 動態(tài)地為某個類添加屬性\方法, 修改屬性值\方法,遍歷一個類的所有成員變量(屬性)\所有方法等笔咽。

相關(guān)知識點

Ivar:定義對象的實例變量搔预,包括類型和名字。
objc_property_t:定義屬性叶组。這個名字可能是為了防止和Objective-C 1.0中的用戶類型沖突拯田,那時候還沒有屬性。
Method:成員方法甩十。這個類型提供了方法的名字(就是選擇器)船庇、參數(shù)數(shù)量和類型吭产,以及返回值(這些信息合起來稱為方法的簽名),還有一個指向代碼的函數(shù)指針(也就是方法的實現(xiàn))鸭轮。
SEL:定義選擇器臣淤。選擇器是方法名的唯一標識符,我理解它就是個字符串。

簡單代碼實現(xiàn)

1.示例結(jié)構(gòu)

示例結(jié)構(gòu)

首先定義了一個繼承NSObject的測試類RC窃爷,里面是一些簡單的屬性邑蒋,方法。
.h文件

#import <Foundation/Foundation.h>

@interface RC : NSObject
@property (nonatomic,copy) NSString *icon;
@property (nonatomic,copy) NSString *intro;
@property (nonatomic,copy) NSString *name;

- (instancetype)initWithDic:(NSDictionary *)dic;
+ (instancetype)testWithDic:(NSDictionary *)dic;

+ (NSArray *)testsList;
@end

.m文件

#import "RC.h"

@implementation RC
- (instancetype)initWithDic:(NSDictionary *)dic
{
if (self = [super init]) {
    [self setValuesForKeysWithDictionary:dic];
}
return self;
}

+ (instancetype)testWithDic:(NSDictionary *)dic
{
return [[self alloc] initWithDic:dic];
}

+ (NSArray *)testsList
{
//加載plist
NSString *path = [[NSBundle mainBundle] pathForResource:@"test" ofType:@"plist"];
NSArray *dicArray = [NSArray arrayWithContentsOfFile:path];

//字典轉(zhuǎn)模型
NSMutableArray *tmpArray = [NSMutableArray array];
for (NSDictionary *dic in dicArray) {
    RC *test = [RC testWithDic:dic];
    [tmpArray addObject:test];
}
return tmpArray;
}
@end

從storyboard里拖一個按鈕吞鸭,關(guān)聯(lián)到viewController里寺董。
viewController.m文件簡單代碼:

#import "ViewController.h"
#import <objc/runtime.h>
#import "RC.h"

@interface ViewController ()

- (IBAction)btn:(UIButton *)sender;

@end

@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];
}

- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}

- (IBAction)btn:(UIButton *)sender {
[[self class] printMethodList];
}

+ (void)printIvarList
{
u_int count;
Ivar *ivarlist = class_copyIvarList([RC class], &count);
for (int i = 0; i < count; i++)
{
    const char *ivarName = ivar_getName(ivarlist[i]);
    NSString *strName  = [NSString stringWithCString:ivarName encoding:NSUTF8StringEncoding];
    NSLog(@"ivarName:%@", strName);
}
}

+ (void)printPropertyList
{
u_int count;
objc_property_t *properties = class_copyPropertyList([RC class], &count);
for (int i = 0; i < count; i++)
{
    const char *propertyName = property_getName(properties[i]);
    NSString *strName  = [NSString stringWithCString:propertyName encoding:NSUTF8StringEncoding];
    NSLog(@"propertyName:%@", strName);
}
}

+ (void)printMethodList
{
u_int count;
Method *methods = class_copyMethodList([RC class], &count);
for (int i = 0; i < count; i++)
{
    SEL methodName = method_getName(methods[i]);
    NSString *strName  = [NSString stringWithCString:sel_getName(methodName) encoding:NSUTF8StringEncoding];
    NSLog(@"methodName:%@", strName);
}
}
@end

因為代碼很簡單,就不上傳項目了刻剥。大家可以看到遮咖,上面最主要的就是取變量,屬性和方法的三個方法:printIvarList造虏,printPropertyList御吞,printMethodList。分別執(zhí)行每個方法漓藕,便能得到RC這個測試類的相關(guān)結(jié)果陶珠。

下面,我們測試下:
執(zhí)行printIvarList方法享钞,我們會得到下面的結(jié)果揍诽。


變量名

執(zhí)行printPropertyList方法,我們會得到下面的結(jié)果栗竖。

屬性名

執(zhí)行printMethodList方法暑脆,我們會得到下面的結(jié)果。

方法名

看完上面的結(jié)果狐肢,以前不太明了的變量和屬性的區(qū)別是不是一目了然了添吗。

程序中self.name其實是調(diào)用的name屬性的getter/setter方法,_name是實例變量。在oc中點表達式其實就是調(diào)用對象的setter和getter方法的一種快捷方式份名。

在ios第一版中糊渊,我們?yōu)檩敵鲂枰瑫r聲明了屬性和底層實例變量栋操,那時馁蒂,屬性是oc語言的一個新的機制用僧,并且要求你必須聲明與之對應(yīng)的實例變量。蘋果將默認編譯器從GCC轉(zhuǎn)換為LLVM(low level virtual machine)辰如,從此不再需要為屬性聲明實例變量了普监。如果LLVM發(fā)現(xiàn)一個沒有匹配實例變量的屬性,它將自動創(chuàng)建一個以下劃線開頭的實例變量。因此鹰椒,我們不再為輸出而聲明實例變量。

在方法名的結(jié)果中我們可以看到呕童,打印出的方法有三個屬性的getter/setter方法和一個實例方法漆际,class_copyMethodList只能得到成員方法的。但是有個奇怪的方法<code>methodName:.cxx_destruct</code>,這個是什么呢夺饲?

.cxx_destruct方法

通過apple的runtime源碼奸汇,不難發(fā)現(xiàn)NSObject執(zhí)行dealloc時調(diào)用_objc_rootDealloc繼而調(diào)用object_dispose隨后調(diào)objc_destructInstance方法,前幾步都是條件判斷和簡單的跳轉(zhuǎn)往声,最后的這個函數(shù)如下:

void *objc_destructInstance(id obj) 
{
if (obj) {
    Class isa_gen = _object_getClass(obj);
    class_t *isa = newcls(isa_gen);

    // Read all of the flags at once for performance.
    bool cxx = hasCxxStructors(isa);
    bool assoc = !UseGC && _class_instancesHaveAssociatedObjects(isa_gen);

    // This order is important.
    if (cxx) object_cxxDestruct(obj);
    if (assoc) _object_remove_assocations(obj);

    if (!UseGC) objc_clear_deallocating(obj);
}

return obj;
}

簡單明確的干了三件事:
1.執(zhí)行一個叫object_cxxDestruct自動釋放實例變量
2.執(zhí)行_object_remove_assocations去除和這個對象assocate的對象(常用于category中添加帶變量的屬性擂找,這也是為什么ARC下沒必要remove一遍的原因)
3.執(zhí)行objc_clear_deallocating,清空引用計數(shù)表并清除弱引用表浩销,將所有weak引用指nil(這也就是weak變量能安全置空的所在)

名為object_cxxDestruct的方法最終成為下面的調(diào)用:

static void object_cxxDestructFromClass(id obj, Class cls)
{
void (*dtor)(id);

// Call cls's dtor first, then superclasses's dtors.

for ( ; cls != NULL; cls = _class_getSuperclass(cls)) {
    if (!_class_hasCxxStructors(cls)) return; 
    dtor = (void(*)(id))
        lookupMethodInClassAndLoadCache(cls, SEL_cxx_destruct);
    if (dtor != (void(*)(id))_objc_msgForward_internal) {
        if (PrintCxxCtors) {
            _objc_inform("CXX: calling C++ destructors for class %s", 
                         _class_getName(cls));
        }
        (*dtor)(obj);
    }
}
}

沿著繼承鏈逐層向上搜尋SEL_cxx_destruct這個selector贯涎,找到函數(shù)實現(xiàn)(void (*)(id)(函數(shù)指針)并執(zhí)行。搜索這個selector的聲明慢洋,發(fā)現(xiàn)是名為.cxx_destruct的方法塘雳,以點開頭的名字,和unix的文件一樣普筹,是有隱藏屬性的败明。

.cxx_destruct方法原本是為了C++對象析構(gòu)的,ARC借用了這個方法插入代碼實現(xiàn)了自動內(nèi)存釋放的工作太防。

經(jīng)過幾次試驗妻顶,發(fā)現(xiàn):
1.只有在ARC下這個方法才會出現(xiàn)
2.只有當前類擁有實例變量時(不論是不是用property)這個方法才會出現(xiàn),且父類的實例變量不會導(dǎo)致子類擁有這個方法
3.出現(xiàn)這個方法和變量是否被賦值蜒车,賦值成什么沒有關(guān)系

結(jié)論

.cxx_destruct是編譯器生成的代碼讳嘱,在.cxx_destruct進行形如objc_storeStrong(&ivar, null)的調(diào)用后,這個實例變量就被release和設(shè)置成nil了醇王。ARC下對象的成員變量于編譯器插入的.cxx_desctruct方法自動釋放呢燥。

更多有趣好玩的東西,我們下次再聊寓娩。

這就是腦洞大開吧
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末叛氨,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子棘伴,更是在濱河造成了極大的恐慌寞埠,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,000評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件焊夸,死亡現(xiàn)場離奇詭異仁连,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評論 3 399
  • 文/潘曉璐 我一進店門饭冬,熙熙樓的掌柜王于貴愁眉苦臉地迎上來使鹅,“玉大人,你說我怎么就攤上這事昌抠』贾欤” “怎么了?”我有些...
    開封第一講書人閱讀 168,561評論 0 360
  • 文/不壞的土叔 我叫張陵炊苫,是天一觀的道長裁厅。 經(jīng)常有香客問我,道長侨艾,這世上最難降的妖魔是什么执虹? 我笑而不...
    開封第一講書人閱讀 59,782評論 1 298
  • 正文 為了忘掉前任,我火速辦了婚禮唠梨,結(jié)果婚禮上袋励,老公的妹妹穿的比我還像新娘。我一直安慰自己姻成,他們只是感情好插龄,可當我...
    茶點故事閱讀 68,798評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著科展,像睡著了一般均牢。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上才睹,一...
    開封第一講書人閱讀 52,394評論 1 310
  • 那天徘跪,我揣著相機與錄音,去河邊找鬼琅攘。 笑死垮庐,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的坞琴。 我是一名探鬼主播哨查,決...
    沈念sama閱讀 40,952評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼剧辐!你這毒婦竟也來了寒亥?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,852評論 0 276
  • 序言:老撾萬榮一對情侶失蹤荧关,失蹤者是張志新(化名)和其女友劉穎溉奕,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體忍啤,經(jīng)...
    沈念sama閱讀 46,409評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡加勤,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,483評論 3 341
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鳄梅。...
    茶點故事閱讀 40,615評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡叠国,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出戴尸,到底是詐尸還是另有隱情煎饼,我是刑警寧澤,帶...
    沈念sama閱讀 36,303評論 5 350
  • 正文 年R本政府宣布校赤,位于F島的核電站,受9級特大地震影響筒溃,放射性物質(zhì)發(fā)生泄漏马篮。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,979評論 3 334
  • 文/蒙蒙 一怜奖、第九天 我趴在偏房一處隱蔽的房頂上張望浑测。 院中可真熱鬧,春花似錦歪玲、人聲如沸迁央。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,470評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽岖圈。三九已至,卻和暖如春钙皮,著一層夾襖步出監(jiān)牢的瞬間蜂科,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,571評論 1 272
  • 我被黑心中介騙來泰國打工短条, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留导匣,地道東北人。 一個月前我還...
    沈念sama閱讀 49,041評論 3 377
  • 正文 我出身青樓茸时,卻偏偏與公主長得像贡定,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子可都,可洞房花燭夜當晚...
    茶點故事閱讀 45,630評論 2 359

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

  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉缓待,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,726評論 0 9
  • OC語言基礎(chǔ) 1.類與對象 類方法 OC的類方法只有2種:靜態(tài)方法和實例方法兩種 在OC中,只要方法聲明在@int...
    奇異果好補閱讀 4,283評論 0 11
  • 數(shù)據(jù)類型 Classoc類的原型typedef struct objc_class* Class; Method方...
    酸菜Amour閱讀 410評論 0 1
  • Runtime庫主要做下面幾件事: 封裝:在這個庫中汹粤,對象可以用C語言中的結(jié)構(gòu)體表示命斧,而方法可以用C函數(shù)來實現(xiàn),另...
    子胥閱讀 295評論 0 0
  • 北加州深度游(10):硅谷人家的Christmas Lights(圣誕燈飾) 圣誕將至嘱兼,我們的鄰居們紛紛用各色燈飾...
    小海_Xiaohai_Chen閱讀 524評論 0 1