iOS方法交換研究

1、Class/SEL/Method/IMP

Class+SEL=>Method=>IMP,...

2、class_

class_addMethod/class_replaceMethod/method_exchangeImplementations

class_addMethod//如果要添加的A方法已經(jīng)存在則返回NO
A->B,B->B
method_exchangeImplementations
A->B,B->A
class_replaceMethod
A->B,B->B

3、消息處理機(jī)制

objc_msgSend/NSInvocation+NSMethodSignature/perfermSelector

1、更低層c語(yǔ)言實(shí)現(xiàn)
id objc_msgSend(id self, SEL op, ...)

2、oc的封裝高級(jí)層
NSMethodSignature=返回值類型+參數(shù)(列)類型
SEL=函數(shù)名字
NSInvocation=target+SEL+NSMethodSignature
getArgumentTypeAtIndex:第0個(gè)參數(shù)是target,第1個(gè)參數(shù)是SEL,其它的參數(shù)(列)從第2,3,4,下標(biāo)開(kāi)始。

3、消息處理機(jī)制總結(jié):

那么 objc_msgSend 到底是怎么工作的呢?

在Objective-C中,消息直到運(yùn)行時(shí)才會(huì)綁定到方法的實(shí)現(xiàn)上。編譯器會(huì)把代碼中[target doSth]轉(zhuǎn)換成 objc_msgSend消息函數(shù),這個(gè)函數(shù)完成了動(dòng)態(tài)綁定的所有事情。它的運(yùn)行流程如下:

檢查selector是否需要忽略。(ps: Mac開(kāi)發(fā)中開(kāi)啟GC就會(huì)忽略retain,release方法。)

檢查target是否為nil。如果為nil,直接cleanup,然后return。(這就是我們可以向nil發(fā)送消息的原因。)

然后在target的Class中根據(jù)Selector去找IMP

尋找IMP的過(guò)程:

先從當(dāng)前class的cache方法列表(cache methodLists)里去找

找到了,跳到對(duì)應(yīng)函數(shù)實(shí)現(xiàn)

沒(méi)找到,就從class的方法列表(methodLists)里找

還找不到,就到super class的方法列表里找,直到找到基類(NSObject)為止

最后再找不到,就會(huì)進(jìn)入動(dòng)態(tài)方法解析和消息轉(zhuǎn)發(fā)的機(jī)制。(這部分知識(shí),下次再細(xì)談)

0:對(duì)應(yīng)SEL(對(duì)應(yīng)參數(shù))找到否 respondsToSelector
1:動(dòng)態(tài)addmethod機(jī)會(huì) 實(shí)例方法調(diào)resolveInstanceMethod, 類方法調(diào)resolveClassMethod
2:重定向target機(jī)會(huì) forwardingTargetForSelector
3:重定向NSInvocation機(jī)會(huì) methodSignatureForSelector/forwardInvocation
4:無(wú)法識(shí)別異常退出 doesNotRecognizeSelector

4、容易混淆的類型


NSObject

@protocol NSObject

OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0)
OBJC_ROOT_CLASS
OBJC_EXPORT
@interface NSObject <NSObject> {
    Class isa  OBJC_ISA_AVAILABILITY;
}




Class
typedef struct objc_class *Class;
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;//方法名字+參數(shù)類型+imp函數(shù)指針
    struct objc_cache *cache                                 OBJC2_UNAVAILABLE;
    struct objc_protocol_list *protocols                     OBJC2_UNAVAILABLE;//協(xié)議?
#endif

} OBJC2_UNAVAILABLE;



SEL
typedef  struct objc_selector *SEL
SEL本質(zhì)是一個(gè)字符串,到底是結(jié)構(gòu)體還是字符串


Method
typedef struct objc_method *Method;
struct objc_method {
    SEL method_name                                          OBJC2_UNAVAILABLE;
    char *method_types                                       OBJC2_UNAVAILABLE;
    IMP method_imp                                           OBJC2_UNAVAILABLE;
}       


IMP


id (*IMP)(id, SEL, ...)
typedef id (*IMP)(id,SEL,...) 要添加的方法


5、方法交換實(shí)踐



