RunTime理解與實(shí)戰(zhàn)(一)

什么是Runtime

Runtime 又叫運(yùn)行時(shí),是一套底層的 C 語(yǔ)言 API,其為 iOS 內(nèi)部的核心之一,我們平時(shí)編寫(xiě)的 OC 代碼,底層都是基于它來(lái)實(shí)現(xiàn)的

  • 我們寫(xiě)的代碼在程序運(yùn)行過(guò)程中都會(huì)被轉(zhuǎn)化成runtime的C代碼執(zhí)行,例如[target doSomething];會(huì)被轉(zhuǎn)化成objc_msgSend(target, @selector(doSomething));。
  • OC中一切都被設(shè)計(jì)成了對(duì)象,我們都知道一個(gè)類(lèi)被初始化成一個(gè)實(shí)例,這個(gè)實(shí)例是一個(gè)對(duì)象。實(shí)際上一個(gè)類(lèi)本質(zhì)上也是一個(gè)對(duì)象,在runtime中用結(jié)構(gòu)體表示。
// 通過(guò)類(lèi)名獲取類(lèi)
Class catClass = objc_getClass("Cat"); 

//注意Class實(shí)際上也是對(duì)象,所以同樣能夠接受消息,向Class發(fā)送alloc消息
Cat *cat = objc_msgSend(catClass, @selector(alloc));

 //發(fā)送init消息給Cat實(shí)例cat
 cat = objc_msgSend(cat, @selector(init));

//發(fā)送eat消息給cat,即調(diào)用eat方法
objc_msgSend(cat, @selector(eat));

//匯總消息傳遞過(guò)程
objc_msgSend(objc_msgSend(objc_msgSend(objc_getClass("Cat"), sel_registerName("alloc")), sel_registerName("init")), sel_registerName("eat"));

/// 描述類(lèi)中的一個(gè)方法
typedef struct objc_method *Method;
/// 實(shí)例變量
typedef struct objc_ivar *Ivar;
/// 類(lèi)別Category
typedef struct objc_category *Category;
/// 類(lèi)中聲明的屬性
typedef struct objc_property *objc_property_t;
//類(lèi)在runtime中的表示
struct objc_class { Class isa;//指針,顧名思義,表示是一個(gè)什么,
 //實(shí)例的isa指向類(lèi)對(duì)象,類(lèi)對(duì)象的isa指向元類(lèi)
#if !__OBJC2__ 
Class super_class;//指向父類(lèi)
 const char *name;//類(lèi)名
 long version;
 long info;
 long instance_size struct objc_ivar_list *ivars //成員變量列表
 struct objc_method_list **methodLists; //方法列表
 struct objc_cache *cache;//緩存
 //一種優(yōu)化,調(diào)用過(guò)的方法存入緩存列表,下次調(diào)用先找緩存
 struct objc_protocol_list *protocols //協(xié)議列表
 #endif
}
 OBJC2_UNAVAILABLE;

objc_ivar_lis結(jié)構(gòu)體用來(lái)存儲(chǔ)成員變量的列表,而 objc_ivar則是存儲(chǔ)了單個(gè)成員變量的信息;同理,objc_method_list 結(jié)構(gòu)體存儲(chǔ)著方法數(shù)組的列表,而單個(gè)方法的信息則由 objc_method結(jié)構(gòu)體存儲(chǔ)。

值得注意的時(shí),objc_class中也有一個(gè) isa 指針,這說(shuō)明 Objc 類(lèi)本身也是一個(gè)對(duì)象。為了處理類(lèi)和對(duì)象的關(guān)系,Runtime 庫(kù)創(chuàng)建了一種叫做 Meta Class(元類(lèi)) 的東西,類(lèi)對(duì)象所屬的類(lèi)就叫做元類(lèi)。Meta Class 表述了類(lèi)對(duì)象本身所具備的元數(shù)據(jù)。

我們所熟悉的類(lèi)方法,就源自于 Meta Class。我們可以理解為類(lèi)方法就是類(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)的 isa 指針最終都指向根元類(lèi)。

