YYKit源碼解讀

依靠大神的肩膀

YYKit有多屌我就不廢話了,上面的文章中各位大神都給出了自己的看法,本文只是記錄一下YYKit中個人學(xué)習(xí)到的東西,如有失誤,歡迎指正。

1. YYKitMacro

一個三目運算符,返回中間值

YY_CLAMP(_x_, _low_, _high_)

交換兩個變量的值

YY_SWAP(_a_, _b_)

然后是一些斷言,沒什么可說的。

這個在分析YYKit--宏定義的使用文中有提到,解釋的很詳細,以及weakify和strongify的使用

YYSYNTH_DUMMY_CLASS(_name_)

下面這個牛X了,我們都知道正常情況下類別中是無法直接添加屬性的,但可以通過runtime來實現(xiàn),這兩個宏可以使你在類別中直接添加屬性

// 添加Objective-C類型屬性
YYSYNTH_DYNAMIC_PROPERTY_OBJECT(_getter_, _setter_, _association_, _type_) 
// 添加C類型屬性
YYSYNTH_DYNAMIC_PROPERTY_CTYPE(_getter_, _setter_, _type_)

后面還有一些封裝的方法

// range轉(zhuǎn)換
NSRange YYNSRangeFromCFRange(CFRange range)
CFRange YYCFRangeFromNSRange(NSRange range)
// time 
YYBenchmark(void (^block)(void), void (^complete)(double ms))
NSDate *_YYCompileTime(const char *data, const char *time)
// GCD
dispatch_time_t dispatch_time_delay(NSTimeInterval second)
dispatch_time_t dispatch_walltime_delay(NSTimeInterval second)
dispatch_time_t dispatch_walltime_date(NSDate *date)
bool dispatch_is_main_queue()
void dispatch_async_on_main_queue(void (^block)())
void dispatch_sync_on_main_queue(void (^block)())

2. Foundation

這部分分別對NSObject, KVO, ARC, NSString, NSNumber, NSDate, NSData, NSArray, NSDictionary, NSNotificationCenter, NSKeyedUnarchiver, NSTimer, NSBundle, NSThread 進行了類別擴展

2.1 NSObject+YYAdd
// 發(fā)送消息,獲取消息的返回值
- (nullable id)performSelectorWithArgs:(SEL)sel, ...;
// 幾個方法,分別與線程和Delay相關(guān)
- (void)performSelectorWithArgs:(SEL)sel afterDelay:(NSTimeInterval)delay, ...;
- (nullable id)performSelectorWithArgsOnMainThread:(SEL)sel waitUntilDone:(BOOL)wait, ...;
- (nullable id)performSelectorWithArgs:(SEL)sel onThread:(NSThread *)thread waitUntilDone:(BOOL)wait, ...;
- (void)performSelectorWithArgsInBackground:(SEL)sel, ...;
- (void)performSelector:(SEL)sel afterDelay:(NSTimeInterval)delay;

Runtime是OC的一大特性,在實際開發(fā)中用到的大概就是通過runtime來獲取屬性列表,以及method swizzling!下面這幾個方法是對runtime的封裝,看名字都可以猜到實現(xiàn)方法!

// 實例方法交換
+ (BOOL)swizzleInstanceMethod:(SEL)originalSel with:(SEL)newSel;
// 類方法交換
+ (BOOL)swizzleClassMethod:(SEL)originalSel with:(SEL)newSel;

//runtime 設(shè)置關(guān)聯(lián)
- (void)setAssociateValue:(nullable id)value withKey:(void *)key;
// 設(shè)置聲明為weak的屬性
- (void)setAssociateWeakValue:(nullable id)value withKey:(void *)key;
// 獲取關(guān)聯(lián)屬性
- (nullable id)getAssociatedValueForKey:(void *)key;
// 移除關(guān)聯(lián)屬性
- (void)removeAssociatedValues;

深拷貝,這個有點黑的感覺,通常情況下,我們要實現(xiàn)深拷貝會調(diào)用copy方法,這里直接將對象的數(shù)據(jù)復(fù)制一份賦給新對象,繞過了中間過程,直接底層訪問,從而達到加快編譯速度的目的;這個思路貫穿整個YYKit。

- (id)deepCopy {
    id obj = nil;
    @try {
        obj = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:self]];
    }
    @catch (NSException *exception) {
        NSLog(@"%@", exception);
    }
    return obj;
}
- (id)deepCopyWithArchiver:(Class)archiver unarchiver:(Class)unarchiver;
2.2. NSString+YYAdd

md2, md4, md5......一大串加密方法,URL和HTML歸檔方法。
計算String 的size, 這個方法用到的可能性比較大!