//RAC的方法交換。selector和block
// 因?yàn)閐ealloc方法不能用selector,報(bào)錯(cuò)信息:ARC forbids use of 'dealloc' in a @selector
static const void *RACObjectCompoundDisposable = &RACObjectCompoundDisposable;

static NSMutableSet *swizzledClasses() {
    static dispatch_once_t onceToken;
    static NSMutableSet *swizzledClasses = nil;
    dispatch_once(&onceToken, ^{
        swizzledClasses = [[NSMutableSet alloc] init];
    });
    
    return swizzledClasses;
}

static void swizzleDeallocIfNeeded(Class classToSwizzle) {
    @synchronized (swizzledClasses()) {
        NSString *className = NSStringFromClass(classToSwizzle);
        if ([swizzledClasses() containsObject:className]) return;

        SEL deallocSelector = sel_registerName("dealloc");
        //__unsafe_unretained: 并不對(duì)其保持強(qiáng)引用,這一點(diǎn)和__weak修飾符的變量一樣。當(dāng)這塊地址的內(nèi)存被系統(tǒng)回收時(shí),它仍然指向這個(gè)地址。weak會(huì)自動(dòng)變?yōu)閚il。 再次訪問(wèn)__unsafe_unretained釋放了內(nèi)存的地址,會(huì)產(chǎn)生奔潰。
        __block void (*originalDealloc)(__unsafe_unretained id, SEL) = NULL;

        id newDealloc = ^(__unsafe_unretained id self) {
            RACCompoundDisposable *compoundDisposable = objc_getAssociatedObject(self, RACObjectCompoundDisposable);
            [compoundDisposable dispose];

            if (originalDealloc == NULL) {
                struct objc_super superInfo = {
                    .receiver = self,
                    .super_class = class_getSuperclass(classToSwizzle)
                };

                void (*msgSend)(struct objc_super *, SEL) = (__typeof__(msgSend))objc_msgSendSuper;
                msgSend(&superInfo, deallocSelector);
            } else {
                originalDealloc(self, deallocSelector);
            }
        };
        
        IMP newDeallocIMP = imp_implementationWithBlock(newDealloc);
        //如果當(dāng)前類沒(méi)有實(shí)現(xiàn)dealloc,則dealloc直接被替換為新的dealloc的block指針;如果實(shí)現(xiàn)了,則保存dealloc的block指針。
        if (! class_addMethod(classToSwizzle,
                            deallocSelector,
                            newDeallocIMP,
                            "v@:")) {
            // The class already contains a method implementation.
            Method deallocMethod = class_getInstanceMethod(classToSwizzle, deallocSelector);
            
            // We need to store original implementation before setting new implementation
            // in case method is called at the time of setting.
            originalDealloc = (__typeof__(originalDealloc))method_getImplementation(deallocMethod);
            
            // We need to store original implementation again, in case it just changed.
            originalDealloc = (__typeof__(originalDealloc))method_setImplementation(deallocMethod, newDeallocIMP);
        }

        [swizzledClasses() addObject:className];
    }
}


//  yykit 不支持協(xié)議方法的交換
BOOL swizzleInstanceMethod(Class aClass, SEL originalSel, SEL newSel) {
    //    SEL
    //    typedef  struct objc_selector *SEL
    //    SEL本質(zhì)是一個(gè)字符串,到底是結(jié)構(gòu)體還是字符串
    // Method
    //    struct objc_method {
    //        SEL method_name                                          OBJC2_UNAVAILABLE;
    //        char *method_types                                       OBJC2_UNAVAILABLE;
    //        IMP method_imp                                           OBJC2_UNAVAILABLE;
    //    }
    Method originalMethod = class_getInstanceMethod(aClass, originalSel);
    Method newMethod = class_getInstanceMethod(aClass, newSel);
    if (!originalMethod || !newMethod) return NO;
    // 動(dòng)態(tài)添加方法
    //BOOL class_addMethod(Class cls, SEL name, IMP imp,  const char *types)
    class_addMethod(aClass,// 被添加的類
                    originalSel, // 方法名。如果存在則添加不了。如果不存在,可以添加。
                    
                    //        id (*IMP)(id, SEL, ...)
                    //        typedef id (*IMP)(id,SEL,...) 要添加的方法
                    class_getMethodImplementation(aClass, originalSel),
                    
                    method_getTypeEncoding(originalMethod));// 添加方法的返回類型和參數(shù)類型
    class_addMethod(aClass,// 被添加的類
                    // 指定一個(gè)類的方法(成員方法/類方法)
                    newSel,
                    // 把一個(gè)類的方法(成員方法/類方法),轉(zhuǎn)化為c語(yǔ)言的函數(shù)指針。
                    class_getMethodImplementation(aClass, newSel),
                    // 參數(shù):返回值類型,參數(shù)列表
                    method_getTypeEncoding(newMethod));
    
    method_exchangeImplementations(class_getInstanceMethod(aClass, originalSel),
                                   class_getInstanceMethod(aClass, newSel));
    return YES;
}