所以當(dāng) [NSObject alloc]這條消息發(fā)送給類(lèi)對(duì)象的時(shí)候,運(yùn)行時(shí)代碼 objc_msgSend() 會(huì)去它元類(lèi)中查找能夠響應(yīng)消息的方法實(shí)現(xiàn),如果找到了,就會(huì)對(duì)這個(gè)類(lèi)對(duì)象執(zhí)行方法調(diào)用。

消息發(fā)送.png

上圖實(shí)現(xiàn)是 super_class 指針,虛線(xiàn)時(shí) isa 指針。而根元類(lèi)的父類(lèi)是 NSObject,isa指向了自己。而 NSObject 沒(méi)有父類(lèi)。最后 objc_class中還有一個(gè) objc_cache,緩存,它的作用很重要,后面會(huì)提到。

獲取列表

我們可以通過(guò)runtime的一系列方法獲取類(lèi)的一些信息(包括屬性列表,方法列表,成員變量列表,和遵循的協(xié)議列表)。

 unsigned int count;
    //獲取屬性列表
    objc_property_t *propertyList = class_copyPropertyList([self class], &count);
    for (unsigned int i=0; i<count; i++) {
        const char *propertyName = property_getName(propertyList[i]);
        NSLog(@"property---->%@", [NSString stringWithUTF8String:propertyName]);
    }
    
    //獲取方法列表
    Method *methodList = class_copyMethodList([self class], &count);
    for (unsigned int i; i<count; i++) {
        Method method = methodList[i];
        NSLog(@"method---->%@", NSStringFromSelector(method_getName(method)));
    }
    
    //獲取成員變量列表
    Ivar *ivarList = class_copyIvarList([self class], &count);
    for (unsigned int i; i<count; i++) {
        Ivar myIvar = ivarList[i];
        const char *ivarName = ivar_getName(myIvar);
        NSLog(@"Ivar---->%@", [NSString stringWithUTF8String:ivarName]);
    }
    
    //獲取協(xié)議列表
    __unsafe_unretained Protocol **protocolList = class_copyProtocolList([self class], &count);
    for (unsigned int i; i<count; i++) {
        Protocol *myProtocal = protocolList[i];
        const char *protocolName = protocol_getName(myProtocal);
        NSLog(@"protocol---->%@", [NSString stringWithUTF8String:protocolName]);
    }

消息(方法)調(diào)用

消息調(diào)用.png

1.首先檢測(cè)這個(gè) selector是不是要忽略。比如 Mac OS X 開(kāi)發(fā),有了垃圾回收就不理會(huì) retain,release 這些函數(shù)。
2.檢測(cè)這個(gè) selector的 target 是不是 nil,Objc 允許我們對(duì)一個(gè) nil 對(duì)象執(zhí)行任何方法不會(huì) Crash,因?yàn)檫\(yùn)行時(shí)會(huì)被忽略掉。
3.在相應(yīng)操作的對(duì)象中的緩存方法列表中找調(diào)用的方法,如果找到,轉(zhuǎn)向相應(yīng)實(shí)現(xiàn)并執(zhí)行。
4.如果沒(méi)找到,在相應(yīng)操作的對(duì)象中的方法列表中找調(diào)用的方法,如果找到,轉(zhuǎn)向相應(yīng)實(shí)現(xiàn)執(zhí)行
5.如果沒(méi)找到,去父類(lèi)指針?biāo)赶虻膶?duì)象中執(zhí)行3,4.
6.以此類(lèi)推,如果一直到根類(lèi)還沒(méi)找到,轉(zhuǎn)向攔截調(diào)用。
7.如果沒(méi)有重寫(xiě)攔截調(diào)用的方法,程序報(bào)錯(cuò)。

  • 重寫(xiě)父類(lèi)的方法,并沒(méi)有覆蓋掉父類(lèi)的方法,只是在當(dāng)前類(lèi)對(duì)象中找到了這個(gè)方法后就不會(huì)再去父類(lèi)中找了。
  • 如果想調(diào)用已經(jīng)重寫(xiě)過(guò)的方法的父類(lèi)的實(shí)現(xiàn),只需使用super這個(gè)編譯器標(biāo)識(shí),它會(huì)在運(yùn)行時(shí)跳過(guò)在當(dāng)前的類(lèi)對(duì)象中尋找方法的過(guò)程。

