提交
This commit is contained in:
22
Pods/YYModel/LICENSE
generated
Normal file
22
Pods/YYModel/LICENSE
generated
Normal file
@@ -0,0 +1,22 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 ibireme <ibireme@gmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
619
Pods/YYModel/README.md
generated
Executable file
619
Pods/YYModel/README.md
generated
Executable file
@@ -0,0 +1,619 @@
|
||||
YYModel
|
||||
==============
|
||||
|
||||
[](https://raw.githubusercontent.com/ibireme/YYModel/master/LICENSE)
|
||||
[](https://github.com/Carthage/Carthage)
|
||||
[](http://cocoapods.org/?q= YYModel)
|
||||
[](http://cocoapods.org/?q= YYModel)
|
||||
[](https://travis-ci.org/ibireme/YYModel)
|
||||
[](https://codecov.io/github/ibireme/YYModel?branch=master)
|
||||
|
||||
High performance model framework for iOS/OSX.<br/>
|
||||
(It's a component of [YYKit](https://github.com/ibireme/YYKit))
|
||||
|
||||
|
||||
Performance
|
||||
==============
|
||||
|
||||
Time cost (process GithubUser 10000 times on iPhone 6):
|
||||
|
||||

|
||||
|
||||
See `Benchmark/ModelBenchmark.xcodeproj` for more benchmark case.
|
||||
|
||||
|
||||
Features
|
||||
==============
|
||||
- **High performance**: The conversion performance is close to handwriting code.
|
||||
- **Automatic type conversion**: The object types can be automatically converted.
|
||||
- **Type Safe**: All data types will be verified to ensure type-safe during the conversion process.
|
||||
- **Non-intrusive**: There is no need to make the model class inherit from other base class.
|
||||
- **Lightwight**: This library contains only 5 files.
|
||||
- **Docs and unit testing**: 100% docs coverage, 99.6% code coverage.
|
||||
|
||||
Usage
|
||||
==============
|
||||
|
||||
###Simple model json convert
|
||||
|
||||
// JSON:
|
||||
{
|
||||
"uid":123456,
|
||||
"name":"Harry",
|
||||
"created":"1965-07-31T00:00:00+0000"
|
||||
}
|
||||
|
||||
// Model:
|
||||
@interface User : NSObject
|
||||
@property UInt64 uid;
|
||||
@property NSString *name;
|
||||
@property NSDate *created;
|
||||
@end
|
||||
@implementation User
|
||||
@end
|
||||
|
||||
|
||||
// Convert json to model:
|
||||
User *user = [User yy_modelWithJSON:json];
|
||||
|
||||
// Convert model to json:
|
||||
NSDictionary *json = [user yy_modelToJSONObject];
|
||||
|
||||
|
||||
If the type of an object in JSON/Dictionary cannot be matched to the property of the model, the following automatic conversion is performed. If the automatic conversion failed, the value will be ignored.
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>JSON/Dictionary</th>
|
||||
<th>Model</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>NSString</td>
|
||||
<td>NSNumber,NSURL,SEL,Class</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>NSNumber</td>
|
||||
<td>NSString</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>NSString/NSNumber</td>
|
||||
<td>C number (BOOL,int,float,NSUInteger,UInt64,...)<br/>
|
||||
NaN and Inf will be ignored</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>NSString</td>
|
||||
<td>NSDate parsed with these formats:<br/>
|
||||
yyyy-MM-dd<br/>
|
||||
yyyy-MM-dd HH:mm:ss<br/>
|
||||
yyyy-MM-dd'T'HH:mm:ss<br/>
|
||||
yyyy-MM-dd'T'HH:mm:ssZ<br/>
|
||||
EEE MMM dd HH:mm:ss Z yyyy
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>NSDate</td>
|
||||
<td>NSString formatted with ISO8601:<br/>
|
||||
"YYYY-MM-dd'T'HH:mm:ssZ"</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>NSValue</td>
|
||||
<td>struct (CGRect,CGSize,...)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>NSNull</td>
|
||||
<td>nil,0</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>"no","false",...</td>
|
||||
<td>@(NO),0</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>"yes","true",...</td>
|
||||
<td>@(YES),1</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
|
||||
###Match model property to different JSON key
|
||||
|
||||
// JSON:
|
||||
{
|
||||
"n":"Harry Pottery",
|
||||
"p": 256,
|
||||
"ext" : {
|
||||
"desc" : "A book written by J.K.Rowing."
|
||||
},
|
||||
"ID" : 100010
|
||||
}
|
||||
|
||||
// Model:
|
||||
@interface Book : NSObject
|
||||
@property NSString *name;
|
||||
@property NSInteger page;
|
||||
@property NSString *desc;
|
||||
@property NSString *bookID;
|
||||
@end
|
||||
@implementation Book
|
||||
+ (NSDictionary *)modelCustomPropertyMapper {
|
||||
return @{@"name" : @"n",
|
||||
@"page" : @"p",
|
||||
@"desc" : @"ext.desc",
|
||||
@"bookID" : @[@"id",@"ID",@"book_id"]};
|
||||
}
|
||||
@end
|
||||
|
||||
You can map a json key (key path) or an array of json key (key path) to one or multiple property name. If there's no mapper for a property, it will use the property's name as default.
|
||||
|
||||
###Nested model
|
||||
|
||||
// JSON
|
||||
{
|
||||
"author":{
|
||||
"name":"J.K.Rowling",
|
||||
"birthday":"1965-07-31T00:00:00+0000"
|
||||
},
|
||||
"name":"Harry Potter",
|
||||
"pages":256
|
||||
}
|
||||
|
||||
// Model: (no need to do anything)
|
||||
@interface Author : NSObject
|
||||
@property NSString *name;
|
||||
@property NSDate *birthday;
|
||||
@end
|
||||
@implementation Author
|
||||
@end
|
||||
|
||||
@interface Book : NSObject
|
||||
@property NSString *name;
|
||||
@property NSUInteger pages;
|
||||
@property Author *author;
|
||||
@end
|
||||
@implementation Book
|
||||
@end
|
||||
|
||||
|
||||
|
||||
### Container property
|
||||
|
||||
@class Shadow, Border, Attachment;
|
||||
|
||||
@interface Attributes
|
||||
@property NSString *name;
|
||||
@property NSArray *shadows; //Array<Shadow>
|
||||
@property NSSet *borders; //Set<Border>
|
||||
@property NSMutableDictionary *attachments; //Dict<NSString,Attachment>
|
||||
@end
|
||||
|
||||
@implementation Attributes
|
||||
+ (NSDictionary *)modelContainerPropertyGenericClass {
|
||||
// value should be Class or Class name.
|
||||
return @{@"shadows" : [Shadow class],
|
||||
@"borders" : Border.class,
|
||||
@"attachments" : @"Attachment" };
|
||||
}
|
||||
@end
|
||||
|
||||
|
||||
|
||||
|
||||
### Whitelist and blacklist
|
||||
|
||||
@interface User
|
||||
@property NSString *name;
|
||||
@property NSUInteger age;
|
||||
@end
|
||||
|
||||
@implementation Attributes
|
||||
+ (NSArray *)modelPropertyBlacklist {
|
||||
return @[@"test1", @"test2"];
|
||||
}
|
||||
+ (NSArray *)modelPropertyWhitelist {
|
||||
return @[@"name"];
|
||||
}
|
||||
@end
|
||||
|
||||
###Data validate and custom transform
|
||||
|
||||
// JSON:
|
||||
{
|
||||
"name":"Harry",
|
||||
"timestamp" : 1445534567
|
||||
}
|
||||
|
||||
// Model:
|
||||
@interface User
|
||||
@property NSString *name;
|
||||
@property NSDate *createdAt;
|
||||
@end
|
||||
|
||||
@implementation User
|
||||
- (BOOL)modelCustomTransformFromDictionary:(NSDictionary *)dic {
|
||||
NSNumber *timestamp = dic[@"timestamp"];
|
||||
if (![timestamp isKindOfClass:[NSNumber class]]) return NO;
|
||||
_createdAt = [NSDate dateWithTimeIntervalSince1970:timestamp.floatValue];
|
||||
return YES;
|
||||
}
|
||||
- (BOOL)modelCustomTransformToDictionary:(NSMutableDictionary *)dic {
|
||||
if (!_createdAt) return NO;
|
||||
dic[@"timestamp"] = @(n.timeIntervalSince1970);
|
||||
return YES;
|
||||
}
|
||||
@end
|
||||
|
||||
###Coding/Copying/hash/equal/description
|
||||
|
||||
@interface YYShadow :NSObject <NSCoding, NSCopying>
|
||||
@property (nonatomic, copy) NSString *name;
|
||||
@property (nonatomic, assign) CGSize size;
|
||||
@end
|
||||
|
||||
@implementation YYShadow
|
||||
- (void)encodeWithCoder:(NSCoder *)aCoder { [self yy_modelEncodeWithCoder:aCoder]; }
|
||||
- (id)initWithCoder:(NSCoder *)aDecoder { self = [super init]; return [self yy_modelInitWithCoder:aDecoder]; }
|
||||
- (id)copyWithZone:(NSZone *)zone { return [self yy_modelCopy]; }
|
||||
- (NSUInteger)hash { return [self yy_modelHash]; }
|
||||
- (BOOL)isEqual:(id)object { return [self yy_modelIsEqual:object]; }
|
||||
- (NSString *)description { return [self yy_modelDescription]; }
|
||||
@end
|
||||
|
||||
|
||||
Installation
|
||||
==============
|
||||
|
||||
### CocoaPods
|
||||
|
||||
1. Add `pod 'YYModel'` to your Podfile.
|
||||
2. Run `pod install` or `pod update`.
|
||||
3. Import \<YYModel/YYModel.h\>.
|
||||
|
||||
|
||||
### Carthage
|
||||
|
||||
1. Add `github "ibireme/YYModel"` to your Cartfile.
|
||||
2. Run `carthage update --platform ios` and add the framework to your project.
|
||||
3. Import \<YYModel/YYModel.h\>.
|
||||
|
||||
|
||||
### Manually
|
||||
|
||||
1. Download all the files in the YYModel subdirectory.
|
||||
2. Add the source files to your Xcode project.
|
||||
3. Import `YYModel.h`.
|
||||
|
||||
|
||||
Documentation
|
||||
==============
|
||||
Full API documentation is available on [CocoaDocs](http://cocoadocs.org/docsets/YYModel/).<br/>
|
||||
You can also install documentation locally using [appledoc](https://github.com/tomaz/appledoc).
|
||||
|
||||
|
||||
Requirements
|
||||
==============
|
||||
This library requires `iOS 6.0+` and `Xcode 7.0+`.
|
||||
|
||||
|
||||
License
|
||||
==============
|
||||
YYModel is provided under the MIT license. See LICENSE file for details.
|
||||
|
||||
|
||||
<br/><br/>
|
||||
---
|
||||
中文介绍
|
||||
==============
|
||||
高性能 iOS/OSX 模型转换框架。<br/>
|
||||
(该项目是 [YYKit](https://github.com/ibireme/YYKit) 组件之一)
|
||||
|
||||
|
||||
性能
|
||||
==============
|
||||
处理 GithubUser 数据 10000 次耗时统计 (iPhone 6):
|
||||
|
||||

|
||||
|
||||
更多测试代码和用例见 `Benchmark/ModelBenchmark.xcodeproj`。
|
||||
|
||||
|
||||
特性
|
||||
==============
|
||||
- **高性能**: 模型转换性能接近手写解析代码。
|
||||
- **自动类型转换**: 对象类型可以自动转换,详情见下方表格。
|
||||
- **类型安全**: 转换过程中,所有的数据类型都会被检测一遍,以保证类型安全,避免崩溃问题。
|
||||
- **无侵入性**: 模型无需继承自其他基类。
|
||||
- **轻量**: 该框架只有 5 个文件 (包括.h文件)。
|
||||
- **文档和单元测试**: 文档覆盖率100%, 代码覆盖率99.6%。
|
||||
|
||||
使用方法
|
||||
==============
|
||||
|
||||
###简单的 Model 与 JSON 相互转换
|
||||
|
||||
// JSON:
|
||||
{
|
||||
"uid":123456,
|
||||
"name":"Harry",
|
||||
"created":"1965-07-31T00:00:00+0000"
|
||||
}
|
||||
|
||||
// Model:
|
||||
@interface User : NSObject
|
||||
@property UInt64 uid;
|
||||
@property NSString *name;
|
||||
@property NSDate *created;
|
||||
@end
|
||||
@implementation User
|
||||
@end
|
||||
|
||||
|
||||
// 将 JSON (NSData,NSString,NSDictionary) 转换为 Model:
|
||||
User *user = [User yy_modelWithJSON:json];
|
||||
|
||||
// 将 Model 转换为 JSON 对象:
|
||||
NSDictionary *json = [user yy_modelToJSONObject];
|
||||
|
||||
当 JSON/Dictionary 中的对象类型与 Model 属性不一致时,YYModel 将会进行如下自动转换。自动转换不支持的值将会被忽略,以避免各种潜在的崩溃问题。
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>JSON/Dictionary</th>
|
||||
<th>Model</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>NSString</td>
|
||||
<td>NSNumber,NSURL,SEL,Class</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>NSNumber</td>
|
||||
<td>NSString</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>NSString/NSNumber</td>
|
||||
<td>基础类型 (BOOL,int,float,NSUInteger,UInt64,...)<br/>
|
||||
NaN 和 Inf 会被忽略</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>NSString</td>
|
||||
<td>NSDate 以下列格式解析:<br/>
|
||||
yyyy-MM-dd<br/>
|
||||
yyyy-MM-dd HH:mm:ss<br/>
|
||||
yyyy-MM-dd'T'HH:mm:ss<br/>
|
||||
yyyy-MM-dd'T'HH:mm:ssZ<br/>
|
||||
EEE MMM dd HH:mm:ss Z yyyy
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>NSDate</td>
|
||||
<td>NSString 格式化为 ISO8601:<br/>
|
||||
"YYYY-MM-dd'T'HH:mm:ssZ"</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>NSValue</td>
|
||||
<td>struct (CGRect,CGSize,...)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>NSNull</td>
|
||||
<td>nil,0</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>"no","false",...</td>
|
||||
<td>@(NO),0</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>"yes","true",...</td>
|
||||
<td>@(YES),1</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
###Model 属性名和 JSON 中的 Key 不相同
|
||||
|
||||
// JSON:
|
||||
{
|
||||
"n":"Harry Pottery",
|
||||
"p": 256,
|
||||
"ext" : {
|
||||
"desc" : "A book written by J.K.Rowing."
|
||||
},
|
||||
"ID" : 100010
|
||||
}
|
||||
|
||||
// Model:
|
||||
@interface Book : NSObject
|
||||
@property NSString *name;
|
||||
@property NSInteger page;
|
||||
@property NSString *desc;
|
||||
@property NSString *bookID;
|
||||
@end
|
||||
@implementation Book
|
||||
//返回一个 Dict,将 Model 属性名对映射到 JSON 的 Key。
|
||||
+ (NSDictionary *)modelCustomPropertyMapper {
|
||||
return @{@"name" : @"n",
|
||||
@"page" : @"p",
|
||||
@"desc" : @"ext.desc",
|
||||
@"bookID" : @[@"id",@"ID",@"book_id"]};
|
||||
}
|
||||
@end
|
||||
|
||||
你可以把一个或一组 json key (key path) 映射到一个或多个属性。如果一个属性没有映射关系,那默认会使用相同属性名作为映射。
|
||||
|
||||
在 json->model 的过程中:如果一个属性对应了多个 json key,那么转换过程会按顺序查找,并使用第一个不为空的值。
|
||||
|
||||
在 model->json 的过程中:如果一个属性对应了多个 json key (key path),那么转换过程仅会处理第一个 json key (key path);如果多个属性对应了同一个 json key,则转换过过程会使用其中任意一个不为空的值。
|
||||
|
||||
###Model 包含其他 Model
|
||||
|
||||
// JSON
|
||||
{
|
||||
"author":{
|
||||
"name":"J.K.Rowling",
|
||||
"birthday":"1965-07-31T00:00:00+0000"
|
||||
},
|
||||
"name":"Harry Potter",
|
||||
"pages":256
|
||||
}
|
||||
|
||||
// Model: 什么都不用做,转换会自动完成
|
||||
@interface Author : NSObject
|
||||
@property NSString *name;
|
||||
@property NSDate *birthday;
|
||||
@end
|
||||
@implementation Author
|
||||
@end
|
||||
|
||||
@interface Book : NSObject
|
||||
@property NSString *name;
|
||||
@property NSUInteger pages;
|
||||
@property Author *author; //Book 包含 Author 属性
|
||||
@end
|
||||
@implementation Book
|
||||
@end
|
||||
|
||||
|
||||
|
||||
###容器类属性
|
||||
|
||||
@class Shadow, Border, Attachment;
|
||||
|
||||
@interface Attributes
|
||||
@property NSString *name;
|
||||
@property NSArray *shadows; //Array<Shadow>
|
||||
@property NSSet *borders; //Set<Border>
|
||||
@property NSMutableDictionary *attachments; //Dict<NSString,Attachment>
|
||||
@end
|
||||
|
||||
@implementation Attributes
|
||||
// 返回容器类中的所需要存放的数据类型 (以 Class 或 Class Name 的形式)。
|
||||
+ (NSDictionary *)modelContainerPropertyGenericClass {
|
||||
return @{@"shadows" : [Shadow class],
|
||||
@"borders" : Border.class,
|
||||
@"attachments" : @"Attachment" };
|
||||
}
|
||||
@end
|
||||
|
||||
|
||||
|
||||
|
||||
###黑名单与白名单
|
||||
|
||||
@interface User
|
||||
@property NSString *name;
|
||||
@property NSUInteger age;
|
||||
@end
|
||||
|
||||
@implementation Attributes
|
||||
// 如果实现了该方法,则处理过程中会忽略该列表内的所有属性
|
||||
+ (NSArray *)modelPropertyBlacklist {
|
||||
return @[@"test1", @"test2"];
|
||||
}
|
||||
// 如果实现了该方法,则处理过程中不会处理该列表外的属性。
|
||||
+ (NSArray *)modelPropertyWhitelist {
|
||||
return @[@"name"];
|
||||
}
|
||||
@end
|
||||
|
||||
###数据校验与自定义转换
|
||||
|
||||
// JSON:
|
||||
{
|
||||
"name":"Harry",
|
||||
"timestamp" : 1445534567
|
||||
}
|
||||
|
||||
// Model:
|
||||
@interface User
|
||||
@property NSString *name;
|
||||
@property NSDate *createdAt;
|
||||
@end
|
||||
|
||||
@implementation User
|
||||
// 当 JSON 转为 Model 完成后,该方法会被调用。
|
||||
// 你可以在这里对数据进行校验,如果校验不通过,可以返回 NO,则该 Model 会被忽略。
|
||||
// 你也可以在这里做一些自动转换不能完成的工作。
|
||||
- (BOOL)modelCustomTransformFromDictionary:(NSDictionary *)dic {
|
||||
NSNumber *timestamp = dic[@"timestamp"];
|
||||
if (![timestamp isKindOfClass:[NSNumber class]]) return NO;
|
||||
_createdAt = [NSDate dateWithTimeIntervalSince1970:timestamp.floatValue];
|
||||
return YES;
|
||||
}
|
||||
|
||||
// 当 Model 转为 JSON 完成后,该方法会被调用。
|
||||
// 你可以在这里对数据进行校验,如果校验不通过,可以返回 NO,则该 Model 会被忽略。
|
||||
// 你也可以在这里做一些自动转换不能完成的工作。
|
||||
- (BOOL)modelCustomTransformToDictionary:(NSMutableDictionary *)dic {
|
||||
if (!_createdAt) return NO;
|
||||
dic[@"timestamp"] = @(n.timeIntervalSince1970);
|
||||
return YES;
|
||||
}
|
||||
@end
|
||||
|
||||
###Coding/Copying/hash/equal/description
|
||||
|
||||
@interface YYShadow :NSObject <NSCoding, NSCopying>
|
||||
@property (nonatomic, copy) NSString *name;
|
||||
@property (nonatomic, assign) CGSize size;
|
||||
@end
|
||||
|
||||
@implementation YYShadow
|
||||
// 直接添加以下代码即可自动完成
|
||||
- (void)encodeWithCoder:(NSCoder *)aCoder { [self yy_modelEncodeWithCoder:aCoder]; }
|
||||
- (id)initWithCoder:(NSCoder *)aDecoder { self = [super init]; return [self yy_modelInitWithCoder:aDecoder]; }
|
||||
- (id)copyWithZone:(NSZone *)zone { return [self yy_modelCopy]; }
|
||||
- (NSUInteger)hash { return [self yy_modelHash]; }
|
||||
- (BOOL)isEqual:(id)object { return [self yy_modelIsEqual:object]; }
|
||||
- (NSString *)description { return [self yy_modelDescription]; }
|
||||
@end
|
||||
|
||||
|
||||
安装
|
||||
==============
|
||||
|
||||
### CocoaPods
|
||||
|
||||
1. 在 Podfile 中添加 `pod 'YYModel'`。
|
||||
2. 执行 `pod install` 或 `pod update`。
|
||||
3. 导入 \<YYModel/YYModel.h\>。
|
||||
|
||||
|
||||
### Carthage
|
||||
|
||||
1. 在 Cartfile 中添加 `github "ibireme/YYModel"`。
|
||||
2. 执行 `carthage update --platform ios` 并将生成的 framework 添加到你的工程。
|
||||
3. 导入 \<YYModel/YYModel.h\>。
|
||||
|
||||
|
||||
### 手动安装
|
||||
|
||||
1. 下载 YYModel 文件夹内的所有内容。
|
||||
2. 将 YYModel 内的源文件添加(拖放)到你的工程。
|
||||
3. 导入 `YYModel.h`。
|
||||
|
||||
|
||||
文档
|
||||
==============
|
||||
你可以在 [CocoaDocs](http://cocoadocs.org/docsets/YYModel/) 查看在线 API 文档,也可以用 [appledoc](https://github.com/tomaz/appledoc) 本地生成文档。
|
||||
|
||||
|
||||
系统要求
|
||||
==============
|
||||
该项目最低支持 `iOS 6.0` 和 `Xcode 7.0`。
|
||||
|
||||
|
||||
许可证
|
||||
==============
|
||||
YYModel 使用 MIT 许可证,详情见 LICENSE 文件。
|
||||
|
||||
相关链接
|
||||
==============
|
||||
|
||||
[iOS JSON 模型转换库评测](http://blog.ibireme.com/2015/10/23/ios_model_framework_benchmark/)
|
||||
|
||||
430
Pods/YYModel/YYModel/NSObject+YYModel.h
generated
Normal file
430
Pods/YYModel/YYModel/NSObject+YYModel.h
generated
Normal file
@@ -0,0 +1,430 @@
|
||||
//
|
||||
// NSObject+YYModel.h
|
||||
// YYModel <https://github.com/ibireme/YYModel>
|
||||
//
|
||||
// Created by ibireme on 15/5/10.
|
||||
// Copyright (c) 2015 ibireme.
|
||||
//
|
||||
// This source code is licensed under the MIT-style license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
Provide some data-model method:
|
||||
|
||||
* Convert json to any object, or convert any object to json.
|
||||
* Set object properties with a key-value dictionary (like KVC).
|
||||
* Implementations of `NSCoding`, `NSCopying`, `-hash` and `-isEqual:`.
|
||||
|
||||
See `YYModel` protocol for custom methods.
|
||||
|
||||
|
||||
Sample Code:
|
||||
|
||||
********************** json convertor *********************
|
||||
@interface YYAuthor : NSObject
|
||||
@property (nonatomic, strong) NSString *name;
|
||||
@property (nonatomic, assign) NSDate *birthday;
|
||||
@end
|
||||
@implementation YYAuthor
|
||||
@end
|
||||
|
||||
@interface YYBook : NSObject
|
||||
@property (nonatomic, copy) NSString *name;
|
||||
@property (nonatomic, assign) NSUInteger pages;
|
||||
@property (nonatomic, strong) YYAuthor *author;
|
||||
@end
|
||||
@implementation YYBook
|
||||
@end
|
||||
|
||||
int main() {
|
||||
// create model from json
|
||||
YYBook *book = [YYBook yy_modelWithJSON:@"{\"name\": \"Harry Potter\", \"pages\": 256, \"author\": {\"name\": \"J.K.Rowling\", \"birthday\": \"1965-07-31\" }}"];
|
||||
|
||||
// convert model to json
|
||||
NSString *json = [book yy_modelToJSONString];
|
||||
// {"author":{"name":"J.K.Rowling","birthday":"1965-07-31T00:00:00+0000"},"name":"Harry Potter","pages":256}
|
||||
}
|
||||
|
||||
********************** Coding/Copying/hash/equal *********************
|
||||
@interface YYShadow :NSObject <NSCoding, NSCopying>
|
||||
@property (nonatomic, copy) NSString *name;
|
||||
@property (nonatomic, assign) CGSize size;
|
||||
@end
|
||||
|
||||
@implementation YYShadow
|
||||
- (void)encodeWithCoder:(NSCoder *)aCoder { [self yy_modelEncodeWithCoder:aCoder]; }
|
||||
- (id)initWithCoder:(NSCoder *)aDecoder { self = [super init]; return [self yy_modelInitWithCoder:aDecoder]; }
|
||||
- (id)copyWithZone:(NSZone *)zone { return [self yy_modelCopy]; }
|
||||
- (NSUInteger)hash { return [self yy_modelHash]; }
|
||||
- (BOOL)isEqual:(id)object { return [self yy_modelIsEqual:object]; }
|
||||
@end
|
||||
|
||||
*/
|
||||
@interface NSObject (YYModel)
|
||||
|
||||
/**
|
||||
Creates and returns a new instance of the receiver from a json.
|
||||
This method is thread-safe.
|
||||
|
||||
@param json A json object in `NSDictionary`, `NSString` or `NSData`.
|
||||
|
||||
@return A new instance created from the json, or nil if an error occurs.
|
||||
*/
|
||||
+ (nullable instancetype)yy_modelWithJSON:(id)json;
|
||||
|
||||
/**
|
||||
Creates and returns a new instance of the receiver from a key-value dictionary.
|
||||
This method is thread-safe.
|
||||
|
||||
@param dictionary A key-value dictionary mapped to the instance's properties.
|
||||
Any invalid key-value pair in dictionary will be ignored.
|
||||
|
||||
@return A new instance created from the dictionary, or nil if an error occurs.
|
||||
|
||||
@discussion The key in `dictionary` will mapped to the reciever's property name,
|
||||
and the value will set to the property. If the value's type does not match the
|
||||
property, this method will try to convert the value based on these rules:
|
||||
|
||||
`NSString` or `NSNumber` -> c number, such as BOOL, int, long, float, NSUInteger...
|
||||
`NSString` -> NSDate, parsed with format "yyyy-MM-dd'T'HH:mm:ssZ", "yyyy-MM-dd HH:mm:ss" or "yyyy-MM-dd".
|
||||
`NSString` -> NSURL.
|
||||
`NSValue` -> struct or union, such as CGRect, CGSize, ...
|
||||
`NSString` -> SEL, Class.
|
||||
*/
|
||||
+ (nullable instancetype)yy_modelWithDictionary:(NSDictionary *)dictionary;
|
||||
|
||||
/**
|
||||
Set the receiver's properties with a json object.
|
||||
|
||||
@discussion Any invalid data in json will be ignored.
|
||||
|
||||
@param json A json object of `NSDictionary`, `NSString` or `NSData`, mapped to the
|
||||
receiver's properties.
|
||||
|
||||
@return Whether succeed.
|
||||
*/
|
||||
- (BOOL)yy_modelSetWithJSON:(id)json;
|
||||
|
||||
/**
|
||||
Set the receiver's properties with a key-value dictionary.
|
||||
|
||||
@param dic A key-value dictionary mapped to the receiver's properties.
|
||||
Any invalid key-value pair in dictionary will be ignored.
|
||||
|
||||
@discussion The key in `dictionary` will mapped to the reciever's property name,
|
||||
and the value will set to the property. If the value's type doesn't match the
|
||||
property, this method will try to convert the value based on these rules:
|
||||
|
||||
`NSString`, `NSNumber` -> c number, such as BOOL, int, long, float, NSUInteger...
|
||||
`NSString` -> NSDate, parsed with format "yyyy-MM-dd'T'HH:mm:ssZ", "yyyy-MM-dd HH:mm:ss" or "yyyy-MM-dd".
|
||||
`NSString` -> NSURL.
|
||||
`NSValue` -> struct or union, such as CGRect, CGSize, ...
|
||||
`NSString` -> SEL, Class.
|
||||
|
||||
@return Whether succeed.
|
||||
*/
|
||||
- (BOOL)yy_modelSetWithDictionary:(NSDictionary *)dic;
|
||||
|
||||
/**
|
||||
Generate a json object from the receiver's properties.
|
||||
|
||||
@return A json object in `NSDictionary` or `NSArray`, or nil if an error occurs.
|
||||
See [NSJSONSerialization isValidJSONObject] for more information.
|
||||
|
||||
@discussion Any of the invalid property is ignored.
|
||||
If the reciver is `NSArray`, `NSDictionary` or `NSSet`, it just convert
|
||||
the inner object to json object.
|
||||
*/
|
||||
- (nullable id)yy_modelToJSONObject;
|
||||
|
||||
/**
|
||||
Generate a json string's data from the receiver's properties.
|
||||
|
||||
@return A json string's data, or nil if an error occurs.
|
||||
|
||||
@discussion Any of the invalid property is ignored.
|
||||
If the reciver is `NSArray`, `NSDictionary` or `NSSet`, it will also convert the
|
||||
inner object to json string.
|
||||
*/
|
||||
- (nullable NSData *)yy_modelToJSONData;
|
||||
|
||||
/**
|
||||
Generate a json string from the receiver's properties.
|
||||
|
||||
@return A json string, or nil if an error occurs.
|
||||
|
||||
@discussion Any of the invalid property is ignored.
|
||||
If the reciver is `NSArray`, `NSDictionary` or `NSSet`, it will also convert the
|
||||
inner object to json string.
|
||||
*/
|
||||
- (nullable NSString *)yy_modelToJSONString;
|
||||
|
||||
/**
|
||||
Copy a instance with the receiver's properties.
|
||||
|
||||
@return A copied instance, or nil if an error occurs.
|
||||
*/
|
||||
- (nullable id)yy_modelCopy;
|
||||
|
||||
/**
|
||||
Encode the receiver's properties to a coder.
|
||||
|
||||
@param aCoder An archiver object.
|
||||
*/
|
||||
- (void)yy_modelEncodeWithCoder:(NSCoder *)aCoder;
|
||||
|
||||
/**
|
||||
Decode the receiver's properties from a decoder.
|
||||
|
||||
@param aDecoder An archiver object.
|
||||
|
||||
@return self
|
||||
*/
|
||||
- (id)yy_modelInitWithCoder:(NSCoder *)aDecoder;
|
||||
|
||||
/**
|
||||
Get a hash code with the receiver's properties.
|
||||
|
||||
@return Hash code.
|
||||
*/
|
||||
- (NSUInteger)yy_modelHash;
|
||||
|
||||
/**
|
||||
Compares the receiver with another object for equality, based on properties.
|
||||
|
||||
@param model Another object.
|
||||
|
||||
@return `YES` if the reciever is equal to the object, otherwise `NO`.
|
||||
*/
|
||||
- (BOOL)yy_modelIsEqual:(id)model;
|
||||
|
||||
/**
|
||||
Description method for debugging purposes based on properties.
|
||||
|
||||
@return A string that describes the contents of the receiver.
|
||||
*/
|
||||
- (NSString *)yy_modelDescription;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
/**
|
||||
Provide some data-model method for NSArray.
|
||||
*/
|
||||
@interface NSArray (YYModel)
|
||||
|
||||
/**
|
||||
Creates and returns an array from a json-array.
|
||||
This method is thread-safe.
|
||||
|
||||
@param cls The instance's class in array.
|
||||
@param json A json array of `NSArray`, `NSString` or `NSData`.
|
||||
Example: [{"name","Mary"},{name:"Joe"}]
|
||||
|
||||
@return A array, or nil if an error occurs.
|
||||
*/
|
||||
+ (nullable NSArray *)yy_modelArrayWithClass:(Class)cls json:(id)json;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
/**
|
||||
Provide some data-model method for NSDictionary.
|
||||
*/
|
||||
@interface NSDictionary (YYModel)
|
||||
|
||||
/**
|
||||
Creates and returns a dictionary from a json.
|
||||
This method is thread-safe.
|
||||
|
||||
@param cls The value instance's class in dictionary.
|
||||
@param json A json dictionary of `NSDictionary`, `NSString` or `NSData`.
|
||||
Example: {"user1":{"name","Mary"}, "user2": {name:"Joe"}}
|
||||
|
||||
@return A dictionary, or nil if an error occurs.
|
||||
*/
|
||||
+ (nullable NSDictionary *)yy_modelDictionaryWithClass:(Class)cls json:(id)json;
|
||||
@end
|
||||
|
||||
|
||||
|
||||
/**
|
||||
If the default model transform does not fit to your model class, implement one or
|
||||
more method in this protocol to change the default key-value transform process.
|
||||
There's no need to add '<YYModel>' to your class header.
|
||||
*/
|
||||
@protocol YYModel <NSObject>
|
||||
@optional
|
||||
|
||||
/**
|
||||
Custom property mapper.
|
||||
|
||||
@discussion If the key in JSON/Dictionary does not match to the model's property name,
|
||||
implements this method and returns the additional mapper.
|
||||
|
||||
Example:
|
||||
|
||||
json:
|
||||
{
|
||||
"n":"Harry Pottery",
|
||||
"p": 256,
|
||||
"ext" : {
|
||||
"desc" : "A book written by J.K.Rowling."
|
||||
},
|
||||
"ID" : 100010
|
||||
}
|
||||
|
||||
model:
|
||||
@interface YYBook : NSObject
|
||||
@property NSString *name;
|
||||
@property NSInteger page;
|
||||
@property NSString *desc;
|
||||
@property NSString *bookID;
|
||||
@end
|
||||
|
||||
@implementation YYBook
|
||||
+ (NSDictionary *)modelCustomPropertyMapper {
|
||||
return @{@"name" : @"n",
|
||||
@"page" : @"p",
|
||||
@"desc" : @"ext.desc",
|
||||
@"bookID": @[@"id", @"ID", @"book_id"]};
|
||||
}
|
||||
@end
|
||||
|
||||
@return A custom mapper for properties.
|
||||
*/
|
||||
+ (nullable NSDictionary<NSString *, id> *)modelCustomPropertyMapper;
|
||||
|
||||
/**
|
||||
The generic class mapper for container properties.
|
||||
|
||||
@discussion If the property is a container object, such as NSArray/NSSet/NSDictionary,
|
||||
implements this method and returns a property->class mapper, tells which kind of
|
||||
object will be add to the array/set/dictionary.
|
||||
|
||||
Example:
|
||||
@class YYShadow, YYBorder, YYAttachment;
|
||||
|
||||
@interface YYAttributes
|
||||
@property NSString *name;
|
||||
@property NSArray *shadows;
|
||||
@property NSSet *borders;
|
||||
@property NSDictionary *attachments;
|
||||
@end
|
||||
|
||||
@implementation YYAttributes
|
||||
+ (NSDictionary *)modelContainerPropertyGenericClass {
|
||||
return @{@"shadows" : [YYShadow class],
|
||||
@"borders" : YYBorder.class,
|
||||
@"attachments" : @"YYAttachment" };
|
||||
}
|
||||
@end
|
||||
|
||||
@return A class mapper.
|
||||
*/
|
||||
+ (nullable NSDictionary<NSString *, id> *)modelContainerPropertyGenericClass;
|
||||
|
||||
/**
|
||||
If you need to create instances of different classes during json->object transform,
|
||||
use the method to choose custom class based on dictionary data.
|
||||
|
||||
@discussion If the model implements this method, it will be called to determine resulting class
|
||||
during `+modelWithJSON:`, `+modelWithDictionary:`, conveting object of properties of parent objects
|
||||
(both singular and containers via `+modelContainerPropertyGenericClass`).
|
||||
|
||||
Example:
|
||||
@class YYCircle, YYRectangle, YYLine;
|
||||
|
||||
@implementation YYShape
|
||||
|
||||
+ (Class)modelCustomClassForDictionary:(NSDictionary*)dictionary {
|
||||
if (dictionary[@"radius"] != nil) {
|
||||
return [YYCircle class];
|
||||
} else if (dictionary[@"width"] != nil) {
|
||||
return [YYRectangle class];
|
||||
} else if (dictionary[@"y2"] != nil) {
|
||||
return [YYLine class];
|
||||
} else {
|
||||
return [self class];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@param dictionary The json/kv dictionary.
|
||||
|
||||
@return Class to create from this dictionary, `nil` to use current class.
|
||||
|
||||
*/
|
||||
+ (nullable Class)modelCustomClassForDictionary:(NSDictionary *)dictionary;
|
||||
|
||||
/**
|
||||
All the properties in blacklist will be ignored in model transform process.
|
||||
Returns nil to ignore this feature.
|
||||
|
||||
@return An array of property's name.
|
||||
*/
|
||||
+ (nullable NSArray<NSString *> *)modelPropertyBlacklist;
|
||||
|
||||
/**
|
||||
If a property is not in the whitelist, it will be ignored in model transform process.
|
||||
Returns nil to ignore this feature.
|
||||
|
||||
@return An array of property's name.
|
||||
*/
|
||||
+ (nullable NSArray<NSString *> *)modelPropertyWhitelist;
|
||||
|
||||
/**
|
||||
This method's behavior is similar to `- (BOOL)modelCustomTransformFromDictionary:(NSDictionary *)dic;`,
|
||||
but be called before the model transform.
|
||||
|
||||
@discussion If the model implements this method, it will be called before
|
||||
`+modelWithJSON:`, `+modelWithDictionary:`, `-modelSetWithJSON:` and `-modelSetWithDictionary:`.
|
||||
If this method returns nil, the transform process will ignore this model.
|
||||
|
||||
@param dic The json/kv dictionary.
|
||||
|
||||
@return Returns the modified dictionary, or nil to ignore this model.
|
||||
*/
|
||||
- (NSDictionary *)modelCustomWillTransformFromDictionary:(NSDictionary *)dic;
|
||||
|
||||
/**
|
||||
If the default json-to-model transform does not fit to your model object, implement
|
||||
this method to do additional process. You can also use this method to validate the
|
||||
model's properties.
|
||||
|
||||
@discussion If the model implements this method, it will be called at the end of
|
||||
`+modelWithJSON:`, `+modelWithDictionary:`, `-modelSetWithJSON:` and `-modelSetWithDictionary:`.
|
||||
If this method returns NO, the transform process will ignore this model.
|
||||
|
||||
@param dic The json/kv dictionary.
|
||||
|
||||
@return Returns YES if the model is valid, or NO to ignore this model.
|
||||
*/
|
||||
- (BOOL)modelCustomTransformFromDictionary:(NSDictionary *)dic;
|
||||
|
||||
/**
|
||||
If the default model-to-json transform does not fit to your model class, implement
|
||||
this method to do additional process. You can also use this method to validate the
|
||||
json dictionary.
|
||||
|
||||
@discussion If the model implements this method, it will be called at the end of
|
||||
`-modelToJSONObject` and `-modelToJSONString`.
|
||||
If this method returns NO, the transform process will ignore this json dictionary.
|
||||
|
||||
@param dic The json dictionary.
|
||||
|
||||
@return Returns YES if the model is valid, or NO to ignore this model.
|
||||
*/
|
||||
- (BOOL)modelCustomTransformToDictionary:(NSMutableDictionary *)dic;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
1840
Pods/YYModel/YYModel/NSObject+YYModel.m
generated
Normal file
1840
Pods/YYModel/YYModel/NSObject+YYModel.m
generated
Normal file
File diff suppressed because it is too large
Load Diff
200
Pods/YYModel/YYModel/YYClassInfo.h
generated
Normal file
200
Pods/YYModel/YYModel/YYClassInfo.h
generated
Normal file
@@ -0,0 +1,200 @@
|
||||
//
|
||||
// YYClassInfo.h
|
||||
// YYModel <https://github.com/ibireme/YYModel>
|
||||
//
|
||||
// Created by ibireme on 15/5/9.
|
||||
// Copyright (c) 2015 ibireme.
|
||||
//
|
||||
// This source code is licensed under the MIT-style license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <objc/runtime.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
Type encoding's type.
|
||||
*/
|
||||
typedef NS_OPTIONS(NSUInteger, YYEncodingType) {
|
||||
YYEncodingTypeMask = 0xFF, ///< mask of type value
|
||||
YYEncodingTypeUnknown = 0, ///< unknown
|
||||
YYEncodingTypeVoid = 1, ///< void
|
||||
YYEncodingTypeBool = 2, ///< bool
|
||||
YYEncodingTypeInt8 = 3, ///< char / BOOL
|
||||
YYEncodingTypeUInt8 = 4, ///< unsigned char
|
||||
YYEncodingTypeInt16 = 5, ///< short
|
||||
YYEncodingTypeUInt16 = 6, ///< unsigned short
|
||||
YYEncodingTypeInt32 = 7, ///< int
|
||||
YYEncodingTypeUInt32 = 8, ///< unsigned int
|
||||
YYEncodingTypeInt64 = 9, ///< long long
|
||||
YYEncodingTypeUInt64 = 10, ///< unsigned long long
|
||||
YYEncodingTypeFloat = 11, ///< float
|
||||
YYEncodingTypeDouble = 12, ///< double
|
||||
YYEncodingTypeLongDouble = 13, ///< long double
|
||||
YYEncodingTypeObject = 14, ///< id
|
||||
YYEncodingTypeClass = 15, ///< Class
|
||||
YYEncodingTypeSEL = 16, ///< SEL
|
||||
YYEncodingTypeBlock = 17, ///< block
|
||||
YYEncodingTypePointer = 18, ///< void*
|
||||
YYEncodingTypeStruct = 19, ///< struct
|
||||
YYEncodingTypeUnion = 20, ///< union
|
||||
YYEncodingTypeCString = 21, ///< char*
|
||||
YYEncodingTypeCArray = 22, ///< char[10] (for example)
|
||||
|
||||
YYEncodingTypeQualifierMask = 0xFF00, ///< mask of qualifier
|
||||
YYEncodingTypeQualifierConst = 1 << 8, ///< const
|
||||
YYEncodingTypeQualifierIn = 1 << 9, ///< in
|
||||
YYEncodingTypeQualifierInout = 1 << 10, ///< inout
|
||||
YYEncodingTypeQualifierOut = 1 << 11, ///< out
|
||||
YYEncodingTypeQualifierBycopy = 1 << 12, ///< bycopy
|
||||
YYEncodingTypeQualifierByref = 1 << 13, ///< byref
|
||||
YYEncodingTypeQualifierOneway = 1 << 14, ///< oneway
|
||||
|
||||
YYEncodingTypePropertyMask = 0xFF0000, ///< mask of property
|
||||
YYEncodingTypePropertyReadonly = 1 << 16, ///< readonly
|
||||
YYEncodingTypePropertyCopy = 1 << 17, ///< copy
|
||||
YYEncodingTypePropertyRetain = 1 << 18, ///< retain
|
||||
YYEncodingTypePropertyNonatomic = 1 << 19, ///< nonatomic
|
||||
YYEncodingTypePropertyWeak = 1 << 20, ///< weak
|
||||
YYEncodingTypePropertyCustomGetter = 1 << 21, ///< getter=
|
||||
YYEncodingTypePropertyCustomSetter = 1 << 22, ///< setter=
|
||||
YYEncodingTypePropertyDynamic = 1 << 23, ///< @dynamic
|
||||
};
|
||||
|
||||
/**
|
||||
Get the type from a Type-Encoding string.
|
||||
|
||||
@discussion See also:
|
||||
https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html
|
||||
https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html
|
||||
|
||||
@param typeEncoding A Type-Encoding string.
|
||||
@return The encoding type.
|
||||
*/
|
||||
YYEncodingType YYEncodingGetType(const char *typeEncoding);
|
||||
|
||||
|
||||
/**
|
||||
Instance variable information.
|
||||
*/
|
||||
@interface YYClassIvarInfo : NSObject
|
||||
@property (nonatomic, assign, readonly) Ivar ivar; ///< ivar opaque struct
|
||||
@property (nonatomic, strong, readonly) NSString *name; ///< Ivar's name
|
||||
@property (nonatomic, assign, readonly) ptrdiff_t offset; ///< Ivar's offset
|
||||
@property (nonatomic, strong, readonly) NSString *typeEncoding; ///< Ivar's type encoding
|
||||
@property (nonatomic, assign, readonly) YYEncodingType type; ///< Ivar's type
|
||||
|
||||
/**
|
||||
Creates and returns an ivar info object.
|
||||
|
||||
@param ivar ivar opaque struct
|
||||
@return A new object, or nil if an error occurs.
|
||||
*/
|
||||
- (instancetype)initWithIvar:(Ivar)ivar;
|
||||
@end
|
||||
|
||||
|
||||
/**
|
||||
Method information.
|
||||
*/
|
||||
@interface YYClassMethodInfo : NSObject
|
||||
@property (nonatomic, assign, readonly) Method method; ///< method opaque struct
|
||||
@property (nonatomic, strong, readonly) NSString *name; ///< method name
|
||||
@property (nonatomic, assign, readonly) SEL sel; ///< method's selector
|
||||
@property (nonatomic, assign, readonly) IMP imp; ///< method's implementation
|
||||
@property (nonatomic, strong, readonly) NSString *typeEncoding; ///< method's parameter and return types
|
||||
@property (nonatomic, strong, readonly) NSString *returnTypeEncoding; ///< return value's type
|
||||
@property (nullable, nonatomic, strong, readonly) NSArray<NSString *> *argumentTypeEncodings; ///< array of arguments' type
|
||||
|
||||
/**
|
||||
Creates and returns a method info object.
|
||||
|
||||
@param method method opaque struct
|
||||
@return A new object, or nil if an error occurs.
|
||||
*/
|
||||
- (instancetype)initWithMethod:(Method)method;
|
||||
@end
|
||||
|
||||
|
||||
/**
|
||||
Property information.
|
||||
*/
|
||||
@interface YYClassPropertyInfo : NSObject
|
||||
@property (nonatomic, assign, readonly) objc_property_t property; ///< property's opaque struct
|
||||
@property (nonatomic, strong, readonly) NSString *name; ///< property's name
|
||||
@property (nonatomic, assign, readonly) YYEncodingType type; ///< property's type
|
||||
@property (nonatomic, strong, readonly) NSString *typeEncoding; ///< property's encoding value
|
||||
@property (nonatomic, strong, readonly) NSString *ivarName; ///< property's ivar name
|
||||
@property (nullable, nonatomic, assign, readonly) Class cls; ///< may be nil
|
||||
@property (nullable, nonatomic, strong, readonly) NSArray<NSString *> *protocols; ///< may nil
|
||||
@property (nonatomic, assign, readonly) SEL getter; ///< getter (nonnull)
|
||||
@property (nonatomic, assign, readonly) SEL setter; ///< setter (nonnull)
|
||||
|
||||
/**
|
||||
Creates and returns a property info object.
|
||||
|
||||
@param property property opaque struct
|
||||
@return A new object, or nil if an error occurs.
|
||||
*/
|
||||
- (instancetype)initWithProperty:(objc_property_t)property;
|
||||
@end
|
||||
|
||||
|
||||
/**
|
||||
Class information for a class.
|
||||
*/
|
||||
@interface YYClassInfo : NSObject
|
||||
@property (nonatomic, assign, readonly) Class cls; ///< class object
|
||||
@property (nullable, nonatomic, assign, readonly) Class superCls; ///< super class object
|
||||
@property (nullable, nonatomic, assign, readonly) Class metaCls; ///< class's meta class object
|
||||
@property (nonatomic, readonly) BOOL isMeta; ///< whether this class is meta class
|
||||
@property (nonatomic, strong, readonly) NSString *name; ///< class name
|
||||
@property (nullable, nonatomic, strong, readonly) YYClassInfo *superClassInfo; ///< super class's class info
|
||||
@property (nullable, nonatomic, strong, readonly) NSDictionary<NSString *, YYClassIvarInfo *> *ivarInfos; ///< ivars
|
||||
@property (nullable, nonatomic, strong, readonly) NSDictionary<NSString *, YYClassMethodInfo *> *methodInfos; ///< methods
|
||||
@property (nullable, nonatomic, strong, readonly) NSDictionary<NSString *, YYClassPropertyInfo *> *propertyInfos; ///< properties
|
||||
|
||||
/**
|
||||
If the class is changed (for example: you add a method to this class with
|
||||
'class_addMethod()'), you should call this method to refresh the class info cache.
|
||||
|
||||
After called this method, `needUpdate` will returns `YES`, and you should call
|
||||
'classInfoWithClass' or 'classInfoWithClassName' to get the updated class info.
|
||||
*/
|
||||
- (void)setNeedUpdate;
|
||||
|
||||
/**
|
||||
If this method returns `YES`, you should stop using this instance and call
|
||||
`classInfoWithClass` or `classInfoWithClassName` to get the updated class info.
|
||||
|
||||
@return Whether this class info need update.
|
||||
*/
|
||||
- (BOOL)needUpdate;
|
||||
|
||||
/**
|
||||
Get the class info of a specified Class.
|
||||
|
||||
@discussion This method will cache the class info and super-class info
|
||||
at the first access to the Class. This method is thread-safe.
|
||||
|
||||
@param cls A class.
|
||||
@return A class info, or nil if an error occurs.
|
||||
*/
|
||||
+ (nullable instancetype)classInfoWithClass:(Class)cls;
|
||||
|
||||
/**
|
||||
Get the class info of a specified Class.
|
||||
|
||||
@discussion This method will cache the class info and super-class info
|
||||
at the first access to the Class. This method is thread-safe.
|
||||
|
||||
@param className A class name.
|
||||
@return A class info, or nil if an error occurs.
|
||||
*/
|
||||
+ (nullable instancetype)classInfoWithClassName:(NSString *)className;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
362
Pods/YYModel/YYModel/YYClassInfo.m
generated
Normal file
362
Pods/YYModel/YYModel/YYClassInfo.m
generated
Normal file
@@ -0,0 +1,362 @@
|
||||
//
|
||||
// YYClassInfo.m
|
||||
// YYModel <https://github.com/ibireme/YYModel>
|
||||
//
|
||||
// Created by ibireme on 15/5/9.
|
||||
// Copyright (c) 2015 ibireme.
|
||||
//
|
||||
// This source code is licensed under the MIT-style license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
//
|
||||
|
||||
#import "YYClassInfo.h"
|
||||
#import <objc/runtime.h>
|
||||
|
||||
YYEncodingType YYEncodingGetType(const char *typeEncoding) {
|
||||
char *type = (char *)typeEncoding;
|
||||
if (!type) return YYEncodingTypeUnknown;
|
||||
size_t len = strlen(type);
|
||||
if (len == 0) return YYEncodingTypeUnknown;
|
||||
|
||||
YYEncodingType qualifier = 0;
|
||||
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 == 0) 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 == 2 && *(type + 1) == '?')
|
||||
return YYEncodingTypeBlock | qualifier;
|
||||
else
|
||||
return YYEncodingTypeObject | qualifier;
|
||||
}
|
||||
default: return YYEncodingTypeUnknown | qualifier;
|
||||
}
|
||||
}
|
||||
|
||||
@implementation YYClassIvarInfo
|
||||
|
||||
- (instancetype)initWithIvar:(Ivar)ivar {
|
||||
if (!ivar) return nil;
|
||||
self = [super init];
|
||||
_ivar = ivar;
|
||||
const char *name = ivar_getName(ivar);
|
||||
if (name) {
|
||||
_name = [NSString stringWithUTF8String:name];
|
||||
}
|
||||
_offset = ivar_getOffset(ivar);
|
||||
const char *typeEncoding = ivar_getTypeEncoding(ivar);
|
||||
if (typeEncoding) {
|
||||
_typeEncoding = [NSString stringWithUTF8String:typeEncoding];
|
||||
_type = YYEncodingGetType(typeEncoding);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation YYClassMethodInfo
|
||||
|
||||
- (instancetype)initWithMethod:(Method)method {
|
||||
if (!method) return nil;
|
||||
self = [super init];
|
||||
_method = method;
|
||||
_sel = method_getName(method);
|
||||
_imp = method_getImplementation(method);
|
||||
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];
|
||||
free(returnType);
|
||||
}
|
||||
unsigned int argumentCount = method_getNumberOfArguments(method);
|
||||
if (argumentCount > 0) {
|
||||
NSMutableArray *argumentTypes = [NSMutableArray new];
|
||||
for (unsigned int i = 0; 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;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation YYClassPropertyInfo
|
||||
|
||||
- (instancetype)initWithProperty:(objc_property_t)property {
|
||||
if (!property) return nil;
|
||||
self = [super init];
|
||||
_property = property;
|
||||
const char *name = property_getName(property);
|
||||
if (name) {
|
||||
_name = [NSString stringWithUTF8String:name];
|
||||
}
|
||||
|
||||
YYEncodingType type = 0;
|
||||
unsigned int attrCount;
|
||||
objc_property_attribute_t *attrs = property_copyAttributeList(property, &attrCount);
|
||||
for (unsigned int i = 0; i < attrCount; i++) {
|
||||
switch (attrs[i].name[0]) {
|
||||
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);
|
||||
}
|
||||
|
||||
NSMutableArray *protocols = nil;
|
||||
while ([scanner scanString:@"<" intoString:NULL]) {
|
||||
NSString* protocol = nil;
|
||||
if ([scanner scanUpToString:@">" intoString: &protocol]) {
|
||||
if (protocol.length) {
|
||||
if (!protocols) protocols = [NSMutableArray new];
|
||||
[protocols addObject:protocol];
|
||||
}
|
||||
}
|
||||
[scanner scanString:@">" intoString:NULL];
|
||||
}
|
||||
_protocols = 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': {
|
||||
type |= YYEncodingTypePropertyCustomGetter;
|
||||
if (attrs[i].value) {
|
||||
_getter = NSSelectorFromString([NSString stringWithUTF8String:attrs[i].value]);
|
||||
}
|
||||
} break;
|
||||
case 'S': {
|
||||
type |= YYEncodingTypePropertyCustomSetter;
|
||||
if (attrs[i].value) {
|
||||
_setter = NSSelectorFromString([NSString stringWithUTF8String:attrs[i].value]);
|
||||
}
|
||||
} // break; commented for code coverage in next line
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
if (attrs) {
|
||||
free(attrs);
|
||||
attrs = NULL;
|
||||
}
|
||||
|
||||
_type = type;
|
||||
if (_name.length) {
|
||||
if (!_getter) {
|
||||
_getter = NSSelectorFromString(_name);
|
||||
}
|
||||
if (!_setter) {
|
||||
_setter = NSSelectorFromString([NSString stringWithFormat:@"set%@%@:", [_name substringToIndex:1].uppercaseString, [_name substringFromIndex:1]]);
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@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:_superCls];
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)_update {
|
||||
_ivarInfos = nil;
|
||||
_methodInfos = nil;
|
||||
_propertyInfos = nil;
|
||||
|
||||
Class cls = self.cls;
|
||||
unsigned int methodCount = 0;
|
||||
Method *methods = class_copyMethodList(cls, &methodCount);
|
||||
if (methods) {
|
||||
NSMutableDictionary *methodInfos = [NSMutableDictionary new];
|
||||
_methodInfos = methodInfos;
|
||||
for (unsigned int i = 0; i < methodCount; i++) {
|
||||
YYClassMethodInfo *info = [[YYClassMethodInfo alloc] initWithMethod:methods[i]];
|
||||
if (info.name) methodInfos[info.name] = info;
|
||||
}
|
||||
free(methods);
|
||||
}
|
||||
unsigned int propertyCount = 0;
|
||||
objc_property_t *properties = class_copyPropertyList(cls, &propertyCount);
|
||||
if (properties) {
|
||||
NSMutableDictionary *propertyInfos = [NSMutableDictionary new];
|
||||
_propertyInfos = propertyInfos;
|
||||
for (unsigned int i = 0; i < propertyCount; i++) {
|
||||
YYClassPropertyInfo *info = [[YYClassPropertyInfo alloc] initWithProperty:properties[i]];
|
||||
if (info.name) propertyInfos[info.name] = info;
|
||||
}
|
||||
free(properties);
|
||||
}
|
||||
|
||||
unsigned int ivarCount = 0;
|
||||
Ivar *ivars = class_copyIvarList(cls, &ivarCount);
|
||||
if (ivars) {
|
||||
NSMutableDictionary *ivarInfos = [NSMutableDictionary new];
|
||||
_ivarInfos = ivarInfos;
|
||||
for (unsigned int i = 0; 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)needUpdate {
|
||||
return _needUpdate;
|
||||
}
|
||||
|
||||
+ (instancetype)classInfoWithClass:(Class)cls {
|
||||
if (!cls) return nil;
|
||||
static CFMutableDictionaryRef classCache;
|
||||
static CFMutableDictionaryRef metaCache;
|
||||
static dispatch_once_t onceToken;
|
||||
static dispatch_semaphore_t lock;
|
||||
dispatch_once(&onceToken, ^{
|
||||
classCache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
|
||||
metaCache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
|
||||
lock = dispatch_semaphore_create(1);
|
||||
});
|
||||
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
|
||||
22
Pods/YYModel/YYModel/YYModel.h
generated
Normal file
22
Pods/YYModel/YYModel/YYModel.h
generated
Normal file
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// YYModel.h
|
||||
// YYModel <https://github.com/ibireme/YYModel>
|
||||
//
|
||||
// Created by ibireme on 15/5/10.
|
||||
// Copyright (c) 2015 ibireme.
|
||||
//
|
||||
// This source code is licensed under the MIT-style license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#if __has_include(<YYModel/YYModel.h>)
|
||||
FOUNDATION_EXPORT double YYModelVersionNumber;
|
||||
FOUNDATION_EXPORT const unsigned char YYModelVersionString[];
|
||||
#import <YYModel/NSObject+YYModel.h>
|
||||
#import <YYModel/YYClassInfo.h>
|
||||
#else
|
||||
#import "NSObject+YYModel.h"
|
||||
#import "YYClassInfo.h"
|
||||
#endif
|
||||
Reference in New Issue
Block a user