// 計算String 的size, 這個方法用到的可能性比較大!
- (CGSize)sizeForFont:(UIFont *)font size:(CGSize)size mode:(NSLineBreakMode)lineBreakMode
- (CGFloat)widthForFont:(UIFont *)font;
- (CGFloat)heightForFont:(UIFont *)font width:(CGFloat)width;

這里面有個容錯處理,很優(yōu)雅的處理方式,再也不用判斷string的length了,我承認我菜!

[self respondsToSelector:@selector(boundingRectWithSize:options:attributes:context:)]
// 遍歷字符串的元素
- (void)enumerateUTF32CharInRange:(NSRange)range usingBlock:(void (^)(UTF32Char char32, NSRange range, BOOL *stop))block;
2.3 NSThread+YYAdd

只有一個方法

+ (void)addAutoreleasePoolToCurrentRunloop {
    if ([NSThread isMainThread]) return; // 當(dāng)前線程為主線程時,直接返回,主線程自帶autoreleasePool
    NSThread *thread = [self currentThread];
    if (!thread) return;
    if (thread.threadDictionary[YYNSThreadAutoleasePoolKey]) return; // already added
    YYRunloopAutoreleasePoolSetup(); // 進行配置,看下面
    thread.threadDictionary[YYNSThreadAutoleasePoolKey] = YYNSThreadAutoleasePoolKey; // mark the state
}

配置AutoreleasePool, 在配置中,添加了兩個觀察者,當(dāng)RunLoop的狀態(tài)發(fā)生改變時,進行YYRunLoopAutoreleasePoolObserverCallBack回調(diào),這部分大神自己管理的內(nèi)存,所以是不能在ARC中調(diào)用的。

static void YYRunloopAutoreleasePoolSetup() {
    CFRunLoopRef runloop = CFRunLoopGetCurrent();

    CFRunLoopObserverRef pushObserver;
    pushObserver = CFRunLoopObserverCreate(CFAllocatorGetDefault(), kCFRunLoopEntry,
                                           true,         // repeat
                                           -0x7FFFFFFF,  // before other observers
                                           YYRunLoopAutoreleasePoolObserverCallBack, NULL);
    CFRunLoopAddObserver(runloop, pushObserver, kCFRunLoopCommonModes);
    CFRelease(pushObserver);
    
    CFRunLoopObserverRef popObserver;
    popObserver = CFRunLoopObserverCreate(CFAllocatorGetDefault(), kCFRunLoopBeforeWaiting | kCFRunLoopExit,
                                          true,        // repeat
                                          0x7FFFFFFF,  // after other observers
                                          YYRunLoopAutoreleasePoolObserverCallBack, NULL);
    CFRunLoopAddObserver(runloop, popObserver, kCFRunLoopCommonModes);
    CFRelease(popObserver);
}

回調(diào)方法, 當(dāng)runloop狀態(tài)發(fā)生改變時,向PoolStack中添加(push)或移除(pop)autoreleasePool。

static void YYRunLoopAutoreleasePoolObserverCallBack(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info) {
    switch (activity) {
        case kCFRunLoopEntry: {
            YYAutoreleasePoolPush();
        } break;
        case kCFRunLoopBeforeWaiting: {
            YYAutoreleasePoolPop();
            YYAutoreleasePoolPush();
        } break;
        case kCFRunLoopExit: {
            YYAutoreleasePoolPop();
        } break;
        default: break;
    }
}

Push

static inline void YYAutoreleasePoolPush() {
    NSMutableDictionary *dic =  [NSThread currentThread].threadDictionary;
    NSMutableArray *poolStack = dic[YYNSThreadAutoleasePoolStackKey];
    
    if (!poolStack) {
        /*
         do not retain pool on push,
         but release on pop to avoid memory analyze warning
         */
        CFArrayCallBacks callbacks = {0};
        callbacks.retain = PoolStackRetainCallBack;
        callbacks.release = PoolStackReleaseCallBack;
        poolStack = (id)CFArrayCreateMutable(CFAllocatorGetDefault(), 0, &callbacks);
        dic[YYNSThreadAutoleasePoolStackKey] = poolStack;
        CFRelease(poolStack);
    }
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; //< create
    [poolStack addObject:pool]; // push
}

Pop

static inline void YYAutoreleasePoolPop() {
    NSMutableDictionary *dic =  [NSThread currentThread].threadDictionary;
    NSMutableArray *poolStack = dic[YYNSThreadAutoleasePoolStackKey];
    [poolStack removeLastObject]; // pop
}

** 小結(jié)一下:** 在Foundation這部分中,其作者添加了很多方便的方法來,上文中只提到了其中的部分代碼;像在NSArray, NSDictionary等其他類目中添加了從頭plist文件中讀取文件,添加,刪除元素,倒敘,打亂排序一類的方法,各位可自行探索,沒有什么難點,都可以看懂的!