1)攔截調(diào)用

在方法調(diào)用中說(shuō)到了,如果沒(méi)有找到方法就會(huì)轉(zhuǎn)向攔截調(diào)用。那么什么是攔截調(diào)用呢。攔截調(diào)用就是,在找不到調(diào)用的方法程序崩潰之前,你有機(jī)會(huì)通過(guò)重寫(xiě)NSObject
的四個(gè)方法來(lái)處理。

+ (BOOL)resolveClassMethod:(SEL)sel;
+ (BOOL)resolveInstanceMethod:(SEL)sel;
//后兩個(gè)方法需要轉(zhuǎn)發(fā)到其他的類(lèi)處理
- (id)forwardingTargetForSelector:(SEL)aSelector;
- (void)forwardInvocation:(NSInvocation *)anInvocation;
  • 第一個(gè)方法是當(dāng)你調(diào)用一個(gè)不存在的類(lèi)方法的時(shí)候,會(huì)調(diào)用這個(gè)方法,默認(rèn)返回NO,你可以加上自己的處理然后返回YES。
  • 第二個(gè)方法和第一個(gè)方法相似,只不過(guò)處理的是實(shí)例方法。
  • 第三個(gè)方法是將你調(diào)用的不存在的方法重定向到一個(gè)其他聲明了這個(gè)方法的類(lèi),只需要你返回一個(gè)有這個(gè)方法的target。
  • 第四個(gè)方法是將你調(diào)用的不存在的方法打包成NSInvocation傳給你。做完你自己的處理后,調(diào)用invokeWithTarget:方法讓某個(gè)target觸發(fā)這個(gè)方法。

2)動(dòng)態(tài)添加方法

當(dāng)調(diào)用一個(gè)未實(shí)現(xiàn)的方法,或者說(shuō)發(fā)送未知的消息給接收者時(shí)候,消息的接受者會(huì)調(diào)用resolveInstanceMethod

//An Objective-C method is simply a C function that take at least two arguments—self and _cmd.
 void run(id self, SEL _cmd, NSNumber *number){ NSLog(@"run for %@", number);}

//收到run:消息時(shí)候,為該類(lèi)添加一個(gè)方法實(shí)現(xiàn)
+ (BOOL)resolveInstanceMethod:(SEL)sel{
 if(sel == NSSelectorFromString(@"run:")){ 
  class_addMethod(self, @selector(run:), run, "v@:*"); 
  return YES;
 }
 return [super resolveInstanceMethod:sel];}
//另外針對(duì)類(lèi)方法的為 resolveClassMethod

其中class_addMethod的四個(gè)參數(shù)分別是:

  • Class cls 給哪個(gè)類(lèi)添加方法,本例中是self
  • SEL name 添加的方法,本例中是重寫(xiě)的攔截調(diào)用傳進(jìn)來(lái)的selector。
  • IMP imp 方法的實(shí)現(xiàn),C方法的方法實(shí)現(xiàn)可以直接獲得。如果是OC方法,可以用+ (IMP)instanceMethodForSelector:(SEL)aSelector;獲得方法的實(shí)現(xiàn)。
  • "v@:*"方法的簽名,代表有一個(gè)參數(shù)的方法。

3)消息轉(zhuǎn)發(fā)

// 第一步,消息接收者沒(méi)有找到對(duì)應(yīng)的方法時(shí)候,會(huì)先調(diào)用此方法,可在此方法實(shí)現(xiàn)中動(dòng)態(tài)添加新的方法
// 返回YES表示相應(yīng)selector的實(shí)現(xiàn)已經(jīng)被找到,或者添加新方法到了類(lèi)中,否則返回NO
+ (BOOL)resolveInstanceMethod:(SEL)sel { 
return YES;
}

