依靠大神的肩膀
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ù)。。。