YYModel 源码解读(二)之NSObject+YYModel.h (1)
本篇文章主要介绍 _YYModelPropertyMeta 前边的内容
首先先解释一下前边的辅助函数和枚举变量,在写一个功能的时候,这些辅助的东西可能不是一开始就能想出来的,应该是在后续的编码过程中 逐步添加的。
#define force_inline __inline__ __attribute__((always_inline))
这行代码用到了C语言的内联函数
内联函数: 是用inline修饰的函数,内联函数在代码层次看和普通的函数结构一样,却不具备函数的性质,内联函数不是在调用时发生控制转移,而是在编译时将函数体嵌入到调用处,和宏很类似,但是有一下不同点
我们先定义一个宏
#define TABLE_COMP(x) ((x)>0?(x):0)
那么我们为什么要使用宏呢?而不是直接调用函数呢?
应为函数的调用需要把当前程序的执行循序切换到函数在内存中的地址,将函数内的程序执行完毕后,再次跳回之前调用函数的地方,这就需要记录函数调用前和调用后的状态,因此函数调用就产生了时间和空间上的开销,会影响效率,而宏只是在预编译的地方把代码展开,不需要时间和空间上的开销,效率比函数高
但是宏也有不尽如人意的地方
1. 宏不能方位对象的私有成员
2. 宏的定义很容易产生二义性
举例说明:
#define TABLE_MULTI(x) (x*x)
TABLE_MULTI(10) ------ > 结果是 100 这个是我们想要的结果
TABLE_MULTI(10+10) --------> 结果是 120 这个不是我们想要的结果 实际上 10 + 10 * 10 + 10 --> 120
为了避免这些错误,我们在使用宏的时候,应该尽量写成这样
#define TABLE_MULTI(x) ((x)*(x))
但仍然存在问题,
TABLE_MULTI(a++)
当a = 4 的时候,展开后是(4++)*(4++) == 16 并不是我们想要的结果 5*5 = 25
事实上,我们完全可以使用内联函数取代宏,他们之间的区别在于: 宏是由预处理器进行替代的,而内联函数则是通过编译器控制实现的,内联函数是真正的函数,只有在需要用到的时候才会像宏那样的展开,所以取消了函数的参数压栈,减少了调用的开销,我们可以像调用函数一样调用内联函数,而不必担心产生于处理宏一样的问题。
好了,内联函数相信大家应该已经明白怎么使用了,接下来,我们看源码
/// Foundation Class Type
typedef NS_ENUM (NSUInteger, YYEncodingNSType) {
YYEncodingTypeNSUnknown = ,
YYEncodingTypeNSString,
YYEncodingTypeNSMutableString,
YYEncodingTypeNSValue,
YYEncodingTypeNSNumber,
YYEncodingTypeNSDecimalNumber, /// 科学计数值 当值很大的时候使用,比如: 310000000 ---> 3.1 * 10的8次方
YYEncodingTypeNSData,
YYEncodingTypeNSMutableData,
YYEncodingTypeNSDate,
YYEncodingTypeNSURL,
YYEncodingTypeNSArray,
YYEncodingTypeNSMutableArray,
YYEncodingTypeNSDictionary,
YYEncodingTypeNSMutableDictionary,
YYEncodingTypeNSSet,
YYEncodingTypeNSMutableSet,
};
这个是Foundation Class 的类型封装,基本上都是我们熟悉的 NSDecimaNumber 是科学计数值的类型,往往 定义了一个枚举后,应该提供一个方法来进行必要的转换
/// Get the Foundation class type from property info.
static force_inline YYEncodingNSType YYClassGetNSType(Class cls) {
if (!cls) return YYEncodingTypeNSUnknown;
if ([cls isSubclassOfClass:[NSMutableString class]]) return YYEncodingTypeNSMutableString;
if ([cls isSubclassOfClass:[NSString class]]) return YYEncodingTypeNSString;
if ([cls isSubclassOfClass:[NSDecimalNumber class]]) return YYEncodingTypeNSDecimalNumber;
if ([cls isSubclassOfClass:[NSNumber class]]) return YYEncodingTypeNSNumber;
if ([cls isSubclassOfClass:[NSValue class]]) return YYEncodingTypeNSValue;
if ([cls isSubclassOfClass:[NSMutableData class]]) return YYEncodingTypeNSMutableData;
if ([cls isSubclassOfClass:[NSData class]]) return YYEncodingTypeNSData;
if ([cls isSubclassOfClass:[NSDate class]]) return YYEncodingTypeNSDate;
if ([cls isSubclassOfClass:[NSURL class]]) return YYEncodingTypeNSURL;
if ([cls isSubclassOfClass:[NSMutableArray class]]) return YYEncodingTypeNSMutableArray;
if ([cls isSubclassOfClass:[NSArray class]]) return YYEncodingTypeNSArray;
if ([cls isSubclassOfClass:[NSMutableDictionary class]]) return YYEncodingTypeNSMutableDictionary;
if ([cls isSubclassOfClass:[NSDictionary class]]) return YYEncodingTypeNSDictionary;
if ([cls isSubclassOfClass:[NSMutableSet class]]) return YYEncodingTypeNSMutableSet;
if ([cls isSubclassOfClass:[NSSet class]]) return YYEncodingTypeNSSet;
return YYEncodingTypeNSUnknown;
}
判断是否是一个C 的类型
/// Whether the type is c number.
static force_inline BOOL YYEncodingTypeIsCNumber(YYEncodingType type) {
switch (type & YYEncodingTypeMask) {
case YYEncodingTypeBool:
case YYEncodingTypeInt8:
case YYEncodingTypeUInt8:
case YYEncodingTypeInt16:
case YYEncodingTypeUInt16:
case YYEncodingTypeInt32:
case YYEncodingTypeUInt32:
case YYEncodingTypeInt64:
case YYEncodingTypeUInt64:
case YYEncodingTypeFloat:
case YYEncodingTypeDouble:
case YYEncodingTypeLongDouble: return YES;
default: return NO;
}
}
接下来的方法是 在id 类型中 解析 NSNumber , 这里用到了NSCharacterSet 这个类,下边对这个类做一些简单介绍
这个类配合NSScanner 做字符串的过滤操作
/// Parse a number value from 'id'.
static force_inline NSNumber *YYNSNumberCreateFromID(__unsafe_unretained id value) {
static NSCharacterSet *dot;
static NSDictionary *dic;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
dot = [NSCharacterSet characterSetWithRange:NSMakeRange('.', )];
dic = @{@"TRUE" : @(YES),
@"True" : @(YES),
@"true" : @(YES),
@"FALSE" : @(NO),
@"False" : @(NO),
@"false" : @(NO),
@"YES" : @(YES),
@"Yes" : @(YES),
@"yes" : @(YES),
@"NO" : @(NO),
@"No" : @(NO),
@"no" : @(NO),
@"NIL" : (id)kCFNull,
@"Nil" : (id)kCFNull,
@"nil" : (id)kCFNull,
@"NULL" : (id)kCFNull,
@"Null" : (id)kCFNull,
@"null" : (id)kCFNull,
@"(NULL)" : (id)kCFNull,
@"(Null)" : (id)kCFNull,
@"(null)" : (id)kCFNull,
@"<NULL>" : (id)kCFNull,
@"<Null>" : (id)kCFNull,
@"<null>" : (id)kCFNull};
}); // 判空
if (!value || value == (id)kCFNull) return nil;
// 如果是NSNumber 直接返回
if ([value isKindOfClass:[NSNumber class]]) return value;
// 如果是字符串
if ([value isKindOfClass:[NSString class]]) { // 取出字典中映射的值
NSNumber *num = dic[value];
if (num) {
if (num == (id)kCFNull) return nil;
return num;
} // 字符串中包含字符‘.’
if ([(NSString *)value rangeOfCharacterFromSet:dot].location != NSNotFound) {
const char *cstring = ((NSString *)value).UTF8String;
if (!cstring) return nil; // 把字符串转换成浮点数
double num = atof(cstring);
if (isnan(num) || isinf(num)) return nil;
return @(num);
} else {
const char *cstring = ((NSString *)value).UTF8String;
if (!cstring) return nil; // 返回字符串 的 长长整型
return @(atoll(cstring));
}
} return nil;
}
上边代码的主要功能就是把id 类型 转换成NSNumber 类型,其中有两个函数
atof() ---- 将字符串转换成浮点类型
atoll() ---- 将字符串转换成长长整型
下边这个方法就是把字符串转换成日期的方法
使用Block来保存 可支持的字符串和转换结果,,然后根据字符串的长度进行转换
/// Parse string to date.
static force_inline NSDate *YYNSDateFromString(__unsafe_unretained NSString *string) {
typedef NSDate* (^YYNSDateParseBlock)(NSString *string);
#define kParserNum 34
static YYNSDateParseBlock blocks[kParserNum + ] = {};
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
{
/*
2014-01-20 // Google
*/
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
formatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
formatter.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:];
formatter.dateFormat = @"yyyy-MM-dd";
blocks[] = ^(NSString *string) { return [formatter dateFromString:string]; };
} {
/*
2014-01-20 12:24:48
2014-01-20T12:24:48 // Google
2014-01-20 12:24:48.000
2014-01-20T12:24:48.000
*/
NSDateFormatter *formatter1 = [[NSDateFormatter alloc] init];
formatter1.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
formatter1.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:];
formatter1.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss"; NSDateFormatter *formatter2 = [[NSDateFormatter alloc] init];
formatter2.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
formatter2.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:];
formatter2.dateFormat = @"yyyy-MM-dd HH:mm:ss"; NSDateFormatter *formatter3 = [[NSDateFormatter alloc] init];
formatter3.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
formatter3.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:];
formatter3.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss.SSS"; NSDateFormatter *formatter4 = [[NSDateFormatter alloc] init];
formatter4.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
formatter4.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:];
formatter4.dateFormat = @"yyyy-MM-dd HH:mm:ss.SSS"; blocks[] = ^(NSString *string) {
if ([string characterAtIndex:] == 'T') {
return [formatter1 dateFromString:string];
} else {
return [formatter2 dateFromString:string];
}
}; blocks[] = ^(NSString *string) {
if ([string characterAtIndex:] == 'T') {
return [formatter3 dateFromString:string];
} else {
return [formatter4 dateFromString:string];
}
};
} {
/*
2014-01-20T12:24:48Z // Github, Apple
2014-01-20T12:24:48+0800 // Facebook
2014-01-20T12:24:48+12:00 // Google
2014-01-20T12:24:48.000Z
2014-01-20T12:24:48.000+0800
2014-01-20T12:24:48.000+12:00
*/
NSDateFormatter *formatter = [NSDateFormatter new];
formatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
formatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ssZ"; NSDateFormatter *formatter2 = [NSDateFormatter new];
formatter2.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
formatter2.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss.SSSZ"; blocks[] = ^(NSString *string) { return [formatter dateFromString:string]; };
blocks[] = ^(NSString *string) { return [formatter dateFromString:string]?: [formatter2 dateFromString:string]; };
blocks[] = ^(NSString *string) { return [formatter dateFromString:string]; };
blocks[] = ^(NSString *string) { return [formatter2 dateFromString:string]; };
blocks[] = ^(NSString *string) { return [formatter2 dateFromString:string]; };
} {
/*
Fri Sep 04 00:12:21 +0800 2015 // Weibo, Twitter
Fri Sep 04 00:12:21.000 +0800 2015
*/
NSDateFormatter *formatter = [NSDateFormatter new];
formatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
formatter.dateFormat = @"EEE MMM dd HH:mm:ss Z yyyy"; NSDateFormatter *formatter2 = [NSDateFormatter new];
formatter2.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
formatter2.dateFormat = @"EEE MMM dd HH:mm:ss.SSS Z yyyy"; blocks[] = ^(NSString *string) { return [formatter dateFromString:string]; };
blocks[] = ^(NSString *string) { return [formatter2 dateFromString:string]; };
}
});
if (!string) return nil;
if (string.length > kParserNum) return nil;
YYNSDateParseBlock parser = blocks[string.length];
if (!parser) return nil;
return parser(string);
#undef kParserNum
}
获得NSBlock 这个类 ,
/// Get the 'NSBlock' class.
static force_inline Class YYNSBlockClass() {
static Class cls;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
void (^block)(void) = ^{};
cls = ((NSObject *)block).class;
while (class_getSuperclass(cls) != [NSObject class]) {
NSLog(@"cls = %@",class_getSuperclass(cls));
cls = class_getSuperclass(cls);
}
});
return cls; // current is "NSBlock"
}
加入了打印我们可以看出 block 的父类的关系是
block -------> NSGlobalBlock ---------> NSBlock
获取标准的时间的格式
/**
Get the ISO date formatter. ISO8601 format example:
2010-07-09T16:13:30+12:00
2011-01-11T11:11:11+0000
2011-01-26T19:06:43Z length: 20/24/25
*/
static force_inline NSDateFormatter *YYISODateFormatter() {
static NSDateFormatter *formatter = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
formatter = [[NSDateFormatter alloc] init];
formatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
formatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ssZ";
});
return formatter;
}
根据字典和key 或者keypath 获取 value
@{
@"name" : @"machao",
@"user" : @{
@"uid":@{
@"name":@"abc",
@"addrs":@"beijing",
},
@"pic" : @"http://7ke.com",
},
};
使用下边的方法的思路是获取这样的一个值
[NSDictionary valueForKeyPath:@"user.uid.name"]
数组keys 装着的是@[@"user",@"uid",@"name"];
/// Get the value with key paths from dictionary
/// The dic should be NSDictionary, and the keyPath should not be nil.
static force_inline id YYValueForKeyPath(__unsafe_unretained NSDictionary *dic, __unsafe_unretained NSArray *keyPaths) {
id value = nil;
for (NSUInteger i = , max = keyPaths.count; i < max; i++) {
value = dic[keyPaths[i]];
if (i + < max) {
if ([value isKindOfClass:[NSDictionary class]]) {
dic = value;
} else {
return nil;
}
}
}
return value;
} /// Get the value with multi key (or key path) from dictionary
/// The dic should be NSDictionary
static force_inline id YYValueForMultiKeys(__unsafe_unretained NSDictionary *dic, __unsafe_unretained NSArray *multiKeys) {
id value = nil;
for (NSString *key in multiKeys) {
if ([key isKindOfClass:[NSString class]]) {
value = dic[key];
if (value) break;
} else {
value = YYValueForKeyPath(dic, (NSArray *)key);
if (value) break;
}
}
return value;
}
----------- 分隔线 -------------
接下来 分析_YYModelPropertyMeta 这个类 ,这个类在本文件中是一个私有类
有一个@package 需要注意一下,
@protected 该类和所有子类中的方法可以直接访问这样的变量。
@private 该类中的方法可以访问,子类不可以访问。
@public 可以被所有的类访问
@package 本包内使用,跨包不可以
/// A property info in object model.
@interface _YYModelPropertyMeta : NSObject {
@package
NSString *_name; ///< property's name
YYEncodingType _type; ///< property's type
YYEncodingNSType _nsType; ///< property's Foundation type
BOOL _isCNumber; ///< is c number type
Class _cls; ///< property's class, or nil
Class _genericCls; ///< container's generic class, or nil if threr's no generic class
SEL _getter; ///< getter, or nil if the instances cannot respond
SEL _setter; ///< setter, or nil if the instances cannot respond
BOOL _isKVCCompatible; ///< YES if it can access with key-value coding
BOOL _isStructAvailableForKeyedArchiver; ///< YES if the struct can encoded with keyed archiver/unarchiver
BOOL _hasCustomClassFromDictionary; ///< class/generic class implements +modelCustomClassForDictionary: /*
property->key: _mappedToKey:key _mappedToKeyPath:nil _mappedToKeyArray:nil
property->keyPath: _mappedToKey:keyPath _mappedToKeyPath:keyPath(array) _mappedToKeyArray:nil
property->keys: _mappedToKey:keys[0] _mappedToKeyPath:nil/keyPath _mappedToKeyArray:keys(array)
*/
NSString *_mappedToKey; ///< the key mapped to
NSArray *_mappedToKeyPath; ///< the key path mapped to (nil if the name is not key path)
NSArray *_mappedToKeyArray; ///< the key(NSString) or keyPath(NSArray) array (nil if not mapped to multiple keys)
YYClassPropertyInfo *_info; ///< property's info
_YYModelPropertyMeta *_next; ///< next meta if there are multiple properties mapped to the same key.
}
@end
这个私有的类中包含了 一条@property 的全部信息,我们可以了解到一条@property 其实是包含很多信息的
1. _name ------> 名称
2. _type -------> @encode()编码 转换后的类型 详见 YYModel源码解读 (二) 之 YYClassInfo.h(1)
3. _nsType ------> 自定义的Foundation 类型
4. _isCNumber -------- > 类型是否是c类型
5. _cls --------> property 的类型
6. _genericCls ---------> 这个是容器的类型, 比如: NSArray NSSet NSDictionary
7. _getter -------- > getter 方法 (可能为空)
8. _setter ---------> setter 方法(可能为空)
9. _isKVCCompatible ---------- > 能否被归档,属性需要实现setter 和 getter 方法
10. _isStructAvailableForKeyedArchiver ------- > 结构体类型是否支持归档
11. _hasCustomClassFromDictionary ---------> 是否实现了 +modelCustomClassForDictionary 方法 自定义了返回类型
12. _mappedToKey --------->
接下来我们看看实现,有些信息是我们在这个参数中可以直接获取的,直接赋值就好,其他的需要判断的就判断,值得注意的是,因为本类是支持归档的 所以要知道哪些是支持归档的,哪些是不支持归档的,
支持归档的结构体:
// 32 bit
[set addObject:@"{CGSize=ff}"];
[set addObject:@"{CGPoint=ff}"];
[set addObject:@"{CGRect={CGPoint=ff}{CGSize=ff}}"];
[set addObject:@"{CGAffineTransform=ffffff}"];
[set addObject:@"{UIEdgeInsets=ffff}"];
[set addObject:@"{UIOffset=ff}"];
// 64 bit
[set addObject:@"{CGSize=dd}"];
[set addObject:@"{CGPoint=dd}"];
[set addObject:@"{CGRect={CGPoint=dd}{CGSize=dd}}"];
[set addObject:@"{CGAffineTransform=dddddd}"];
[set addObject:@"{UIEdgeInsets=dddd}"];
[set addObject:@"{UIOffset=dd}"];
不支持归档的:
long double
pointer (such as SEL/CoreFoundation object)
+ (instancetype)metaWithClassInfo:(YYClassInfo *)classInfo propertyInfo:(YYClassPropertyInfo *)propertyInfo generic:(Class)generic { // 初始化信息
_YYModelPropertyMeta *meta = [self new];
meta->_name = propertyInfo.name;
meta->_type = propertyInfo.type;
meta->_info = propertyInfo;
meta->_genericCls = generic; // 如果是Foundation 类型
if ((meta->_type & YYEncodingTypeMask) == YYEncodingTypeObject) {
meta->_nsType = YYClassGetNSType(propertyInfo.cls);
}
// 否则判断是不是C 类型
else {
meta->_isCNumber = YYEncodingTypeIsCNumber(meta->_type);
} // 结构体类型
if ((meta->_type & YYEncodingTypeMask) == YYEncodingTypeStruct) {
/*
It seems that NSKeyedUnarchiver cannot decode NSValue except these structs:
*/
static NSSet *types = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSMutableSet *set = [NSMutableSet new];
// 32 bit
[set addObject:@"{CGSize=ff}"];
[set addObject:@"{CGPoint=ff}"];
[set addObject:@"{CGRect={CGPoint=ff}{CGSize=ff}}"];
[set addObject:@"{CGAffineTransform=ffffff}"];
[set addObject:@"{UIEdgeInsets=ffff}"];
[set addObject:@"{UIOffset=ff}"];
// 64 bit
[set addObject:@"{CGSize=dd}"];
[set addObject:@"{CGPoint=dd}"];
[set addObject:@"{CGRect={CGPoint=dd}{CGSize=dd}}"];
[set addObject:@"{CGAffineTransform=dddddd}"];
[set addObject:@"{UIEdgeInsets=dddd}"];
[set addObject:@"{UIOffset=dd}"];
types = set;
}); // 由此看来,ios的归档大概只支持上边的几种结构体类型
if ([types containsObject:propertyInfo.typeEncoding]) {
meta->_isStructAvailableForKeyedArchiver = YES;
}
} meta->_cls = propertyInfo.cls; // 判断是否实现了modelCustomClassForDictionary方法
if (generic) {
meta->_hasCustomClassFromDictionary = [generic respondsToSelector:@selector(modelCustomClassForDictionary:)];
} else if (meta->_cls && meta->_nsType == YYEncodingTypeNSUnknown) {
meta->_hasCustomClassFromDictionary = [meta->_cls respondsToSelector:@selector(modelCustomClassForDictionary:)];
} // 判断property 所属的类能否相应自身的getter 方法
if (propertyInfo.getter) {
if ([classInfo.cls instancesRespondToSelector:propertyInfo.getter]) {
meta->_getter = propertyInfo.getter;
}
}
// 判断property 所属的类能否相应自身的setter 方法
if (propertyInfo.setter) {
if ([classInfo.cls instancesRespondToSelector:propertyInfo.setter]) {
meta->_setter = propertyInfo.setter;
}
} // 判断能够被归档,
if (meta->_getter && meta->_setter) {
/*
KVC invalid type:
long double
pointer (such as SEL/CoreFoundation object)
*/
switch (meta->_type & YYEncodingTypeMask) {
case YYEncodingTypeBool:
case YYEncodingTypeInt8:
case YYEncodingTypeUInt8:
case YYEncodingTypeInt16:
case YYEncodingTypeUInt16:
case YYEncodingTypeInt32:
case YYEncodingTypeUInt32:
case YYEncodingTypeInt64:
case YYEncodingTypeUInt64:
case YYEncodingTypeFloat:
case YYEncodingTypeDouble:
case YYEncodingTypeObject:
case YYEncodingTypeClass:
case YYEncodingTypeBlock:
case YYEncodingTypeStruct:
case YYEncodingTypeUnion: {
meta->_isKVCCompatible = YES;
} break;
default: break;
}
} return meta;
}
YYModel 源码解读(二)之NSObject+YYModel.h (1)的更多相关文章
- jQuery.Callbacks 源码解读二
一.参数标记 /* * once: 确保回调列表仅只fire一次 * unique: 在执行add操作中,确保回调列表中不存在重复的回调 * stopOnFalse: 当执行回调返回值为false,则 ...
- (转)go语言nsq源码解读二 nsqlookupd、nsqd与nsqadmin
转自:http://www.baiyuxiong.com/?p=886 ---------------------------------------------------------------- ...
- YYModel 源码解读(二)之YYClassInfo.h (3)
前边3篇介绍了YYClassinfo 文件的组成单元,算是功能的分割,按照业务的设计思想来说,方向应该是相反的 由此引申出我们在设计api的思想其实和项目管理是很类似的----- 一些题外话 1.目的 ...
- YYModel 源码解读 总结
在使用swfit写代码的过程中,使用了下oc写的字典转模型,发现有些属性转不成功,就萌生了阅读源码的想法. 其实一直都知道Runtime机制,但并没有系统的学习,可能是因为平时的使用比较少,无意间在g ...
- YYModel 源码解读(一)之YYModel.h
#if __has_include(<YYModel/YYModel.h>) FOUNDATION_EXPORT double YYModelVersionNumber; FOUNDATI ...
- mybatis源码解读(二)——构建Configuration对象
Configuration 对象保存了所有mybatis的配置信息,主要包括: ①. mybatis-configuration.xml 基础配置文件 ②. mapper.xml 映射器配置文件 1. ...
- ConcurrentHashMap源码解读二
接下来就讲解put里面的三个方法,分别是 1.数组初始化方法initTable() 2.线程协助扩容方法helpTransfer() 3.计数方法addCount() 首先是数组初始化,再将源码之前, ...
- go语言nsq源码解读二 nsqlookupd、nsqd与nsqadmin
nsqlookupd: 官方文档解释见:http://bitly.github.io/nsq/components/nsqlookupd.html 用官方话来讲是:nsqlookupd管理拓扑信息,客 ...
- vue2.0 源码解读(二)
小伞最近比较忙,阅读源码的速度越来越慢了 最近和朋友交流的时候,发现他们对于源码的目录结构都不是很清楚 红色圈子内是我们需要关心的地方 compiler 模板编译部分 core 核心实现部分 ent ...
随机推荐
- CYQ.Data、ASP.NET Aries 百家企业使用名单
如果您或您所在的公司正在使用此框架,请联系左侧的扣扣,告知我信息,我将为您添加链接: 以下内容为已反馈的用户,(收集始于:2016-08-08),仅展示99家: 序号 企业名称 企业网址 备注 1 山 ...
- Objective-C三种定时器CADisplayLink / NSTimer / GCD的使用
OC中的三种定时器:CADisplayLink.NSTimer.GCD 我们先来看看CADiskplayLink, 点进头文件里面看看, 用注释来说明下 @interface CADisplayLin ...
- 【组织级项目管理】P2 MSP P3O
组织级项目管理--有你,有我,有大家 在过去的2年,无论对于企业来讲,还是对于我们个人都有很多大脑的冲击,有几个词大家应该特别耳熟能详:转型,变革,敏捷,互联网+,组织的项目化管理等.就是这些让我们的 ...
- SQL Server 常用内置函数(built-in)持续整理
本文用于收集在运维中经常使用的系统内置函数,持续整理中 一,常用Metadata函数 1,查看数据库的ID和Name db_id(‘DB Name’),db_name('DB ID') 2,查看对象的 ...
- 免费高效实用的.NET操作Excel组件NPOI(.NET组件介绍之六)
很多的软件项目几乎都包含着对文档的操作,前面已经介绍过两款操作文档的组件,现在介绍一款文档操作的组件NPOI. NPOI可以生成没有安装在您的服务器上的Microsoft Office套件的Excel ...
- Ubuntu设置root用户登录图形界面
Ubuntu默认的是root用户不能登录图形界面的,只能以其他用户登录图形界面.这样就很麻烦,因为权限的问题,不能随意复制删除文件,用gedit编辑文件时经常不能保存,只能用vim去编辑. 解决的办法 ...
- windows下的命令行工具babun
什么是babun babun是windows上的一个第三方shell,在这个shell上面你可以使用几乎所有linux,unix上面的命令,他几乎可以取代windows的shell.用官方的题目说就是 ...
- Java中的进程与线程(总结篇)
详细文档: Java中的进程与线程.rar 474KB 1/7/2017 6:21:15 PM 概述: 几乎任何的操作系统都支持运行多个任务,通常一个任务就是一个程序,而一个程序就是一个进程.当一个进 ...
- Yeoman 学习笔记
yoeman 简介:http://www.infoq.com/cn/news/2012/09/yeoman yeoman 官网: http://yeoman.io/ yeoman 是快速创建骨架应用程 ...
- Mono下的WCF的Bug?
最近一段时间,一直在折腾Mono,折腾Linux.让我无比痛苦的是Mono下的WCF的坑真的是太多了,这不又遇到了一个莫名其妙的问题. 环境:mono 3.2.1,Jexus 5.4.3,OS Cen ...