// 第二步, 如果第一步的返回NO或者直接返回了YES而沒(méi)有添加方法,該方法被調(diào)用
// 在這個(gè)方法中,我們可以指定一個(gè)可以返回一個(gè)可以響應(yīng)該方法的對(duì)象, 注意如果返回self就會(huì)死循環(huán)
- (id)forwardingTargetForSelector:(SEL)aSelector {
 return nil;
}

// 第三步, 如果forwardingTargetForSelector:返回了nil,則該方法會(huì)被調(diào)用,
系統(tǒng)會(huì)詢(xún)問(wèn)我們要一個(gè)合法的『類(lèi)型編碼(Type Encoding)』
// 若返回 nil,則不會(huì)進(jìn)入下一步,而是無(wú)法處理消息
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector { 
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}

// 當(dāng)實(shí)現(xiàn)了此方法后,-doesNotRecognizeSelector: 將不會(huì)被調(diào)用
// 在這里進(jìn)行消息轉(zhuǎn)發(fā)
- (void)forwardInvocation:(NSInvocation *)anInvocation { 
// 在這里可以改變方法選擇器
 [anInvocation setSelector:@selector(unknown)]; 
// 改變方法選擇器后,需要指定消息的接收者
 [anInvocation invokeWithTarget:self];
}
- (void)unknown {
 NSLog(@"unkown method.......");
}
// 如果沒(méi)有實(shí)現(xiàn)消息轉(zhuǎn)發(fā) forwardInvocation 則調(diào)用此方法
- (void)doesNotRecognizeSelector:(SEL)aSelector {
 NSLog(@"unresolved method :%@", NSStringFromSelector(aSelector));
}

重定向

消息轉(zhuǎn)發(fā)機(jī)制執(zhí)行前,Runtime 系統(tǒng)允許我們替換消息的接收者為其他對(duì)象。通過(guò) - (id)forwardingTargetForSelector:(SEL)aSelector
方法。

- (id)forwardingTargetForSelector:(SEL)aSelector{
 if(aSelector == @selector(mysteriousMethod:)){
 return alternateObject;
 } 
return [super forwardingTargetForSelector:aSelector];}

如果此方法返回 nil 或者 self,則會(huì)計(jì)入消息轉(zhuǎn)發(fā)機(jī)制(forwardInvocation:),否則將向返回的對(duì)象重新發(fā)送消息。

轉(zhuǎn)發(fā)

當(dāng)動(dòng)態(tài)方法解析不做處理返回 NO時(shí),則會(huì)觸發(fā)消息轉(zhuǎn)發(fā)機(jī)制。這時(shí) forwardInvocation:
方法會(huì)被執(zhí)行,我們可以重寫(xiě)這個(gè)方法來(lái)自定義我們的轉(zhuǎn)發(fā)邏輯:

- (void)forwardInvocation:(NSInvocation *)anInvocation{
 if ([someOtherObject respondsToSelector: [anInvocation selector]]) 
[anInvocation invokeWithTarget:someOtherObject];
 else [super forwardInvocation:anInvocation];}

唯一參數(shù)是個(gè) NSInvocation類(lèi)型的對(duì)象,該對(duì)象封裝了原始的消息和消息的參數(shù)。我們可以實(shí)現(xiàn) forwardInvocation:方法來(lái)對(duì)不能處理的消息做一些處理。也可以將消息轉(zhuǎn)發(fā)給其他對(duì)象處理,而不拋出錯(cuò)誤。

轉(zhuǎn)發(fā)與繼承

雖然轉(zhuǎn)發(fā)可以實(shí)現(xiàn)繼承的功能,但是 NSObject 還是必須表面上很?chē)?yán)謹(jǐn),像 respondsToSelector: 和 isKindOfClass:這類(lèi)方法只會(huì)考慮繼承體系,不會(huì)考慮轉(zhuǎn)發(fā)鏈。
如果上圖中的 Warrior對(duì)象被問(wèn)到是否能響應(yīng) negotiate消息:
if ( [aWarrior respondsToSelector:@selector(negotiate)] ) ...