3. YYModel

YYModel用來將網(wǎng)絡(luò)請求到的數(shù)據(jù)轉(zhuǎn)換為需要的model,具有以下特性:

  • 優(yōu)良的性能
  • 自動類型轉(zhuǎn)換
  • 類型安全
  • 不需要繼承其他基礎(chǔ)類
  • 輕量級
// 創(chuàng)建一個YYTestModel類
@interface YYTestModel : NSObject

@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSInteger uid;
@property (nonatomic, strong) NSDate *created;

@end

// 測試代碼
- (void)yy_test
{
    // 數(shù)據(jù),可以是NSData,NSDictionary,NSString類型
    NSDictionary *data = @{
                               @"uid":@123456,
                               @"name":@"Harry",
                               @"created":@"1965-07-31T00:00:00+0000"
                           };
    // 數(shù)據(jù)轉(zhuǎn)模型
    YYTestModel *model = [YYTestModel modelWithJSON:data];
}

點modelWithJSON:進去,其思路通過_YYModelMeta類獲取到屬性信息,然后通過消息機制調(diào)用Setter方法進行賦值,前面通過一系列轉(zhuǎn)換后拿到Dictionary,看核心方法

- (BOOL)modelSetWithDictionary:(NSDictionary *)dic {
    if (!dic || dic == (id)kCFNull) return NO;
    if (![dic isKindOfClass:[NSDictionary class]]) return NO;
    // 創(chuàng)建元類,元類中會包含model的屬性信息
    _YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:object_getClass(self)];
    // 如果屬性個數(shù)為0,則返回
    if (modelMeta->_keyMappedCount == 0) return NO;
    
    if (modelMeta->_hasCustomWillTransformFromDictionary) {
        dic = [((id<YYModel>)self) modelCustomWillTransformFromDictionary:dic];
        if (![dic isKindOfClass:[NSDictionary class]]) return NO;
    }
    
    ModelSetContext context = {0};
    context.modelMeta = (__bridge void *)(modelMeta);
    context.model = (__bridge void *)(self);
    context.dictionary = (__bridge void *)(dic);

    // 對屬性賦值,在ModelSetWithPropertyMetaArrayFunction方法內(nèi)部通過消息機制調(diào)用setter方法,對屬性進行賦值,下面的幾個方法也類似
    if (modelMeta->_keyMappedCount >= CFDictionaryGetCount((CFDictionaryRef)dic)) {
        
        CFDictionaryApplyFunction((CFDictionaryRef)dic, ModelSetWithDictionaryFunction, &context);
        if (modelMeta->_keyPathPropertyMetas) {
            CFArrayApplyFunction((CFArrayRef)modelMeta->_keyPathPropertyMetas,
                                 CFRangeMake(0, CFArrayGetCount((CFArrayRef)modelMeta->_keyPathPropertyMetas)),
                                 ModelSetWithPropertyMetaArrayFunction,
                                 &context);
        }
        if (modelMeta->_multiKeysPropertyMetas) {
            CFArrayApplyFunction((CFArrayRef)modelMeta->_multiKeysPropertyMetas,
                                 CFRangeMake(0, CFArrayGetCount((CFArrayRef)modelMeta->_multiKeysPropertyMetas)),
                                 ModelSetWithPropertyMetaArrayFunction,
                                 &context);
        }
    } else {
        CFArrayApplyFunction((CFArrayRef)modelMeta->_allPropertyMetas,
                             CFRangeMake(0, modelMeta->_keyMappedCount),
                             ModelSetWithPropertyMetaArrayFunction,
                             &context);
    }
    
    if (modelMeta->_hasCustomTransformFromDictionary) {
        return [((id<YYModel>)self) modelCustomTransformFromDictionary:dic];
    }
    return YES;
}

未完待續(xù)。。。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

  • *面試心聲:其實這些題本人都沒怎么背,但是在上海 兩周半 面了大約10家 收到差不多3個offer,總結(jié)起來就是把...
    Dove_iOS閱讀 27,652評論 30 472
  • 1.內(nèi)存管理 2.單例的理解 3.post和get的區(qū)別 4.md5和base64是什么,有什么區(qū)別 5.簡單談?wù)?..
    coder_Wg閱讀 1,354評論 1 6
  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 2,082評論 0 9
  • 設(shè)計模式是什么? 你知道哪些設(shè)計模式,并簡要敘述? 設(shè)計模式是一種編碼經(jīng)驗,就是用比較成熟的邏輯去處理某一種類型的...
    small_Sun閱讀 487評論 0 4
  • [玫瑰]20170317徐海波讀《不輸在家庭教育上》分享(上海,第221天) 《我最欣賞的教育理念》(作者:周國平...
    覺之燈閱讀 204評論 0 0

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