//   支持協(xié)議方法的交換
BOOL rp_classMethodSwizzle(Class aClass, SEL originalSelector, SEL swizzleSelector, SEL nopSelector) {
    
    Method originalMethod = class_getInstanceMethod(aClass, originalSelector);
    Method swizzleMethod = class_getInstanceMethod(aClass, swizzleSelector);
    
    BOOL didAddMethod = class_addMethod(aClass,
                                        originalSelector,
                                        method_getImplementation(swizzleMethod),
                                        method_getTypeEncoding(swizzleMethod));
    
    if (didAddMethod) {
        Method nopMehtod = class_getInstanceMethod(aClass, nopSelector);
        // 方法1--最妥
        class_replaceMethod(aClass,
                        swizzleSelector,
                        method_getImplementation(nopMehtod),
                        method_getTypeEncoding(nopMehtod));
        // 方法2--如果直接掉nopMethod會(huì)麻煩
        //method_exchangeImplementations(nopMehtod, swizzleMethod);
        
        // 方法3-- 不支持協(xié)議方法的交換
        //char *typeeconding=method_getTypeEncoding(originalMethod);
        //IMP imp1=method_getImplementation(originalMethod);
        //// 如果imp參數(shù)為nil則不能替換
        //class_replaceMethod(aClass, swizzleSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
    } else {
        // 交換method1和method2的
        // A->B,B->A
        method_exchangeImplementations(originalMethod, swizzleMethod);
        //這種不行。沒(méi)有交換,只是僅僅替換。A->B,B->B
        //   class_replaceMethod(aClass, originalSelector, method_getImplementation(swizzleMethod), method_getTypeEncoding(swizzleMethod));
    }
    
    return YES;
}


 // 處理特點(diǎn):強(qiáng)制轉(zhuǎn)化對(duì)應(yīng)的函數(shù)指針。根據(jù)參數(shù)的個(gè)數(shù),強(qiáng)轉(zhuǎn)對(duì)應(yīng)參數(shù)個(gè)數(shù)類型的函數(shù)
 id objc_msgSend(id self, SEL op, ...)
 
 
 //  先把objc_msgSend函數(shù)指針強(qiáng)制轉(zhuǎn)化為void*類型,在強(qiáng)制轉(zhuǎn)化為“返回void類型(無(wú)返回參數(shù)),3個(gè)參數(shù):id,SEL,int8_t”這個(gè)類型的函數(shù)的指針。再調(diào)用此函數(shù)。
 ((void (*)(id, SEL, int8_t))(void *) objc_msgSend)((id)model, meta->_setter, (int8_t)num.charValue);
 
 //  先把objc_msgSend函數(shù)指針強(qiáng)制轉(zhuǎn)化為void*類型,在強(qiáng)制轉(zhuǎn)化為“返回SEL類型,2個(gè)參數(shù):id,SEL”這個(gè)類型的函數(shù)的指針。再調(diào)用此函數(shù)。
 SEL value = ((SEL (*)(id, SEL))(void *)objc_msgSend)((id)self, propertyMeta->_getter);
 
 //  先把objc_msgSend函數(shù)指針強(qiáng)制轉(zhuǎn)化為void*類型,在強(qiáng)制轉(zhuǎn)化為“返回double類型,2個(gè)參數(shù):id,SEL”這個(gè)類型的函數(shù)的指針。再調(diào)用次此數(shù)。
 double num = ((double (*)(id, SEL))(void *) objc_msgSend)((id)self, propertyMeta->_getter);
 
 
 // 先把objc_msgSend函數(shù)指針強(qiáng)制轉(zhuǎn)化為void*類型,在強(qiáng)制轉(zhuǎn)化為“返回void類型(無(wú)返回參數(shù)),3個(gè)參數(shù):id,SEL,double這個(gè)類型的函數(shù)的指針。再調(diào)用次此數(shù)。
 ((void (*)(id, SEL, double))(void *) objc_msgSend)((id)one, propertyMeta->_setter, num);
 
 
 Class v = ((Class (*)(id, SEL))(void *) objc_msgSend)((id)model, propertyMeta->_getter);
 value = v ? NSStringFromClass(v) : nil;
 void *pointer = ((void* (*)(id, SEL))(void *) objc_msgSend)((id)model, property->_getter);
 propertyDesc = [NSString stringWithFormat:@"%p",pointer];