回答當(dāng)然是 NO, 盡管它能接受 negotiate消息而不報(bào)錯(cuò),因?yàn)樗哭D(zhuǎn)發(fā)消息給 Diplomat類(lèi)響應(yīng)消息。如果你就是想要讓別人以為 Warrior 繼承到了 Diplomat 的 negotiate方法,你得重新實(shí)現(xiàn) respondsToSelector:和 isKindOfClass:來(lái)加入你的轉(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;
}

除了 respondsToSelector:和 isKindOfClass:之外,instancesRespondToSelector: 中也應(yīng)該寫(xiě)一份轉(zhuǎn)發(fā)算法。如果使用了協(xié)議,conformsToProtocol:同樣也要加入到這一行列中。
如果一個(gè)對(duì)象想要轉(zhuǎn)發(fā)它接受的任何遠(yuǎn)程消息,它得給出一個(gè)方法標(biāo)簽來(lái)返回準(zhǔn)確的方法描述 methodSignatureForSelector:,這個(gè)方法會(huì)最終響應(yīng)被轉(zhuǎn)發(fā)的消息。從而生成一個(gè)確定的 NSInvocation對(duì)象描述消息和消息參數(shù)。這個(gè)方法最終響應(yīng)被轉(zhuǎn)發(fā)的消息。它需要像下面這樣實(shí)現(xiàn):

- (NSMethodSignature*)methodSignatureForSelector:(SEL)selector{ 
NSMethodSignature* signature = [super methodSignatureForSelector:selector]; 
if (!signature) {
 signature = [surrogate methodSignatureForSelector:selector];
 }
 return signature;
}

4)關(guān)聯(lián)對(duì)象

使用關(guān)聯(lián),我們可以不用修改類(lèi)的定義而為其對(duì)象增加存儲(chǔ)空間。這在我們無(wú)法訪(fǎng)問(wèn)到類(lèi)的源碼的時(shí)候或者是考慮到二進(jìn)制兼容性的時(shí)候是非常有用。 關(guān)聯(lián)是基于關(guān)鍵字的,因此,我們可以為任何對(duì)象增加任意多的關(guān)聯(lián),每個(gè)都使用不同的關(guān)鍵字即可。關(guān)聯(lián)是可以保證被關(guān)聯(lián)的對(duì)象在關(guān)聯(lián)對(duì)象的整個(gè)生命周期都是可用的(在垃圾自動(dòng)回收環(huán)境下也不會(huì)導(dǎo)致資源不可回收)。

//首先定義一個(gè)全局變量,用它的地址作為關(guān)聯(lián)對(duì)象的key
static char associatedObjectKey;
//設(shè)置關(guān)聯(lián)對(duì)象
objc_setAssociatedObject(target, &associatedObjectKey, @"添加的字符串屬性", OBJC_ASSOCIATION_RETAIN_NONATOMIC); 
//獲取關(guān)聯(lián)對(duì)象
NSString *string = objc_getAssociatedObject(target, &associatedObjectKey);
objc_setAssociatedObject的四個(gè)參數(shù):
  • id object給誰(shuí)設(shè)置關(guān)聯(lián)對(duì)象。
  • const void *key關(guān)聯(lián)對(duì)象唯一的key,獲取時(shí)會(huì)用到。
  • id value關(guān)聯(lián)對(duì)象。
  • objc_AssociationPolicy關(guān)聯(lián)策略,有以下幾種策略:
