YYModel学习总结YYClassInfo(1)
OC的run-time 机制,简直像是网络上的猫! 我在开发中很少用到,但是作为iOS开发 人家肯定会问这个东西,所以深入的学习了下。
对于 run-time的入手,YYModel的学习,简直让人美滋滋。 对于YYModel的 源码解析什么的,网上很多,我主要参考 马在路上 的总结,他都这个总结,是我看过对于新手最友好的总结。浅显易懂,逐条分析,流程分析。很到位!
我呢,就记录下一下我是如何一步步学习YYModel的。
先从YYClassInfo.h开始看下图1-1-1,
图 1-1-1
先说这个 NS_ASSUME_NONNULL_BEGIN 这个宏定义, 要和 NS_ASSUME_NONNULL_END 配对使用。表示在这两个宏之间声明的属性默认是nonnull的。当然还有 NS_ASSUME_NULLABLE_BEGIN 和 NS_ASSUME_NONNULL_END.
具体 参考《Object-C中的黑魔法》。
再看这个 typedef ,这是一个枚举类型,是一个位移枚举,而它(NS_OPTIONS)和通用枚举(NS_ENUM)的区别在于 位移枚举可以在你需要的地方有多个选项,而通用枚举只能存在一个。
typedef NS_OPTIONS(NSUInteger, YYEncodingType){
YYEncodingTypeMask = 0xFF, // 值类型的mask
YYEncodingTypeUnknow = ,
YYEncodingTypeVoid = ,
YYEncodingTypeBool = ,
YYEncodingTypeInt8 = ,
YYEncodingTypeUInt8 = ,
YYEncodingTypeInt16 = ,
YYEncodingTypeUInt16 = ,
YYEncodingTypeInt32 = ,
YYEncodingTypeUInt32 = ,
YYEncodingTypeInt64 = ,
YYEncodingTypeUInt64 = ,
YYEncodingTypeFloat = ,
YYEncodingTypeDouble = ,
YYEncodingTypeLongDoubel = ,
YYEncodingTypeObject = ,
YYEncodingTypeClass = ,
YYEncodingTypeSEL = ,
YYEncodingTypeBlock = ,
YYEncodingTypePointer = ,
YYEncodingTypeStruct = ,
YYEncodingTypeUnion = ,// 共用体,内存长度为内存对齐方式(可以暂时理解为以成员变量最长的为准),和结构体类似,但结构体长度为成员变量的总和。它是以覆盖的方式储存在同一内存段中,所以,只能读取最后添加的成员变量
YYEncodingTypeCString = ,
YYEncodingTypeCArray = , YYEncodingTypeQualifierMask = 0xFF00,//限定符的mask
YYEncodingTypeQualifierConst = << ,
YYEncodingTypeQualifierIn = << ,
YYEncodingTypeQualifierInOut = << , //swift中通过一个函数改变函数外面变量的值(将一个值类型参数以引用方式传递),
YYEncodingTypeQualifierOut = << ,
YYEncodingTypeQualifierBycopy = << ,
YYEncodingTypeQualifierByref = << ,
YYEncodingTypeQualifierOneway = << , YYEncodingTypePropertyMask = 0xFF0000, //属性的mask
YYEncodingTypePropertyReadOnly = << ,
YYEncodingTypePropertyCopy = << ,
YYEncodingTypePropertyRetain = << ,
YYEncodingTypePropertyNonatomic = << ,
YYEncodingTypePropertyWeak = << ,
YYEncodingTypePropertyCustomGetter = << ,
YYEncodingTypePropertyCustoSetter = << ,
YYEncodingTypePropertyDynamic = << , };
通过这个位移枚举的名字,我了解到了 TypeEncoding 这个OC的runtime的知识点。关于Type Encodings 的官方解释 , @encode 是一个编译器指令,返回个内部表示的字符串 , 比如: @encode(int)
→ i ,作用就是可以加快运行时库的消息分发
, @encode 是一个编译器指令,返回个内部表示的字符串 , 比如: @encode(int)
→ i ,作用就是可以加快运行时库的消息分发。
图1-1-2
上图1-1-2 就是OC 的类型编码, 值得注意是 1. 一个指针类型编码是前面加^而char * 却有自己的编码(*),意味着它吧字符串类型当做一个整体。
2. 还有一点是没有标出来的,BOOL 类型 就是被当做一个char ,而不是一个 int 类型。
在接着看位移枚举中他们的枚举值 0XFF,低八位表示 值类型,0xFF00高八位,表示限定符类型,0xFF0000十六位到二十四位,表示属性修饰词的类型。见下图1-1-3为低八位
ps:这个东西真是巧妙,居然还可以这么玩,6666
图1-1-3
在向下看, 它写了个函数
// 这里声明一个函数,来处理类型编码(type Encoding)和枚举的转换,
YYEncodingType YYEncodingGetType(const char *typeEncoding);
切换到YYClassInfo.m 文件看下它的实现
YYEncodingType YYEncodingGetType(const char *typeEncoding) { // 判断外部传入值 是不是nil,如果为空 ,返回 YYEncodingTypeUnknown
// 转换const 限定符
char *type = (char *)typeEncoding;
if (!type) return YYEncodingTypeUnknown;
size_t len = strlen(type);
if (len == ) return YYEncodingTypeUnknown;
//https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html#//apple_ref/doc/uid/TP40008048-CH100-SW1 encoding type 和 qualifier
// 找出修饰语
YYEncodingType qualifier = ;
bool prefix = true; // 可能多个修饰符
while (prefix) { switch (*type) {
case 'r': {
qualifier |= YYEncodingTypeQualifierConst;
type++;
} break;
case 'n': {
qualifier |= YYEncodingTypeQualifierIn;
type++;
} break;
case 'N': {
qualifier |= YYEncodingTypeQualifierInout;
type++;
} break;
case 'o': {
qualifier |= YYEncodingTypeQualifierOut;
type++;
} break;
case 'O': {
qualifier |= YYEncodingTypeQualifierBycopy;
type++;
} break;
case 'R': {
qualifier |= YYEncodingTypeQualifierByref;
type++;
} break;
case 'V': {
qualifier |= YYEncodingTypeQualifierOneway;
type++;
} break;
default: { prefix = false; } break;
}
} // 是否还存在后续的字符
len = strlen(type);
if (len == ) return YYEncodingTypeUnknown | qualifier; // 查找数据类型
switch (*type) {
case 'v': return YYEncodingTypeVoid | qualifier;
case 'B': return YYEncodingTypeBool | qualifier;
case 'c': return YYEncodingTypeInt8 | qualifier;
case 'C': return YYEncodingTypeUInt8 | qualifier;
case 's': return YYEncodingTypeInt16 | qualifier;
case 'S': return YYEncodingTypeUInt16 | qualifier;
case 'i': return YYEncodingTypeInt32 | qualifier;
case 'I': return YYEncodingTypeUInt32 | qualifier;
case 'l': return YYEncodingTypeInt32 | qualifier;
case 'L': return YYEncodingTypeUInt32 | qualifier;
case 'q': return YYEncodingTypeInt64 | qualifier;
case 'Q': return YYEncodingTypeUInt64 | qualifier;
case 'f': return YYEncodingTypeFloat | qualifier;
case 'd': return YYEncodingTypeDouble | qualifier;
case 'D': return YYEncodingTypeLongDouble | qualifier;
case '#': return YYEncodingTypeClass | qualifier;
case ':': return YYEncodingTypeSEL | qualifier;
case '*': return YYEncodingTypeCString | qualifier;
case '^': return YYEncodingTypePointer | qualifier;
case '[': return YYEncodingTypeCArray | qualifier;
case '(': return YYEncodingTypeUnion | qualifier;
case '{': return YYEncodingTypeStruct | qualifier;
case '@': {
if (len == && *(type + ) == '?')
return YYEncodingTypeBlock | qualifier;
else
return YYEncodingTypeObject | qualifier;
}
default: return YYEncodingTypeUnknown | qualifier;
}
}
上面的注释已经和很清楚了 ,然后切换回YYClassInfo.h
声明了一个抽象类 ,YYClassIvarInfo
@interface YYClassIvarInfo : NSObject
@property(nonatomic,assign,readonly) Ivar ivar;
@property(nonatomic,strong,readonly)NSString *name;
@property(nonatomic,assign,readonly)ptrdiff_t offset; //内存地址偏移
@property(nonatomic,strong,readonly)NSString *typeEncoding;
@property(nonatomic,assign,readonly) YYEncodingType type; -(instancetype)initWithIvar:(Ivar)ivar;
@end
然后到YYClassInfo.m 去实现
- (instancetype)initWithIvar:(Ivar)ivar {
// 初始化判空 如果为空 就返回nil
if (!ivar) return nil;
self = [super init];
_ivar = ivar; // 获取成员变量的名称
const char *name = ivar_getName(ivar);
if (name) { // 把c的字符串转化成oc的字符串
_name = [NSString stringWithUTF8String:name];
} _offset = ivar_getOffset(ivar); // 获取类型编码
const char *typeEncoding = ivar_getTypeEncoding(ivar);
if (typeEncoding) { // 转为oc的字符穿
_typeEncoding = [NSString stringWithUTF8String:typeEncoding]; // 转成枚举值
_type = YYEncodingGetType(typeEncoding); }
return self;
}
最后总结一句话,就是通过一个抽象类来返回需要的实例变量的信息。在切换回YYClassInfo.h
下面又声明两个类似的类 YYClassMethodInfo 和 YYClassPropertyInfo
/**
方法 的信息
*/
@interface YYClassMethodInfo : NSObject
@property (nonatomic,assign,readonly) Method method;
@property (nonatomic,strong,readonly) NSString *name;
@property (nonatomic,assign,readonly) SEL sel; // 一种数据类型, 调用方法,可以通过方法名还有SEL 调用
@property (nonatomic,assign,readonly) IMP imp; //方法实现的地址
@property (nonatomic,strong,readonly) NSString *typeEnconding;
@property (nonatomic,strong,readonly) NSString *returnTypeEncoding;
@property (nullable,nonatomic,strong,readonly) NSArray<NSString *> *argumentTypeEncodings; -(instancetype)initWithMethod:(Method)method;
@end /**
属性 的信息
*/
@interface YYClassPropertyInfo : NSObject
@property (nonatomic,assign,readonly) objc_property_t property; // 类的属性列表
@property (nonatomic, strong, readonly) NSString *name;
@property (nonatomic, assign, readonly) YYEncodingType type;
@property (nonatomic, strong, readonly) NSString *typeEncoding;
@property (nonatomic, strong, readonly) NSString *ivarName;
@property (nullable, nonatomic, assign, readonly) Class cls;
@property (nullable, nonatomic, strong, readonly) NSArray<NSString *> *protocls;
@property (nonatomic, assign ,readonly) SEL getter;
@property (nonatomic,assign,readonly) SEL setter; -(instancetype)initWithProperty:(objc_property_t)property;
@end
先从这个 YYClassMethodInfo 方法类说起, 不怎么熟悉的有三个 Method SEL IMP
IMP和SEL 我已经打上注释了,剩下一个Method,去官方文档里找下发现下图1-1-4和图1-1-5
图1-1-4
图1-1-4说明Method是个结构体,
图1-1-5
图1-1-5中可以看出这个结构体类的定义,需要注意的typs是方法的参数类型
1.method_name
:方法名为此方法的方法签名,相同函数名和参数的方法名是一样的
2.method_types
: 描述方法的参数类型
3.method_imp:
方法真实实现代码块的地址指针,可像C 一样直接调用
在看下 YYClassMethodInfo 类的实现
- (instancetype)initWithMethod:(Method)method {
if (!method) return nil;
self = [super init];
_method = method; // Method获取方法的名称
_sel = method_getName(method); // 方法的实现地址
_imp = method_getImplementation(method); // SEL 获取方法名
const char *name = sel_getName(_sel);
if (name) {
_name = [NSString stringWithUTF8String:name];
} // 获取类型
const char *typeEncoding = method_getTypeEncoding(method);
if (typeEncoding) {
_typeEncoding = [NSString stringWithUTF8String:typeEncoding];
} // 获取返回值类型
char *returnType = method_copyReturnType(method);
if (returnType) {
_returnTypeEncoding = [NSString stringWithUTF8String:returnType]; // 但凡 通过copy retain alloc 系统方法得到的内存,必须使用relea() 或 free() 进行释放
free(returnType);
} // 获取参数列表
unsigned int argumentCount = method_getNumberOfArguments(method);
if (argumentCount > ) { NSMutableArray *argumentTypes = [NSMutableArray new]; for (unsigned int i = ; i < argumentCount; i++) { // 获取参数中的某一个参数
char *argumentType = method_copyArgumentType(method, i); NSString *type = argumentType ? [NSString stringWithUTF8String:argumentType] : nil;
[argumentTypes addObject:type ? type : @""];
if (argumentType) free(argumentType);
}
_argumentTypeEncodings = argumentTypes;
}
return self;
}
在 看下面的 YYClassPropertyInfo 类的实现
//https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html#//apple_ref/doc/uid/TP40008048-CH101-SW5 属性的encongding type
- (instancetype)initWithProperty:(objc_property_t)property {
if (!property) return nil;
self = [self init]; _property = property; // 1. 获取属性名称
const char *name = property_getName(property);
if (name) {
_name = [NSString stringWithUTF8String:name];
} // 2.获取每一个属性的编码字符串(所有特性)看下图1-1-6
YYEncodingType type = ;
unsigned int attrCount;//存储特性数量
objc_property_attribute_t *attrs = property_copyAttributeList(property, &attrCount); // 3. 编译每一个属性的 objc_property_attribute_t attrs是一个 存储结构体(name和value)的数组
for (unsigned int i = ; i < attrCount; i++) {
// 3.1 根据objc_property_attribute_t 中的name 做一些事
switch (attrs[i].name[]) { // T 代码属性的类型编码
case 'T': { // Type encoding
if (attrs[i].value) {
_typeEncoding = [NSString stringWithUTF8String:attrs[i].value];
type = YYEncodingGetType(attrs[i].value); // 计算属性的实体类型
if((type & YYEncodingTypeMask) == YYEncodingTypeObject && _typeEncoding.length){
NSScanner *scanner = [NSScanner scannerWithString:_typeEncoding]; // 字符串扫描 详情在下面
if (![scanner scanString:@"@\"" intoString:NULL]) continue;
NSString *clsName = nil;
if ([scanner scanUpToCharactersFromSet:[NSCharacterSet characterSetWithCharactersInString:@"\"<"] intoString:&clsName]) {
if (clsName.length) _cls = objc_getClass(clsName.UTF8String); //这个 除了一个错误,让我注意到下面这个
/**
object_getClass(<#id _Nullable obj#>) 参数是id 指针,如果是一个实例对象就返回该实例的类,如果是一个类,返回的是元类名(isa指向的类)
objc_getClass(<#const char * _Nonnull name#>) 参数是字符串,就是根据字符串获取类名
*/
}
NSMutableArray *protocols = nil;
while ([scanner scanUpToString:@"<" intoString:NULL]) {
NSString *protocol = nil;
if ([scanner scanUpToString:@">" intoString:&protocol]) {
if (protocol.length) {
if(!protocol) protocols = [NSMutableArray new];
[protocols addObject:protocol];
}
}
[scanner scanString:@">" intoString:NULL];
}
_protocls = protocols;
}
} break;
case 'V': { // Instance variable
if (attrs[i].value) {
_ivarName = [NSString stringWithUTF8String:attrs[i].value];
}
} break;
case 'R': {
type |= YYEncodingTypePropertyReadonly;
} break;
case 'C': {
type |= YYEncodingTypePropertyCopy;
} break;
case '&': {
type |= YYEncodingTypePropertyRetain;
} break;
case 'N': {
type |= YYEncodingTypePropertyNonatomic;
} break;
case 'D': {
type |= YYEncodingTypePropertyDynamic;
} break;
case 'W': {
type |= YYEncodingTypePropertyWeak;
} break;
case 'G': { // getter 方法
type |= YYEncodingTypePropertyCustomGetter;
if (attrs[i].value) {
_getter = NSSelectorFromString([NSString stringWithUTF8String:attrs[i].value]);
}
} break;
case 'S': { // setter 方法
type |= YYEncodingTypePropertyCustomSetter;
if (attrs[i].value) {
_setter = NSSelectorFromString([NSString stringWithUTF8String:attrs[i].value]);
}
} // break; 作者这里注视了break,说下面代码是解释,我是没看出来,我个人的理解是如果有setter方法,就一定有getter方法(如果只存不取,没意义),所以setter 不跳出循环,直到getter才跳
default: break;
}
}
if (attrs) {
free(attrs);
attrs = NULL;
} _type = type; // 获取setter 和 getter 方法
if (_name.length) {
if (!_getter) {
_getter = NSSelectorFromString(_name);
}
if (!_setter) {
_setter = NSSelectorFromString([NSString stringWithFormat:@"set%@%@:", [_name substringToIndex:].uppercaseString, [_name substringFromIndex:]]);
}
}
return self;
}
关于 NSScanner 的具体使用,参考链接:http://blog.csdn.net/hdfqq188816190/article/details/53170711
图1-1-6
emmmm.... 属性 方法 实例变量 的抽象类都已经搞定了,之后就直接调用编码就OK了 。切换回 YYClassInfo.h 文件,接着往下看声明了一个关于类信息的类YYClassInfo,
@interface YYClassInfo : NSObject
@property(nonatomic,assign,readonly) Class cls;
@property(nullable,nonatomic,assign,readonly) Class superCls;
@property(nullable,nonatomic,assign,readonly) Class metaCls;
@property(nonatomic,readonly) BOOL isMeta;
@property(nonatomic,strong,readonly) NSString *name;
@property(nullable,nonatomic,strong,readonly) YYClassInfo *superClassInfo;
@property(nullable,nonatomic,strong,readonly)NSDictionary<NSString *,YYClassIvarInfo *> *ivarInfos;
@property(nullable,nonatomic,strong,readonly)NSDictionary<NSString *,YYClassMethodInfo *> *methodInfos;
@property(nullable,nonatomic,strong,readonly)NSDictionary<NSString *,YYClassPropertyInfo *> *propertyInfos;
/**
如果这个类改变了,你就要调用这个方法去更新这个类的信息;(原作者举的例子:使用 class_addMethod(),添加了一个方法,你就该调用本方法,去更新)
之后调用needUpdate ,它会返回YES,然后调用 classInfoWithClass 或者 classInfoWithClassName 来得到已经更新过得类信息;
*/
-(void)setNeedUpdate;
/**
如果返回了YES,你应该停止使用这个实例,要要用 classInfoWithClass 或者 classInfoWithClassName 来得到已经更新过得类信息;
*/
-(BOOL)neeUpdate;
/**
通过类得到一个类的信息(在的第一次接触的时候会缓存该类及父类的信息), 这个方法是线程安全的,
return 一个类信息,或者nil(发生错误)
*/
+(nullable instancetype)classInfoWithClass:(Class)cls;
/**
通过类名得到一个类的信息在的第一次接触的时候会缓存该类及父类的信息), 这个方法是线程安全的,
return 一个类信息,或者nil(发生错误)
*/
+(nullable instancetype)classInfoWithClassName:(NSString *)className; @end
我觉的我的注释写的听清楚的,就不多说了,去他的实现文件 看看,
@implementation YYClassInfo{
BOOL _needUpdate;
}
-(instancetype)initWithClass:(Class)cls{
if (!cls) {
return nil;
}
self = [super init];
_cls = cls;
_superCls = class_getSuperclass(cls);
_isMeta = class_isMetaClass(cls);
if (!_isMeta) {
_metaCls = objc_getMetaClass(class_getName(cls));
}
_name = NSStringFromClass(cls);
[self _update];
_superClassInfo = [self.class classInfoWithClass:cls];
return self;
}
-(void)_update{
_ivarInfos = nil;
_methodInfos = nil;
_propertyInfos = nil; Class cls = self.cls;
unsigned int methodCount = ;
Method *methods = class_copyMethodList(cls, &methodCount);
if (methods) {
NSMutableDictionary *methodInfos = [NSMutableDictionary new];
_methodInfos = methodInfos;
for (unsigned int i = ; i<methodCount; i++) {
YYClassMethodInfo *info = [[YYClassMethodInfo alloc]initWithMethod:methods[i]];
if (info.name) {
methodInfos[info.name] = info;
}
free(methods);
}
}
unsigned int propertyCount = ;
objc_property_t *properties = class_copyPropertyList(cls, &propertyCount);
if (properties) {
NSMutableDictionary *propertyInfos = [NSMutableDictionary new];
_propertyInfos = propertyInfos;
for (unsigned int i = ; i<propertyCount; i++) {
YYClassPropertyInfo *info = [[YYClassPropertyInfo alloc]initWithProperty:properties[i]];
if (info.name) {
propertyInfos[info.name] = info;
}
}
free(properties);
} unsigned int ivarCount = ;
Ivar *ivars = class_copyIvarList(cls, &ivarCount);
if (ivars) {
NSMutableDictionary *ivarInfos = [NSMutableDictionary new];
_ivarInfos = ivarInfos;
for (unsigned int i =; i < ivarCount; i++) {
YYClassIvarInfo *info = [[YYClassIvarInfo alloc]initWithIvar:ivars[i]];
if (info.name) {
ivarInfos[info.name] = info;
}
}
free(ivars);
}
if(!_ivarInfos) _ivarInfos = @{};
if(!_methodInfos) _methodInfos = @{};
if(!_propertyInfos) _propertyInfos = @{};
_needUpdate = NO;
}
-(void)setNeedUpdate{
_needUpdate = YES;
}
-(BOOL)neeUpdate{
return _needUpdate;
}
+(instancetype)classInfoWithClass:(Class)cls{
if(!cls) return nil;
static CFMutableDictionaryRef classCache;//这里使用Core Foundation 框架的字典,性能速度是有提升,但就是写起来麻烦些,具体的详情参考下面 static CFMutableDictionaryRef metaCache; static dispatch_semaphore_t lock; // 信号量
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
classCache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), , &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
metaCache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), , &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
lock = dispatch_semaphore_create();
});
dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
YYClassInfo *info = CFDictionaryGetValue(class_isMetaClass(cls) ? metaCache : classCache,(__bridge const void *)cls);
if (info && info -> _needUpdate) {
[info _update];
}
dispatch_semaphore_signal(lock);
if (!info) {
info = [[YYClassInfo alloc]initWithClass:cls];
if (info) {
dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
CFDictionarySetValue(info.isMeta ? metaCache :classCache, (__bridge const void *)cls, (__bridge const void *)info);
dispatch_semaphore_signal(lock);
}
}
return info;
}
+(instancetype)classInfoWithClassName:(NSString *)className{
Class cls = NSClassFromString(className);
return [self classInfoWithClass:cls];
} @end
关于CFDictonary的详情 参考:http://www.jianshu.com/p/a39509bb24eb
关于 GCD 信号量知识,参考:http://www.cnblogs.com/yajunLi/p/6274282.html
ps:怪不得YYModel 只有四个文件 , 他把类都都写在一个文件里了 - -|| 吐血。。。
YYModel学习总结YYClassInfo(1)的更多相关文章
- YYModel 源码解读(一)之YYModel.h
#if __has_include(<YYModel/YYModel.h>) FOUNDATION_EXPORT double YYModelVersionNumber; FOUNDATI ...
- YYModel 源码历险记 代码结构
前言 因为公司需要开发一个内部使用的字典转模型的项目,所以最近也是在看关于字典转模型的内容.有Mantle,jsonModel,MJExtension等众多框架,最后还是选择了先从YYModel源码读 ...
- YYModel底层解析- Runtime
这段时间一直在忙新的需求,没有时间来整理代码,发表自己技术博客,今天我们来看一下YYModel的底层解析以及如何使用,希望对大家有所帮助! 一 概述 概括 YYModel是一个轻量级的JSON模型转换 ...
- YYModel 源码解读(二)之YYClassInfo.h (3)
前边3篇介绍了YYClassinfo 文件的组成单元,算是功能的分割,按照业务的设计思想来说,方向应该是相反的 由此引申出我们在设计api的思想其实和项目管理是很类似的----- 一些题外话 1.目的 ...
- YYModel 源码解读(二)之YYClassInfo.h (2)
/** Instance variable information. */ @interface YYClassIvarInfo : NSObject @property (nonatomic, as ...
- YYModel 源码解读(二)之YYClassInfo.h (1)
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_END 为了兼容Swift 中的 ? 和 ! oc 在6.3引入了两个新的类型注释:__nullable和__non ...
- YYKit之YYModel
原文:http://www.cnblogs.com/lujianwenance/p/5706548.html 本文主要是对YYModel文件结构的简单分析,能帮助你更快的熟悉和学习YYModel ...
- YYModel 源码解读 总结
在使用swfit写代码的过程中,使用了下oc写的字典转模型,发现有些属性转不成功,就萌生了阅读源码的想法. 其实一直都知道Runtime机制,但并没有系统的学习,可能是因为平时的使用比较少,无意间在g ...
- YYModel 源码解读(二)之NSObject+YYModel.h (2)
_YYModelMeta 这个内部的类主要是对这个类的描述.包含了和此类转换相关的数据. /// A class info in object model. @interface _YYModel ...
随机推荐
- 你所不知道的 CSS 滤镜技巧与细节
承接上一篇你所不知道的 CSS 动画技巧与细节,本文主要介绍 CSS 滤镜的不常用用法,希望能给读者带来一些干货! OK,下面直接进入正文.本文所描述的滤镜,指的是 CSS3 出来后的滤镜,不是 IE ...
- 英语学习APP案例分析
第一部分 调研, 评测 1.上手体验 界面简洁,有常规的词典翻译功能,针对四六级或考研的人有特别的"单词挑战"模块,以及针对口语训练的"我爱说英语"模块,多功能 ...
- 集美大学网络1413第十一次作业成绩(团队七) -- Alpha冲刺之事后诸葛亮
题目 团队作业7--Alpha冲刺之事后诸葛亮 团队作业7成绩 团队/分值 设想和目标 计划 资源 变更管理 设计/实现 测试/发布 团队角色. 管理.合作 总结 讨论照片 团队成员 角色.贡献 总 ...
- 结对编程1-四则运算GUI实现(58、59)
题目描述 我们在个人作业1中,用各种语言实现了一个命令行的四则运算小程序.进一步,本次要求把这个程序做成GUI(可以是Windows PC 上的,也可以是Mac.Linux,web,手机上的),成为一 ...
- 个人作业2-英语学习案例app分析
第一部分 调研, 评测 (软件的bug,功能评测,黑箱测试, 第8章 用户调研, 12 章 软件的用户体验) 下载并使用,描述最简单直观的个人第一次上手体验. ①个人感觉还不错,词典的首页页面挺好看的 ...
- 201521123054《Java程序设计》第1周学习总结
#1. 本章学习总结 你对于本章知识的学习总结 本章我们学习了各种java相关文件的使用,能够进行基本的程序操作: 学会使用博客.码云与PTA管理java: #2. 书面作业 1.为什么java程序可 ...
- 201521123025《java程序设计》第14周学习总结
1. 本周学习总结 2. 书面作业 1. MySQL数据库基本操作 1.1建立数据库,将自己的姓名.学号作为一条记录插入.(截图,需出现自己的学号.姓名) 1.2在自己建立的数据库上执行常见SQL语句 ...
- 201521123016《Java程序设计》第10周学习总结
1. 本周学习总结 2. 书面作业 本次PTA作业题集异常.多线程 1.finally 题目4-2 1.1 截图你的提交结果(出现学号) 1.2 4-2中finally中捕获异常需要注意什么? 只有执 ...
- 201521123024 《java程序设计》 第12周学习总结
1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多流与文件相关内容.
- 201521123089 《Java程序设计》第9周学习总结
1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结异常相关内容. 2. 书面作业 本次PTA作业题集异常 常用异常 题目5-11.1 截图你的提交结果(出现学号) 1.2 自己以前编 ...