/**
 1、
 - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector OBJC_SWIFT_UNAVAILABLE("");
 
 + (NSMethodSignature *)instanceMethodSignatureForSelector:(SEL)aSelector OBJC_SWIFT_UNAVAILABLE("");
 
 2、
 NSInvocation和NSMethodSingature的target,sel都不匹配也能行。sel的名不一樣也行。
 但是sel的參數(shù)個(gè)數(shù)不一致,會(huì)閃退。
 如果NSMethodSignature返回nil,NSInvocation構(gòu)建會(huì)閃退。
 所以target,sel 最好是一致匹配的。
 為了保證NSMethodSignature不返回nil,NSMethodSingature的target和NSInvocation的target要不一樣。
 
 3、
 target是0,sel是1,其它參數(shù)是2,3,4....
 
 
 總結(jié):
 NSMethodSignature/NSInvocation  可以向任意的target發(fā)送sel消息,傳遞參數(shù),獲取返回值。
 NSMethodSignature:定義:Sel名,參數(shù),返回值
 
 */
+ (void)testNSInvocation{
    //SimpleNSInvocationClassA *a =[SimpleNSInvocationClassA new];
    //NSMethodSignature*signature= [a methodSignatureForSelector:@selector(method_b1:)];
    
    
    
    {
        SimpleNSInvocationClassB*b=[SimpleNSInvocationClassB new];
        NSMethodSignature*signature = [b methodSignatureForSelector:@selector(method_b1:)];
        NSInvocation *invocation=[NSInvocation  invocationWithMethodSignature:signature];
        invocation.target=b;
        invocation.selector=@selector(method_b1:);
        NSString*arg1=@"helloworld";
        [invocation setArgument:&arg1 atIndex:2];
        [invocation invoke];
    }
    
    
    
    {
        SimpleNSInvocationClassB*b=[SimpleNSInvocationClassB new];
        NSMethodSignature*signature = [b methodSignatureForSelector:@selector(method_b4:str2:str3:)];
        NSInvocation *invocation=[NSInvocation  invocationWithMethodSignature:signature];
        invocation.target=b;
        invocation.selector=@selector(method_b4:str2:str3:);
        NSString*arg1=@"helloworld";
        [invocation setArgument:&arg1 atIndex:2];
        BOOL result;
        [invocation getReturnValue:&result];
        
        
        /*
__unsafe_unretained: 并不對(duì)其保持強(qiáng)引用,這一點(diǎn)和__weak修飾符的變量一樣。當(dāng)這塊地址的內(nèi)存被系統(tǒng)回收時(shí),它仍然指向這個(gè)地址。weak會(huì)自動(dòng)變?yōu)閚il。
再次訪問(wèn)__unsafe_unretained釋放了內(nèi)存的地址,會(huì)產(chǎn)生奔潰。
         NSNumber __unsafe_unretained *tempResult;
         [invocation getReturnValue:&tempResult];
         NSNumber *result = tempResult;
         return result;
         
         
         void *tempResult = NULL;
         [invocation getReturnValue:&tempResult];
         NSNumber *result = (__bridge NSNumber *)tempResult;
         return result;
         */
        [invocation invoke];
    }
    
    
    
}