enum { OBJC_ASSOCIATION_ASSIGN = 0, 
OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, 
OBJC_ASSOCIATION_COPY_NONATOMIC = 3, 
OBJC_ASSOCIATION_RETAIN = 01401, OBJC_ASSOCIATION_COPY = 01403 
};
  • 關(guān)鍵字是一個(gè)void類(lèi)型的指針。每一個(gè)關(guān)聯(lián)的關(guān)鍵字必須是唯一的。通常都是會(huì)采用靜態(tài)變量來(lái)作為關(guān)鍵字。
  • 關(guān)聯(lián)策略表明了相關(guān)的對(duì)象是通過(guò)賦值,保留引用還是復(fù)制的方式進(jìn)行關(guān)聯(lián)的;還有這種關(guān)聯(lián)是原子的還是非原子的。這里的關(guān)聯(lián)策略和聲明屬性時(shí)的很類(lèi)似。這種關(guān)聯(lián)策略是通過(guò)使用預(yù)先定義好的常量來(lái)表示的。

5)方法交換

方法交換,顧名思義,就是將兩個(gè)方法的實(shí)現(xiàn)交換。例如,將A方法和B方法交換,調(diào)用A方法的時(shí)候,就會(huì)執(zhí)行B方法中的代碼,反之亦然。



- (void)viewDidLoad {
    [super viewDidLoad];
    
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
    
        SEL simpleness_Sel = @selector(simpleness_jsonToModle);
        SEL complex_Sel = @selector(complex_dicToObject);
        //兩個(gè)方法的Method
        Method simpleMethod = class_getInstanceMethod([self class], simpleness_Sel);
        Method complexMethod = class_getInstanceMethod([self class], complex_Sel);
        
        //首先動(dòng)態(tài)添加方法,實(shí)現(xiàn)是被交換的方法,返回值表示添加成功還是失敗
        BOOL isAdd = class_addMethod([self class], simpleness_Sel, method_getImplementation(complexMethod), method_getTypeEncoding(complexMethod));
        if (isAdd) {
            //如果成功,說(shuō)明類(lèi)中不存在這個(gè)方法的實(shí)現(xiàn)
            //將被交換方法的實(shí)現(xiàn)替換到這個(gè)并不存在的實(shí)現(xiàn)
            class_replaceMethod([self class], simpleness_Sel, method_getImplementation(simpleMethod), method_getTypeEncoding(simpleMethod));
        }else{
            //否則,交換兩個(gè)方法的實(shí)現(xiàn)
            method_exchangeImplementations(simpleMethod, complexMethod);
        }
        
    });
    
    
    //方法交換后 這里實(shí)際調(diào)的是complex_dicToObject 的實(shí)現(xiàn)
    [self simpleness_jsonToModle];
    
//    [self complex_dicToObject];
}

參考文檔

runtime理解
runtime介紹

如果你都看到這里了,請(qǐng)給我點(diǎn)個(gè)贊吧,你的點(diǎn)贊是我裝逼(~ 啊不,堅(jiān)持原創(chuàng))的不竭動(dòng)力。

另外.....

我的愿望是.......

世界和平.........

最后編輯于
?著作權(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)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 2,041評(píng)論 0 9
  • 這篇文章完全是基于南峰子老師博客的轉(zhuǎn)載 這篇文章完全是基于南峰子老師博客的轉(zhuǎn)載 這篇文章完全是基于南峰子老師博客的...
    西木閱讀 30,881評(píng)論 33 466
  • 參考鏈接: http://www.cnblogs.com/ioshe/p/5489086.html 簡(jiǎn)介 Runt...
    樂(lè)樂(lè)的簡(jiǎn)書(shū)閱讀 2,247評(píng)論 0 9
  • Objective-C語(yǔ)言是一門(mén)動(dòng)態(tài)語(yǔ)言,他將很多靜態(tài)語(yǔ)言在編譯和鏈接時(shí)期做的事情放到了運(yùn)行時(shí)來(lái)處理。這種動(dòng)態(tài)語(yǔ)言...
    tigger丨閱讀 1,582評(píng)論 0 8
  • 轉(zhuǎn)載:http://yulingtianxia.com/blog/2014/11/05/objective-c-r...
    F麥子閱讀 828評(píng)論 0 2

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