+ (void)testNSINovcationReWrite {

    SimpleNSInvocationClassC*a=[SimpleNSInvocationClassC new];
    a.nocrash=[NoSelDoObj new];
    [a method_a];
    
    {
        TestMethod*tmp= [[self class]new];
        // 因?yàn)镾impleNSInvocationClassC沒(méi)有method_b方法,所以NSMethodSignature用自己類method_b方法來(lái)構(gòu)建,簡(jiǎn)單。如果用signatureWithObjCTypes,則比較麻煩。
        NSMethodSignature*sig=[tmp methodSignatureForSelector:@selector(method_b)];
        NSInvocation*invocation=[NSInvocation invocationWithMethodSignature:sig];
        [invocation setTarget:a];
        [invocation setSelector:@selector(method_b)];
        [invocation invoke];
    }
    
    
    {
        
        [a performSelector:@selector(method_b)];
    }
    
    
    {
        ((void (*)(id, SEL)) (void *)objc_msgSend)((id)a, @selector(method_b) );
    }
}


-(void)method_b{
    
    
}



@interface NoSelDoObj : NSObject

@end
@interface SimpleNSInvocationClassC : NSObject
@property(nonatomic,strong)NoSelDoObj*nocrash;


-(void)method_a;
@end


/**
 執(zhí)行順序:
 1、如果有對(duì)應(yīng)的SEL,則直接執(zhí)行SEL方法
 2、如果不存在對(duì)應(yīng)的SEL則:
 resolveInstanceMethod/resolveClassMethod->返回YES,并動(dòng)態(tài)添加方法,重新發(fā)送消息至動(dòng)態(tài)添加的方法。返回NO,或者未動(dòng)態(tài)添加方法,跳轉(zhuǎn)到forwardingTargetForSelector
 forwardingTargetForSelector->由哪個(gè)target來(lái)對(duì)應(yīng),如果此target有對(duì)應(yīng)的sel,則立即執(zhí)行,如果此target沒(méi)有對(duì)應(yīng)的sel,則crash。如果返回target為nil,則繼續(xù)判斷。
 methodSignatureForSelector->由哪個(gè)NSMethodSignature(sel,返回值類型,參數(shù)類型)來(lái)對(duì)應(yīng),如果對(duì)應(yīng)NSMethodSignature為nil,則跳轉(zhuǎn)到最后一步doesNotRecognizeSelector,到這里發(fā)生crash
 forwardInvocation->重定向到此執(zhí)行invoke
 doesNotRecognizeSelector->如果以上都無(wú)法對(duì)應(yīng),跳轉(zhuǎn)到這里crash
 */


@implementation SimpleNSInvocationClassC
- (void)method_a{
    NSLog(@"a");
    
}

- (IMP)methodForSelector:(SEL)aSelector {
    
    
    return [super methodForSelector:aSelector];
}

+ (IMP)instanceMethodForSelector:(SEL)aSelector{
    
    return [super instanceMethodForSelector:aSelector];
}

/**
 unrecognized selector sent to instance 0x60000000ce90
 找不到sel,會(huì)閃退
 */
- (void)doesNotRecognizeSelector:(SEL)aSelector{
    
    return [super doesNotRecognizeSelector:aSelector];
}

/**
 2: 重定向到其它消息接受者來(lái)執(zhí)行。
 返回其它消息SEL接受者。檢查是否有合適的target,如果不自定義,默認(rèn)返回nil
 */
- (id)forwardingTargetForSelector:(SEL)aSelector{
    id r= [super forwardingTargetForSelector:aSelector];
    //    if (!r&&sel_isEqual(aSelector, @selector(method_b))) {
    //        return _nocrash;
    //    }
    //    if (!r) {
    //        return _nocrash;
    //    }
    //
    
    return r;
}


/**
 3:  NSInvocation
 重定向到其它消息接受者、并對(duì)應(yīng)SEL方法
 */
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    
    //An array of characters containing the type encodings for the method arguments.
    //NSMethodSignature只包括參數(shù)。返回類型,參數(shù)(列)類型.通過(guò)Class和SEL可以獲取參數(shù)
    //NSMethodSignature*sig=  [self.nocrash methodSignatureForSelector:aSelector];
    //NSMethodSignature*sig=[super methodSignatureForSelector:aSelector];
    NSMethodSignature*sig=  [self.nocrash methodSignatureForSelector:@selector(method_c)];
    return sig;
}


/**
 methodSignatureForSelector -> forwardInvocation
 */
- (void)forwardInvocation:(NSInvocation *)anInvocation {
    NSInvocation *invocation=anInvocation;
      //NSInvocation *invocation=[NSInvocation invocationWithMethodSignature:anInvocation.methodSignature];
    // 消息接受者。
    invocation.target=_nocrash;
    // SEL函數(shù)名。默認(rèn)是找不到的消息SEL,可以改
    //invocation.selector=anInvocation.selector;
    invocation.selector=@selector(method_c);
    [invocation invoke];
    //   [super forwardInvocation:invocation];
    //   [super forwardInvocation:anInvocation];
}

+ (BOOL)resolveClassMethod:(SEL)sel {
    BOOL r= [super resolveClassMethod:sel];
    return r;
}

/**
 1: 執(zhí)行動(dòng)態(tài)添加方法的機(jī)會(huì)class_addMethod
 */
+ (BOOL)resolveInstanceMethod:(SEL)sel{
    
    BOOL r= [super resolveInstanceMethod:sel];
    // 返回YES,并動(dòng)態(tài)添加方法,重新發(fā)送消息至動(dòng)態(tài)添加的方法。
    //    if (sel==@selector(method_b)) {
    //        //Class+SEL->IMP, Class+SEL->Method->TypeEncoding
    //        class_addMethod([self class], sel, class_getMethodImplementation([TestMethod class], sel), method_getTypeEncoding(class_getInstanceMethod([TestMethod class], sel)));
    //        return YES;
    //    }
    // 返回NO,或者未動(dòng)態(tài)添加方法,跳轉(zhuǎn)到forwardingTargetForSelector
    return r;
}

- (BOOL)respondsToSelector:(SEL)aSelector {
    BOOL r=[super respondsToSelector:aSelector];
    return r;
}
@end

@implementation NoSelDoObj

-(void)method_b{
    MyLog(@"");
}
-(void)method_c {
    MyLog(@"");
}
@end




- (id)performSelectorWithArgs:(SEL)sel, ...{

NSMethodSignature * sig = [self methodSignatureForSelector:sel];
if (!sig) {
    [self doesNotRecognizeSelector:sel];
    return ((void *)0);
}
NSInvocation *inv = [NSInvocation invocationWithMethodSignature:sig];
if (!inv) {
    [self doesNotRecognizeSelector:sel];
    return ((void *)0);
}

[inv setTarget:self];

[inv setSelector:sel];

va_list args;

__builtin_va_start(args, sel);

[NSObject setInv:inv withSig:sig andArgs:args];

__builtin_va_end(args);;

[inv invoke];

return [NSObject getReturnFromInv:inv withSig:sig];


}





最后編輯于
?著作權(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,053評(píng)論 0 9
  • 我們常常會(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,334評(píng)論 0 7
  • 本文轉(zhuǎn)載自:http://yulingtianxia.com/blog/2014/11/05/objective-...
    ant_flex閱讀 886評(píng)論 0 1
  • 繼上Runtime梳理(四) 通過(guò)前面的學(xué)習(xí),我們了解到Objective-C的動(dòng)態(tài)特性:Objective-C不...
    小名一峰閱讀 848評(píng)論 0 3
  • 大學(xué)的圖書(shū)館也分淡季和旺季,平時(shí)上課期間就是淡季,圖書(shū)館鮮有人來(lái),但是到了期末就是旺季,圖書(shū)館隨時(shí)會(huì)發(fā)生占座風(fēng)波,...
    了望臺(tái)閱讀 318評(píng)論 0 1

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