├── README.md
├── ADModel.xcodeproj
├── xcuserdata
│ └── Aodong.xcuserdatad
│ │ ├── xcdebugger
│ │ └── Breakpoints_v2.xcbkptlist
│ │ └── xcschemes
│ │ ├── xcschememanagement.plist
│ │ └── ADModel.xcscheme
├── project.xcworkspace
│ └── contents.xcworkspacedata
└── project.pbxproj
├── ADModel
├── ViewController.h
├── AppDelegate.h
├── main.m
├── ADModel
│ ├── ADModel.h
│ ├── ADClassInfo.h
│ ├── NSObject+ADModel.h
│ ├── ADClassInfo.m
│ └── NSObject+ADModel.m
├── Assets.xcassets
│ └── AppIcon.appiconset
│ │ └── Contents.json
├── Base.lproj
│ ├── Main.storyboard
│ └── LaunchScreen.storyboard
├── Info.plist
├── AppDelegate.m
└── ViewController.m
├── ADModelTests
├── Info.plist
└── ADModelTests.m
└── ADModelUITests
├── Info.plist
└── ADModelUITests.m
/README.md:
--------------------------------------------------------------------------------
1 | # ADModel
2 | 仿YYModel 全英文注释讲解翻译+超详细解析!超详细!!
3 | 讲解在ViewController.m里。
4 | 最近工作感觉压抑好大,若觉得不错,可否支持一下。。真的。。
5 | 顺手给颗star吧。。。
6 |
--------------------------------------------------------------------------------
/ADModel.xcodeproj/xcuserdata/Aodong.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
--------------------------------------------------------------------------------
/ADModel.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/ADModel/ViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.h
3 | // ADModel
4 | //
5 | // Created by 王奥东 on 16/8/2.
6 | // Copyright © 2016年 王奥东. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface ViewController : UIViewController
12 |
13 |
14 | @end
15 |
16 |
--------------------------------------------------------------------------------
/ADModel/AppDelegate.h:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.h
3 | // ADModel
4 | //
5 | // Created by 王奥东 on 16/8/2.
6 | // Copyright © 2016年 王奥东. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface AppDelegate : UIResponder
12 |
13 | @property (strong, nonatomic) UIWindow *window;
14 |
15 |
16 | @end
17 |
18 |
--------------------------------------------------------------------------------
/ADModel/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // ADModel
4 | //
5 | // Created by 王奥东 on 16/8/2.
6 | // Copyright © 2016年 王奥东. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "AppDelegate.h"
11 |
12 | int main(int argc, char * argv[]) {
13 | @autoreleasepool {
14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/ADModel/ADModel/ADModel.h:
--------------------------------------------------------------------------------
1 | //
2 | // ADModel.h
3 | // ADModel
4 | //
5 | // Created by 王奥东 on 16/8/2.
6 | // Copyright © 2016年 王奥东. All rights reserved.
7 | //
8 | #import
9 | //如果包含方式是,则通过形式导入剩下两个.h文件
10 | #if __has_include()
11 | //定义常量
12 | FOUNDATION_EXPORT double ADModelVersionNumber;
13 | FOUNDATION_EXPORT const unsigned char ADModelVersionString[];
14 | #import
15 | #import
16 | #else
17 | #import "NSObject+ADModel.h"
18 | #import "ADClassInfo.h"
19 | #endif
20 |
21 |
--------------------------------------------------------------------------------
/ADModelTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/ADModelUITests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/ADModel.xcodeproj/xcuserdata/Aodong.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | ADModel.xcscheme
8 |
9 | orderHint
10 | 0
11 |
12 |
13 | SuppressBuildableAutocreation
14 |
15 | 163329591D5021A10027BE35
16 |
17 | primary
18 |
19 |
20 | 163329721D5021A10027BE35
21 |
22 | primary
23 |
24 |
25 | 1633297D1D5021A10027BE35
26 |
27 | primary
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/ADModelTests/ADModelTests.m:
--------------------------------------------------------------------------------
1 | //
2 | // ADModelTests.m
3 | // ADModelTests
4 | //
5 | // Created by 王奥东 on 16/8/2.
6 | // Copyright © 2016年 王奥东. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface ADModelTests : XCTestCase
12 |
13 | @end
14 |
15 | @implementation ADModelTests
16 |
17 | - (void)setUp {
18 | [super setUp];
19 | // Put setup code here. This method is called before the invocation of each test method in the class.
20 | }
21 |
22 | - (void)tearDown {
23 | // Put teardown code here. This method is called after the invocation of each test method in the class.
24 | [super tearDown];
25 | }
26 |
27 | - (void)testExample {
28 | // This is an example of a functional test case.
29 | // Use XCTAssert and related functions to verify your tests produce the correct results.
30 | }
31 |
32 | - (void)testPerformanceExample {
33 | // This is an example of a performance test case.
34 | [self measureBlock:^{
35 | // Put the code you want to measure the time of here.
36 | }];
37 | }
38 |
39 | @end
40 |
--------------------------------------------------------------------------------
/ADModelUITests/ADModelUITests.m:
--------------------------------------------------------------------------------
1 | //
2 | // ADModelUITests.m
3 | // ADModelUITests
4 | //
5 | // Created by 王奥东 on 16/8/2.
6 | // Copyright © 2016年 王奥东. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface ADModelUITests : XCTestCase
12 |
13 | @end
14 |
15 | @implementation ADModelUITests
16 |
17 | - (void)setUp {
18 | [super setUp];
19 |
20 | // Put setup code here. This method is called before the invocation of each test method in the class.
21 |
22 | // In UI tests it is usually best to stop immediately when a failure occurs.
23 | self.continueAfterFailure = NO;
24 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method.
25 | [[[XCUIApplication alloc] init] launch];
26 |
27 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
28 | }
29 |
30 | - (void)tearDown {
31 | // Put teardown code here. This method is called after the invocation of each test method in the class.
32 | [super tearDown];
33 | }
34 |
35 | - (void)testExample {
36 | // Use recording to get started writing UI tests.
37 | // Use XCTAssert and related functions to verify your tests produce the correct results.
38 | }
39 |
40 | @end
41 |
--------------------------------------------------------------------------------
/ADModel/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "29x29",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "29x29",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "40x40",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "40x40",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "60x60",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "60x60",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "ipad",
35 | "size" : "29x29",
36 | "scale" : "1x"
37 | },
38 | {
39 | "idiom" : "ipad",
40 | "size" : "29x29",
41 | "scale" : "2x"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "size" : "40x40",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "size" : "40x40",
51 | "scale" : "2x"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "size" : "76x76",
56 | "scale" : "1x"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "size" : "76x76",
61 | "scale" : "2x"
62 | }
63 | ],
64 | "info" : {
65 | "version" : 1,
66 | "author" : "xcode"
67 | }
68 | }
--------------------------------------------------------------------------------
/ADModel/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/ADModel/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIMainStoryboardFile
28 | Main
29 | UIRequiredDeviceCapabilities
30 |
31 | armv7
32 |
33 | UISupportedInterfaceOrientations
34 |
35 | UIInterfaceOrientationPortrait
36 | UIInterfaceOrientationLandscapeLeft
37 | UIInterfaceOrientationLandscapeRight
38 |
39 | UISupportedInterfaceOrientations~ipad
40 |
41 | UIInterfaceOrientationPortrait
42 | UIInterfaceOrientationPortraitUpsideDown
43 | UIInterfaceOrientationLandscapeLeft
44 | UIInterfaceOrientationLandscapeRight
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/ADModel/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/ADModel/AppDelegate.m:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.m
3 | // ADModel
4 | //
5 | // Created by 王奥东 on 16/8/2.
6 | // Copyright © 2016年 王奥东. All rights reserved.
7 | //
8 |
9 | #import "AppDelegate.h"
10 |
11 | @interface AppDelegate ()
12 |
13 | @end
14 |
15 | @implementation AppDelegate
16 |
17 |
18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
19 | // Override point for customization after application launch.
20 | return YES;
21 | }
22 |
23 | - (void)applicationWillResignActive:(UIApplication *)application {
24 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
25 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
26 | }
27 |
28 | - (void)applicationDidEnterBackground:(UIApplication *)application {
29 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
30 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
31 | }
32 |
33 | - (void)applicationWillEnterForeground:(UIApplication *)application {
34 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
35 | }
36 |
37 | - (void)applicationDidBecomeActive:(UIApplication *)application {
38 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
39 | }
40 |
41 | - (void)applicationWillTerminate:(UIApplication *)application {
42 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
43 | }
44 |
45 | @end
46 |
--------------------------------------------------------------------------------
/ADModel.xcodeproj/xcuserdata/Aodong.xcuserdatad/xcschemes/ADModel.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
33 |
39 |
40 |
41 |
43 |
49 |
50 |
51 |
52 |
53 |
59 |
60 |
61 |
62 |
63 |
64 |
74 |
76 |
82 |
83 |
84 |
85 |
86 |
87 |
93 |
95 |
101 |
102 |
103 |
104 |
106 |
107 |
110 |
111 |
112 |
--------------------------------------------------------------------------------
/ADModel/ADModel/ADClassInfo.h:
--------------------------------------------------------------------------------
1 | //
2 | // ADClassInfo.h
3 | // ADModel
4 | //
5 | // Created by 王奥东 on 16/8/2.
6 | // Copyright © 2016年 王奥东. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 |
12 | NS_ASSUME_NONNULL_BEGIN
13 | ///**
14 | // Type encoding's type.
15 | // encoding类型
16 | // */
17 | typedef NS_OPTIONS(NSUInteger, ADEncodingType) {
18 |
19 | ADEncodingTypeMask = 0xFF, ///< mask of type value,遮盖type值
20 | ADEncodingTypeUnknown = 0, ///< unknown
21 | ADEncodingTypeVoid = 1, ///< void
22 | ADEncodingTypeBool = 2, ///< bool
23 | ADEncodingTypeInt8 = 3, ///< char / BOOL
24 | ADEncodingTypeUInt8 = 4, ///< unsigned char
25 | ADEncodingTypeInt16 = 5, ///< short
26 | ADEncodingTypeUInt16 = 6, ///< unsigned short
27 | ADEncodingTypeInt32 = 7, ///< int
28 | ADEncodingTypeUInt32 = 8, ///< unsigned int
29 | ADEncodingTypeInt64 = 9, ///< long long
30 | ADEncodingTypeUInt64 = 10, ///< unsigned long long
31 |
32 | ADEncodingTypeFloat = 11, ///< float
33 | ADEncodingTypeDouble = 12, ///< double
34 | ADEncodingTypeLongDouble = 13, ///< long double
35 | ADEncodingTypeObject = 14, ///< id
36 | ADEncodingTypeClass = 15, ///< Class
37 | ADEncodingTypeSEL = 16, ///< SEL
38 | ADEncodingTypeBlock = 17, ///< block
39 | ADEncodingTypePointer = 18, ///< void*
40 | ADEncodingTypeStruct = 19, ///< struct
41 | ADEncodingTypeUnion = 20, ///< union
42 | ADEncodingTypeCString = 21, ///< char*
43 | ADEncodingTypeCArray = 22, ///< char[10] (for example)
44 |
45 |
46 | // 这里的1 << 8 等操作是位运算
47 | ADEncodingTypeQualifierMask = 0xFF00, ///< mask of qualifier,遮盖修饰
48 | ADEncodingTypeQualifierConst = 1 << 8, ///< const
49 | ADEncodingTypeQualifierIn = 1 << 9, ///< in
50 | ADEncodingTypeQualifierInout = 1 << 10, ///< inout
51 | ADEncodingTypeQualifierOut = 1 << 11, ///< out
52 | ADEncodingTypeQualifierBycopy = 1 << 12, ///< bycopy
53 | ADEncodingTypeQualifierByref = 1 << 13, ///< byref
54 | ADEncodingTypeQualifierOneway = 1 << 14, ///< oneway
55 |
56 |
57 | ADEncodingTypePropertyMask = 0xFF0000,///mask of property,遮盖property
58 | ADEncodingTypePropertyReadonly = 1 << 16, ///< readonly
59 | ADEncodingTypePropertyCopy = 1 << 17, ///< copy
60 | ADEncodingTypePropertyRetain = 1 << 18, ///< retain
61 | ADEncodingTypePropertyNonatomic = 1 << 19, ///< nonatomic
62 | ADEncodingTypePropertyWeak = 1 << 20, ///< weak
63 | ADEncodingTypePropertyCustomGetter = 1 << 21, ///< getter=
64 | ADEncodingTypePropertyCustomSetter = 1 << 22, ///< setter=
65 | ADEncodingTypePropertyDynamic = 1 << 23, ///< @dynamic
66 |
67 | };
68 |
69 |
70 | /**
71 | Get the type from a Type-Encoding string.
72 | 从一个type-Encoding string获取type值
73 |
74 | @discussion See also(描述也可以看):
75 | https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html
76 | https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html
77 |
78 | @param typeEncoding A Type-Encoding string.
79 | 参数 typeEncoding 一个type-Encoding string
80 | @return The encoding type.
81 | 返回 这个encoding的type值
82 | */
83 | ADEncodingType ADEncodingGetType(const char *typeEncoding);
84 |
85 | /**
86 | Instance variable information.
87 | 对象的变量信息
88 | */
89 | @interface ADClassIvarInfo : NSObject
90 |
91 | @property (nonatomic, assign, readonly) Ivar ivar; ///< ivar opaque struct ,不透明结构体opaque struct
92 | // ivar runtime使用时用到的属性,详情可参考我git中的runtime使用Demo
93 | // https://github.com/DrunkenMouse/rutime
94 | @property (nonatomic, strong, readonly) NSString *name; ///< Ivar's name
95 |
96 | @property (nonatomic, assign, readonly) ptrdiff_t offset; ///< Ivar's offset
97 |
98 | @property (nonatomic, strong, readonly) NSString *typeEncoding; ///< Ivar's type encoding
99 |
100 | @property (nonatomic, assign, readonly) ADEncodingType type; ///< Ivar's type
101 |
102 | /**
103 | Creates and returns an ivar info object.
104 | 创建并返回一个对象的ivar 信息
105 | @param ivar ivar opaque struct
106 | 参数 ivar 不透明结构体ivar
107 | @return A new object, or nil if an error occurs.
108 | 返回 一个新的对象,若产生错误返回Nil
109 | */
110 | //不透明结构体:结构体定义被隐藏,通常通过指针访问其值
111 | -(instancetype)initWithIvar:(Ivar)ivar;
112 | @end
113 |
114 |
115 | /**
116 | Method information.
117 | 方法信息
118 | */
119 | @interface ADClassMethodInfo : NSObject
120 | @property (nonatomic, assign, readonly) Method method; ///< method opaque struct 不透明结构体方法
121 | @property (nonatomic, strong, readonly) NSString *name; ///< method name 方法的选择子名
122 | @property (nonatomic, assign, readonly) SEL sel; ///< method's selector 方法sel
123 | @property (nonatomic, assign, readonly) IMP imp; ///< method's implementation 方法实现
124 | @property (nonatomic, strong, readonly) NSString *typeEncoding; ///< method's parameter and return types,方法参数和返回值类型
125 | @property (nonatomic, strong, readonly) NSString *returnTypeEncoding; ///< return value's type,方法的返回值类型的字符串
126 | @property (nullable, nonatomic, strong, readonly) NSArray *argumentTypeEncodings; ///< array of arguments' type, type的主题数组,方法的参数类型数组
127 | /**
128 | Creates and returns a method info object.
129 |
130 | 创建并返回一个对象方法信息
131 |
132 | @param method method opaque struct
133 | 参数 method 不透明结构体方法
134 | @return A new object, or nil if an error occurs.
135 | 返回 一个新的对象,发生错误返回nil
136 | */
137 | - (instancetype)initWithMethod:(Method)method;
138 | @end
139 |
140 | /**
141 | Property information.
142 | property 信息
143 | */
144 | @interface ADClassPropertyInfo : NSObject
145 | @property (nonatomic, assign, readonly) objc_property_t property; ///< property's opaque struct ,property的不透明结构体
146 | @property (nonatomic, strong, readonly) NSString *name; ///< property's name ,property的名称
147 | @property (nonatomic, assign, readonly) ADEncodingType type; ///< property's type,通过属性特性列表获取,包括强弱指针、原子性和getter、setter
148 | @property (nonatomic, strong, readonly) NSString *typeEncoding; ///< property's encoding value
149 | @property (nonatomic, strong, readonly) NSString *ivarName; ///< property's ivar name
150 | @property (nullable, nonatomic, assign, readonly) Class cls; ///< may be nil ,可以是nil,如果属性是个对象,则保存对象的isa指针
151 | @property (nullable, nonatomic, strong, readonly) NSArray *protocols; ///< may nil,保存对象后面"\<"到">"中的所有信息
152 | @property (nonatomic, assign, readonly) SEL getter; ///< getter (nonnull)
153 | @property (nonatomic, assign, readonly) SEL setter; ///< setter (nonnull)
154 | /**
155 | Creates and returns a property info object.
156 | 创建并返回一个对象的property信息
157 |
158 | @param property property opaque struct
159 | 参数 property 不透明结构体property
160 | @return A new object, or nil if an error occurs.
161 | 返回 一个新的对象,发生错误返回nil
162 | */
163 | -(instancetype)initWithProperty:(objc_property_t)property;
164 | @end
165 |
166 |
167 | /**
168 | Class information for a class.
169 | 一个class的class 信息
170 | */
171 | @interface ADClassInfo : NSObject
172 |
173 | @property (nonatomic, assign, readonly) Class cls; ///< class object,自身
174 | @property (nullable, nonatomic, assign, readonly) Class superCls; ///< super class object,父类
175 | @property (nullable, nonatomic, assign, readonly) Class metaCls; ///< class's meta class object , 不是元类元类则保存元类
176 | @property (nonatomic, readonly) BOOL isMeta; ///< whether this class is meta class ,这个元素是否为元类
177 | @property (nonatomic, strong, readonly) NSString *name; ///< class name,保存类名
178 | @property (nullable, nonatomic, strong, readonly) ADClassInfo *superClassInfo; ///< super class's class info ,父类的class信息
179 | @property (nullable, nonatomic, strong, readonly) NSDictionary *ivarInfos; ///< ivars
180 | @property (nullable, nonatomic, strong, readonly) NSDictionary *methodInfos; ///< methods,保存例子中所有方法信息,以方法的选择子名为Key,方法信息(ADClassMethodInfo)为value
181 | @property (nullable, nonatomic, strong, readonly) NSDictionary *propertyInfos; ///< properties
182 |
183 | /**
184 | If the class is changed (for example: you add a method to this class with
185 | 'class_addMethod()'), you should call this method to refresh the class info cache.
186 | 如果这个Class是改变的(列如: 你通过class_addMethod()添加了一个方法给这个类),你应该告诉这个方法去刷新class信息缓存
187 | After called this method, `needUpdate` will returns `YES`, and you should call
188 | 'classInfoWithClass' or 'classInfoWithClassName' to get the updated class info.
189 | 被方法告知之后,"needUpdate"应返回"YES",而且你应该告知'classInfoWithClass' or 'classInfoWithClassName'获取这个class更新信息
190 | */
191 | -(void)setNeedUpdate;
192 |
193 | /**
194 | If this method returns `YES`, you should stop using this instance and call
195 | `classInfoWithClass` or `classInfoWithClassName` to get the updated class info.
196 | 如果这个方法返回"YES",你应该停止使用这个对象并告知`classInfoWithClass` or `classInfoWithClassName` 去获取class更新信息
197 | @return Whether this class info need update.
198 | 返回 这个class 信息是否需要更新
199 | */
200 | -(BOOL)needUpdate;
201 |
202 | /**
203 | Get the class info of a specified Class.
204 |
205 | 获取一个Class的class信息说明
206 | @discussion This method will cache the class info and super-class info
207 | at the first access to the Class. This method is thread-safe.
208 |
209 | 描述 这个方法将缓存这个class信息 和 父类class信息,在第一次进入这个class时
210 | 这个方法是线程安全的
211 | @param cls A class.
212 | 参数 cls 一个class
213 | @return A class info, or nil if an error occurs.
214 | 返回 一个Class 信息, 如果发生错误返回Nil
215 | */
216 | +(nullable instancetype)classInfoWithClass:(Class)cls;
217 | /**
218 | Get the class info of a specified Class.
219 | 获取一个Class的class信息说明
220 |
221 | @discussion This method will cache the class info and super-class info
222 | at the first access to the Class. This method is thread-safe.
223 |
224 | 描述 这个方法将缓存这个class信息和父类class 信息在第一次进入这个class时。
225 | 这个方法是线程安全的
226 | @param className A class name.
227 | 参数 className 一个class name
228 | @return A class info, or nil if an error occurs.
229 | 返回 一个class info,如果出现错误返回Nil
230 | */
231 | +(nullable instancetype)classInfoWithClassName:(NSString *)className;
232 |
233 | @end
234 |
235 | NS_ASSUME_NONNULL_END
--------------------------------------------------------------------------------
/ADModel/ADModel/NSObject+ADModel.h:
--------------------------------------------------------------------------------
1 | //
2 | // NSObject+ADModel.h
3 | // ADModel
4 | //
5 | // Created by 王奥东 on 16/8/2.
6 | // Copyright © 2016年 王奥东. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | NS_ASSUME_NONNULL_BEGIN
12 | //mapped to 映射
13 | //列子 instancetype 通常也指实例
14 |
15 | /**
16 | Provide some data-model method:
17 | 提供一些数据模型方法
18 |
19 | * Convert json to any object, or convert any object to json.
20 | * Set object properties with a key-value dictionary (like KVC).
21 | * Implementations of `NSCoding`, `NSCopying`, `-hash` and `-isEqual:`.
22 | 转化json为任何对象,或转换任何对象为json
23 | 通过key-value字典设置对象属性(像KVC)
24 | 实现"NSCoding","NSCopying","-hash"和"isEqual:"
25 |
26 | See `ADModel` protocol for custom methods.
27 | 看ADModel协议通过方法
28 | Sample Code:
29 |
30 | ********************** json convertor *********************
31 | @interface ADAuthor : NSObject
32 | @property (nonatomic, strong) NSString *name;
33 | @property (nonatomic, assign) NSDate *birthday;
34 | @end
35 | @implementation ADAuthor
36 | @end
37 |
38 | @interface ADBook : NSObject
39 | @property (nonatomic, copy) NSString *name;
40 | @property (nonatomic, assign) NSUInteger pages;
41 | @property (nonatomic, strong) ADAuthor *author;
42 | @end
43 | @implementation ADBook
44 | @end
45 |
46 | int main() {
47 | // create model from json
48 | ADBook *book = [ADBook ad_modelWithJSON:@"{\"name\": \"Harry Potter\", \"pages\": 256, \"author\": {\"name\": \"J.K.Rowling\", \"birthday\": \"1965-07-31\" }}"];
49 |
50 | // convert model to json
51 | NSString *json = [book ad_modelToJSONString];
52 | // {"author":{"name":"J.K.Rowling","birthday":"1965-07-31T00:00:00+0000"},"name":"Harry Potter","pages":256}
53 | }
54 |
55 | ********************** Coding/Copying/hash/equal *********************
56 | @interface ADShadow :NSObject
57 | @property (nonatomic, copy) NSString *name;
58 | @property (nonatomic, assign) CGSize size;
59 | @end
60 |
61 | @implementation ADShadow
62 | - (void)encodeWithCoder:(NSCoder *)aCoder { [self ad_modelEncodeWithCoder:aCoder]; }
63 | - (id)initWithCoder:(NSCoder *)aDecoder { self = [super init]; return [self ad_modelInitWithCoder:aDecoder]; }
64 | - (id)copyWithZone:(NSZone *)zone { return [self ad_modelCopy]; }
65 | - (NSUInteger)hash { return [self ad_modelHash]; }
66 | - (BOOL)isEqual:(id)object { return [self ad_modelIsEqual:object]; }
67 | @end
68 |
69 | */
70 | @interface NSObject (ADModel)
71 |
72 | /**
73 | Creates and returns a new instance of the receiver from a json.
74 | This method is thread-safe.
75 |
76 | @param json A json object in `NSDictionary`, `NSString` or `NSData`.
77 |
78 | @return A new instance created from the json, or nil if an error occurs.
79 | */
80 | // 创建并返回一个新的列子,通过收取的一个Json文件
81 | //这个方法是安全的
82 | //参数:json Json包含的类型可以是NSDictionary、NSString、NSData
83 | //返回:通过json创建的新的对象,如果解析错误就返回为空
84 | + (nullable instancetype)ad_modelWithJSON:(id)json;
85 |
86 | /**
87 | Creates and returns a new instance of the receiver from a key-value dictionary.
88 | This method is thread-safe.
89 |
90 | @param dictionary A key-value dictionary mapped to the instance's properties.
91 | Any invalid key-value pair in dictionary will be ignored.
92 |
93 | @return A new instance created from the dictionary, or nil if an error occurs.
94 |
95 | @discussion The key in `dictionary` will mapped to the reciever's property name,
96 | and the value will set to the property. If the value's type does not match the
97 | property, this method will try to convert the value based on these rules:
98 |
99 | `NSString` or `NSNumber` -> c number, such as BOOL, int, long, float, NSUInteger...
100 | `NSString` -> NSDate, parsed with format "yyyy-MM-dd'T'HH:mm:ssZ", "yyyy-MM-dd HH:mm:ss" or "yyyy-MM-dd".
101 | `NSString` -> NSURL.
102 | `NSValue` -> struct or union, such as CGRect, CGSize, ...
103 | `NSString` -> SEL, Class.
104 | */
105 | /**
106 | 创建并返回一个新的列子通过参数的key-value字典
107 | 这个方法是安全的
108 | 参数:dictionary 一个key-value字典映射到列子的属性
109 | 字典中任何一对无效的key-value都将被忽视
110 | 返回一个新的粒子通过字典创建的,如果解析失败返回为nil
111 | 描述:字典中的key将映射到接收者的property name
112 | 而值将设置给这个Property,如果这个值类型与property不匹配
113 | 这个方法将试图转变这个值基于这些结果:
114 | */
115 | + (nullable instancetype)ad_modelWithDictionary:(NSDictionary *)dictionary;
116 | /**
117 | Set the receiver's properties with a json object.
118 |
119 | @discussion Any invalid data in json will be ignored.
120 |
121 | @param json A json object of `NSDictionary`, `NSString` or `NSData`, mapped to the
122 | receiver's properties.
123 |
124 | @return Whether succeed.
125 | */
126 | /**
127 | 通过一个json对象设置调用者的property
128 | json中任何无效的数据都将被忽视
129 | 参数:json 一个关于NSDictionary,NSString,NSData的json对象将映射到调用者的property
130 | 返回:是否成功
131 |
132 | */
133 | - (BOOL)ad_modelSetWithJSON:(id)json;
134 |
135 | /**
136 | Set the receiver's properties with a key-value dictionary.
137 |
138 | @param dic A key-value dictionary mapped to the receiver's properties.
139 | Any invalid key-value pair in dictionary will be ignored.
140 |
141 | @discussion The key in `dictionary` will mapped to the reciever's property name,
142 | and the value will set to the property. If the value's type doesn't match the
143 | property, this method will try to convert the value based on these rules:
144 |
145 | `NSString`, `NSNumber` -> c number, such as BOOL, int, long, float, NSUInteger...
146 | `NSString` -> NSDate, parsed with format "yyyy-MM-dd'T'HH:mm:ssZ", "yyyy-MM-dd HH:mm:ss" or "yyyy-MM-dd".
147 | `NSString` -> NSURL.
148 | `NSValue` -> struct or union, such as CGRect, CGSize, ...
149 | `NSString` -> SEL, Class.
150 |
151 | @return Whether succeed.
152 | */
153 | /**
154 | 通过一个key-value字典设置调用者的属性
155 | 参数:dic 一个Key-Value字典映射到调用者property,字典中任何一对无效的Key-Value都将被忽视
156 | 描述 dictionary中的Key将被映射到调用者的property name 而这个value将设置给property.
157 | 如果value类型与property类型不匹配,这个方法将试图转换这个value基于以下这些值:
158 | 返回 转换是否成功
159 | */
160 | - (BOOL)ad_modelSetWithDictionary:(NSDictionary *)dic;
161 | /**
162 | Generate a json object from the receiver's properties.
163 |
164 | @return A json object in `NSDictionary` or `NSArray`, or nil if an error occurs.
165 | See [NSJSONSerialization isValidJSONObject] for more information.
166 |
167 | @discussion Any of the invalid property is ignored.
168 | If the reciver is `NSArray`, `NSDictionary` or `NSSet`, it just convert
169 | the inner object to json object.
170 | */
171 | /**
172 | 产生一个json对象通过调用者的property
173 | 返回一个NSDictionary或NSArray的json对象,如果解析失败返回一个Nil
174 | 了解更多消息观看[NSJSONSerialization isValidJSONObject]
175 | 描述:任何无效的property都将被忽视
176 | 如果调用者是NSArray,NSDictionary或NSSet,他将转换里面的对象为json对象
177 | */
178 | - (nullable id)ad_modelToJSONObject;
179 |
180 | /**
181 | Generate a json string's data from the receiver's properties.
182 |
183 | @return A json string's data, or nil if an error occurs.
184 |
185 | @discussion Any of the invalid property is ignored.
186 | If the reciver is `NSArray`, `NSDictionary` or `NSSet`, it will also convert the
187 | inner object to json string.
188 | */
189 | /**
190 | 创建一个json string‘s data(json字符串二进制数据)通过调用者的property
191 | 返回一个json string's data,如果解析失败返回为空
192 | 描述:任何无效的property都将被忽视
193 | 如果调用者是一个NSArray,NSDictionary或NSSet,它也将转换内部对象为一个Json字符串
194 | */
195 | - (nullable NSData *)ad_modelToJSONData;
196 |
197 | /**
198 | Generate a json string from the receiver's properties.
199 |
200 | @return A json string, or nil if an error occurs.
201 |
202 | @discussion Any of the invalid property is ignored.
203 | If the reciver is `NSArray`, `NSDictionary` or `NSSet`, it will also convert the
204 | inner object to json string.
205 | */
206 | /**
207 | 创建一个json string通过调用者的property
208 | 返回一个json string,如果错误产生返回一个nil
209 | 描述 任何无效的property都将被忽视
210 | 如果调用者是NSArray,NSDictionary或NSSet,它也将转换内部对象为一个json string
211 | */
212 | - (nullable NSString *)ad_modelToJSONString;
213 |
214 | /**
215 | Copy a instance with the receiver's properties.
216 |
217 | @return A copied instance, or nil if an error occurs.
218 | */
219 | /**
220 | copy一个对象通过调用者的properties
221 | 返回一个copy的对象,如果解析失败则返回为nil
222 | */
223 | - (nullable id)ad_modelCopy;
224 | /**
225 | Encode the receiver's properties to a coder.
226 |
227 | @param aCoder An archiver object.
228 | 将调用者property编码为一个Coder
229 | 参数 aCoder 一个对象档案
230 | */
231 | - (void)ad_modelEncodeWithCoder:(NSCoder *)aCoder;
232 |
233 | /**
234 | Decode the receiver's properties from a decoder.
235 |
236 | @param aDecoder An archiver object.
237 |
238 | @return self
239 | 通过一个decoder解码成对象的property
240 | 参数 aDecoder 一个对象档案
241 | 返回 调用者自己
242 | */
243 | - (id)ad_modelInitWithCoder:(NSCoder *)aDecoder;
244 |
245 | /**
246 | Get a hash code with the receiver's properties.
247 |
248 | @return Hash code.
249 | 通过调用者Property获取到一个哈希Code
250 | 返回 hashCode
251 | */
252 | - (NSUInteger)ad_modelHash;
253 |
254 | /**
255 | Compares the receiver with another object for equality, based on properties.
256 |
257 | @param model Another object.
258 |
259 | @return `YES` if the reciever is equal to the object, otherwise `NO`.
260 | 比较这个调用者和另一个对象是否相同,基于property
261 | 参数 model 另一个对象
262 | 返回 如果两个对象相同则返回YES 否则为NO
263 | */
264 | - (BOOL)ad_modelIsEqual:(id)model;
265 |
266 | /**
267 | Description method for debugging purposes based on properties.
268 |
269 | @return A string that describes the contents of the receiver.
270 | 描述方法为基于属性的Debug目的(Debug模式中基于属性的描述方法)
271 | 返回一个字符串描述调用者的内容
272 | */
273 | - (NSString *)ad_modelDescription;
274 |
275 | @end
276 |
277 |
278 |
279 | /**
280 | Provide some data-model method for NSArray.
281 | 提供一些关于NSArray的data-model方法
282 | */
283 | @interface NSArray (ADModel)
284 |
285 | /**
286 | Creates and returns an array from a json-array.
287 | This method is thread-safe.
288 |
289 | @param cls The instance's class in array.
290 | @param json A json array of `NSArray`, `NSString` or `NSData`.
291 | Example: [{"name","Mary"},{name:"Joe"}]
292 |
293 | @return A array, or nil if an error occurs.
294 |
295 | 通过一个json-array创建并返回一个数组
296 | 这个方法是安全的
297 |
298 | 参数:cls array中的对象类
299 | 参数:json 一个json array 关于"NSArray","NSString"或"NSData"
300 | 列子:[{"name","Mary"},{name:"Joe"}]
301 | 返回一个数组,如果解析错误则返回nil
302 | */
303 | + (nullable NSArray *)ad_modelArrayWithClass:(Class)cls json:(id)json;
304 |
305 | @end
306 |
307 |
308 |
309 | /**
310 | Provide some data-model method for NSDictionary.
311 | 提供一些data-model方法通过NSDictionary
312 | */
313 | @interface NSDictionary (ADModel)
314 |
315 | /**
316 | Creates and returns a dictionary from a json.
317 | This method is thread-safe.
318 |
319 | @param cls The value instance's class in dictionary.
320 | @param json A json dictionary of `NSDictionary`, `NSString` or `NSData`.
321 | Example: {"user1":{"name","Mary"}, "user2": {name:"Joe"}}
322 |
323 | @return A dictionary, or nil if an error occurs.
324 | 通过一个json文件创建并返回一个字典
325 | 这个方法是安全的
326 | 参数cls 字典中value的对象class
327 | 参数json 一个json的字典是"NSDictionary","NSStirng"或"NSData"的
328 | 列子: {"user1":{"name","Mary"}, "user2": {name:"Joe"}}
329 | */
330 | + (nullable NSDictionary *)ad_modelDictionaryWithClass:(Class)cls json:(id)json;
331 | @end
332 |
333 |
334 |
335 | /**
336 | If the default model transform does not fit to your model class, implement one or
337 | more method in this protocol to change the default key-value transform process.
338 | There's no need to add '' to your class header.
339 |
340 | 如果默认的model改变并符合你的model class,在这个协议里实现一个或更多的方法去改变默认的key-value修改过程
341 | 这些不需要添加到你的头文件
342 | */
343 | @protocol ADModel
344 | @optional
345 |
346 | /**
347 | Custom property mapper.
348 | 定制属性元素
349 |
350 | @discussion If the key in JSON/Dictionary does not match to the model's property name,
351 | implements this method and returns the additional mapper.
352 |
353 | 描述 如果JSON/Dictionary的key并不能匹配model的property name
354 | 实现这个方法并返回额外的元素
355 |
356 | Example:
357 |
358 | json:
359 | {
360 | "n":"Harry Pottery",
361 | "p": 256,
362 | "ext" : {
363 | "desc" : "A book written by J.K.Rowling."
364 | },
365 | "ID" : 100010
366 | }
367 |
368 | model:
369 | @interface ADBook : NSObject
370 | @property NSString *name;
371 | @property NSInteger page;
372 | @property NSString *desc;
373 | @property NSString *bookID;
374 | @end
375 |
376 | @implementation ADBook
377 | + (NSDictionary *)modelCustomPropertyMapper {
378 | return @{@"name" : @"n",
379 | @"page" : @"p",
380 | @"desc" : @"ext.desc",
381 | @"bookID": @[@"id", @"ID", @"book_id"]};
382 | }
383 | @end
384 |
385 | @return A custom mapper for properties.
386 | 通过Property返回一个定制元素
387 | */
388 | + (nullable NSDictionary *)modelCustomPropertyMapper;
389 |
390 | /**
391 | The generic class mapper for container properties.
392 | 通过property容器获得自定义的class元素
393 |
394 | @discussion If the property is a container object, such as NSArray/NSSet/NSDictionary,
395 | implements this method and returns a property->class mapper, tells which kind of
396 | object will be add to the array/set/dictionary.
397 | 描述: 如果这个property是一个对象容器,列如NSArray/NSSet/NSDictionary
398 | 实现这个方法并返回一个属性->类元素,告知哪一个对象将被添加到这个array /set /dictionary
399 |
400 | Example:
401 | @class ADShadow, ADBorder, ADAttachment;
402 |
403 | @interface ADAttributes
404 | @property NSString *name;
405 | @property NSArray *shadows;
406 | @property NSSet *borders;
407 | @property NSDictionary *attachments;
408 | @end
409 |
410 | @implementation ADAttributes
411 | + (NSDictionary *)modelContainerPropertyGenericClass {
412 | return @{@"shadows" : [ADShadow class],
413 | @"borders" : ADBorder.class,
414 | @"attachments" : @"ADAttachment" };
415 | }
416 | @end
417 |
418 | @return A class mapper.
419 | 返回一个对象元素
420 | */
421 | + (nullable NSDictionary *)modelContainerPropertyGenericClass;
422 |
423 | /**
424 | If you need to create instances of different classes during json->object transform,
425 | use the method to choose custom class based on dictionary data.
426 | 如果你需要在json->object的改变时创建关于不同类的对象
427 | 使用这个方法基于dictionary data去改变custom class
428 |
429 | @discussion If the model implements this method, it will be called to determine resulting class
430 | 描述 如果model实现了这个方法,他将被认为通知是确定的class结果
431 |
432 | during `+modelWithJSON:`, `+modelWithDictionary:`, conveting object of properties of parent objects
433 | (both singular and containers via `+modelContainerPropertyGenericClass`).
434 | 在"+modelWithJson","+modelWithDictionary"期间,父对象包含的property是一个对象
435 | (两个单数的并经由`+modelContainerPropertyGenericClass`包含)
436 |
437 | Example:
438 | @class ADCircle, ADRectangle, ADLine;
439 | @implementation ADShape
440 |
441 | + (Class)modelCustomClassForDictionary:(NSDictionary*)dictionary {
442 | if (dictionary[@"radius"] != nil) {
443 | return [ADCircle class];
444 | } else if (dictionary[@"width"] != nil) {
445 | return [ADRectangle class];
446 | } else if (dictionary[@"y2"] != nil) {
447 | return [ADLine class];
448 | } else {
449 | return [self class];
450 | }
451 | }
452 |
453 | @end
454 |
455 | @param dictionary The json/kv dictionary.
456 | 参数 json/kv字典
457 |
458 | @return Class to create from this dictionary, `nil` to use current class.
459 | 返回 通过字典创建的class,nil指使用当前class
460 |
461 | 重写时通过判断哪个key对应的有Value值,就返回一个自己需求的类
462 | */
463 | + (nullable Class)modelCustomClassForDictionary:(NSDictionary *)dictionary;
464 |
465 | /**
466 | All the properties in blacklist will be ignored in model transform process.
467 | Returns nil to ignore this feature.
468 |
469 | @return An array of property's name.
470 |
471 | 在model变换时所有在黑名单里的property都将被忽视
472 | 返回 一个关于property name的数组
473 | */
474 | + (nullable NSArray *)modelPropertyBlacklist;
475 |
476 | /**
477 | If a property is not in the whitelist, it will be ignored in model transform process.
478 | Returns nil to ignore this feature.
479 |
480 | @return An array of property's name.
481 |
482 | 如果一个property不在白名单,在model转变时它将被忽视
483 | 返回nil忽视这方面
484 |
485 | 返回 一个包含property name的数组
486 | */
487 | + (nullable NSArray *)modelPropertyWhitelist;
488 |
489 | /**
490 | This method's behavior is similar to `- (BOOL)modelCustomTransformFromDictionary:(NSDictionary *)dic;`,
491 | but be called before the model transform.
492 | 这个方法行为是相似的与 "- (BOOL)modelCustomTransformFromDictionary:(NSDictionary *)dic"
493 | 但在model转换前被调用的
494 |
495 | @discussion If the model implements this method, it will be called before
496 | `+modelWithJSON:`, `+modelWithDictionary:`, `-modelSetWithJSON:` and `-modelSetWithDictionary:`.
497 | If this method returns nil, the transform process will ignore this model.
498 | 描述 如果model实现了这个方法,它将被调用在"+modelWithJson:","+modelWithDictionary:","-modelSetWithJSON:"and"-modelSetWithDictionary:"之前
499 | 如果方法返回为nil,转换过程中将忽视这个model
500 |
501 | @param dic The json/kv dictionary.
502 | 参数 dic json/kv 字典
503 | @return Returns the modified dictionary, or nil to ignore this model.
504 | 返回 返回修改的字典,如果忽视这个model返回Nil
505 |
506 | */
507 | - (NSDictionary *)modelCustomWillTransformFromDictionary:(NSDictionary *)dic;
508 |
509 | /**
510 | If the default json-to-model transform does not fit to your model object, implement
511 | this method to do additional process. You can also use this method to validate the
512 | model's properties.
513 |
514 | 如果默认的json-to-model转换并不符合你的model对象,实现这个方法去增加额外的过程。
515 | 你也可以使用这个方法使model的property生效
516 |
517 |
518 | @discussion If the model implements this method, it will be called at the end of
519 | `+modelWithJSON:`, `+modelWithDictionary:`, `-modelSetWithJSON:` and `-modelSetWithDictionary:`.
520 | If this method returns NO, the transform process will ignore this model.
521 |
522 | 描述 如果model实现了这个方法,它将被调用在"+modelWithJSON:","+modelWithDictionary","-modelSetWithJSON:" and "-modelSetWithDictionary:"结束
523 |
524 |
525 | @param dic The json/kv dictionary.
526 |
527 | 参数 dic json/kv 字典
528 |
529 | @return Returns YES if the model is valid, or NO to ignore this model.
530 |
531 | 返回 如果这个model是有效的,返回YES 或返回NO忽视这个model
532 |
533 |
534 | */
535 | - (BOOL)modelCustomTransformFromDictionary:(NSDictionary *)dic;
536 |
537 | /**
538 | If the default model-to-json transform does not fit to your model class, implement
539 | this method to do additional process. You can also use this method to validate the
540 | json dictionary.
541 |
542 | 如果默认的model-to-json转换并不符合你的model class,实现这个方法添加额外的过程。
543 | 你也可以使用这个方法使这个json dictionary有效
544 |
545 | @discussion If the model implements this method, it will be called at the end of
546 | `-modelToJSONObject` and `-modelToJSONString`.
547 | If this method returns NO, the transform process will ignore this json dictionary.
548 |
549 | 描述 如果这个model实现了这个方法,它将被调用在"-modelToJSONObject"和"-modelToJSONStrign"结束
550 | 如果这个方法返回NO,这个转换过程将忽视这个json dictionary
551 |
552 | @param dic The json dictionary.
553 |
554 | @return Returns YES if the model is valid, or NO to ignore this model.
555 | */
556 | - (BOOL)modelCustomTransformToDictionary:(NSMutableDictionary *)dic;
557 |
558 | @end
559 |
560 | NS_ASSUME_NONNULL_END
561 |
--------------------------------------------------------------------------------
/ADModel/ADModel/ADClassInfo.m:
--------------------------------------------------------------------------------
1 | //
2 | // ADClassInfo.m
3 | // ADModel
4 | //
5 | // Created by 王奥东 on 16/8/2.
6 | // Copyright © 2016年 王奥东. All rights reserved.
7 | //
8 |
9 | #import "ADClassInfo.h"
10 | #import
11 |
12 | /**
13 | 从一个不可变的type-Encoding 字符数组 获取type值
14 | @param typeEncoding A Type-Encoding string.
15 | 参数 typeEncoding 一个type-Encoding string
16 | @return The encoding type.
17 | 返回 这个encoding的type值
18 | */
19 |
20 |
21 | //C语言函数,返回值为ADEncodingType,接收参数为不可变的字符数组typeEncoding
22 | //字符串就是字符数组
23 | ADEncodingType ADEncodingGetType(const char *typeEncoding){
24 |
25 | //用一个可变字符数组type保存传过来的不可变字符数组typeEncoding
26 | //如果type不存在就返回unknown
27 | char *type = (char *)typeEncoding;
28 | if (!type) return ADEncodingTypeUnknown;
29 | //获取字符数组的长度
30 | //如果长度为0返回unknown
31 | size_t len = strlen(type);
32 | if (len == 0) return ADEncodingTypeUnknown;
33 |
34 | //声明一个encoding类型
35 | ADEncodingType qualifier = 0;
36 | //设置一个值为true的bool用于while死循环
37 | bool prefix = true;
38 |
39 | //以死循环的方式遍历字符数组type
40 | while (prefix) {
41 | // 属性描述为 T@"NSString",&,V_str 的 str
42 | // 属性的描述:T 值:@"NSString"
43 | // 属性的描述:& 值:
44 | // 属性的描述:V 值:_str
45 | //从字符数组type的首地址开始一个一个取出内部的字符
46 | switch (*type) {
47 |
48 | case 'r':{
49 | //对qualifier进行或运算
50 | //0 | 1 = 1 , 1 | 1 = 0
51 | //qualifier = qualifier | ADEncodingTypeQualifierConst
52 | qualifier |= ADEncodingTypeQualifierConst;
53 | //随后地址指针+1
54 | type++;
55 | } break;
56 |
57 | case 'n': {
58 | qualifier |= ADEncodingTypeQualifierIn;
59 | type++;
60 | }break;
61 |
62 | case 'N': {
63 | qualifier |= ADEncodingTypeQualifierInout;
64 | type++;
65 | }break;
66 |
67 | case 'o':{
68 | qualifier |= ADEncodingTypeQualifierOut;
69 | type++;
70 | }break;
71 |
72 | case 'O':{
73 | qualifier |= ADEncodingTypeQualifierBycopy;
74 | type++;
75 | }break;
76 |
77 | case 'R':{
78 | qualifier |= ADEncodingTypeQualifierByref;
79 | type++;
80 | }break;
81 |
82 | case 'V':{
83 | qualifier |= ADEncodingTypeQualifierOneway;
84 | type++;
85 | }break;
86 | //以上条件都不满足,跳出死循环
87 | default:{
88 | prefix = false;
89 | }
90 | break;
91 | }
92 |
93 | }
94 | //获取剩余字符数组的长度
95 | len = strlen(type);
96 |
97 |
98 | if (len == 0) return ADEncodingTypeUnknown | qualifier;
99 |
100 | switch (*type) {
101 |
102 | case 'v': return ADEncodingTypeVoid | qualifier;
103 | case 'B': return ADEncodingTypeBool | qualifier;
104 | case 'c': return ADEncodingTypeInt8 | qualifier;
105 | case 'C': return ADEncodingTypeUInt8 | qualifier;
106 | case 's': return ADEncodingTypeInt16 | qualifier;
107 | case 'S': return ADEncodingTypeUInt16 | qualifier;
108 | case 'i': return ADEncodingTypeInt32 | qualifier;
109 | case 'I': return ADEncodingTypeUInt32 | qualifier;
110 | case 'l': return ADEncodingTypeInt32 | qualifier;
111 | case 'L': return ADEncodingTypeUInt32 | qualifier;
112 | case 'q': return ADEncodingTypeInt64 | qualifier;
113 | case 'Q': return ADEncodingTypeUInt64 | qualifier;
114 | case 'f': return ADEncodingTypeFloat | qualifier;
115 | case 'd': return ADEncodingTypeDouble | qualifier;
116 | case 'D': return ADEncodingTypeLongDouble | qualifier;
117 | case '#': return ADEncodingTypeClass | qualifier;
118 | case ':': return ADEncodingTypeSEL |qualifier;
119 | case '*': return ADEncodingTypeCString | qualifier;
120 | case '^': return ADEncodingTypePointer | qualifier;
121 | case '[': return ADEncodingTypeCArray | qualifier;
122 | case '(': return ADEncodingTypeUnion | qualifier;
123 | case '{': return ADEncodingTypeStruct | qualifier;
124 | case '@': {
125 | if (len == 2 && *(type + 1) == '?')
126 | return ADEncodingTypeBlock | qualifier;
127 | else
128 | return ADEncodingTypeObject | qualifier;
129 | }
130 | default:
131 | return ADEncodingTypeUnknown | qualifier;
132 | }
133 |
134 |
135 | }
136 |
137 | /**
138 | Instance variable information.
139 | 对象的变量信息
140 |
141 | Creates and returns an ivar info object.
142 | 创建并返回一个对象的ivar 信息
143 | @param ivar ivar opaque struct
144 | 参数 ivar 不透明结构体ivar
145 | @return A new object, or nil if an error occurs.
146 | 返回 一个新的对象,若产生错误返回Nil
147 | */
148 | @implementation ADClassIvarInfo
149 |
150 | -(instancetype)initWithIvar:(Ivar)ivar{
151 |
152 | if (!ivar) return nil;
153 | self = [super init];
154 | _ivar = ivar;
155 | // ivar_getName 获取成员变量名,可通过[valueForKeyPath:name]获取属性值
156 | const char *name = ivar_getName(ivar);
157 | if (name) {
158 | _name = [NSString stringWithUTF8String:name];
159 | }
160 | //获取成员变量的偏移量,runtime会计算ivar的地址偏移来找ivar的最终地址
161 | _offset = ivar_getOffset(ivar);
162 | //获取ivar的成员变量类型编码
163 | const char *typeEncoding = ivar_getTypeEncoding(ivar);
164 | if (typeEncoding) {
165 | //对type string 进行UTF-8编码处理
166 | _typeEncoding = [NSString stringWithUTF8String:typeEncoding];
167 |
168 | // 从一个不可变的type-Encoding 字符数组 获取type值
169 | _type = ADEncodingGetType(typeEncoding);
170 | }
171 | return self;
172 |
173 | }
174 |
175 | @end
176 |
177 | /**
178 | Method information.
179 | 方法信息
180 |
181 | 创建并返回一个对象方法信息
182 |
183 | @param method method opaque struct
184 | 参数 method 不透明结构体方法
185 | @return A new object, or nil if an error occurs.
186 | 返回 一个新的对象,发生错误返回nil
187 | */
188 |
189 | @implementation ADClassMethodInfo
190 |
191 | -(instancetype)initWithMethod:(Method)method{
192 |
193 | if (!method) return nil;
194 |
195 | self = [super init];
196 | _method = method;
197 | //获取方法的sel
198 | _sel = method_getName(method);
199 | //获取方法的imp
200 | _imp = method_getImplementation(method);
201 |
202 | // Returns the name of the method specified by a given selector.
203 | // 通过获得的一个selector返回这个方法的说明名字
204 | // @return A C string indicating the name of the selector.
205 | // 返回一个c string 标示这个selector的name
206 | // 返回值:值不可变的字符指针name,字符指针通常指向一个字符数组的首地址,字符数组类似于字符串
207 | const char *name = sel_getName(_sel);
208 | if (name) {
209 | _name = [NSString stringWithUTF8String:name];
210 | }
211 |
212 | // Returns a string describing a method's parameter and return types.
213 | // 通过接收的一个方法返回一个string类型描述(OC实现的编码类型)
214 | // @return A C string. The string may be \c NULL.
215 | // 返回一个C string . 这个string 可能是 \c NULL
216 | // 获取方法的参数和返回值类型
217 | const char *typeEncoding = method_getTypeEncoding(method);
218 | if (typeEncoding) {
219 | _typeEncoding = [NSString stringWithUTF8String:typeEncoding];
220 | }
221 | // Returns a string describing a method's return type.
222 | // 通过一个方法返回一个string(字符数组)描述(方法的返回值类型的字符串)
223 | // @return A C string describing the return type. You must free the string with \c free().
224 | // 返回一个C string 描述. 你必须释放这个string 使用 \c free()
225 | // 获取方法的返回值类型的字符串
226 | char *returnType = method_copyReturnType(method);
227 | if (returnType) {
228 | _returnTypeEncoding = [NSString stringWithUTF8String:returnType];
229 | free(returnType);
230 | }
231 | // Returns the number of arguments accepted by a method
232 | // 通过一个方法返回主题采用数字(返回方法的参数的个数)
233 | // @return An integer containing the number of arguments accepted by the given method.
234 | // 返回 一个integer 包含这个主题使用数字通过给予的方法
235 | // 返回方法的参数的个数
236 | unsigned int argumentCount = method_getNumberOfArguments(method);
237 | if (argumentCount > 0) {
238 | NSMutableArray *argumentTypes = [NSMutableArray new];
239 | for (unsigned int i = 0; i < argumentCount; i++) {
240 | // Returns a string describing a single parameter type of a method.
241 | // 通过关于一个方法的一个单独的type参数返回一个string描述(获取方法的指定位置参数的类型字符串)
242 | // 获取方法的指定位置参数的类型字符串
243 | char *argumentType = method_copyArgumentType(method, i);
244 | //有值就通过UTF-8编码后获取否则为nil
245 | NSString *type = argumentType ? [NSString stringWithUTF8String:argumentType] : nil;
246 | //argumentTypes能否添加type 如果能则添加type,否则添加@""
247 | //保证绝对有个对象被添加到可变数组
248 | [argumentTypes addObject:type ? type: @""];
249 | //如果argumentType有值就释放,加个判断防止释放的是野指针
250 | if (argumentType) free(argumentType);
251 | }
252 | _argumentTypeEncodings = argumentTypes;
253 | }
254 | return self;
255 | }
256 |
257 | @end
258 |
259 | /**
260 | Property information.
261 | property 信息
262 |
263 | Creates and returns a property info object.
264 | 创建并返回一个property的对象信息
265 |
266 | @param property property opaque struct
267 | 参数 property 不透明结构体property
268 | @return A new object, or nil if an error occurs.
269 | 返回 一个新的对象,发生错误返回nil
270 | */
271 |
272 |
273 | @implementation ADClassPropertyInfo
274 |
275 | -(instancetype)initWithProperty:(objc_property_t)property{
276 |
277 |
278 | if (!property) return nil;
279 | self = [super init];
280 | _property = property;
281 | //查找属性名称
282 | const char *name = property_getName(property);
283 | if (name) {
284 | _name = [NSString stringWithUTF8String:name];
285 | }
286 | ADEncodingType type = 0;
287 | unsigned int attrCount;
288 | // 获取属性的特性列表,数量值保存在&attrCount
289 | objc_property_attribute_t * attrs = property_copyAttributeList(property, &attrCount);
290 | for (unsigned int i = 0; i < attrCount; i++) {
291 | //name属性的描述 value属性值
292 | /**
293 | 结构体中的name与Value:
294 | 属性类型 name值:T value:变化
295 | 编码类型 name值:C(copy) &(strong) W(weak) 空(assign) 等 value:无
296 | 非/原子性 name值:空(atomic) N(Nonatomic) value:无
297 | 变量名称 name值:V value:变化
298 |
299 | 属性描述为 T@"NSString",&,V_str 的 str
300 | 属性的描述:T 值:@"NSString"
301 | 属性的描述:& 值:
302 | 属性的描述:V 值:_str
303 |
304 | G为getter方法,S为setter方法
305 | D为Dynamic(@dynamic ,告诉编译器不自动生成属性的getter、setter方法)
306 | */
307 | switch (attrs[i].name[0]) {
308 | case 'T':{//Type encoding
309 | if (attrs[i].value) {
310 | _typeEncoding = [NSString stringWithUTF8String:attrs[i].value];
311 | type = ADEncodingGetType(attrs[i].value);
312 |
313 | if ((type & ADEncodingTypeMask) == ADEncodingTypeObject && _typeEncoding.length) {
314 | //条件判断
315 | NSScanner *scanner = [NSScanner scannerWithString:_typeEncoding];
316 | // scanString:intoString:从当前的扫描位置开始扫描,判断扫描字符串是否从当前位置能扫描到和传入字符串相同的一串字符,如果能扫描到就返回YES,指针指向的地址存储的就是这段字符串的内容。
317 | if (![scanner scanString:@"@\"" intoString:NULL]) {
318 | continue;
319 | }
320 |
321 | NSString *clsName = nil;
322 | // scanUpToCharactersFromSet:扫描字符串直到遇到NSCharacterSet字符集的字符时停止,指针指向的地址存储的内容为遇到跳过字符集字符之前的内容。
323 | // NSCharacterSet为一组Unicode字符,常用与NSScanner,NSString处理
324 | if ([scanner scanUpToCharactersFromSet:[NSCharacterSet characterSetWithCharactersInString:@"\"<"] intoString:&clsName]) {
325 | if (clsName.length)
326 | // objc_getClass获取对象的isa,isa是一个指针指向对象自身
327 | _cls = objc_getClass(clsName.UTF8String);
328 | }
329 |
330 | NSMutableArray *protocols = nil;
331 |
332 | while ([scanner scanString:@"<" intoString:NULL]) {
333 | NSString *protocol = nil;
334 | // 获取当前位置的某个字符串的内容,可以使用scanUpToString:intoString:方法(如果你不想保留这些字符,可以传递一个NULL给第2个参数)
335 | if ([scanner scanUpToString:@">" intoString:&protocol]) {
336 | if (protocol.length) {
337 | if (!protocols)
338 | protocols = [NSMutableArray new];
339 | [protocols addObject:protocol];
340 | }
341 | }
342 | [scanner scanString:@">" intoString:NULL];
343 | }
344 | _protocols = protocols;
345 |
346 | }
347 | }
348 | } break;
349 |
350 | case 'V':{// Instance variable
351 | if (attrs[i].value) {
352 | _ivarName = [NSString stringWithUTF8String:attrs[i].value];
353 | }
354 | }break;
355 |
356 | case 'R':{
357 | type |= ADEncodingTypePropertyReadonly;
358 | }break;
359 |
360 | case 'C':{
361 | type |= ADEncodingTypePropertyCopy;
362 | }break;
363 |
364 | case '&':{
365 | type |= ADEncodingTypePropertyRetain;
366 | }break;
367 |
368 | case 'N':{
369 | type |= ADEncodingTypePropertyNonatomic;
370 | }break;
371 |
372 | case 'D':{
373 | //Dynamic ,@dynamic告诉编译器不自动生成属性的getter、setter方法
374 | type |= ADEncodingTypePropertyDynamic;
375 | }break;
376 |
377 | case 'W':{
378 | type |= ADEncodingTypePropertyWeak;
379 | }break;
380 |
381 | case 'G':{
382 | type |= ADEncodingTypePropertyCustomGetter;
383 | if (attrs[i].value) {
384 | _getter = NSSelectorFromString([NSString stringWithUTF8String:attrs[i].value]);
385 | }
386 | }break;
387 |
388 | case 'S':{
389 | type |= ADEncodingTypePropertyCustomSetter;
390 | if (attrs[i].value) {
391 | _setter = NSSelectorFromString([NSString stringWithUTF8String:attrs[i].value]);
392 | }
393 | }break;//commented for code coverage in next line
394 |
395 | default:
396 | break;
397 | }
398 |
399 | }
400 |
401 | if (attrs) {
402 | free(attrs);
403 | attrs = NULL;
404 | }
405 |
406 | _type = type;
407 |
408 | if (_name.length) {
409 | if (!_getter) {
410 | _getter = NSSelectorFromString(_name);
411 | }
412 | if (!_setter) {
413 | _setter = NSSelectorFromString([NSString stringWithFormat:@"set%@%@:",[_name substringToIndex:1].uppercaseString,[_name substringFromIndex:1]]);
414 | }
415 | }
416 | return self;
417 | }
418 |
419 | @end
420 |
421 | /**
422 | Class information for a class.
423 | 一个class的class 信息
424 | */
425 | //@implementation YYClassInfo {
426 | // BOOL _needUpdate;
427 | //}
428 | @implementation ADClassInfo {
429 | BOOL _needUpdate;
430 | }
431 |
432 |
433 | //私有方法
434 | -(instancetype)initWithClass:(Class)cls{
435 |
436 | if (!cls) return nil;
437 |
438 | self = [super init];
439 | //自身
440 | _cls = cls;
441 | //父类
442 | _superCls = class_getSuperclass(cls);
443 | //是否为元类
444 | _isMeta = class_isMetaClass(cls);
445 | //不是元类
446 | if (!_isMeta) {
447 | //保存元类
448 | _metaCls = objc_getMetaClass(class_getName(cls));
449 | }
450 | //保存类名
451 | _name = NSStringFromClass(cls);
452 | //每次获取每次更新
453 | [self _update];
454 | //获取父类信息,直至父类不存在.
455 | //每次获取都会更新存储的数据
456 | _superClassInfo = [self.class classInfoWithClass:_superCls];
457 |
458 | return self;
459 | }
460 |
461 | //私有方法
462 | -(void)_update{
463 |
464 | _ivarInfos = nil;
465 | _methodInfos = nil;
466 | _propertyInfos = nil;
467 |
468 | Class cls = self.cls;
469 | unsigned int methodCount = 0;
470 | // Describes the instance methods implemented by a class.
471 | // 描述这个列子的方法实现通过一个class
472 | // @return An array of pointers of type Method describing the instance methods
473 | // 返回关于列子的方法描述的一个数组指针
474 | // 数组长度保存在methodCount地址里
475 | Method *methods = class_copyMethodList(cls, &methodCount);
476 |
477 | if (methods) {
478 | NSMutableDictionary * methodInfos = [NSMutableDictionary new];
479 | _methodInfos = methodInfos;
480 | for (unsigned int i = 0; i < methodCount; i++) {
481 | // 使用自定义方法,获取一个对象,对象是根据传过来的一个方法创建
482 | ADClassMethodInfo *info = [[ADClassMethodInfo alloc] initWithMethod:methods[i]];
483 | if (info.name) methodInfos[info.name] = info;
484 |
485 | }
486 | free(methods);
487 | }
488 |
489 |
490 | //属性的操作
491 | unsigned int propertyCount = 0;
492 | //同上,只是关于属性了
493 | objc_property_t *properties = class_copyPropertyList(cls, &propertyCount);
494 | if (properties) {
495 | NSMutableDictionary *propertyInfos = [NSMutableDictionary new];
496 | _propertyInfos = propertyInfos;
497 | for (unsigned int i = 0; i < propertyCount; i++) {
498 | ADClassPropertyInfo *info = [[ADClassPropertyInfo alloc] initWithProperty:properties[i]];
499 | if (info.name) propertyInfos[info.name] = info;
500 |
501 | }
502 | free(properties);
503 | }
504 |
505 | //成员变量的操作,与属性的操作区别在于:成员变量{}中声明的, 属性@property声明的
506 | //属性会有相应的getter方法和setter方法,而成员变量没有,另外,外部访问属性可以用"."来访问,访问成员变量需要用"->"来访问
507 |
508 |
509 | unsigned int ivarCount = 0;
510 | Ivar *ivars = class_copyIvarList(cls, &ivarCount);
511 | if (ivars) {
512 | NSMutableDictionary *ivarInfos = [NSMutableDictionary new];
513 | _ivarInfos = ivarInfos;
514 | for (unsigned int i = 0; i < ivarCount; i++) {
515 | ADClassIvarInfo *info = [[ADClassIvarInfo alloc] initWithIvar:ivars[i]];
516 | if (info.name) ivarInfos[info.name] = info;
517 | }
518 | free(ivars);
519 | }
520 | if (!_ivarInfos) _ivarInfos = @{};
521 | if (!_methodInfos) _methodInfos = @{};
522 | if (!_propertyInfos) _propertyInfos = @{};
523 |
524 | _needUpdate = NO;
525 | }
526 |
527 | /**
528 | If the class is changed (for example: you add a method to this class with
529 | 'class_addMethod()'), you should call this method to refresh the class info cache.
530 | 如果这个Class是改变的(列如: 你通过class_addMethod()添加了一个方法给这个类),你应该告诉这个方法去刷新class信息缓存
531 | After called this method, `needUpdate` will returns `YES`, and you should call
532 | 'classInfoWithClass' or 'classInfoWithClassName' to get the updated class info.
533 | 被方法告知之后,"needUpdate"应返回"YES",而且你应该告知'classInfoWithClass' or 'classInfoWithClassName'获取这个class更新信息
534 | */
535 | -(void)setNeedUpdate{
536 | _needUpdate = YES;
537 | }
538 | /**
539 | If this method returns `YES`, you should stop using this instance and call
540 | `classInfoWithClass` or `classInfoWithClassName` to get the updated class info.
541 | 如果这个方法返回"YES",你应该停止使用这个对象并告知`classInfoWithClass` or `classInfoWithClassName` 去获取class更新信息
542 | @return Whether this class info need update.
543 | 返回 这个class 信息是否需要更新
544 | */
545 | -(BOOL)needUpdate{
546 | return _needUpdate;
547 | }
548 | /**
549 | Get the class info of a specified Class.
550 | 获取一个Class的class信息说明
551 |
552 | @discussion This method will cache the class info and super-class info
553 | at the first access to the Class. This method is thread-safe.
554 |
555 | 描述 这个方法将缓存这个class信息 和 父类class信息,在第一次进入这个class时
556 | 这个方法是线程安全的
557 | @param cls A class.
558 | 参数 cls 一个class
559 | @return A class info, or nil if an error occurs.
560 | 返回 一个Class 信息, 如果发生错误返回Nil
561 | */
562 | +(instancetype)classInfoWithClass:(Class)cls{
563 | if (!cls) return nil;
564 | // NSMutableDictionary的底层,直接使用CFMutableDictionaryRef可提高效率
565 | //存放对象(NSArray也是一个对象,详情看下方)
566 | static CFMutableDictionaryRef classCache;
567 | //存放元类
568 | static CFMutableDictionaryRef metaCache;
569 |
570 | static dispatch_once_t onceToken;
571 | // 为了线程安全 同步信号量
572 | static dispatch_semaphore_t lock;
573 | dispatch_once(&onceToken, ^{
574 | /**
575 |
576 | @功能 CFDictionaryCreateMutable创建一个新的词典。
577 |
578 | @参数 CFAllocator 分配器应该用于分配CFAllocator字典的内存及其值的存储。这
579 | 参数可能为空,在这种情况下,当前的默认值CFAllocator使用。如果这个引用不是一个有效的cfallocator,其行为是未定义的。
580 |
581 | @参数 capacity 暗示值得个数,通过0实现可能忽略这个提示,或者可以使用它来优化各种
582 |
583 | @参数 keyCallBacks 指向CFDictionaryKeyCallBacks结构为这本字典使用回调函数初始化在字典中的每一个键,初始化规则太多而且看的有点迷糊就不多说了,毕竟不敢乱说。。
584 | @参数 valueCallBacks 指向CFDictionaryValueCallBacks结构为这本词典使用回调函数初始化字典中的每一个值
585 |
586 |
587 | 操作
588 |
589 | */
590 | classCache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
591 | metaCache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
592 | //这里我们指定一个资源 wait 返回值就不会为0 执行发出信号操作
593 | lock = dispatch_semaphore_create(1);
594 | });
595 | // 没有资源,会一直触发信号控制
596 | dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
597 | // class_isMetaClass(Class cls),判断指定类是否是一个元类(元类是类对象的类,如[NSArray array]中的NSArray也是一个对象,NSArray通过元类生成,而元类又是一个对象,元类的类是根源类NSObject,NSObject父类为Nil)
598 | ADClassInfo *info = CFDictionaryGetValue(class_isMetaClass(cls) ? metaCache : classCache, (__bridge const void *)(cls));
599 | if (info && info->_needUpdate) {
600 | [info _update];
601 | }
602 | dispatch_semaphore_signal(lock);
603 | if (!info) {
604 | info = [[ADClassInfo alloc] initWithClass:cls];
605 | if (info) {
606 | dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
607 | CFDictionarySetValue(info.isMeta ? metaCache : classCache, (__bridge const void *)(cls),(__bridge const void *)(info));
608 | dispatch_semaphore_signal(lock);
609 | }
610 | }
611 | return info;
612 | }
613 |
614 | /**
615 | Get the class info of a specified Class.
616 | 获取一个Class的class信息说明
617 |
618 | @discussion This method will cache the class info and super-class info
619 | at the first access to the Class. This method is thread-safe.
620 |
621 | 描述 这个方法将缓存这个class信息和父类class 信息在第一次进入这个class时。
622 | 这个方法是线程安全的
623 | @param className A class name.
624 | 参数 className 一个class name
625 | @return A class info, or nil if an error occurs.
626 | 返回 一个class info,如果出现错误返回Nil
627 | */
628 | +(instancetype)classInfoWithClassName:(NSString *)className{
629 | Class cls = NSClassFromString(className);
630 | return [self classInfoWithClass:cls];
631 | }
632 |
633 | @end
--------------------------------------------------------------------------------
/ADModel.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 1633295F1D5021A10027BE35 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 1633295E1D5021A10027BE35 /* main.m */; };
11 | 163329621D5021A10027BE35 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 163329611D5021A10027BE35 /* AppDelegate.m */; };
12 | 163329651D5021A10027BE35 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 163329641D5021A10027BE35 /* ViewController.m */; };
13 | 163329681D5021A10027BE35 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 163329661D5021A10027BE35 /* Main.storyboard */; };
14 | 1633296A1D5021A10027BE35 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 163329691D5021A10027BE35 /* Assets.xcassets */; };
15 | 1633296D1D5021A10027BE35 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 1633296B1D5021A10027BE35 /* LaunchScreen.storyboard */; };
16 | 163329781D5021A10027BE35 /* ADModelTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 163329771D5021A10027BE35 /* ADModelTests.m */; };
17 | 163329831D5021A10027BE35 /* ADModelUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = 163329821D5021A10027BE35 /* ADModelUITests.m */; };
18 | 163329931D5022ED0027BE35 /* ADClassInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 163329921D5022ED0027BE35 /* ADClassInfo.m */; };
19 | 163329971D507FF40027BE35 /* NSObject+ADModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 163329961D507FF40027BE35 /* NSObject+ADModel.m */; };
20 | /* End PBXBuildFile section */
21 |
22 | /* Begin PBXContainerItemProxy section */
23 | 163329741D5021A10027BE35 /* PBXContainerItemProxy */ = {
24 | isa = PBXContainerItemProxy;
25 | containerPortal = 163329521D5021A10027BE35 /* Project object */;
26 | proxyType = 1;
27 | remoteGlobalIDString = 163329591D5021A10027BE35;
28 | remoteInfo = ADModel;
29 | };
30 | 1633297F1D5021A10027BE35 /* PBXContainerItemProxy */ = {
31 | isa = PBXContainerItemProxy;
32 | containerPortal = 163329521D5021A10027BE35 /* Project object */;
33 | proxyType = 1;
34 | remoteGlobalIDString = 163329591D5021A10027BE35;
35 | remoteInfo = ADModel;
36 | };
37 | /* End PBXContainerItemProxy section */
38 |
39 | /* Begin PBXFileReference section */
40 | 1633295A1D5021A10027BE35 /* ADModel.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ADModel.app; sourceTree = BUILT_PRODUCTS_DIR; };
41 | 1633295E1D5021A10027BE35 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; };
42 | 163329601D5021A10027BE35 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; };
43 | 163329611D5021A10027BE35 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; };
44 | 163329631D5021A10027BE35 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; };
45 | 163329641D5021A10027BE35 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; };
46 | 163329671D5021A10027BE35 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
47 | 163329691D5021A10027BE35 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
48 | 1633296C1D5021A10027BE35 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
49 | 1633296E1D5021A10027BE35 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
50 | 163329731D5021A10027BE35 /* ADModelTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ADModelTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
51 | 163329771D5021A10027BE35 /* ADModelTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ADModelTests.m; sourceTree = ""; };
52 | 163329791D5021A10027BE35 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
53 | 1633297E1D5021A10027BE35 /* ADModelUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ADModelUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
54 | 163329821D5021A10027BE35 /* ADModelUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ADModelUITests.m; sourceTree = ""; };
55 | 163329841D5021A10027BE35 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
56 | 163329911D5022ED0027BE35 /* ADClassInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ADClassInfo.h; sourceTree = ""; };
57 | 163329921D5022ED0027BE35 /* ADClassInfo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ADClassInfo.m; sourceTree = ""; };
58 | 163329941D507F080027BE35 /* ADModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ADModel.h; sourceTree = ""; };
59 | 163329951D507FF40027BE35 /* NSObject+ADModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+ADModel.h"; sourceTree = ""; };
60 | 163329961D507FF40027BE35 /* NSObject+ADModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+ADModel.m"; sourceTree = ""; };
61 | /* End PBXFileReference section */
62 |
63 | /* Begin PBXFrameworksBuildPhase section */
64 | 163329571D5021A10027BE35 /* Frameworks */ = {
65 | isa = PBXFrameworksBuildPhase;
66 | buildActionMask = 2147483647;
67 | files = (
68 | );
69 | runOnlyForDeploymentPostprocessing = 0;
70 | };
71 | 163329701D5021A10027BE35 /* Frameworks */ = {
72 | isa = PBXFrameworksBuildPhase;
73 | buildActionMask = 2147483647;
74 | files = (
75 | );
76 | runOnlyForDeploymentPostprocessing = 0;
77 | };
78 | 1633297B1D5021A10027BE35 /* Frameworks */ = {
79 | isa = PBXFrameworksBuildPhase;
80 | buildActionMask = 2147483647;
81 | files = (
82 | );
83 | runOnlyForDeploymentPostprocessing = 0;
84 | };
85 | /* End PBXFrameworksBuildPhase section */
86 |
87 | /* Begin PBXGroup section */
88 | 163329511D5021A00027BE35 = {
89 | isa = PBXGroup;
90 | children = (
91 | 1633295C1D5021A10027BE35 /* ADModel */,
92 | 163329761D5021A10027BE35 /* ADModelTests */,
93 | 163329811D5021A10027BE35 /* ADModelUITests */,
94 | 1633295B1D5021A10027BE35 /* Products */,
95 | );
96 | sourceTree = "";
97 | };
98 | 1633295B1D5021A10027BE35 /* Products */ = {
99 | isa = PBXGroup;
100 | children = (
101 | 1633295A1D5021A10027BE35 /* ADModel.app */,
102 | 163329731D5021A10027BE35 /* ADModelTests.xctest */,
103 | 1633297E1D5021A10027BE35 /* ADModelUITests.xctest */,
104 | );
105 | name = Products;
106 | sourceTree = "";
107 | };
108 | 1633295C1D5021A10027BE35 /* ADModel */ = {
109 | isa = PBXGroup;
110 | children = (
111 | 163329901D5021D00027BE35 /* ADModel */,
112 | 163329601D5021A10027BE35 /* AppDelegate.h */,
113 | 163329611D5021A10027BE35 /* AppDelegate.m */,
114 | 163329631D5021A10027BE35 /* ViewController.h */,
115 | 163329641D5021A10027BE35 /* ViewController.m */,
116 | 163329661D5021A10027BE35 /* Main.storyboard */,
117 | 163329691D5021A10027BE35 /* Assets.xcassets */,
118 | 1633296B1D5021A10027BE35 /* LaunchScreen.storyboard */,
119 | 1633296E1D5021A10027BE35 /* Info.plist */,
120 | 1633295D1D5021A10027BE35 /* Supporting Files */,
121 | );
122 | path = ADModel;
123 | sourceTree = "";
124 | };
125 | 1633295D1D5021A10027BE35 /* Supporting Files */ = {
126 | isa = PBXGroup;
127 | children = (
128 | 1633295E1D5021A10027BE35 /* main.m */,
129 | );
130 | name = "Supporting Files";
131 | sourceTree = "";
132 | };
133 | 163329761D5021A10027BE35 /* ADModelTests */ = {
134 | isa = PBXGroup;
135 | children = (
136 | 163329771D5021A10027BE35 /* ADModelTests.m */,
137 | 163329791D5021A10027BE35 /* Info.plist */,
138 | );
139 | path = ADModelTests;
140 | sourceTree = "";
141 | };
142 | 163329811D5021A10027BE35 /* ADModelUITests */ = {
143 | isa = PBXGroup;
144 | children = (
145 | 163329821D5021A10027BE35 /* ADModelUITests.m */,
146 | 163329841D5021A10027BE35 /* Info.plist */,
147 | );
148 | path = ADModelUITests;
149 | sourceTree = "";
150 | };
151 | 163329901D5021D00027BE35 /* ADModel */ = {
152 | isa = PBXGroup;
153 | children = (
154 | 163329911D5022ED0027BE35 /* ADClassInfo.h */,
155 | 163329921D5022ED0027BE35 /* ADClassInfo.m */,
156 | 163329941D507F080027BE35 /* ADModel.h */,
157 | 163329951D507FF40027BE35 /* NSObject+ADModel.h */,
158 | 163329961D507FF40027BE35 /* NSObject+ADModel.m */,
159 | );
160 | path = ADModel;
161 | sourceTree = "";
162 | };
163 | /* End PBXGroup section */
164 |
165 | /* Begin PBXNativeTarget section */
166 | 163329591D5021A10027BE35 /* ADModel */ = {
167 | isa = PBXNativeTarget;
168 | buildConfigurationList = 163329871D5021A10027BE35 /* Build configuration list for PBXNativeTarget "ADModel" */;
169 | buildPhases = (
170 | 163329561D5021A10027BE35 /* Sources */,
171 | 163329571D5021A10027BE35 /* Frameworks */,
172 | 163329581D5021A10027BE35 /* Resources */,
173 | );
174 | buildRules = (
175 | );
176 | dependencies = (
177 | );
178 | name = ADModel;
179 | productName = ADModel;
180 | productReference = 1633295A1D5021A10027BE35 /* ADModel.app */;
181 | productType = "com.apple.product-type.application";
182 | };
183 | 163329721D5021A10027BE35 /* ADModelTests */ = {
184 | isa = PBXNativeTarget;
185 | buildConfigurationList = 1633298A1D5021A10027BE35 /* Build configuration list for PBXNativeTarget "ADModelTests" */;
186 | buildPhases = (
187 | 1633296F1D5021A10027BE35 /* Sources */,
188 | 163329701D5021A10027BE35 /* Frameworks */,
189 | 163329711D5021A10027BE35 /* Resources */,
190 | );
191 | buildRules = (
192 | );
193 | dependencies = (
194 | 163329751D5021A10027BE35 /* PBXTargetDependency */,
195 | );
196 | name = ADModelTests;
197 | productName = ADModelTests;
198 | productReference = 163329731D5021A10027BE35 /* ADModelTests.xctest */;
199 | productType = "com.apple.product-type.bundle.unit-test";
200 | };
201 | 1633297D1D5021A10027BE35 /* ADModelUITests */ = {
202 | isa = PBXNativeTarget;
203 | buildConfigurationList = 1633298D1D5021A10027BE35 /* Build configuration list for PBXNativeTarget "ADModelUITests" */;
204 | buildPhases = (
205 | 1633297A1D5021A10027BE35 /* Sources */,
206 | 1633297B1D5021A10027BE35 /* Frameworks */,
207 | 1633297C1D5021A10027BE35 /* Resources */,
208 | );
209 | buildRules = (
210 | );
211 | dependencies = (
212 | 163329801D5021A10027BE35 /* PBXTargetDependency */,
213 | );
214 | name = ADModelUITests;
215 | productName = ADModelUITests;
216 | productReference = 1633297E1D5021A10027BE35 /* ADModelUITests.xctest */;
217 | productType = "com.apple.product-type.bundle.ui-testing";
218 | };
219 | /* End PBXNativeTarget section */
220 |
221 | /* Begin PBXProject section */
222 | 163329521D5021A10027BE35 /* Project object */ = {
223 | isa = PBXProject;
224 | attributes = {
225 | CLASSPREFIX = AD;
226 | LastUpgradeCheck = 0730;
227 | ORGANIZATIONNAME = "王奥东";
228 | TargetAttributes = {
229 | 163329591D5021A10027BE35 = {
230 | CreatedOnToolsVersion = 7.3;
231 | };
232 | 163329721D5021A10027BE35 = {
233 | CreatedOnToolsVersion = 7.3;
234 | TestTargetID = 163329591D5021A10027BE35;
235 | };
236 | 1633297D1D5021A10027BE35 = {
237 | CreatedOnToolsVersion = 7.3;
238 | TestTargetID = 163329591D5021A10027BE35;
239 | };
240 | };
241 | };
242 | buildConfigurationList = 163329551D5021A10027BE35 /* Build configuration list for PBXProject "ADModel" */;
243 | compatibilityVersion = "Xcode 3.2";
244 | developmentRegion = English;
245 | hasScannedForEncodings = 0;
246 | knownRegions = (
247 | en,
248 | Base,
249 | );
250 | mainGroup = 163329511D5021A00027BE35;
251 | productRefGroup = 1633295B1D5021A10027BE35 /* Products */;
252 | projectDirPath = "";
253 | projectRoot = "";
254 | targets = (
255 | 163329591D5021A10027BE35 /* ADModel */,
256 | 163329721D5021A10027BE35 /* ADModelTests */,
257 | 1633297D1D5021A10027BE35 /* ADModelUITests */,
258 | );
259 | };
260 | /* End PBXProject section */
261 |
262 | /* Begin PBXResourcesBuildPhase section */
263 | 163329581D5021A10027BE35 /* Resources */ = {
264 | isa = PBXResourcesBuildPhase;
265 | buildActionMask = 2147483647;
266 | files = (
267 | 1633296D1D5021A10027BE35 /* LaunchScreen.storyboard in Resources */,
268 | 1633296A1D5021A10027BE35 /* Assets.xcassets in Resources */,
269 | 163329681D5021A10027BE35 /* Main.storyboard in Resources */,
270 | );
271 | runOnlyForDeploymentPostprocessing = 0;
272 | };
273 | 163329711D5021A10027BE35 /* Resources */ = {
274 | isa = PBXResourcesBuildPhase;
275 | buildActionMask = 2147483647;
276 | files = (
277 | );
278 | runOnlyForDeploymentPostprocessing = 0;
279 | };
280 | 1633297C1D5021A10027BE35 /* Resources */ = {
281 | isa = PBXResourcesBuildPhase;
282 | buildActionMask = 2147483647;
283 | files = (
284 | );
285 | runOnlyForDeploymentPostprocessing = 0;
286 | };
287 | /* End PBXResourcesBuildPhase section */
288 |
289 | /* Begin PBXSourcesBuildPhase section */
290 | 163329561D5021A10027BE35 /* Sources */ = {
291 | isa = PBXSourcesBuildPhase;
292 | buildActionMask = 2147483647;
293 | files = (
294 | 163329651D5021A10027BE35 /* ViewController.m in Sources */,
295 | 163329621D5021A10027BE35 /* AppDelegate.m in Sources */,
296 | 1633295F1D5021A10027BE35 /* main.m in Sources */,
297 | 163329971D507FF40027BE35 /* NSObject+ADModel.m in Sources */,
298 | 163329931D5022ED0027BE35 /* ADClassInfo.m in Sources */,
299 | );
300 | runOnlyForDeploymentPostprocessing = 0;
301 | };
302 | 1633296F1D5021A10027BE35 /* Sources */ = {
303 | isa = PBXSourcesBuildPhase;
304 | buildActionMask = 2147483647;
305 | files = (
306 | 163329781D5021A10027BE35 /* ADModelTests.m in Sources */,
307 | );
308 | runOnlyForDeploymentPostprocessing = 0;
309 | };
310 | 1633297A1D5021A10027BE35 /* Sources */ = {
311 | isa = PBXSourcesBuildPhase;
312 | buildActionMask = 2147483647;
313 | files = (
314 | 163329831D5021A10027BE35 /* ADModelUITests.m in Sources */,
315 | );
316 | runOnlyForDeploymentPostprocessing = 0;
317 | };
318 | /* End PBXSourcesBuildPhase section */
319 |
320 | /* Begin PBXTargetDependency section */
321 | 163329751D5021A10027BE35 /* PBXTargetDependency */ = {
322 | isa = PBXTargetDependency;
323 | target = 163329591D5021A10027BE35 /* ADModel */;
324 | targetProxy = 163329741D5021A10027BE35 /* PBXContainerItemProxy */;
325 | };
326 | 163329801D5021A10027BE35 /* PBXTargetDependency */ = {
327 | isa = PBXTargetDependency;
328 | target = 163329591D5021A10027BE35 /* ADModel */;
329 | targetProxy = 1633297F1D5021A10027BE35 /* PBXContainerItemProxy */;
330 | };
331 | /* End PBXTargetDependency section */
332 |
333 | /* Begin PBXVariantGroup section */
334 | 163329661D5021A10027BE35 /* Main.storyboard */ = {
335 | isa = PBXVariantGroup;
336 | children = (
337 | 163329671D5021A10027BE35 /* Base */,
338 | );
339 | name = Main.storyboard;
340 | sourceTree = "";
341 | };
342 | 1633296B1D5021A10027BE35 /* LaunchScreen.storyboard */ = {
343 | isa = PBXVariantGroup;
344 | children = (
345 | 1633296C1D5021A10027BE35 /* Base */,
346 | );
347 | name = LaunchScreen.storyboard;
348 | sourceTree = "";
349 | };
350 | /* End PBXVariantGroup section */
351 |
352 | /* Begin XCBuildConfiguration section */
353 | 163329851D5021A10027BE35 /* Debug */ = {
354 | isa = XCBuildConfiguration;
355 | buildSettings = {
356 | ALWAYS_SEARCH_USER_PATHS = NO;
357 | CLANG_ANALYZER_NONNULL = YES;
358 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
359 | CLANG_CXX_LIBRARY = "libc++";
360 | CLANG_ENABLE_MODULES = YES;
361 | CLANG_ENABLE_OBJC_ARC = YES;
362 | CLANG_WARN_BOOL_CONVERSION = YES;
363 | CLANG_WARN_CONSTANT_CONVERSION = YES;
364 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
365 | CLANG_WARN_EMPTY_BODY = YES;
366 | CLANG_WARN_ENUM_CONVERSION = YES;
367 | CLANG_WARN_INT_CONVERSION = YES;
368 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
369 | CLANG_WARN_UNREACHABLE_CODE = YES;
370 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
371 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
372 | COPY_PHASE_STRIP = NO;
373 | DEBUG_INFORMATION_FORMAT = dwarf;
374 | ENABLE_STRICT_OBJC_MSGSEND = YES;
375 | ENABLE_TESTABILITY = YES;
376 | GCC_C_LANGUAGE_STANDARD = gnu99;
377 | GCC_DYNAMIC_NO_PIC = NO;
378 | GCC_NO_COMMON_BLOCKS = YES;
379 | GCC_OPTIMIZATION_LEVEL = 0;
380 | GCC_PREPROCESSOR_DEFINITIONS = (
381 | "DEBUG=1",
382 | "$(inherited)",
383 | );
384 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
385 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
386 | GCC_WARN_UNDECLARED_SELECTOR = YES;
387 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
388 | GCC_WARN_UNUSED_FUNCTION = YES;
389 | GCC_WARN_UNUSED_VARIABLE = YES;
390 | IPHONEOS_DEPLOYMENT_TARGET = 9.3;
391 | MTL_ENABLE_DEBUG_INFO = YES;
392 | ONLY_ACTIVE_ARCH = YES;
393 | SDKROOT = iphoneos;
394 | TARGETED_DEVICE_FAMILY = "1,2";
395 | };
396 | name = Debug;
397 | };
398 | 163329861D5021A10027BE35 /* Release */ = {
399 | isa = XCBuildConfiguration;
400 | buildSettings = {
401 | ALWAYS_SEARCH_USER_PATHS = NO;
402 | CLANG_ANALYZER_NONNULL = YES;
403 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
404 | CLANG_CXX_LIBRARY = "libc++";
405 | CLANG_ENABLE_MODULES = YES;
406 | CLANG_ENABLE_OBJC_ARC = YES;
407 | CLANG_WARN_BOOL_CONVERSION = YES;
408 | CLANG_WARN_CONSTANT_CONVERSION = YES;
409 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
410 | CLANG_WARN_EMPTY_BODY = YES;
411 | CLANG_WARN_ENUM_CONVERSION = YES;
412 | CLANG_WARN_INT_CONVERSION = YES;
413 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
414 | CLANG_WARN_UNREACHABLE_CODE = YES;
415 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
416 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
417 | COPY_PHASE_STRIP = NO;
418 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
419 | ENABLE_NS_ASSERTIONS = NO;
420 | ENABLE_STRICT_OBJC_MSGSEND = YES;
421 | GCC_C_LANGUAGE_STANDARD = gnu99;
422 | GCC_NO_COMMON_BLOCKS = YES;
423 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
424 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
425 | GCC_WARN_UNDECLARED_SELECTOR = YES;
426 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
427 | GCC_WARN_UNUSED_FUNCTION = YES;
428 | GCC_WARN_UNUSED_VARIABLE = YES;
429 | IPHONEOS_DEPLOYMENT_TARGET = 9.3;
430 | MTL_ENABLE_DEBUG_INFO = NO;
431 | SDKROOT = iphoneos;
432 | TARGETED_DEVICE_FAMILY = "1,2";
433 | VALIDATE_PRODUCT = YES;
434 | };
435 | name = Release;
436 | };
437 | 163329881D5021A10027BE35 /* Debug */ = {
438 | isa = XCBuildConfiguration;
439 | buildSettings = {
440 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
441 | INFOPLIST_FILE = ADModel/Info.plist;
442 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
443 | PRODUCT_BUNDLE_IDENTIFIER = com.aodong.ADModel;
444 | PRODUCT_NAME = "$(TARGET_NAME)";
445 | };
446 | name = Debug;
447 | };
448 | 163329891D5021A10027BE35 /* Release */ = {
449 | isa = XCBuildConfiguration;
450 | buildSettings = {
451 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
452 | INFOPLIST_FILE = ADModel/Info.plist;
453 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
454 | PRODUCT_BUNDLE_IDENTIFIER = com.aodong.ADModel;
455 | PRODUCT_NAME = "$(TARGET_NAME)";
456 | };
457 | name = Release;
458 | };
459 | 1633298B1D5021A10027BE35 /* Debug */ = {
460 | isa = XCBuildConfiguration;
461 | buildSettings = {
462 | BUNDLE_LOADER = "$(TEST_HOST)";
463 | INFOPLIST_FILE = ADModelTests/Info.plist;
464 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
465 | PRODUCT_BUNDLE_IDENTIFIER = com.aodong.ADModelTests;
466 | PRODUCT_NAME = "$(TARGET_NAME)";
467 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ADModel.app/ADModel";
468 | };
469 | name = Debug;
470 | };
471 | 1633298C1D5021A10027BE35 /* Release */ = {
472 | isa = XCBuildConfiguration;
473 | buildSettings = {
474 | BUNDLE_LOADER = "$(TEST_HOST)";
475 | INFOPLIST_FILE = ADModelTests/Info.plist;
476 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
477 | PRODUCT_BUNDLE_IDENTIFIER = com.aodong.ADModelTests;
478 | PRODUCT_NAME = "$(TARGET_NAME)";
479 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ADModel.app/ADModel";
480 | };
481 | name = Release;
482 | };
483 | 1633298E1D5021A10027BE35 /* Debug */ = {
484 | isa = XCBuildConfiguration;
485 | buildSettings = {
486 | INFOPLIST_FILE = ADModelUITests/Info.plist;
487 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
488 | PRODUCT_BUNDLE_IDENTIFIER = com.aodong.ADModelUITests;
489 | PRODUCT_NAME = "$(TARGET_NAME)";
490 | TEST_TARGET_NAME = ADModel;
491 | };
492 | name = Debug;
493 | };
494 | 1633298F1D5021A10027BE35 /* Release */ = {
495 | isa = XCBuildConfiguration;
496 | buildSettings = {
497 | INFOPLIST_FILE = ADModelUITests/Info.plist;
498 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
499 | PRODUCT_BUNDLE_IDENTIFIER = com.aodong.ADModelUITests;
500 | PRODUCT_NAME = "$(TARGET_NAME)";
501 | TEST_TARGET_NAME = ADModel;
502 | };
503 | name = Release;
504 | };
505 | /* End XCBuildConfiguration section */
506 |
507 | /* Begin XCConfigurationList section */
508 | 163329551D5021A10027BE35 /* Build configuration list for PBXProject "ADModel" */ = {
509 | isa = XCConfigurationList;
510 | buildConfigurations = (
511 | 163329851D5021A10027BE35 /* Debug */,
512 | 163329861D5021A10027BE35 /* Release */,
513 | );
514 | defaultConfigurationIsVisible = 0;
515 | defaultConfigurationName = Release;
516 | };
517 | 163329871D5021A10027BE35 /* Build configuration list for PBXNativeTarget "ADModel" */ = {
518 | isa = XCConfigurationList;
519 | buildConfigurations = (
520 | 163329881D5021A10027BE35 /* Debug */,
521 | 163329891D5021A10027BE35 /* Release */,
522 | );
523 | defaultConfigurationIsVisible = 0;
524 | };
525 | 1633298A1D5021A10027BE35 /* Build configuration list for PBXNativeTarget "ADModelTests" */ = {
526 | isa = XCConfigurationList;
527 | buildConfigurations = (
528 | 1633298B1D5021A10027BE35 /* Debug */,
529 | 1633298C1D5021A10027BE35 /* Release */,
530 | );
531 | defaultConfigurationIsVisible = 0;
532 | };
533 | 1633298D1D5021A10027BE35 /* Build configuration list for PBXNativeTarget "ADModelUITests" */ = {
534 | isa = XCConfigurationList;
535 | buildConfigurations = (
536 | 1633298E1D5021A10027BE35 /* Debug */,
537 | 1633298F1D5021A10027BE35 /* Release */,
538 | );
539 | defaultConfigurationIsVisible = 0;
540 | };
541 | /* End XCConfigurationList section */
542 | };
543 | rootObject = 163329521D5021A10027BE35 /* Project object */;
544 | }
545 |
--------------------------------------------------------------------------------
/ADModel/ViewController.m:
--------------------------------------------------------------------------------
1 | /**
2 | 我的英文能力不够,所以我把作者提供的英文注释解析通过字典百度自己的理解等方法翻译后写下来,再对照自己的翻译与各方查询的资料来分析。若觉得不错,可否给颗star?你的支持将是我的动力。
3 |
4 | 最近工作感觉压抑好大,若觉得不错,可否支持一下。。真的。。
5 | */
6 |
7 | /**
8 | Json转Model:
9 |
10 | //mapped to 映射//列子 instancetype 通常也指实例
11 |
12 | 只有解析,注释请下载后观看(麻烦下载前顺手star一下,你的支持将是我的动力!),建议最好对比源码观看。
13 |
14 | 由于内容过多,先只说下通过JSON创建并返回一个实例的解析
15 |
16 | +(instancetype)ad_modelWithJSON:(id)json:
17 | 在其内部其实通过类方法[self _ad_dictionaryWithJSON:json]创建了一个字典,而后通过类方法[self ad_modelWithDictionary:dic]创建并返回所需的实例
18 |
19 | +(NSDictionary *)_ad_dictionaryWithJSON:(id)json
20 | 类方法,自定义的实现方法并没有相应的方法声明,接收到Json文件后先判断Json文件是否为空,判断有两种方式 if (!json || json == (id)kCFNull)
21 |
22 | kCFNull: NSNull的单例,也就是空的意思那为什么不用Null、Nil或nil呢?
23 |
24 | 以下为nil,Nil,Null,NSNull的区别
25 |
26 | Nil:对类进行赋空值
27 |
28 | ni:对对象进行赋空值
29 |
30 | Null:对C指针进行赋空操作,如字符串数组的首地址 char *name = NULL
31 |
32 | NSNull:对组合值,如NSArray,Json而言,其内部有值,但值为空
33 |
34 | 所以判断条件json不存在或json存在,但是其内部值为空,就直接返回nil。若json存在且其内部有值,则创建一个空字典(dic)与空NSData(jsonData)值而后再判断,若json是NSDictionary类,就直接赋值给字典。
35 |
36 | 若是NSString类,就将其强制转化为NSString,而后用UTF-8编码处理赋值给jsonData。
37 |
38 | 若是NSData,就直接赋值给jsonData而后判断,而jsonData存在就代表json值转化为二进制NSData。
39 |
40 | 用官方提供的JSON解析就可获取到所需的值赋值为dic。若发现解析后取到得值不是NSDictionary,就代表值不能为dict,因为不是同一类型值,就让dict为nil最后返回dict。
41 |
42 | 在这个方法里相当于若JSON文件为NSDictionary类型或可解析成dict的NSData、NSString类型就赋值给dict返回,若不能则返回的dict为nil
43 |
44 | +(instancetype)ad_modelWithDictionary:(NSDictionary *)dictionary
45 | 类方法,方法内部会先判断若字典不存在,或字典存在但值为空的情况下直接返回nil而后在判断若传过来的字典其实不是NSDictionary类型,则也返回nil。所以,到了下一步时就代表字典存在且值不为空,而且传过来的字典就是NSDictionary类型,则创建一个类Cls为当前调用方法的类,而后通过自定义方法[_ADModelMeta metaWithClass:Cls]将自身类传过去。
46 |
47 | _ADModelMeta为延展类实现方法里声明的类,metaWithClass:Cls方法为
48 |
49 | +(instancetype)metaWithClass:(Class)cls
50 | 类方法,方法返回这个class元素,model缓存在方法内部,会先判断若cls不存在则返回nil而若存在,则创建三个静态属性:CFMutableDictionaryRef cache(static,静态修饰符,被static修饰的属性在程序结束前不会被销毁。CFNSMutableDictionaryRef,可变字符串底层,效率比其高).
51 |
52 | dispatch_semaphore_t lock(同步信号量,一次执行里面让其值为1则wait时就会通过继续执行),dispatch_once_t oncetoken(一次执行,配合dispatch_once使用,整个程序只会执行一次)一次执行中创建CFMutableDictionaryRef与dispatch_semaphore_create(1)
53 |
54 | 其中CFMutableDictionary的创建中四个参数意义为: cache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
55 |
56 | @功能 CFDictionaryCreateMutable创建一个新的词典。
57 |
58 | @参数 CFAllocator 分配器应该用于分配CFAllocator字典的内存及其值的存储。这 参数可能为空,在这种情况下,当前的默认值CFAllocator使用。如果这个引用不是一个有效的cfallocator,其行为是未定义的。这里是内存及其值的存储为默认。
59 |
60 | @参数 capacity 暗示值得个数,通过0实现可能忽略这个提示,或者可以使用它来优化各种。这里是忽略。
61 |
62 | @参数 keyCallBacks 指向CFDictionaryKeyCallBacks结构为这本字典使用回调函数初始化在字典中的每一个键,初始化规则太多而且看的有点迷糊就不多说了,毕竟不敢乱说。意思应该是对键值对中的键进行初始化,并有相应的优化方式。
63 |
64 | @参数 valueCallBacks 指向CFDictionaryValueCallBacks结构为这本词典使用回调函数初始化字典中的每一个值。对键值对中的值进行初始化,并有相应地优化方式。
65 | 一次执行之后通过wait判断可否继续执行,由于Create值为1所以可通行一次,此时在CF字典cache中获得值不可变的函数指针cls对应的值_ADModelMeta *meta(const 值不可被更改,void * 函数指针,通过__bridge桥接转换)而后通过signal操作让lock锁的信号加1,再去执行判断语句。
66 |
67 | 判断内容为若meta不存在,或meta存在但其属性classInfo(ADClassInfo *类型)的needUpdate为YES代表meta需要更新或存储,则通过_ADModelMeta对象方法(instancetype)initWithClass:(Class)cls 获取一个实例。
68 |
69 | -(instancetype)initWithClass:(Class)cls
70 | 方法内,会先通过[ADClassInfo classInfoWithClass:(Class)cls]获取到cls的信息而在[ADClassInfo classInfoWithClass:(Class)cls]方法内部,又会先去判断Cls是否存在,若不存在返回nil若存在,则创建两个CFMutableDictionaryRef(metaCache与classCache)与一个一次执行,一个同步信号量。
71 |
72 | 而后通过一次执行初始化两个CFMutableDictionaryRef与同步信号量的初始化创建完毕后会先判断同步信号量,由于初始化有值则减一后继续执行此时创建一个ADClassInfo *info用于保存通过CFDictionaryGetValue获取到的Class信息。
73 |
74 | 其中class_isMetaClass判断指定类是否是一个元类
75 |
76 | 元类是类对象的类,如[NSArray array]中的NSArray也是一个对象,NSArray通过元类生成,而元类又是一个对象,元类的类是根源类NSObject,NSObject父类为Nil
77 | 如果是元类则存储在metaCache中,所以也是从其中取值,如果不是就存储在classCache中也就是从classCache中取值,通过一个不可变的函数指针cls取值而后同步信号量加1 若取得到信息info则直接返回,若取不到就借助[[ADClassInfo alloc] initWithClass:cls];去获取Info。
78 |
79 | [[ADClassInfo alloc] initWithClass:cls] 私有方法,未在.h文件中声明在此方法中会先判断传过来的cls是否存在,若不存在返回nil若存在,就先通过父类初始化一次并保存传过来的_cls = Cls,与Cls的父类(_superCls = class_getSuperclass(cls)获取父类)以及Cls是否为元类的Bool值( _isMeta = class_isMetaClass(cls)),若不是元类则获得其元类并保存(_metaCls = objc_getMetaClass(class_getName(cls))),以及类的名字(_name = NSStringFromClass(cls))而后调用一次_update方法。
80 |
81 | 在update方法里会先对ivarInfos(ivars)methodInfos(methods) propertyInfos(properties)三个属性初始化,ivars属性,methods方法,properties属性操作并临时保存self.cls(之前刚赋值)。
82 |
83 | 声明一个 保存 方法描述的数组指针 长度值 methodCount。 Method *methods = class_copyMethodList(cls, &methodCount);返回关于对象方法描述的一个数组指针,数组长度保存在methodCount地址里。
84 |
85 | 如果指针存在,就创建一个NSMutableArray字典存储方法,并保存此字典_methodInfos = methodInfos; 而后再通过自定义方法[[ADClassMethodInfo alloc] initWithMethod:methods[i]];通过传过来的方法返回一个实例-(instancetype)initWithMethod:(Method)method通过传过来的方法返回一个实例。
86 |
87 | 在此方法里会先判断方法是否存在,若不存在返回nil,若存在则保存方法method,与方法操作SEL(method_getName(method)),方法实现IMP(method_getImplementation(method)),方法操作的名字( const char *name =sel_getName(_sel),返回值是一个值不可变的字符指针name,字符指针通常指向一个字符数组的首地址,字符数组类似于字符串) 。
88 |
89 | 若取得到名字则将方法名用UTF-8编码后保存。方法参数和返回值类型(method_getTypeEncoding(method))若取得到通过UTF-8编码后保存。方法的返回值类型(method_copyReturnType(method))若取得到则通过UTF-8编码后保存。
90 |
91 | 方法的参数个数(method_getNumberOfArguments(method))若个数大于0,则创建一个可变数组,而后去遍历获取参数类型(method_copyArgumentType(method, i)获取方法的指定位置参数的类型字符串),若获取到的参数类型存在就通过UTF-8编码后获取,不存在就为nil,而后保存到可变数组里,若有值则存储,没有值用@“”代替,存储后若获取到的参数类型存在就free释放其内存,全部获取后保存此数组。
92 |
93 | 最后将保存好信息的self返回此时回到_update方法里,已经通过对象获得到一个方法的描述信息,判断方法名(name,方法操作的名字)是否有值,如有值则以此为key,对象为值存储在可变字典methodInfos里,而methodInfos之前已经被保存。没有自然就不存了。
94 |
95 | 全部存储完毕后释放methods内存(之前获取的关于对象方法描述的一个数组指针)。获取到数组后,去获取cls的属性。同理,先通过class_copyPropertyList获取到属性个数存储在一个unsigned int(无符号整型)值中,再获取属性描述properties,如果有属性则属性描述有值。
96 |
97 | 此时创建并保存一个可变字典,而后遍历属性,并将遍历时的属性通过[[ADClassPropertyInfo alloc] initWithProperty:properties[i]];封装成一个ADClassPropertyInfo对象,以属性名为Key属性,对象为值存储在字典里存储完后释放属性描述内存。
98 |
99 | -(instancetype)initWithProperty:(objc_property_t)property
100 | 方法中,依旧是先判断后保存,保存了传过来的属性,属性的名称,属性的特性列表指针objc_property_attribute_t *(通常代表其是一个数组,一个结构体包含一个name属性的描述,一个value属性值),而后遍历此指针指向的数组开始一个一个取值保存在新建的objc_property_attribute_t中。
101 |
102 | 若取出来的name皆是以T开头后面跟随属性类型如字典,属性是强弱指针(如&为强指针,空为Value)原子性非原子性(空为atomic,N为nonatomic),变量名称,使用UTF-8编码后保存。
103 |
104 | 在使用自定义C语言函数结构体objc_property_attribute_t中的name与Value:
105 | 结构体中的name与Value:
106 |
107 | 属性类型 name值:T value:变化
108 |
109 | 编码类型 name值:C(copy) &(strong) W(weak) 空(assign) 等 value:无
110 |
111 | 非/原子性 name值:空(atomic) N(Nonatomic) value:无
112 |
113 | 变量名称 name值:V value:变化
114 | 属性描述为 T@"NSString",&,V_str 的 str
115 |
116 | 属性的描述:T 值:@"NSString"
117 |
118 | 属性的描述:& 值:
119 |
120 | 属性的描述:V 值:_str2
121 |
122 |
123 | ADEncodingGetType(返回值为ADEncodingType,接收参数为不可变的字符数组typeEncoding)转换value值。
124 |
125 | 在ADEncodingGetType中,先用可变字符数组type保存传过来的Value值,若值不存在则返回自定义枚举ADEncodingTypeUnknown,若有值则获取type长度,若长度为0仍旧返回Unknown,若长度不为0则声明一个枚举值qualifier,在设置一个值为true的Bool值用于死循环,在死循环内部从字符数组type的首地址开始一个一个取出内部的字符,若满足要求则让qualifier进行位运算(1 | 0 = 1, 1 | 1 = 0)而后指针+1指向下一个值,当值不满足任何条件跳出while循环。
126 |
127 | 再获取剩余字符数组长度,若长度为0则或运算Unknow,若不是则判断此时的内部为哪种类型与qualifier进行或运算后返回,若为@类型即为block或Object类型,长度为2以?结尾则是Block否则就是Object,如果类型不可知则返回qualifier与Unknow或运算值。
128 |
129 | 返回值之前的switch选择中,此时我们得到了value的类型与修饰符类型(如不可变的int16类型),再去判断若Value有值,其类型又是Object类型,则使用NSScanner(条件判断)获取字符串通过scanString:intoString:判断值是否包含NULL
130 |
131 | @\”中 \”转义符,转义成“ 所以值为@“,从@“开始遍历若找到NULL则返回YES
132 | 不包含结束此次循环。若包含则创建一个空字符串clsName,而后通过NSCharacterSet(一组Unicode字符,常用与NSScanner,NSString处理)转换字符串@“\“<”再通转换后的NSCharterSet扫描scanner中是否包含转换后的对象,若包含则将传入的字符串指向的遇到charterSet字符之前内容的指针保存到&clsName地址中。
133 |
134 | 此时的&clsName代表字符串头地址的地址,存储后若地址中有值,则将值通过UTF-8编码处理后,通过objc_getClass获取到其isa保存(objc_getClass来获取对象的isa,isa是一个指针指向对象自身)而后再声明一个空的可变数组,用于获取字符串中从“<”到“ >”中的内容,保存在新建的字符串protocol中,若有内容且之前的空数组没有值(不明白为何多这一步)则添加字符串给可变数组,至此,属性名为T的操作就结束了。
135 |
136 | 若name属性名为V代表是值名称(如字符串Str,V的值Str),直接使用UTF-8编码后保存R代表属性为只读属性,则让type或运算ADEncodingTypePropertyCopyC为Copy,&为Reatin(引用计数加一,形同强指针)
137 |
138 | N为nonatomic,D为Dynamic(@dynamic ,告诉编译器不自动生成属性的getter、setter方法)
139 |
140 | W为weak,G为getter方法,S为setter方法
141 |
142 | 获取到属性后释放属性的特性列表,保存已获取的type值,若属性名称存在而属性的getter、setter方法没有获取就通过属性名称获取,至此属性列表获取完毕。
143 |
144 | 接下来就是获取成员变量了(成员变量与属性的操作区别在于:成员变量{}中声明的, 属性@property声明的),这个比较简单。
145 |
146 | 先获取成员变量列表、个数,若列表有值则声明一个可变字典并保存,而后通过自定义方法[[ADClassIvarInfo alloc] initWithIvar:ivars[i]];获取成员变量。
147 |
148 | -(instancetype)initWithIvar:(Ivar)ivar
149 | 方法里,依旧先判断传过来的是否为空,而后保存传过来的ivar,获取成员变量名(ivar_getName(ivar)),获取得到通过UTF-8编码后保存,在获取成员变量首地址的偏移量(ivar_getOffset(ivar)),runtime会计算ivar的地址偏移来找ivar的最终地址,获取成员变量类型编码(ivar_getTypeEncoding(ivar)),若获取得到通过UTF-8编码后保存,而后再通过自定义C函数ADEncodingGetType获取type值(之前已经描述)。
150 |
151 | 至此,方法method,属性property,成员变量ivar全部获取。
152 |
153 | 获取后判断若不存在就赋空值,将_needUpdate赋值为NO回到-(instancetype)initWithClass:(Class)cls方法中,更新结束后通过自定义方法[self.class classInfoWithClass:_superCls]获取父类信息并保存,而后返回self此时回到classInfoWithClass,此时已经获得Cls的info信息。
154 |
155 | 若获得到info信息就执行一次同步信号灯wait操作,而后通过info.isMeta判断Cls是否为元类来存储Cls的info,元类存储在metaCache中,非元类存储在classCache中,存储后让线程同步信号灯加1返回info。
156 |
157 | 为什么加1?因为下次进入方法后要通过wait,然后才能通过静态可变CF字典取值。
158 | 此时回到initWithClass,获取到Cls的信息,如果信息不存在返回nil,存在就先通过父类初始化一次,而后判断并获取白名单,黑名单里的的属性。再定义一个可变字典genericMapper用于获取自定义Class中包含的集合属性。
159 |
160 | 先让Cls执行方法modelContainerPropertyGenericClass(若方法存在执行, 描述: 如果这个property是一个对象容器,列如NSArray/NSSet/NSDictionary 实现这个方法并返回一个属性->类mapper,告知哪一个对象将被添加到这个array /set /)获取返回回来的字典(如:@"shadows" : [ADShadow class],由用户书写)
161 |
162 | 若字典有值则再声明一个可变字典tmp,而后通过Block遍历字典genericMapper,若字典Key不是字符串类型返回,若是字典则获取Key对应值的类,获取不到则返回,获取到后判断是否是元类,如果是元类则tmp字典以genericMapper的key为自身key,以genericMapper的value为自身value,如果不是元类而是字符串类,则以此字符串创建一个类(NSClassFromString(obj))。
163 |
164 | 若创建成功则以以genericMapper的key为自身key,以创建的类为自身value,遍历完后保存genericMapper为tmp。而后创建所有的porperty元素,先取得classInfo,如果classInfo存在并且父类不为空(预先解析父类,但忽视根类(NSObject/NSProxy))。
165 |
166 | 而后遍历curClassInfo.propertyInfos.allValues(curClassInfo的字典属性propertyInfos的所有值)保存在ADClassPropertyInfo * propertyInfo中,若propertyInfo.name不存在 或黑名单存在且黑名单包含propertyInfo.name 或白名单存在且白名单不包含propertyInfo.name 则结束当前循环。
167 |
168 | 随后通过自定义方法[_ADModelPropertyMeta metaWithClassInfo:classInfo propertyInfo:propertyInfo generic:genericMapper[propertyInfo.name]]获取model对象中的property信息。
169 |
170 | + (instancetype)metaWithClassInfo:(ADClassInfo *)classInfo propertyInfo:(ADClassPropertyInfo *)propertyInfo generic:(Class)generic
171 | 方法下次在描述,感觉本文已经写得实在有点多,总之就是通过此获取到model对象中的property信息,包括getter、setter方法,可否使用KVC,可否归档解档,以及Key、keyPath、keyArray映射等。
172 |
173 | 回到initWithClass方法,若获取到model对象中的property信息meta不存在,或meta->_name(成员变量不支持点语法)不存在,或meta->_getter、meta->_setter不存在或已保存在字典allPropertyMetas中就结束当前循环,若都不满足则保存在字典allPropertyMetas中。遍历结束后保存curClassInfo为其父类,而后再while循环直至根类while结束后若allPropertyMetas有值则保存,而后创建一个可变字典,两个可变数组。通过modelCustomPropertyMapper方法实现定制元素
174 |
175 | + (nullable NSDictionary*)modelCustomPropertyMapper;
176 | 定制属性元素,描述 如果JSON/Dictionary的key并不能匹配model的property name,实现这个方法并返回额外的元素。
177 |
178 | 先去判断能否响应这个方法,如果能,就创建一个字典,声明是NSDictionary,但是实现时是自定义属性映射字典,相当于响应了这个方法返回值赋值给字典customMapper,而后Block遍历次字典,通过之前存储的allPropertyMetas字典取出对应Key的值赋值给propertyMeta,若取不出则直接return,若取得出则allPropertyMetas删除Key与Key对应的值
179 |
180 | 而后判断customMapper中的Value是否属于NSString类型,若属于但长度为0则return,否则保存在propertyMeta中,随后以"."分割字符串为一个数组keyPath ,而后遍历数组,若遍历时的字符串长度为0,则创建一个临时数组保存keyPath移除@“”对象而后再赋值给KeyPath。遍历结束后,若此时KeyPath的Count大于1,则保存在propertyMeta中,并将propertyMeta保存在之前新建的可变数组keyPathPropertyMetas中,随后判断mapper[mappedToKey](mapper,之前创建的用于映射的可变字典)是否有值,没有则赋值空,有就赋值给propertyMeta的_next,而后保存propertyMeta。
181 |
182 | 若值是NSArray类型,则将其强转成NSArray类型而后字符串oneKey遍历,遍历前创建一个可变数组mappedToKeyArray,如果遍历时oneKey不是字符串或长度为0则结束此次遍历,如果都不符合则通过“。”分割成一个数组,若数组的数量大于1则保存数组否则保存oneKey,而后判断若propertyMeta->_mappedToKey不存在则保存其为oneKey,如果数组数量大于1则保存_mappedToKeyPath为数组,若保存失败则return,随后保存mappedToKeyArray为_mappedToKeyArray等。
183 |
184 | 处理完定制属性之后再去遍历保存的所有Property元素的可变字典allPropertyMetas,保存字典的Value值的成员变量并保存Value值 为mapper[name],若保存成功则保存mapper,而后保存classInfo,所有property元素的个数,通过自定义强制内联方法ADClassGetNSType保存Cls的类型,而后保存几个用户自定义方法的返回值,代码中有详细文档翻译。而后返回self,就此initWithClass方法结束。
185 |
186 | 此时返回到metaWithClass中,我们已经获取了需要的meta,若获取成功则线程信号量wait一次操作,并设置其值到缓存中,随后信号量加1返回Meta.
187 |
188 | 此时返回到ad_modelWithDictionary方法中,我们已经获取了modelMeta,若其_hasCustomClassFromDictionary值为YES就让Cls执行一次+ (nullable Class)modelCustomClassForDictionary:(NSDictionary *)dictionary;(通过字典创建的class,nil指使用当前class,用户自定义的方法),而后通过Cls创键一个对象one,让One执行方法-(BOOL)ad_modelSetWithDictionary:(NSDictionary *)dic ; 若执行成功返回one否则为nil
189 |
190 | -(BOOL)ad_modelSetWithDictionary:(NSDictionary *)dic ;
191 | 这个方法也是下次在解说吧
192 |
193 | 至此Json转Model分析完毕。撒花~~~~~~~~
194 |
195 | */
196 |
197 |
198 |
199 | /**
200 |
201 | Json转model后半部分:
202 |
203 | */
204 |
205 |
206 | /*
207 | //meta 元素
208 | + (instancetype)metaWithClassInfo:(ADClassInfo *)classInfo propertyInfo:(ADClassPropertyInfo *)propertyInfo generic:(Class)generic
209 | support pseudo generic class with protocol name
210 | 支持假的generic class通过协议名
211 | generic class自己写的类
212 | 如果generic不存在(genericMapper[propertyInfo.name])且PropertyInfo.protocols存在
213 | 遍历propertyInfo信息的所有protocols,将其通过UTF-8编码后转换为class类型
214 | 若转换成功则generic值为此类,结束遍历
215 | 创建一个_ADModelPropertyMeta 对象meta,用于保存propertyInfo里的name、type、propertyInfo自身与generic
216 | 如果meta ->_type是object类型,就通过自定义方法ADClassGetNSType根据propertyInfo.cls设置meta->_nsType值,否则就通过ADEncodingTypeIsCNumber根据meta->_type设置meta->_isCNumber值
217 | 如果meta->_type是一个ADEncodingTypeStruct类型值,声明一个NSSet对象,一个一次执行。
218 | 一次执行内部创建一个NSMutableSet *set集合添加了32位与64位下的size、point、Rect、AffineTransform、EdgeInsets、Offset值类型并设置给types
219 | 而后判断若types包含propertyInfo.typeEncoding则设置meta->_isStructAvailableForKeyedArchiver = YES
220 | 保存propertyInfo.cls为meta->_cls
221 | 如果generic有值或meta->_cls有值并且meta->_nsType是NSUnknown类型,就保存meta->_hasCustomClassFromDictionary值为响应是否成功。
222 | 其余部分千篇一律,没啥好说的。其中instancesRespondToSelector: 是判断类的实例对象能否响应方法,如果能就保存相应的操作。如果meta->_getter与_setter都存在就判断meta->_type是否为可KVC操作的类型。 最后返回meta。
223 | —(BOOL)ad_modelSetWithDictionary:(NSDictionary *)dic
224 | 通过一个key-value字典设置调用者的属性
225 | 参数:dic 一个Key-Value字典映射到调用者property,字典中任何一对无效的Key-Value都将被忽视
226 | 描述 dictionary中的Key将被映射到调用者的property name 而这个value将设置给property.
227 | 如果value类型与property类型不匹配,这个方法将试图转换这个value基于以下这些值:
228 | 返回 转换是否成功
229 | Json转Model最后一步所调用的方法中的最后一步,现在我们就来解析一下。
230 | 内部会先判断若字典不存在,或字典内部值为空则返回NO
231 | 如果字典存在且内部有值但不是字典类型也返回NO
232 | 于是就确定字典存在且内部有值属于NSDictionary类型,则先去获取model类的元素缓存通过 [_ADModelMeta metaWithClass:object_getClass(self)];临时存储在modelMeta中
233 | 如果modelMeta中的keyMappedCount为0代表缓存的元素数为0则返回NO
234 | 如果自身有响应方法_hasCustomWillTransformFromDictionary(在model转换前被命名,返回修改的字典),就让自身的代理去响应此方法,将字典传过去并将返回值覆盖自身,如果覆盖后字典不是NSDictionary类型就返回NO
235 | 创建一个结构体ModelSetContext,初始化是0,内部存储有三个函数指针,分别代表modelMeta(模型元素),model(自身),dictionary(字典dic)
236 | 如果modelMeta中存储的_keyMappedCount大于等于dic中值的个数
237 | 就执行 CFDictionaryApplyFunction((CFDictionaryRef)dic, ModelSetWithDictionaryFunction, &context);
238 | 这里需要讲下这个CFDictionaryApplyFunction的作用及其三个参数
239 | CFDictionaryApplyFunction 对所有键值执行同一个方法
240 | @function CFDictionaryApplyFunction调用一次函数字典中的每个值。
241 | @param 字典。如果这个参数不是一个有效的CFDictionary,行为是未定义的。
242 | @param 调用字典中每一个值执行一次这个方法。如果这个参数不是一个指针指向一个函数的正确的原型,行为是未定义的。
243 | @param 一个用户自定义的上下文指针大小的值,通过第三个参数作用于这个函数,另有没使用此函数的。如果上下文不是预期的应用功能,则这个行为未定义。
244 | 第三个参数的意思,感觉像是让字典所有的键值去执行完方法后,保存在这个上下文指针(如自定义结构体)的指针(指向一个地址,所以自定义的结构体要用&取地址符)所指向的地址,也就是自定义的结构体中。
245 | 也就是dic里面的键值对全部执行完参数2的方法后保存在参数3中。
246 | 其中参数2是自定义方法ModelSetWithDictionaryFunction,关于此方法
247 | static void ModelSetWithDictionaryFunction(const void *_key, const void *_value, void * _context)
248 | 对于字典的函数应用,设置key-value配对给model
249 | 参数 _key 不应该是nil,NSString
250 | 参数 _value 不应该是nil
251 | 参数 _context _context.modelMeta 和 _context.model 不应该是nil
252 | 表示是一个静态的无返回值的C函数,接收参数为不可变的函数指针key,不可变的函数指针value,不可变的函数指针_context
253 | 方法内部会先声明一个ModelSetContext结构体指针context保存传过来的_context
254 | 方法中用到了很多__unsafe__unretained修饰符。
255 | __unsafe_unretained 指针所指向的地址即使已经被释放没有值了,依旧会指向,如同野指针一样,weak/strong这些则会被置为nil。一般应用于iOS 4与OS X Snow Leopard(雪豹)中,因为iOS 5以上才能使用weak。
256 | __unsafe_unretained与weak一样,不能持有对象,也就是对象的引用计数不会加1
257 | unsafe_unretained修饰符以外的 strong/ weak/ autorealease修饰符保证其指定的变量初始化为nil。同样的,附有 strong/ weak/ _autorealease修饰符变量的数组也可以保证其初始化为nil。
258 | autorealease(延迟释放,给对象添加延迟释放的标记,出了作用域之后,会被自动添加到"最近创建的"自动释放池中)
259 | 为什么使用unsafe_unretained?
260 | 作者回答:在 ARC 条件下,默认声明的对象是 strong 类型的,赋值时有可能会产生 retain/release 调用,如果一个变量在其生命周期内不会被释放,则使用 unsafe_unretained 会节省很大的开销。
261 | 网友提问: 楼主的偏好是说用unsafe_unretained来代替weak的使用,使用后自行解决野指针的问题吗?
262 | 作者回答:关于 unsafe_unretained 这个属性,我只提到需要在性能优化时才需要尝试使用,平时开发自然是不推荐用的。
263 | 以下就简称un。
264 | 将context中的modelMeta通过桥接转化为ADModelMeta对象并用un修饰的_ADModelMeta对象meta保存。
265 | 将传过来的函数指针Key通过桥接转换为id对象并以此为Key,通过meta—>__mapper 取出key对应的值保存在un修饰的_ADModelPropertyMeta对象中。
266 | 将context中的model通过桥接转换为id对象并保存在一个un修饰的id对象model中
267 | 然后以propertyMeta为while判断条件,若其存在则为真,如果其存在并且存储有_setter方法,则通过自定义静态函数方法ModelSetValueForProperty取出model中其对应的value值。
268 | static void ModelSetValueForProperty方法,接收三个参数,__unsafe_unretained id model,
269 | __unsafe_unretained id value, __unsafe_unretained _ADModelPropertyMeta *meta
270 | 如果传过来的property是C类型,就通过自定义方法ADNSNumberCreateFromID分析出一个数值。
271 | ADNSNumberCreateFromID一个静态的force_inline(代码中有解释,内容过多不赘述)修饰的返回值为NSNumber指针(意义通常为NSNumber对象,再加一个指针就是NSNumber对象数组),参数为__unsafe_unretained id value
272 | 方法内部声明了三个属性,一个NSCharacterSet(Unicode字符,常用于NSString、NSScanner处理),一个字典,一个一次执行
273 | 在一次执行内部,NSCharacterSet通过Range初始化,dic初始化中将正确、不正确、空的常用类型都进行了封装。
274 | 而后判断若传过来的value不存在,或value为空返回nil,若value为NSNumber类型则直接返回value,若是NSString类型则先创建一个NSNumber *num初始化为dic[value],先判断取出的值是否为NO,若是则再判断取出的值是否为空,如果是则返回nil,如果不是则直接返回num,num应为@(YES)即为1。
275 | 而若num值为NO,则将value强转为NSString类型,然后通过刚才定义的dot在其内部查询字符 ‘ . ’ 若查询得到则创建一个值不可变的字符数组接收强转后并UTF-8编码后的value值。
276 | 如果强转后没有值则返回nil,如果有值则atof处理(将字符数组转化为数字,而字符串需要UTF-8编码后才能转为字符数组。其实字符串可以直接doubleValue转换,但大神用这种方法应该是效率快吧)
277 | 若处理后的结果num,使用isnan(值是无穷大或无穷小的不确定值)或isinf(值是无限循环)判断,若有一个成立返回nil,否则将num包装成一个对象返回。
278 | 如果不包含,则直接将value通过UTF-8编码成一个字符数组,若编码后没值则返回nil 有值就通过atoll转换后返回( long long atoll(const char *nptr); 把字符串转换成长长整型数(64位))
279 | 如果value不是NSNumber也不是NSString类型就直接返回nil
280 | 回到函数ModelSetValueForProperty中,我们已经获取了num值,而后调用自定义静态内联无返回值的函数ModelSetNumberToProperty
281 | 函数ModelSetNumberToProperty (__unsafe_unretained id model, __unsafe_unretained NSNumber *num, __unsafe_unretained _ADModelPropertyMeta *meta)
282 | 设置数字给property
283 | 描述 调用者(caller 来访者)应对这个参数保持强引用在这个函数返回之前
284 | 参数 model 不应该是nil
285 | 参数 num 可以是nil
286 | 参数 meta 不应该是nil,meta.isCNumber应该是YES,meta.setter不应该是Nil
287 | 方法内部使用了大量的objc_msgSend,这里简单说下
288 | objc_msgSend OC消息传递机制中选择子发送的一种方式,代表是当前对象发送且没有结构体返回值
289 | 选择子简单说就是@selector(),OC会提供一张选择子表供其查询,查询得到就去调用,查询不到就添加而后查询对应的实现函数。通过_class_lookupMethodAndLoadCache3(仅提供给派发器用于方法查找的函数),其内部会调用lookUpImpOrForward方法查找,查找之后还会有初始化枷锁缓存之类的操作,详情请自行搜索,就不赘述了。
290 | 这里的意思是,通过objc_msgSend给强转成id类型的model对象发送一个选择子meta,选择子调用的方法所需参数为一个bool类型的值num.boolValue
291 | 再通俗点就是让对象model去执行方法meta->_setter,方法所需参数是num.bollValue
292 | 再通俗点:((void (*)(id, SEL, bool))(void *) objc_msgSend) 一位一个无返回值的函数指针,指向id的SEL方法,SEL方法所需参数是bool类型,使用objc_msgSend完成这个id调用SEL方法传递参数bool类型,(void *)objc_msgSend为什么objc_msgSend前加一个(void *)呢?我查了众多资料,众多。最后终于皇天不负有心人有了个结果,是为了避免某些错误,比如model对象的内存被意外侵占了、model对象的isa是一个野指针之类的。要是有大牛能说明白,麻烦再说下。而((id)model, meta->_setter, num.boolValue)则一一对应前面的id,SEL,bool
293 | 再通俗点。。你找别家吧。。
294 | switch遍历meta->_type,如果是ADEncodingTypeBool类型,就让model对象执行方法meta—>_setter,将num.boolValue作为参数发送过去。
295 | 如果是Int8类型就让对象执行方法meta—>_setter,将num.charValue强转为(int8_t)类型发过去
296 | 如果是UInt8(无符号的Int8类型)同上,其余同上一直到Int64类型,为什么?因为int64代表此时的num值特别长,长到你需要判断一下传过来的num值是不是NSDecimalNumber类型(NSDecimalNumber数字精确,其值确定后不可修改,是NSNumber的子类)
297 | 如果是float、double、long double类型,在对num得值转换后需要判断其是否是无穷大或无穷小或无限循环的,如果是就令值为0再去执行同样的操作
298 | 如果num有值就创建一个[num class]用来保存这个num
299 | 如果meta不是C number类型,就判断是否为meta—>_nsType是否有值,如果有值就判断value是否为空值,空值就调用objc_msgSend,传参数为nil,否则就Switch判断meta—>_nsType值。
300 | 如果是NSString或NSMutableString类型就判断value是否为NSString类型,如果是就再判断meta—>_nsType是否为NSString类型,如果都是则调用objc_msgSend将value作为参数发过去,如果meta—>_nsType不是则将其value强转成NSString再.mutableCopy后作为参数发过去。
301 | 如果Value不是则依次判断是否为NSNumber、NSData、NSURL或NSAttributedString,如果满足则在判断meta—>_nsType是否为ADEncodingTypeNSString,如果是就value转换相应的值后作为参数传递,如果不是就多进行一步.mutableCopy操作,NSData是个例外。
302 | meta—>_nsType如果是ADEncodingTypeNSValue、ADEncodingTypeNSNumber、ADEncodingTypeNSDecimalNumber,则依旧是判断meta—>_nsType类型,难点已经描述,这些就是千篇一律了。如果meta—>_nsType没有值就声明一个Bool值isNull保存value是否为kCFNull的结果,而后switch遍历meta—>_type枚举值。
303 | 其中说一下strcmp(比较两个字符串)
304 | strcmp(const char string1, const char string2);
305 | 比较字符串string1和string2大小.
306 | 返回值< 0, 表示string1小于string2;
307 | 返回值为0, 表示string1等于string2;
308 | 返回值> 0, 表示string1大于string2.
309 | 方法结束返回到ModelSetWithDictionaryFunction,while循环中会不变的通过propertyMeta = propertyMeta—>_next改变propertyMeta的值直至为空跳出while循环则此时方法结束。
310 | 回到ad_modelSetWithDictionary中会发现余下的操作也是一样的。
311 | 于是此方法结束,至此son转Model结束。
312 | 怎么样,是不是还有点迷?还有点乱?好像自己还什么都不理解一样?
313 | 没事,自己穿针引线多来几回就行了。我平常学习一个动画效果有时候都要好几个来回,更何况YYModel这种大作、只是再一次来回时你会感受到,什么叫一马平川仿似行云流水。
314 |
315 | */
316 |
317 |
318 | /**
319 |
320 |
321 | 总结:
322 |
323 | */
324 |
325 |
326 | /**
327 |
328 | + (nullable instancetype)ad_modelWithJSON:(id)json;
329 | 通过json创建的新的对象,如果解析错误就返回为空,是NSObject的类方法,创建时以NSObject为类创建一个对象。
330 |
331 | + (nullable instancetype)ad_modelWithDictionary:(NSDictionary *)dictionary;
332 | 创建并返回一个新的对象通过参数的key-value字典,是NSObject类方法
333 | 字典中的key将映射到接收者的property name
334 | 而值将设置给这个Property,如果这个值类型与property不匹配
335 | 这个方法将试图转变这个值基于这些结果:
336 | `NSString` or `NSNumber` -> c number, such as BOOL, int, long, float, NSUInteger...
337 | `NSString` -> NSDate, parsed with format "yyyy-MM-dd'T'HH:mm:ssZ", "yyyy-MM-dd HH:mm:ss" or "yyyy-MM-dd".
338 | `NSString` -> NSURL.
339 | `NSValue` -> struct or union, such as CGRect, CGSize, ...
340 | `NSString` -> SEL, Class.
341 |
342 |
343 | - (BOOL)ad_modelSetWithJSON:(id)json;
344 | 通过一个json生成一个对象,对象是调用者的类生成,会根据类中的变量设置对应的值
345 | json中任何无效的数据都将被忽视
346 | 参数:json 一个关于NSDictionary,NSString,NSData的json对象将映射到调用者的property
347 | 返回:是否成功
348 |
349 | - (BOOL)ad_modelSetWithDictionary:(NSDictionary *)dic;
350 | 通过一个key-value字典设置调用者的属性,对象是调用者的类生成,会根据类中的变量设置对应的值
351 | 参数:dic 一个Key-Value字典映射到调用者property,字典中任何一对无效的Key-Value都将被忽视
352 | 描述 dictionary中的Key将被映射到调用者的property name 而这个value将设置给property.
353 | 如果value类型与property类型不匹配,这个方法将试图转换这个value基于以下这些值:
354 | 返回 转换是否成功
355 |
356 | - (nullable id)ad_modelToJSONObject;
357 | 产生一个json对象通过调用者的变量生成,是模型转Json
358 | 返回一个NSDictionary或NSArray的json对象,如果解析失败返回一个Nil
359 | 了解更多消息观看[NSJSONSerialization isValidJSONObject]
360 | 描述:任何无效的property都将被忽视
361 | 如果调用者是NSArray,NSDictionary或NSSet,他将转换里面的对象为json对象
362 |
363 | - (nullable NSData *)ad_modelToJSONData;
364 | 创建一个 json字符串二进制数据,通过调用者的变量,是模型转Json字符串二进制数据
365 | 返回一个json string's data,如果解析失败返回为空
366 | 描述:任何无效的property都将被忽视
367 | 如果调用者是一个NSArray,NSDictionary或NSSet,它也将转换内部对象为一个Json字符串
368 |
369 |
370 |
371 | - (nullable NSString *)ad_modelToJSONString;
372 | 创建一个json 字符串通过调用者的变量,是模型转Json字符串
373 | 返回一个json string,如果错误产生返回一个nil
374 | 描述 任何无效的property都将被忽视
375 | 如果调用者是NSArray,NSDictionary或NSSet,它也将转换内部对象为一个json string
376 |
377 |
378 |
379 | - (nullable id)ad_modelCopy;
380 | copy一个对象通过调用者的properties,是一个对象方法,简单说就是copy调用者的变量与变量值和get、set方法后返回copy后的对象
381 | 返回一个copy的对象,如果解析失败则返回为nil
382 |
383 |
384 |
385 | - (void)ad_modelEncodeWithCoder:(NSCoder *)aCoder;
386 | 将调用者的变量进行编码
387 | 参数 aCoder 一个对象档案
388 |
389 |
390 |
391 | - (id)ad_modelInitWithCoder:(NSCoder *)aDecoder;
392 | 通过一个decoder解码成对象的变量
393 | 参数 aDecoder 一个对象档案
394 | 返回 调用者自己
395 |
396 |
397 |
398 | - (NSUInteger)ad_modelHash;
399 | 通过调用者Property获取到一个哈希Code
400 | 返回 hashCode
401 |
402 |
403 | - (BOOL)ad_modelIsEqual:(id)model;
404 | 比较变量比较调用者和另一个对象是否相同
405 | 参数 model 另一个对象
406 | 返回 如果两个对象相同则返回YES 否则为NO
407 |
408 |
409 |
410 | - (NSString *)ad_modelDescription;
411 | 描述方法为基于属性的Debug目的(Debug模式中基于属性的描述方法)
412 | 返回一个字符串描述调用者的内容
413 |
414 |
415 |
416 | + (nullable NSArray *)ad_modelArrayWithClass:(Class)cls json:(id)json;
417 | 通过一个json-array创建并返回一个数组,数组里面存放都是对象,对象是cls通过json里面的字典与自身变量的映射生成的。
418 | 这个方法是安全的
419 | 参数:cls array中的对象类
420 | 参数:json 一个json array 关于"NSArray","NSString"或"NSData"
421 | 列子:[{"name","Mary"},{name:"Joe”}],cls里面的name属性值为Mary或Joe,生成的对象全部保存到一个数组里返回。
422 | 返回一个数组,如果解析错误则返回nil
423 |
424 |
425 |
426 | + (nullable NSDictionary *)ad_modelDictionaryWithClass:(Class)cls json:(id)json;
427 | 通过一个json文件创建并返回一个字典
428 | 这个方法是安全的
429 | 参数cls 字典中value的对象class
430 | 参数json 一个json的字典是"NSDictionary","NSStirng"或"NSData"的
431 | 列子: {"user1":{"name","Mary"}, "user2": {name:"Joe”}},以字符串user1或user2为key,value为一个cls对象,传进来的Class中的name为变量,name值为Mary或Joe
432 |
433 |
434 |
435 | + (nullable NSDictionary *)modelCustomPropertyMapper;
436 | 如果JSON/Dictionary的key并不能匹配model的property name,实现这个方法并返回额外的元素,会通过变量返回一个定制元素
437 | 实例:
438 | json:
439 | {
440 | "n":"Harry Pottery",
441 | "p": 256,
442 | "ext" : {
443 | "desc" : "A book written by J.K.Rowling."
444 | },
445 | "ID" : 100010
446 | }
447 |
448 | model:
449 | @interface ADBook : NSObject
450 | @property NSString *name;
451 | @property NSInteger page;
452 | @property NSString *desc;
453 | @property NSString *bookID;
454 | @end
455 |
456 | @implementation ADBook
457 | + (NSDictionary *)modelCustomPropertyMapper {
458 | return @{@"name" : @"n",
459 | @"page" : @"p",
460 | @"desc" : @"ext.desc",
461 | @"bookID": @[@"id", @"ID", @"book_id"]};
462 | }
463 | 其中name对应Json中的n,page对应数字p,desc对应ext的数组字典中desc的值,bookID对应三个值:id、ID、book_id
464 |
465 |
466 |
467 | + (nullable NSDictionary *)modelContainerPropertyGenericClass;
468 | 返回一个对象元素,如果这个变量是一个对象容器,列如NSArray/NSSet/NSDictionary
469 | 实现这个方法并返回一个属性->类元素,告知哪一个对象将被添加到这个array /set /dictionary
470 | Example:
471 | @class ADShadow, ADBorder, ADAttachment;
472 |
473 | @interface ADAttributes
474 | @property NSString *name;
475 | @property NSArray *shadows;
476 | @property NSSet *borders;
477 | @property NSDictionary *attachments;
478 | @end
479 |
480 | @implementation ADAttributes
481 | + (NSDictionary *)modelContainerPropertyGenericClass {
482 | return @{@"shadows" : [ADShadow class],
483 | @"borders" : ADBorder.class,
484 | @"attachments" : @"ADAttachment" };
485 | }
486 | @end
487 |
488 |
489 | + (nullable Class)modelCustomClassForDictionary:(NSDictionary *)dictionary;
490 | 如果你需要在json->object的改变时创建关于不同类的对象
491 | 使用这个方法基于dictionary data去改变custom class
492 | 重写时通过判断哪个key对应的有Value值,就返回一个自己需求的类。
493 | Example:
494 | @class ADCircle, ADRectangle, ADLine;
495 | @implementation ADShape
496 |
497 | + (Class)modelCustomClassForDictionary:(NSDictionary*)dictionary {
498 | if (dictionary[@"radius"] != nil) {
499 | return [ADCircle class];
500 | } else if (dictionary[@"width"] != nil) {
501 | return [ADRectangle class];
502 | } else if (dictionary[@"y2"] != nil) {
503 | return [ADLine class];
504 | } else {
505 | return [self class];
506 | }
507 | }
508 |
509 |
510 | + (nullable NSArray *)modelPropertyBlacklist;
511 | 在model变换时所有在黑名单里的property都将被忽视
512 | 返回 一个关于property name的数组
513 |
514 |
515 |
516 | + (nullable NSArray *)modelPropertyWhitelist;
517 | 如果一个property不在白名单,在model转变时它将被忽视
518 | 返回nil忽视这方面
519 |
520 |
521 |
522 |
523 | - (NSDictionary *)modelCustomWillTransformFromDictionary:(NSDictionary *)dic;
524 | 描述 如果model实现了这个方法,它将被调用在"+modelWithJson:","+modelWithDictionary:","-modelSetWithJSON:"and"-modelSetWithDictionary:"之前
525 | 如果方法返回为nil,转换过程中将忽视这个model
526 |
527 |
528 | - (BOOL)modelCustomTransformFromDictionary:(NSDictionary *)dic;
529 | 如果model实现了这个方法,它将被调用在"+modelWithJSON:","+modelWithDictionary","-modelSetWithJSON:" and "-modelSetWithDictionary:"结束。如果这个model是有效的,返回YES 或返回NO忽视这个model。
530 |
531 |
532 | - (BOOL)modelCustomTransformToDictionary:(NSMutableDictionary *)dic;
533 | 如果这个model实现了这个方法,它将被调用在"-modelToJSONObject"和"-modelToJSONStrign"结束
534 | 如果这个方法返回NO,这个转换过程将忽视这个json dictionary
535 |
536 |
537 | */
538 |
539 |
540 |
541 |
542 |
543 |
544 |
545 |
546 |
547 |
548 |
549 |
550 |
551 |
552 |
553 |
554 |
555 |
--------------------------------------------------------------------------------
/ADModel/ADModel/NSObject+ADModel.m:
--------------------------------------------------------------------------------
1 | //
2 | // NSObject+ADModel.m
3 | // ADModel
4 | //
5 | // Created by 王奥东 on 16/8/2.
6 | // Copyright © 2016年 王奥东. All rights reserved.
7 | //
8 |
9 | #import "NSObject+ADModel.h"
10 | #import "ADClassInfo.h"
11 | #import
12 |
13 | /**
14 | attribute((always_inline))强制内联,所有加了attribute((always_inline))的函数在被调用时不会被编译成函数调用而是直接扩展到调用函数体内
15 | 所以force_inline修饰的方法被执行时,不会跳到方法内部执行,而是将方法内部的代码放到调用者内容去执行
16 | 如force_inline修饰的方法a,在方法b中调用不会跳到a中而是将a中代码放到b中直接执行
17 | 需要注意的是对于#define force_inline __inline__ __attribute__((always_inline))而言
18 | force_inline 指的是 __inline__ __attribute__((always_inline))
19 | 而并不是单独的attribute((always_inline))
20 | */
21 |
22 | #define force_inline __inline__ __attribute__((always_inline))
23 |
24 | // Foundation class Type
25 | // 创建Class Type
26 | typedef NS_ENUM(NSUInteger, ADEncodingNSType) {
27 | ADEncodingTypeNSUnknown = 0,
28 | ADEncodingTypeNSString,
29 | ADEncodingTypeNSMutableString,
30 | ADEncodingTypeNSValue,
31 | ADEncodingTypeNSNumber,
32 | ADEncodingTypeNSDecimalNumber,
33 | ADEncodingTypeNSData,
34 | ADEncodingTypeNSMutableData,
35 | ADEncodingTypeNSDate,
36 | ADEncodingTypeNSURL,
37 | ADEncodingTypeNSArray,
38 | ADEncodingTypeNSMutableArray,
39 | ADEncodingTypeNSDictionary,
40 | ADEncodingTypeNSMutableDictionary,
41 | ADEncodingTypeNSSet,
42 | ADEncodingTypeNSMutableSet,
43 | };
44 |
45 | /// Get the Foundation class type from property info.
46 | /**
47 | 通过property信息获取创建者的class type
48 |
49 | isSubclassOfClass: 从自身开始,它沿着类的层次结构,在每个等级与目标类逐一进行比较。如果发现一个相匹配的对象,返回YES。如果它从类的层次结构自顶向下没有发现符合的对象,返回NO
50 |
51 | 方法意思为:静态的force_inline修饰的返回值为ADEncodingNSType的方法ADClassGetNSType其所需参数为(Class cls)
52 | */
53 | static force_inline ADEncodingNSType ADClassGetNSType(Class cls) {
54 | if (!cls) return ADEncodingTypeNSUnknown;
55 | if ([cls isSubclassOfClass:[NSMutableString class]]) return ADEncodingTypeNSMutableString;
56 | if ([cls isSubclassOfClass:[NSString class]]) return ADEncodingTypeNSString;
57 | if ([cls isSubclassOfClass:[NSDecimalNumber class]]) return ADEncodingTypeNSDecimalNumber;
58 | if ([cls isSubclassOfClass:[NSNumber class]]) return ADEncodingTypeNSNumber;
59 | if ([cls isSubclassOfClass:[NSValue class]]) return ADEncodingTypeNSValue;
60 | if ([cls isSubclassOfClass:[NSMutableData class]]) return ADEncodingTypeNSMutableData;
61 | if ([cls isSubclassOfClass:[NSData class]]) return ADEncodingTypeNSData;
62 | if ([cls isSubclassOfClass:[NSDate class]]) return ADEncodingTypeNSDate;
63 | if ([cls isSubclassOfClass:[NSURL class]]) return ADEncodingTypeNSURL;
64 | if ([cls isSubclassOfClass:[NSMutableArray class]]) return ADEncodingTypeNSMutableArray;
65 | if ([cls isSubclassOfClass:[NSArray class]]) return ADEncodingTypeNSArray;
66 | if ([cls isSubclassOfClass:[NSMutableDictionary class]]) return ADEncodingTypeNSMutableDictionary;
67 | if ([cls isSubclassOfClass:[NSDictionary class]]) return ADEncodingTypeNSDictionary;
68 | if ([cls isSubclassOfClass:[NSMutableSet class]]) return ADEncodingTypeNSMutableSet;
69 | if ([cls isSubclassOfClass:[NSSet class]]) return ADEncodingTypeNSSet;
70 |
71 | return ADEncodingTypeNSUnknown;
72 | }
73 |
74 | /// Whether the type is c number.
75 | //这个类型是否是C类型
76 | static force_inline BOOL ADEncodingTypeIsCNumber(ADEncodingType type) {
77 | switch (type & ADEncodingTypeMask) {
78 | case ADEncodingTypeBool:
79 | case ADEncodingTypeInt8:
80 | case ADEncodingTypeUInt8:
81 | case ADEncodingTypeInt16:
82 | case ADEncodingTypeUInt16:
83 | case ADEncodingTypeInt32:
84 | case ADEncodingTypeUInt32:
85 | case ADEncodingTypeInt64:
86 | case ADEncodingTypeUInt64:
87 | case ADEncodingTypeFloat:
88 | case ADEncodingTypeDouble:
89 | case ADEncodingTypeLongDouble: return YES;
90 | default: return NO;
91 | }
92 | }
93 |
94 | /// Parse a number value from 'id'.
95 | /**
96 | 通过id分析出一个数值
97 | __unsafe_unretained 和assign类似,但是它适用于对象类型,当目标被摧毁时,属性值不会自动清空(unsafe,不安全 unretained引用计数不加一)
98 | */
99 | static force_inline NSNumber *ADNSNumberCreateFromID(__unsafe_unretained id value) {
100 |
101 | static NSCharacterSet *dot;
102 | static NSDictionary *dic;
103 | static dispatch_once_t onceToken;
104 | dispatch_once(&onceToken, ^{
105 | dot = [NSCharacterSet characterSetWithRange:NSMakeRange('.', 1)];
106 | dic = @{@"TRUE" : @(YES),
107 | @"True" : @(YES),
108 | @"true" : @(YES),
109 | @"FALSE" : @(NO),
110 | @"False" : @(NO),
111 | @"false" : @(NO),
112 | @"YES" : @(YES),
113 | @"Yes" : @(YES),
114 | @"yes" : @(YES),
115 | @"NO" : @(NO),
116 | @"No" : @(NO),
117 | @"no" : @(NO),
118 | @"NIL" : (id)kCFNull,
119 | @"Nil" : (id)kCFNull,
120 | @"nil" : (id)kCFNull,
121 | @"NULL" : (id)kCFNull,
122 | @"Null" : (id)kCFNull,
123 | @"null" : (id)kCFNull,
124 | @"(NULL)" : (id)kCFNull,
125 | @"(Null)" : (id)kCFNull,
126 | @"(null)" : (id)kCFNull,
127 | @"" : (id)kCFNull,
128 | @"" : (id)kCFNull,
129 | @"" : (id)kCFNull};
130 | });
131 |
132 | if (!value || value == (id)kCFNull) return nil;
133 | if ([value isKindOfClass:[NSNumber class]]) return value;
134 | if ([value isKindOfClass:[NSString class]]) {
135 | NSNumber *num = dic[value];
136 | if (num) {
137 | if (num == (id)kCFNull) return nil;
138 | return num;
139 | }
140 | if ([(NSString *)value rangeOfCharacterFromSet:dot].location != NSNotFound) {
141 | const char *cstring = ((NSString *)value).UTF8String;
142 | if (!cstring) return nil;
143 | double num = atof(cstring);
144 | // isnan(值是无穷大或无穷小的不确定值)或isinf(值是无限循环)
145 | if (isnan(num) || isinf(num)) return nil;
146 | return @(num);
147 | } else {
148 | const char *cstring = ((NSString *)value).UTF8String;
149 | if (!cstring) return nil;
150 | // long long atoll(const char *nptr); 把字符串转换成长长整型数(64位)
151 | return @(atoll(cstring));
152 | }
153 | }
154 | return nil;
155 | }
156 |
157 | /// Parse string to date.
158 | //根据字符串分析出一个date
159 | static force_inline NSDate *ADNSDateFromString(__unsafe_unretained NSString *string) {
160 |
161 | typedef NSDate* (^ADNSDateParseBlock)(NSString *string);
162 | //从这开始声明一个宏定义kParserNum
163 | #define kParserNum 34
164 | //定义一个ADNSDateParseBlock数组,数组长度为[kParserNum + 1],并全部初始化值为0
165 | static ADNSDateParseBlock blocks[kParserNum + 1] = {0};
166 | static dispatch_once_t onceToken;
167 | dispatch_once(&onceToken, ^{
168 | {
169 | /*
170 | 2014-01-20 // Google
171 | */
172 | #pragma mark - - - - - - - - -
173 | NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
174 | formatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
175 | formatter.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0];
176 | formatter.dateFormat = @"yyyy-MM-dd";
177 | blocks[10] = ^(NSString *string) { return [formatter dateFromString:string]; };
178 | }
179 |
180 | {
181 | /*
182 | 2014-01-20 12:24:48
183 | 2014-01-20T12:24:48 // Google
184 | 2014-01-20 12:24:48.000
185 | 2014-01-20T12:24:48.000
186 | */
187 | NSDateFormatter *formatter1 = [[NSDateFormatter alloc] init];
188 | formatter1.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
189 | formatter1.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0];
190 | formatter1.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss";
191 |
192 | NSDateFormatter *formatter2 = [[NSDateFormatter alloc] init];
193 | formatter2.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
194 | formatter2.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0];
195 | formatter2.dateFormat = @"yyyy-MM-dd HH:mm:ss";
196 |
197 | NSDateFormatter *formatter3 = [[NSDateFormatter alloc] init];
198 | formatter3.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
199 | formatter3.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0];
200 | formatter3.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss.SSS";
201 |
202 | NSDateFormatter *formatter4 = [[NSDateFormatter alloc] init];
203 | formatter4.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
204 | formatter4.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0];
205 | formatter4.dateFormat = @"yyyy-MM-dd HH:mm:ss.SSS";
206 |
207 | blocks[19] = ^(NSString *string) {
208 | if ([string characterAtIndex:10] == 'T') {
209 | return [formatter1 dateFromString:string];
210 | } else {
211 | return [formatter2 dateFromString:string];
212 | }
213 | };
214 |
215 | blocks[23] = ^(NSString *string) {
216 | if ([string characterAtIndex:10] == 'T') {
217 | return [formatter3 dateFromString:string];
218 | } else {
219 | return [formatter4 dateFromString:string];
220 | }
221 | };
222 | }
223 |
224 | {
225 | /*
226 | 2014-01-20T12:24:48Z // Github, Apple
227 | 2014-01-20T12:24:48+0800 // Facebook
228 | 2014-01-20T12:24:48+12:00 // Google
229 | 2014-01-20T12:24:48.000Z
230 | 2014-01-20T12:24:48.000+0800
231 | 2014-01-20T12:24:48.000+12:00
232 | */
233 | NSDateFormatter *formatter = [NSDateFormatter new];
234 | formatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
235 | formatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ssZ";
236 |
237 | NSDateFormatter *formatter2 = [NSDateFormatter new];
238 | formatter2.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
239 | formatter2.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss.SSSZ";
240 |
241 | blocks[20] = ^(NSString *string) { return [formatter dateFromString:string]; };
242 | blocks[24] = ^(NSString *string) { return [formatter dateFromString:string]?: [formatter2 dateFromString:string]; };
243 | blocks[25] = ^(NSString *string) { return [formatter dateFromString:string]; };
244 | blocks[28] = ^(NSString *string) { return [formatter2 dateFromString:string]; };
245 | blocks[29] = ^(NSString *string) { return [formatter2 dateFromString:string]; };
246 | }
247 |
248 | {
249 | /*
250 | Fri Sep 04 00:12:21 +0800 2015 // Weibo, Twitter
251 | Fri Sep 04 00:12:21.000 +0800 2015
252 | */
253 | NSDateFormatter *formatter = [NSDateFormatter new];
254 | formatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
255 | formatter.dateFormat = @"EEE MMM dd HH:mm:ss Z yyyy";
256 |
257 | NSDateFormatter *formatter2 = [NSDateFormatter new];
258 | formatter2.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
259 | formatter2.dateFormat = @"EEE MMM dd HH:mm:ss.SSS Z yyyy";
260 |
261 | blocks[30] = ^(NSString *string) { return [formatter dateFromString:string]; };
262 | blocks[34] = ^(NSString *string) { return [formatter2 dateFromString:string]; };
263 | }
264 | });
265 | if (!string) return nil;
266 | if (string.length > kParserNum) return nil;
267 | ADNSDateParseBlock parser = blocks[string.length];
268 | if (!parser) return nil;
269 | return parser(string);
270 | //宏定义kParserNum到这里结束
271 | #undef kParserNum
272 | }
273 |
274 |
275 | /// Get the 'NSBlock' class.
276 | //获得'NSBlock'类,返回值为强转成类的无参无返Block,且其父类为[NSObject class]
277 | static force_inline Class ADNSBlockClass() {
278 |
279 | static Class cls;
280 |
281 | static dispatch_once_t onceToken;
282 | dispatch_once(&onceToken, ^{
283 | //定义一个无参无返回值的空block
284 | //将block强转成NSObject类赋值给cls
285 | #pragma mark - - - - - - - - -
286 | void (^block)(void) = ^{};
287 | cls = ((NSObject *)block).class;
288 | //cls不断向上转换直至父类为[NSObject class]
289 | while (class_getSuperclass(cls) != [NSObject class]) {
290 | cls = class_getSuperclass(cls);
291 | }
292 | });
293 | return cls; // current is "NSBlock"
294 | }
295 |
296 |
297 | /**
298 | Get the ISO date formatter.
299 | 获取ISO日期格式
300 | ISO:国际标准化
301 |
302 | ISO8601 format example:
303 | 2010-07-09T16:13:30+12:00
304 | 2011-01-11T11:11:11+0000
305 | 2011-01-26T19:06:43Z
306 |
307 | length: 20/24/25
308 | */
309 |
310 | static force_inline NSDateFormatter *ADISODateFormatter() {
311 |
312 | static NSDateFormatter *formatter = nil;
313 | static dispatch_once_t onceToken;
314 | dispatch_once(&onceToken, ^{
315 | formatter = [[NSDateFormatter alloc]init];
316 | formatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
317 | formatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ssZ";
318 | });
319 |
320 | return formatter;
321 | }
322 |
323 | /// Get the value with key paths from dictionary
324 | /// The dic should be NSDictionary, and the keyPath should not be nil.
325 | /**
326 | 从一个字典里中根据key paths中包含的key获取一个value
327 | 如果value为字典类型则设置dic为当前value
328 | 但是只获取最后一个value
329 | dic应该为NSDictionary,keyPath不应该是nil
330 | */
331 | static force_inline id ADValueForKeyPath(__unsafe_unretained NSDictionary *dic, __unsafe_unretained NSArray *keyPaths){
332 |
333 | id value = nil;
334 | for (NSUInteger i = 0 ,max = keyPaths.count; i < max; i++) {
335 | value = dic[keyPaths[i]];
336 | if (i + 1 < max) {
337 | if ([value isKindOfClass:[NSDictionary class]]) {
338 | dic = value;
339 | }else{
340 | return nil;
341 | }
342 | }
343 | }
344 | return value;
345 | }
346 |
347 | /// Get the value with multi key (or key path) from dictionary
348 | /// The dic should be NSDictionary
349 | /**
350 | 从字典中根据多个Key(或key path)获取值
351 | 这个dic应该是NSDictionary
352 | */
353 | static force_inline id ADValueForMultiKeys(__unsafe_unretained NSDictionary *dic, __unsafe_unretained NSArray *multiKeys) {
354 |
355 | id value = nil;
356 | for (NSString *key in multiKeys) {
357 | if ([key isKindOfClass:[NSString class]]) {
358 | value = dic[key];
359 | if (value) break;
360 | } else {
361 | value = ADValueForKeyPath(dic, (NSArray *)key);
362 | if (value) break;
363 | }
364 | }
365 | return value;
366 | }
367 |
368 |
369 | /// A property info in object model.
370 | //model对象中的property信息
371 | @interface _ADModelPropertyMeta : NSObject{
372 |
373 | @package
374 | NSString * _name; ///< property's name
375 | ADEncodingType _type; ///< property's type
376 | ADEncodingNSType _nsType; ///< property's Foundation type
377 | BOOL _isCNumber; ///< is c number type
378 | Class _cls; ///< property's class, or nil
379 |
380 | // Class _genericCls; 包含的自己创建的class,如果没有则为空
381 | Class _genericCls; ///< container's generic class, or nil if threr's no generic class
382 |
383 | // getter方法,如果对象无法响应则为Nil
384 | SEL _getter; ///< getter, or nil if the instances cannot respond
385 |
386 | // setter方法,如果对象无法响应则为Nil
387 | SEL _setter; ///< setter, or nil if the instances cannot respond
388 |
389 |
390 | // _isKVCCompatible 如果它可以成功使用KVC则返回YES
391 | BOOL _isKVCCompatible;///< YES if it can access with key-value coding
392 |
393 | // _isStructAvailableForKeyedArchiver 如果这个struct可以通过key实现encode的archiver/unarchiver则返回YES
394 | BOOL _isStructAvailableForKeyedArchiver;///< YES if the struct can encoded with keyed archiver/unarchiver
395 |
396 | // _hasCustomClassFromDictionary class/自己创建的Class 能否实现+modelCustomClassForDictionary
397 | BOOL _hasCustomClassFromDictionary; ///< class/generic class implements +modelCustomClassForDictionary:
398 |
399 | /*
400 | mapped to 映射
401 | property->key: _mappedToKey:key _mappedToKeyPath:nil _mappedToKeyArray:nil
402 | property->keyPath: _mappedToKey:keyPath _mappedToKeyPath:keyPath(array) _mappedToKeyArray:nil
403 | property->keys: _mappedToKey:keys[0] _mappedToKeyPath:nil/keyPath _mappedToKeyArray:keys(array)
404 | */
405 | // _mappedToKey 映射到的Key
406 | NSString *_mappedToKey; ///< the key mapped to
407 |
408 | // _mappedToKeyPath 映射到的keyPath (如果值不是key path则为Nil)
409 | NSArray *_mappedToKeyPath; ///< the key path mapped to (nil if the name is not key path)
410 |
411 | // _mappedToKeyArray key(NSString)或keyPath(NSArray)数组 (如果不能映射到多个key则为nil)
412 | NSArray *_mappedToKeyArray; ///< the key(NSString) or keyPath(NSArray) array (nil if not mapped to multiple keys)
413 |
414 | // _info property的信息
415 | ADClassPropertyInfo *_info; ///< property's info
416 |
417 | // _next 下一个元素,如果是多个Property映射到同一个key
418 | _ADModelPropertyMeta *_next; ///< next meta if there are multiple properties mapped to the same key.
419 |
420 | }
421 |
422 | @end
423 |
424 |
425 | @implementation _ADModelPropertyMeta
426 | //meta 元素
427 | // support pseudo generic class with protocol name
428 | // 支持假的generic class通过协议名
429 | // generic class自己写的类
430 | + (instancetype)metaWithClassInfo:(ADClassInfo *)classInfo propertyInfo:(ADClassPropertyInfo *)propertyInfo generic:(Class)generic {
431 |
432 |
433 | //如果自定义class不存在且Property信息存在protocols
434 | if (!generic && propertyInfo.protocols) {
435 | //遍历propertyInfo信息的所有protocols,将其通过UTF-8编码后转换为class类型
436 | //若转换成功则generic值为此类,结束遍历
437 | for (NSString *protocol in propertyInfo.protocols) {
438 | Class cls = objc_getClass(protocol.UTF8String);
439 |
440 | if (cls) {
441 | generic = cls;
442 | break;
443 | }
444 |
445 | }
446 |
447 | }
448 |
449 | _ADModelPropertyMeta *meta = [self new];
450 | meta->_name = propertyInfo.name;
451 | meta->_type = propertyInfo.type;
452 | meta->_info = propertyInfo;
453 | meta->_genericCls = generic;
454 |
455 | if ((meta->_type & ADEncodingTypeMask) == ADEncodingTypeObject) {
456 | meta->_nsType = ADClassGetNSType(propertyInfo.cls);
457 | } else {
458 | meta->_isCNumber = ADEncodingTypeIsCNumber(meta->_type);
459 | }
460 |
461 | if ((meta->_type & ADEncodingTypeMask) == ADEncodingTypeStruct) {
462 | /*
463 | It seems that NSKeyedUnarchiver cannot decode NSValue except these structs:
464 | 看来除structs之外不能decode NSValue通过NSKeyedUnarchiver
465 | */
466 | static NSSet *types = nil;
467 | static dispatch_once_t onceToken;
468 | dispatch_once(&onceToken, ^{
469 | NSMutableSet *set = [NSMutableSet new];
470 | // 32 bit
471 | [set addObject:@"{CGSize=ff}"];
472 | [set addObject:@"{CGPoint=ff}"];
473 | [set addObject:@"{CGRect={CGPoint=ff}{CGSize=ff}}"];
474 | [set addObject:@"{CGAffineTransform=ffffff}"];
475 | [set addObject:@"{UIEdgeInsets=ffff}"];
476 | [set addObject:@"{UIOffset=ff}"];
477 | // 64 bit
478 | [set addObject:@"{CGSize=dd}"];
479 | [set addObject:@"{CGPoint=dd}"];
480 | [set addObject:@"{CGRect={CGPoint=dd}{CGSize=dd}}"];
481 | [set addObject:@"{CGAffineTransform=dddddd}"];
482 | [set addObject:@"{UIEdgeInsets=dddd}"];
483 | [set addObject:@"{UIOffset=dd}"];
484 | types = set;
485 | });
486 |
487 | //如果自定义的集合包含propertyInfo.typeEncoding
488 | //则设置meta的isStructAvailableForKeyedArchiver属性为YES
489 | if ([types containsObject:propertyInfo.typeEncoding]) {
490 | meta->_isStructAvailableForKeyedArchiver = YES;
491 | }
492 |
493 | }
494 | meta->_cls = propertyInfo.cls;
495 |
496 | /**
497 | 如果你需要在json->object的改变时创建关于不同类的对象
498 | 使用这个方法基于dictionary data去改变custom class
499 |
500 | 描述 如果model实现了这个方法,他将被认为通知是确定的class结果
501 | 参数 json/kv字典
502 | 返回 通过字典创建的class,nil指使用当前class
503 | */
504 | if (generic) {
505 | meta->_hasCustomClassFromDictionary = [generic respondsToSelector:@selector(modelCustomClassForDictionary:)];
506 | } else if (meta->_cls && meta->_nsType == ADEncodingTypeNSUnknown) {
507 | meta->_hasCustomClassFromDictionary = [meta->_cls respondsToSelector:@selector(modelCustomClassForDictionary:)];
508 | }
509 |
510 | if (propertyInfo.getter) {
511 | if ([classInfo.cls instancesRespondToSelector:propertyInfo.getter]) {
512 | meta->_getter = propertyInfo.getter;
513 | }
514 | }
515 | if (propertyInfo.setter) {
516 | if ([classInfo.cls instancesRespondToSelector:propertyInfo.setter]) {
517 | meta->_setter = propertyInfo.setter;
518 | }
519 | }
520 |
521 | if (meta->_getter && meta->_setter) {
522 | /*
523 | KVC invalid type:
524 | long double
525 | pointer (such as SEL/CoreFoundation object)
526 | KVC 无效的类型:
527 | long double
528 | 提示(例如 SEL/CoreFoundation 对象)
529 | */
530 | switch (meta->_type & ADEncodingTypeMask) {
531 | case ADEncodingTypeBool:
532 | case ADEncodingTypeInt8:
533 | case ADEncodingTypeUInt8:
534 | case ADEncodingTypeInt16:
535 | case ADEncodingTypeUInt16:
536 | case ADEncodingTypeInt32:
537 | case ADEncodingTypeUInt32:
538 | case ADEncodingTypeInt64:
539 | case ADEncodingTypeUInt64:
540 | case ADEncodingTypeFloat:
541 | case ADEncodingTypeDouble:
542 | case ADEncodingTypeObject:
543 | case ADEncodingTypeClass:
544 | case ADEncodingTypeBlock:
545 | case ADEncodingTypeStruct:
546 | case ADEncodingTypeUnion: {
547 | meta->_isKVCCompatible = YES;
548 | } break;
549 | default: break;
550 | }
551 | }
552 |
553 | return meta;
554 | }
555 | @end
556 |
557 |
558 | /// A class info in object model.
559 | //model对象中一个class 信息
560 | @interface _ADModelMeta : NSObject{
561 |
562 | @package
563 |
564 | // 一个class的class 信息
565 | ADClassInfo *_classInfo;
566 |
567 | // Key:映射key和key path,value:_YYModelPropertyMeta
568 | /// Key:mapped key and key path, Value:_YYModelPropertyMeta.
569 | NSDictionary *_mapper;
570 |
571 | // 数组<_YYModelPropertyMeta>,关于model的所有property元素
572 | /// Array<_YYModelPropertyMeta>, all property meta of this model.
573 | NSArray *_allPropertyMetas;
574 |
575 | // 数组<_YYModelPropertyMeta>,property元素映射到的一个key path
576 | /// Array<_YYModelPropertyMeta>, property meta which is mapped to a key path.
577 | NSArray *_keyPathPropertyMetas;
578 |
579 | // 数组<_YYModelPropertyMeta>,property元素映射到的多个key
580 | /// Array<_YYModelPropertyMeta>, property meta which is mapped to multi keys.
581 | NSArray *_multiKeysPropertyMetas;
582 |
583 | // 关于映射的key(与key path)的数字,等同于_mapper.count
584 | /// The number of mapped key (and key path), same to _mapper.count.
585 | NSUInteger _keyMappedCount;
586 |
587 | // Model class 类型
588 | /// Model class type.
589 | ADEncodingNSType _nsType;
590 |
591 | BOOL _hasCustomWillTransformFromDictionary;
592 | BOOL _hasCustomTransformFromDictionary;
593 | BOOL _hasCustomTransformToDictionary;
594 | BOOL _hasCustomClassFromDictionary;
595 | }
596 | @end
597 |
598 | @implementation _ADModelMeta
599 |
600 | //自定义方法,未在.h声明
601 | - (instancetype)initWithClass:(Class)cls {
602 | ADClassInfo *classInfo = [ADClassInfo classInfoWithClass:cls];
603 | if (!classInfo) return nil;
604 | self = [super init];
605 | // Get black list
606 | // 获取黑名单
607 | /**
608 | 在model变换时所有在黑名单里的property都将被忽视
609 | 返回 一个关于property name的数组
610 | */
611 | NSSet *blacklist = nil;
612 | if ([cls respondsToSelector:@selector(modelPropertyBlacklist)]) {
613 | NSArray *properties = [(id)cls modelPropertyBlacklist];
614 | if (properties) {
615 | blacklist = [NSSet setWithArray:properties];
616 | }
617 | }
618 |
619 | // Get white list
620 | // 获取白名单
621 | NSSet *whitelist = nil;
622 | /**
623 | 如果一个property不在白名单,在model转变时它将被忽视
624 | 返回nil忽视这方面
625 |
626 | 返回 一个包含property name的数组
627 | */
628 | if ([cls respondsToSelector:@selector(modelPropertyWhitelist)]) {
629 | NSArray *properties = [(id)cls modelPropertyWhitelist];
630 | if (properties) {
631 | whitelist = [NSSet setWithArray:properties];
632 | }
633 | }
634 |
635 | // Get container property's generic class
636 | // 获取自定义class中包含的property
637 | NSDictionary *genericMapper = nil;
638 | /**
639 | 描述: 如果这个property是一个对象容器,列如NSArray/NSSet/NSDictionary
640 | 实现这个方法并返回一个属性->类mapper,告知哪一个对象将被添加到这个array /set /
641 | */
642 | if ([cls respondsToSelector:@selector(modelContainerPropertyGenericClass)]) {
643 | genericMapper = [(id)cls modelContainerPropertyGenericClass];
644 | if (genericMapper) {
645 | NSMutableDictionary *tmp = [NSMutableDictionary new];
646 | [genericMapper enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
647 | if (![key isKindOfClass:[NSString class]]) return;
648 | //object_getClass 返回对象的类
649 | Class meta = object_getClass(obj);
650 | if (!meta) return;
651 | if (class_isMetaClass(meta)) {
652 | tmp[key] = obj;
653 | } else if ([obj isKindOfClass:[NSString class]]) {
654 | Class cls = NSClassFromString(obj);
655 | if (cls) {
656 | tmp[key] = cls;
657 | }
658 | }
659 | }];
660 | genericMapper = tmp;
661 | }
662 | }
663 |
664 | // Create all property metas.
665 | // 创建所有的property 元素
666 | NSMutableDictionary *allPropertyMetas = [NSMutableDictionary new];
667 | //保存根据传过来的class得到的classInfo
668 | ADClassInfo *curClassInfo = classInfo;
669 |
670 | while (curClassInfo && curClassInfo.superCls != nil) { // recursive parse super class, but ignore root class (NSObject/NSProxy)
671 | //预先解析父类,但忽视根类(NSObject/NSProxy)
672 | for (ADClassPropertyInfo *propertyInfo in curClassInfo.propertyInfos.allValues) {
673 |
674 | // 若propertyInfo.name不存在
675 | // 或黑名单存在且黑名单包含propertyInfo.name
676 | // 或白名单存在且白名单不包含propertyInfo.name
677 | // 则结束当前循环
678 |
679 | if (!propertyInfo.name) continue;
680 |
681 | if (blacklist && [blacklist containsObject:propertyInfo.name]) continue;
682 | if (whitelist && ![whitelist containsObject:propertyInfo.name]) continue;
683 |
684 | _ADModelPropertyMeta *meta = [_ADModelPropertyMeta metaWithClassInfo:classInfo propertyInfo:propertyInfo generic:genericMapper[propertyInfo.name]];
685 |
686 | if (!meta || !meta->_name) continue;
687 | if (!meta->_getter || !meta->_setter) continue;
688 | if (allPropertyMetas[meta->_name]) continue;
689 | allPropertyMetas[meta->_name] = meta;
690 | }
691 | curClassInfo = curClassInfo.superClassInfo;
692 | }
693 | if (allPropertyMetas.count) _allPropertyMetas = allPropertyMetas.allValues.copy;
694 |
695 | // create mapper
696 | // 创建映射
697 | NSMutableDictionary *mapper = [NSMutableDictionary new];
698 | NSMutableArray *keyPathPropertyMetas = [NSMutableArray new];
699 | NSMutableArray *multiKeysPropertyMetas = [NSMutableArray new];
700 | /**
701 | modelCustomPropertyMapper
702 | 定制属性元素
703 | 描述 如果JSON/Dictionary的key并不能匹配model的property name
704 | 实现这个方法并返回额外的元素
705 | */
706 | if ([cls respondsToSelector:@selector(modelCustomPropertyMapper)]) {
707 | // 返回自定义属性映射字典
708 | NSDictionary *customMapper = [(id )cls modelCustomPropertyMapper];
709 | [customMapper enumerateKeysAndObjectsUsingBlock:^(NSString *propertyName, NSString *mappedToKey, BOOL *stop) {
710 | _ADModelPropertyMeta *propertyMeta = allPropertyMetas[propertyName];
711 | if (!propertyMeta) return;
712 | [allPropertyMetas removeObjectForKey:propertyName];
713 |
714 | if ([mappedToKey isKindOfClass:[NSString class]]) {
715 | if (mappedToKey.length == 0) return;
716 |
717 | propertyMeta->_mappedToKey = mappedToKey;
718 | // 以"."分割字符串为一个数组
719 | NSArray *keyPath = [mappedToKey componentsSeparatedByString:@"."];
720 | for (NSString *onePath in keyPath) {
721 | if (onePath.length == 0) {
722 | NSMutableArray *tmp = keyPath.mutableCopy;
723 | [tmp removeObject:@""];
724 | keyPath = tmp;
725 | break;
726 | }
727 | }
728 | if (keyPath.count > 1) {
729 | propertyMeta->_mappedToKeyPath = keyPath;
730 | [keyPathPropertyMetas addObject:propertyMeta];
731 | }
732 | propertyMeta->_next = mapper[mappedToKey] ?: nil;
733 | mapper[mappedToKey] = propertyMeta;
734 |
735 | } else if ([mappedToKey isKindOfClass:[NSArray class]]) {
736 |
737 | NSMutableArray *mappedToKeyArray = [NSMutableArray new];
738 | for (NSString *oneKey in ((NSArray *)mappedToKey)) {
739 | if (![oneKey isKindOfClass:[NSString class]]) continue;
740 | if (oneKey.length == 0) continue;
741 |
742 | NSArray *keyPath = [oneKey componentsSeparatedByString:@"."];
743 | if (keyPath.count > 1) {
744 | [mappedToKeyArray addObject:keyPath];
745 | } else {
746 | [mappedToKeyArray addObject:oneKey];
747 | }
748 |
749 | if (!propertyMeta->_mappedToKey) {
750 | propertyMeta->_mappedToKey = oneKey;
751 | propertyMeta->_mappedToKeyPath = keyPath.count > 1 ? keyPath : nil;
752 | }
753 | }
754 | if (!propertyMeta->_mappedToKey) return;
755 |
756 | propertyMeta->_mappedToKeyArray = mappedToKeyArray;
757 | [multiKeysPropertyMetas addObject:propertyMeta];
758 |
759 | propertyMeta->_next = mapper[mappedToKey] ?: nil;
760 | mapper[mappedToKey] = propertyMeta;
761 | }
762 | }];
763 | }
764 |
765 | [allPropertyMetas enumerateKeysAndObjectsUsingBlock:^(NSString *name, _ADModelPropertyMeta *propertyMeta, BOOL *stop) {
766 | propertyMeta->_mappedToKey = name;
767 | propertyMeta->_next = mapper[name] ?: nil;
768 | mapper[name] = propertyMeta;
769 | }];
770 |
771 | if (mapper.count) _mapper = mapper;
772 | if (keyPathPropertyMetas) _keyPathPropertyMetas = keyPathPropertyMetas;
773 | if (multiKeysPropertyMetas) _multiKeysPropertyMetas = multiKeysPropertyMetas;
774 |
775 | _classInfo = classInfo;
776 | _keyMappedCount = _allPropertyMetas.count;
777 | _nsType = ADClassGetNSType(cls);
778 | /**
779 | modelCustomWillTransformFromDictionary:
780 | 这个方法行为是相似的与 "- (BOOL)modelCustomTransformFromDictionary:(NSDictionary *)dic"
781 | 但在model转换前被命名的
782 | 描述 如果model实现了这个方法,它将被命名在"+modelWithJson:","+modelWithDictionary:","-modelSetWithJSON:"and"-modelSetWithDictionary:"之前
783 | 如果方法返回为nil,转换过程中将忽视这个model
784 | @param dic The json/kv dictionary.
785 | 参数 dic json/kv 字典
786 | @return Returns the modified dictionary, or nil to ignore this model.
787 | 返回 返回修改的字典,如果忽视这个model返回Nil
788 | */
789 | _hasCustomWillTransformFromDictionary = ([cls instancesRespondToSelector:@selector(modelCustomWillTransformFromDictionary:)]);
790 | /**
791 | modelCustomTransformFromDictionary:
792 | 如果默认的json-to-model转换并不符合你的model对象,实现这个方法去增加额外的过程。
793 | 你也可以使用这个方法使model的property生效
794 | 描述 如果model实现了这个方法,它将被命名在"+modelWithJSON:","+modelWithDictionary","-modelSetWithJSON:" and "-modelSetWithDictionary:"结束
795 | @param dic The json/kv dictionary.
796 |
797 | 参数 dic json/kv 字典
798 |
799 | @return Returns YES if the model is valid, or NO to ignore this model.
800 |
801 | 返回 如果这个model是有效的,返回YES 或返回NO忽视这个model
802 |
803 | */
804 | _hasCustomTransformFromDictionary = ([cls instancesRespondToSelector:@selector(modelCustomTransformFromDictionary:)]);
805 | /**
806 | modelCustomTransformToDictionary:
807 | 如果默认的model-to-json转换并不符合你的model class,实现这个方法添加额外的过程。
808 | 你也可以使用这个方法使这个json dictionary有效
809 | 描述 如果这个model实现了这个方法,它将被调用在"-modelToJSONObject"和"-modelToJSONStrign"结束
810 | 如果这个方法返回NO,这个转换过程将忽视这个json dictionary
811 | */
812 | _hasCustomTransformToDictionary = ([cls instancesRespondToSelector:@selector(modelCustomTransformToDictionary:)]);
813 | /**
814 | modelCustomClassForDictionary:
815 | 如果你需要在json->object的改变时创建关于不同类的对象
816 | 使用这个方法基于dictionary data去改变custom class
817 | 描述 如果model实现了这个方法,他将被认为通知是确定的class结果
818 | 在"+modelWithJson","+modelWithDictionary"期间,父对象包含的property是一个对象
819 | (两个单数的并经由`+modelContainerPropertyGenericClass`包含)
820 | */
821 | _hasCustomClassFromDictionary = ([cls respondsToSelector:@selector(modelCustomClassForDictionary:)]);
822 |
823 | return self;
824 | }
825 |
826 | /// Returns the cached model class meta
827 | //返回这个class元素model缓存
828 | +(instancetype)metaWithClass:(Class)cls{
829 |
830 | if (!cls) return nil;
831 | static CFMutableDictionaryRef cache;
832 | static dispatch_semaphore_t lock;
833 | static dispatch_once_t onceToken;
834 | dispatch_once(&onceToken, ^{
835 |
836 | cache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
837 | lock = dispatch_semaphore_create(1);
838 | });
839 | dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
840 | //第一次进来是没有,但第二次不就有了~
841 | _ADModelMeta *meta = CFDictionaryGetValue(cache, (__bridge const void *)(cls));
842 |
843 | dispatch_semaphore_signal(lock);
844 | //如果model元素不存在,或model元素缓存需要更新
845 | if (!meta || meta -> _classInfo.needUpdate) {
846 | //重新创建meta
847 | meta = [[_ADModelMeta alloc] initWithClass:cls];
848 | if (meta) {
849 | dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
850 | CFDictionarySetValue(cache, (__bridge const void *)(cls), (__bridge const void*)(meta));
851 | dispatch_semaphore_signal(lock);
852 | }
853 | }
854 | return meta;
855 | }
856 |
857 | @end
858 | //
859 | //
860 | ///**
861 | // Get number from property.
862 | // @discussion Caller should hold strong reference to the parameters before this function returns.
863 | // @param model Should not be nil.
864 | // @param meta Should not be nil, meta.isCNumber should be YES, meta.getter should not be nil.
865 | // @return A number object, or nil if failed.
866 | // */
867 | ///**
868 | // 通过property获取一个数字
869 | // 描述 调用者(caller 来访者)应对这个参数保持强引用在这个函数返回之前
870 | // 参数 model 不应该是nil
871 | // 参数 meta 不应该是nil,meta.isCNumber 应该是YES,meta.getter不应该是nil
872 | // 返回 一个数字对象,如果获取失败返回nil
873 | // */
874 | static force_inline NSNumber *ModelCreateNumberFromProperty(__unsafe_unretained id model,__unsafe_unretained _ADModelPropertyMeta *meta) {
875 | switch (meta->_type & ADEncodingTypeMask) {
876 | case ADEncodingTypeBool: {
877 | return @(((bool (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter));
878 | }
879 | case ADEncodingTypeInt8: {
880 | return @(((int8_t (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter));
881 | }
882 | case ADEncodingTypeUInt8: {
883 | return @(((uint8_t (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter));
884 | }
885 | case ADEncodingTypeInt16: {
886 | return @(((int16_t (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter));
887 | }
888 | case ADEncodingTypeUInt16: {
889 | return @(((uint16_t (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter));
890 | }
891 | case ADEncodingTypeInt32: {
892 | return @(((int32_t (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter));
893 | }
894 | case ADEncodingTypeUInt32: {
895 | return @(((uint32_t (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter));
896 | }
897 | case ADEncodingTypeInt64: {
898 | return @(((int64_t (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter));
899 | }
900 | case ADEncodingTypeUInt64: {
901 | return @(((uint64_t (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter));
902 | }
903 | case ADEncodingTypeFloat: {
904 | float num = ((float (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter);
905 | if (isnan(num) || isinf(num)) return nil;
906 | return @(num);
907 | }
908 | case ADEncodingTypeDouble: {
909 | double num = ((double (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter);
910 | if (isnan(num) || isinf(num)) return nil;
911 | return @(num);
912 | }
913 | case ADEncodingTypeLongDouble: {
914 | double num = ((long double (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter);
915 | if (isnan(num) || isinf(num)) return nil;
916 | return @(num);
917 | }
918 | default: return nil;
919 | }
920 | }
921 |
922 | /**
923 | Set number to property.
924 | @discussion Caller should hold strong reference to the parameters before this function returns.
925 | @param model Should not be nil.
926 | @param num Can be nil.
927 | @param meta Should not be nil, meta.isCNumber should be YES, meta.setter should not be nil.
928 | */
929 | /**
930 | 设置数字给property
931 | 描述 调用者(caller 来访者)应对这个参数保持强引用在这个函数返回之前
932 | 参数 model 不应该是nil
933 | 参数 num 可以是nil
934 | 参数 meta 不应该是nil,meta.isCNumber应该是YES,meta.setter不应该是Nil
935 | */
936 | static force_inline void ModelSetNumberToProperty(__unsafe_unretained id model, __unsafe_unretained NSNumber *num, __unsafe_unretained _ADModelPropertyMeta *meta) {
937 | switch (meta->_type & ADEncodingTypeMask) {
938 | // objc_msgSend OC消息传递机制中选择子发送的一种方式,代表是当前对象发送且没有结构体返回值
939 | // 选择子简单说就是@selector(),OC会提供一张选择子表供其查询,查询得到就去调用,查询不到就添加而后查询对应的实现函数。通过_class_lookupMethodAndLoadCache3(仅提供给派发器用于方法查找的函数),其内部会调用lookUpImpOrForward方法查找,查找之后还会有初始化枷锁缓存之类的操作,详情请自行搜索,就不赘述了。
940 | // 这里的意思是,通过objc_msgSend给强转成id类型的model对象发送一个选择子meta,选择子调用的方法所需参数为一个bool类型的值num.boolValue
941 | // 再通俗点就是让对象model去执行方法meta->_setter,方法所需参数是num.bollValue
942 | // 再通俗点:((void (*)(id, SEL, bool))(void *) objc_msgSend) 一位一个无返回值的函数指针,指向id的SEL方法,SEL方法所需参数是bool类型,使用objc_msgSend完成这个id调用SEL方法传递参数bool类型,(void *)objc_msgSend为什么objc_msgSend前加一个(void *)呢?我查了众多资料,众多。最后终于皇天不负有心人有了个结果,是为了避免某些错误,比如model对象的内存被意外侵占了、model对象的isa是一个野指针之类的。要是有大牛能说明白,麻烦再说下。
943 | // 而((id)model, meta->_setter, num.boolValue)则一一对应前面的id,SEL,bool
944 | // 再通俗点。。你找别家吧。。
945 | case ADEncodingTypeBool: {
946 | ((void (*)(id, SEL, bool))(void *) objc_msgSend)((id)model, meta->_setter, num.boolValue);
947 | } break;
948 | case ADEncodingTypeInt8: {
949 | ((void (*)(id, SEL, int8_t))(void *) objc_msgSend)((id)model, meta->_setter, (int8_t)num.charValue);
950 | } break;
951 | case ADEncodingTypeUInt8: {
952 | ((void (*)(id, SEL, uint8_t))(void *) objc_msgSend)((id)model, meta->_setter, (uint8_t)num.unsignedCharValue);
953 | } break;
954 | case ADEncodingTypeInt16: {
955 | ((void (*)(id, SEL, int16_t))(void *) objc_msgSend)((id)model, meta->_setter, (int16_t)num.shortValue);
956 | } break;
957 | case ADEncodingTypeUInt16: {
958 | ((void (*)(id, SEL, uint16_t))(void *) objc_msgSend)((id)model, meta->_setter, (uint16_t)num.unsignedShortValue);
959 | } break;
960 | case ADEncodingTypeInt32: {
961 | ((void (*)(id, SEL, int32_t))(void *) objc_msgSend)((id)model, meta->_setter, (int32_t)num.intValue);
962 | }
963 | case ADEncodingTypeUInt32: {
964 | ((void (*)(id, SEL, uint32_t))(void *) objc_msgSend)((id)model, meta->_setter, (uint32_t)num.unsignedIntValue);
965 | } break;
966 | case ADEncodingTypeInt64: {
967 | if ([num isKindOfClass:[NSDecimalNumber class]]) {
968 | ((void (*)(id, SEL, int64_t))(void *) objc_msgSend)((id)model, meta->_setter, (int64_t)num.stringValue.longLongValue);
969 | } else {
970 | ((void (*)(id, SEL, uint64_t))(void *) objc_msgSend)((id)model, meta->_setter, (uint64_t)num.longLongValue);
971 | }
972 | } break;
973 | case ADEncodingTypeUInt64: {
974 | // NSDecimalNumber数字精确,其值确定后不可修改,是NSNumber的子类
975 | if ([num isKindOfClass:[NSDecimalNumber class]]) {
976 | ((void (*)(id, SEL, int64_t))(void *) objc_msgSend)((id)model, meta->_setter, (int64_t)num.stringValue.longLongValue);
977 | } else {
978 | ((void (*)(id, SEL, uint64_t))(void *) objc_msgSend)((id)model, meta->_setter, (uint64_t)num.unsignedLongLongValue);
979 | }
980 | } break;
981 | case ADEncodingTypeFloat: {
982 | float f = num.floatValue;
983 | if (isnan(f) || isinf(f)) f = 0;
984 | ((void (*)(id, SEL, float))(void *) objc_msgSend)((id)model, meta->_setter, f);
985 | } break;
986 | case ADEncodingTypeDouble: {
987 | double d = num.doubleValue;
988 | if (isnan(d) || isinf(d)) d = 0;
989 | ((void (*)(id, SEL, double))(void *) objc_msgSend)((id)model, meta->_setter, d);
990 | } break;
991 | case ADEncodingTypeLongDouble: {
992 | long double d = num.doubleValue;
993 | if (isnan(d) || isinf(d)) d = 0;
994 | ((void (*)(id, SEL, long double))(void *) objc_msgSend)((id)model, meta->_setter, (long double)d);
995 | } // break; commented for code coverage in next line
996 | default: break;
997 | }
998 | }
999 |
1000 | /**
1001 | Set value to model with a property meta.
1002 | 设置value给model通过一个property 元素
1003 |
1004 | @discussion Caller should hold strong reference to the parameters before this function returns.
1005 | @param model Should not be nil.
1006 | @param value Should not be nil, but can be NSNull.
1007 | @param meta Should not be nil, and meta->_setter should not be nil.
1008 | 描述 调用者(caller 来访者)应对这个参数保持强引用在这个函数返回之前
1009 | 参数 model 不应该是nil
1010 | 参数 value 不应该是nil,但可以是NSNull
1011 | 参数 meta 不应该是nil,且meta->_setter 不应该是nil
1012 | */
1013 | static void ModelSetValueForProperty(__unsafe_unretained id model,
1014 | __unsafe_unretained id value, __unsafe_unretained _ADModelPropertyMeta *meta) {
1015 | if (meta->_isCNumber) {
1016 | //自定义的ADNSNumberCreateFromID
1017 | NSNumber *num = ADNSNumberCreateFromID(value);
1018 | //自定义的ModelSetNumberToProperty
1019 | ModelSetNumberToProperty(model, num, meta);
1020 | if (num) [num class]; // hold the number
1021 | } else if (meta->_nsType) {
1022 | if (value == (id)kCFNull) {
1023 | ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, (id)nil);
1024 | } else {
1025 | switch (meta->_nsType) {
1026 | case ADEncodingTypeNSString:
1027 | case ADEncodingTypeNSMutableString: {
1028 | if ([value isKindOfClass:[NSString class]]) {
1029 | if (meta->_nsType == ADEncodingTypeNSString) {
1030 | // objc_msgSend,这个函数将消息接收者和方法名作为基础参数。消息发送给一个对象时,objc_msgSend通过对象的isa指针获得类的结构体,先在Cache里找,找到就执行,没找到就在分发列表里查找方法的selector,没找到就通过objc_msgSend结构体中指向父类的指针找到父类,然后在父类分发列表找,直到root class(NSObject)。
1031 | // 在64位下,直接使用objc_msgSend一样会引起崩溃,必须进行一次强转
1032 | // ((void(*)(id, SEL,int))objc_msgSend)(self, @selector(doSomething:), 0);
1033 | // 调用无参数无返回值方法 ((void (*)(id, SEL))objc_msgSend)((id)msg, @selector(noArgumentsAndNoReturnValue));
1034 | ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, value);
1035 | } else {
1036 | ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, ((NSString *)value).mutableCopy);
1037 | }
1038 | } else if ([value isKindOfClass:[NSNumber class]]) {
1039 | ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, (meta->_nsType == ADEncodingTypeNSString) ? ((NSNumber *)value).stringValue : ((NSNumber *)value).stringValue.mutableCopy);
1040 | } else if ([value isKindOfClass:[NSData class]]) {
1041 | NSMutableString *string = [[NSMutableString alloc] initWithData:value encoding:NSUTF8StringEncoding];
1042 | ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, string);
1043 | } else if ([value isKindOfClass:[NSURL class]]) {
1044 | ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, (meta->_nsType == ADEncodingTypeNSString) ? ((NSURL *)value).absoluteString : ((NSURL *)value).absoluteString.mutableCopy);
1045 | } else if ([value isKindOfClass:[NSAttributedString class]]) {
1046 | ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, (meta->_nsType == ADEncodingTypeNSString) ? ((NSAttributedString *)value).string : ((NSAttributedString *)value).string.mutableCopy);
1047 | }
1048 | } break;
1049 |
1050 | case ADEncodingTypeNSValue:
1051 | case ADEncodingTypeNSNumber:
1052 | case ADEncodingTypeNSDecimalNumber: {
1053 | if (meta->_nsType == ADEncodingTypeNSNumber) {
1054 | ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, ADNSNumberCreateFromID(value));
1055 | } else if (meta->_nsType == ADEncodingTypeNSDecimalNumber) {
1056 | if ([value isKindOfClass:[NSDecimalNumber class]]) {
1057 | ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, value);
1058 | } else if ([value isKindOfClass:[NSNumber class]]) {
1059 | NSDecimalNumber *decNum = [NSDecimalNumber decimalNumberWithDecimal:[((NSNumber *)value) decimalValue]];
1060 | ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, decNum);
1061 | } else if ([value isKindOfClass:[NSString class]]) {
1062 | NSDecimalNumber *decNum = [NSDecimalNumber decimalNumberWithString:value];
1063 | NSDecimal dec = decNum.decimalValue;
1064 | if (dec._length == 0 && dec._isNegative) {
1065 | decNum = nil; // NaN
1066 | }
1067 | ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, decNum);
1068 | }
1069 | } else { // YYEncodingTypeNSValue
1070 | if ([value isKindOfClass:[NSValue class]]) {
1071 | ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, value);
1072 | }
1073 | }
1074 | } break;
1075 |
1076 | case ADEncodingTypeNSData:
1077 | case ADEncodingTypeNSMutableData: {
1078 | if ([value isKindOfClass:[NSData class]]) {
1079 | if (meta->_nsType == ADEncodingTypeNSData) {
1080 | ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, value);
1081 | } else {
1082 | NSMutableData *data = ((NSData *)value).mutableCopy;
1083 | ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, data);
1084 | }
1085 | } else if ([value isKindOfClass:[NSString class]]) {
1086 | NSData *data = [(NSString *)value dataUsingEncoding:NSUTF8StringEncoding];
1087 | if (meta->_nsType == ADEncodingTypeNSMutableData) {
1088 | data = ((NSData *)data).mutableCopy;
1089 | }
1090 | ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, data);
1091 | }
1092 | } break;
1093 |
1094 | case ADEncodingTypeNSDate: {
1095 | if ([value isKindOfClass:[NSDate class]]) {
1096 | ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, value);
1097 | } else if ([value isKindOfClass:[NSString class]]) {
1098 | ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, ADNSDateFromString(value));
1099 | }
1100 | } break;
1101 |
1102 | case ADEncodingTypeNSURL: {
1103 | if ([value isKindOfClass:[NSURL class]]) {
1104 | ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, value);
1105 | } else if ([value isKindOfClass:[NSString class]]) {
1106 | NSCharacterSet *set = [NSCharacterSet whitespaceAndNewlineCharacterSet];
1107 | NSString *str = [value stringByTrimmingCharactersInSet:set];
1108 | if (str.length == 0) {
1109 | ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, nil);
1110 | } else {
1111 | ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, [[NSURL alloc] initWithString:str]);
1112 | }
1113 | }
1114 | } break;
1115 |
1116 | case ADEncodingTypeNSArray:
1117 | case ADEncodingTypeNSMutableArray: {
1118 | if (meta->_genericCls) {
1119 | NSArray *valueArr = nil;
1120 | if ([value isKindOfClass:[NSArray class]]) valueArr = value;
1121 | else if ([value isKindOfClass:[NSSet class]]) valueArr = ((NSSet *)value).allObjects;
1122 | if (valueArr) {
1123 | NSMutableArray *objectArr = [NSMutableArray new];
1124 | for (id one in valueArr) {
1125 | if ([one isKindOfClass:meta->_genericCls]) {
1126 | [objectArr addObject:one];
1127 | } else if ([one isKindOfClass:[NSDictionary class]]) {
1128 | Class cls = meta->_genericCls;
1129 | if (meta->_hasCustomClassFromDictionary) {
1130 | cls = [cls modelCustomClassForDictionary:one];
1131 | if (!cls) cls = meta->_genericCls; // for xcode code coverage
1132 | }
1133 | NSObject *newOne = [cls new];
1134 | [newOne ad_modelSetWithDictionary:one];
1135 | if (newOne) [objectArr addObject:newOne];
1136 | }
1137 | }
1138 | ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, objectArr);
1139 | }
1140 | } else {
1141 | if ([value isKindOfClass:[NSArray class]]) {
1142 | if (meta->_nsType == ADEncodingTypeNSArray) {
1143 | ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, value);
1144 | } else {
1145 | ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter,
1146 | ((NSArray *)value).mutableCopy);
1147 | }
1148 | } else if ([value isKindOfClass:[NSSet class]]) {
1149 | if (meta->_nsType == ADEncodingTypeNSArray) {
1150 | ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, ((NSSet *)value).allObjects);
1151 | } else {
1152 | ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter,
1153 | ((NSSet *)value).allObjects.mutableCopy);
1154 | }
1155 | }
1156 | }
1157 | } break;
1158 |
1159 | case ADEncodingTypeNSDictionary:
1160 | case ADEncodingTypeNSMutableDictionary: {
1161 | if ([value isKindOfClass:[NSDictionary class]]) {
1162 | if (meta->_genericCls) {
1163 | NSMutableDictionary *dic = [NSMutableDictionary new];
1164 | [((NSDictionary *)value) enumerateKeysAndObjectsUsingBlock:^(NSString *oneKey, id oneValue, BOOL *stop) {
1165 | if ([oneValue isKindOfClass:[NSDictionary class]]) {
1166 | Class cls = meta->_genericCls;
1167 | if (meta->_hasCustomClassFromDictionary) {
1168 | cls = [cls modelCustomClassForDictionary:oneValue];
1169 | if (!cls) cls = meta->_genericCls; // for xcode code coverage
1170 | }
1171 | NSObject *newOne = [cls new];
1172 | [newOne ad_modelSetWithDictionary:(id)oneValue];
1173 | if (newOne) dic[oneKey] = newOne;
1174 | }
1175 | }];
1176 | ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, dic);
1177 | } else {
1178 | if (meta->_nsType == ADEncodingTypeNSDictionary) {
1179 | ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, value);
1180 | } else {
1181 | ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter,
1182 | ((NSDictionary *)value).mutableCopy);
1183 | }
1184 | }
1185 | }
1186 | } break;
1187 |
1188 | case ADEncodingTypeNSSet:
1189 | case ADEncodingTypeNSMutableSet: {
1190 | NSSet *valueSet = nil;
1191 | if ([value isKindOfClass:[NSArray class]]) valueSet = [NSMutableSet setWithArray:value];
1192 | else if ([value isKindOfClass:[NSSet class]]) valueSet = ((NSSet *)value);
1193 |
1194 | if (meta->_genericCls) {
1195 | NSMutableSet *set = [NSMutableSet new];
1196 | for (id one in valueSet) {
1197 | if ([one isKindOfClass:meta->_genericCls]) {
1198 | [set addObject:one];
1199 | } else if ([one isKindOfClass:[NSDictionary class]]) {
1200 | Class cls = meta->_genericCls;
1201 | if (meta->_hasCustomClassFromDictionary) {
1202 | cls = [cls modelCustomClassForDictionary:one];
1203 | if (!cls) cls = meta->_genericCls; // for xcode code coverage
1204 | }
1205 | NSObject *newOne = [cls new];
1206 | [newOne ad_modelSetWithDictionary:one];
1207 | if (newOne) [set addObject:newOne];
1208 | }
1209 | }
1210 | ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, set);
1211 | } else {
1212 | if (meta->_nsType == ADEncodingTypeNSSet) {
1213 | ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, valueSet);
1214 | } else {
1215 | ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, ((NSSet *)valueSet).mutableCopy);
1216 | }
1217 | }
1218 | } // break; commented for code coverage in next line
1219 |
1220 | default: break;
1221 | }
1222 | }
1223 | } else {
1224 | BOOL isNull = (value == (id)kCFNull);
1225 | switch (meta->_type & ADEncodingTypeMask) {
1226 | case ADEncodingTypeObject: {
1227 | if (isNull) {
1228 | ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, (id)nil);
1229 | } else if ([value isKindOfClass:meta->_cls] || !meta->_cls) {
1230 | ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, (id)value);
1231 | } else if ([value isKindOfClass:[NSDictionary class]]) {
1232 | NSObject *one = nil;
1233 | if (meta->_getter) {
1234 | one = ((id (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter);
1235 | }
1236 | if (one) {
1237 | [one ad_modelSetWithDictionary:value];
1238 | } else {
1239 | Class cls = meta->_cls;
1240 | if (meta->_hasCustomClassFromDictionary) {
1241 | cls = [cls modelCustomClassForDictionary:value];
1242 | if (!cls) cls = meta->_genericCls; // for xcode code coverage
1243 | }
1244 | one = [cls new];
1245 | [one ad_modelSetWithDictionary:value];
1246 | ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, (id)one);
1247 | }
1248 | }
1249 | } break;
1250 |
1251 | case ADEncodingTypeClass: {
1252 | if (isNull) {
1253 | ((void (*)(id, SEL, Class))(void *) objc_msgSend)((id)model, meta->_setter, (Class)NULL);
1254 | } else {
1255 | Class cls = nil;
1256 | if ([value isKindOfClass:[NSString class]]) {
1257 | cls = NSClassFromString(value);
1258 | if (cls) {
1259 | ((void (*)(id, SEL, Class))(void *) objc_msgSend)((id)model, meta->_setter, (Class)cls);
1260 | }
1261 | } else {
1262 | cls = object_getClass(value);
1263 | if (cls) {
1264 | if (class_isMetaClass(cls)) {
1265 | ((void (*)(id, SEL, Class))(void *) objc_msgSend)((id)model, meta->_setter, (Class)value);
1266 | }
1267 | }
1268 | }
1269 | }
1270 | } break;
1271 |
1272 | case ADEncodingTypeSEL: {
1273 | if (isNull) {
1274 | ((void (*)(id, SEL, SEL))(void *) objc_msgSend)((id)model, meta->_setter, (SEL)NULL);
1275 | } else if ([value isKindOfClass:[NSString class]]) {
1276 | SEL sel = NSSelectorFromString(value);
1277 | if (sel) ((void (*)(id, SEL, SEL))(void *) objc_msgSend)((id)model, meta->_setter, (SEL)sel);
1278 | }
1279 | } break;
1280 |
1281 | case ADEncodingTypeBlock: {
1282 | if (isNull) {
1283 | ((void (*)(id, SEL, void (^)()))(void *) objc_msgSend)((id)model, meta->_setter, (void (^)())NULL);
1284 | } else if ([value isKindOfClass:ADNSBlockClass()]) {
1285 | ((void (*)(id, SEL, void (^)()))(void *) objc_msgSend)((id)model, meta->_setter, (void (^)())value);
1286 | }
1287 | } break;
1288 |
1289 | case ADEncodingTypeStruct:
1290 | case ADEncodingTypeUnion:
1291 | case ADEncodingTypeCArray: {
1292 | if ([value isKindOfClass:[NSValue class]]) {
1293 | const char *valueType = ((NSValue *)value).objCType;
1294 | const char *metaType = meta->_info.typeEncoding.UTF8String;
1295 | if (valueType && metaType && strcmp(valueType, metaType) == 0) {
1296 | [model setValue:value forKey:meta->_name];
1297 | }
1298 | }
1299 | } break;
1300 |
1301 | case ADEncodingTypePointer:
1302 | case ADEncodingTypeCString: {
1303 | if (isNull) {
1304 | ((void (*)(id, SEL, void *))(void *) objc_msgSend)((id)model, meta->_setter, (void *)NULL);
1305 | } else if ([value isKindOfClass:[NSValue class]]) {
1306 | NSValue *nsValue = value;
1307 | if (nsValue.objCType && strcmp(nsValue.objCType, "^v") == 0) {
1308 | ((void (*)(id, SEL, void *))(void *) objc_msgSend)((id)model, meta->_setter, nsValue.pointerValue);
1309 | }
1310 | }
1311 | } // break; commented for code coverage in next line
1312 | //break; 注解对于代码覆盖在下一行
1313 | default: break;
1314 | }
1315 | }
1316 | }
1317 |
1318 |
1319 | typedef struct {
1320 | void *modelMeta; ///< _ADModelMeta
1321 | void *model; ///< id (self)
1322 | void *dictionary; ///< NSDictionary (json)
1323 | } ModelSetContext;
1324 |
1325 | /**
1326 | Apply function for dictionary, to set the key-value pair to model.
1327 |
1328 | @param _key should not be nil, NSString.
1329 | @param _value should not be nil.
1330 | @param _context _context.modelMeta and _context.model should not be nil.
1331 |
1332 | 对于字典的函数应用,设置key-value配对给model
1333 | 参数 _key 不应该是nil,NSString
1334 | 参数 _value 不应该是nil
1335 | 参数 _context _context.modelMeta 和 _context.model 不应该是nil
1336 | */
1337 |
1338 | static void ModelSetWithDictionaryFunction(const void *_key, const void *_value, void * _context) {
1339 |
1340 | ModelSetContext *context = _context;
1341 |
1342 | // __unsafe_unretained 指针所指向的地址即使已经被释放没有值了,依旧会指向,如同野指针一样,weak/strong这些则会被置为nil。一般应用于iOS 4与OS X Snow Leopard(雪豹)中,因为iOS 5以上才能使用weak。
1343 | //
1344 | // __unsafe_unretained与weak一样,不能持有对象,也就是对象的引用计数不会加1
1345 | //
1346 | // unsafe_unretained修饰符以外的 strong/ weak/ autorealease修饰符保证其指定的变量初始化为nil。同样的,附有 strong/ weak/ _autorealease修饰符变量的数组也可以保证其初始化为nil。
1347 | //
1348 | // autorealease(延迟释放,给对象添加延迟释放的标记,出了作用域之后,会被自动添加到"最近创建的"自动释放池中)
1349 | // 为什么使用unsafe_unretained?
1350 | // 作者回答:在 ARC 条件下,默认声明的对象是 strong 类型的,赋值时有可能会产生 retain/release 调用,如果一个变量在其生命周期内不会被释放,则使用 unsafe_unretained 会节省很大的开销。
1351 | // 网友提问: 楼主的偏好是说用unsafe_unretained来代替weak的使用,使用后自行解决野指针的问题吗?
1352 | // 作者回答:关于 unsafe_unretained 这个属性,我只提到需要在性能优化时才需要尝试使用,平时开发自然是不推荐用的。
1353 |
1354 | //结构体context中的void函数modelMeta通过桥接转换成_ADModelMeta
1355 | __unsafe_unretained _ADModelMeta *meta = (__bridge _ADModelMeta *)(context -> modelMeta);
1356 | __unsafe_unretained _ADModelPropertyMeta *propertyMeta = [meta->_mapper objectForKey:(__bridge id)(_key)];
1357 | __unsafe_unretained id model = (__bridge id)(context->model);
1358 |
1359 | while (propertyMeta) {
1360 | if (propertyMeta -> _setter) {
1361 | //自定义方法ModelSetValueForProperty
1362 | ModelSetValueForProperty(model, (__bridge __unsafe_unretained id)_value, propertyMeta);
1363 | }
1364 | propertyMeta = propertyMeta -> _next;
1365 | };
1366 |
1367 | }
1368 |
1369 | /**
1370 | Apply function for model property meta, to set dictionary to model.
1371 |
1372 | @param _propertyMeta should not be nil, _YYModelPropertyMeta.
1373 | @param _context _context.model and _context.dictionary should not be nil.
1374 |
1375 | 对于model property 元素的函数应用,设置dictionary给model
1376 | 参数 _propertyMeta 不应该是nil,_YYModelPropertyMeta
1377 | 参数 _context _context.model 和 _context.dictionary 不应该是nil
1378 | */
1379 |
1380 | static void ModelSetWithPropertyMetaArrayFunction(const void *_propertyMeta,void * _context){
1381 | ModelSetContext *context = _context;
1382 | __unsafe_unretained NSDictionary *dictionary = (__bridge NSDictionary *)(context->dictionary);
1383 | __unsafe_unretained _ADModelPropertyMeta *propertyMeta = (__bridge _ADModelPropertyMeta *)(_propertyMeta);
1384 | if (!propertyMeta -> _setter) return;
1385 | id value = nil;
1386 |
1387 | if (propertyMeta -> _mappedToKeyArray) {
1388 | value = ADValueForMultiKeys(dictionary, propertyMeta->_mappedToKeyArray);
1389 | }else if(propertyMeta -> _mappedToKeyPath){
1390 | value = ADValueForKeyPath(dictionary, propertyMeta->_mappedToKeyPath);
1391 | }else{
1392 | value = [dictionary objectForKey:propertyMeta->_mappedToKey];
1393 | }
1394 |
1395 | if (value) {
1396 | __unsafe_unretained id model = (__bridge id)(context->model);
1397 | ModelSetValueForProperty(model, value, propertyMeta);
1398 | }
1399 |
1400 | }
1401 |
1402 | /**
1403 | Returns a valid JSON object (NSArray/NSDictionary/NSString/NSNumber/NSNull),
1404 | or nil if an error occurs.
1405 |
1406 | @param model Model, can be nil.
1407 | @return JSON object, nil if an error occurs.
1408 |
1409 | 返回一个有效的JSON对象(NSArray / NSDictionary / NSString / NSNumber / NSNull)
1410 | 如果有错误发生则返回nil
1411 |
1412 | 参数 model Model,可以是空
1413 | 返回 json对象,如果有错误发生返回nil
1414 | */
1415 | static id ModelToJSONObjectRecursive(NSObject *model){
1416 |
1417 |
1418 | if (!model || model == (id)kCFNull) return model;
1419 |
1420 | if ([model isKindOfClass:[NSString class]]) return model;
1421 |
1422 | if ([model isKindOfClass:[NSNumber class]]) return model;
1423 |
1424 | if ([model isKindOfClass:[NSDictionary class]]) {
1425 | // isValidJSONObject 判断是否为Json数据
1426 | if ([NSJSONSerialization isValidJSONObject:model]) return model;
1427 |
1428 | NSMutableDictionary *newDic = [NSMutableDictionary new];
1429 | [((NSDictionary *)model) enumerateKeysAndObjectsUsingBlock:^(NSString * key, id obj, BOOL * stop) {
1430 | NSString *stringKey = [key isKindOfClass:[NSString class]] ? key : key.description;
1431 | if (!stringKey) return;
1432 | id jsonObj = ModelToJSONObjectRecursive(obj);
1433 | if (!jsonObj) jsonObj = (id)kCFNull;
1434 | newDic[stringKey] = jsonObj;
1435 | }];
1436 | return newDic;
1437 | }
1438 | if ([model isKindOfClass:[NSSet class]]) {
1439 | NSArray *array = ((NSSet *)model).allObjects;
1440 | if ([NSJSONSerialization isValidJSONObject:array]) return array;
1441 | NSMutableArray *newArray = [NSMutableArray new];
1442 | for (id obj in array) {
1443 | if ([obj isKindOfClass:[NSString class]] || [obj isKindOfClass:[NSNumber class]]) {
1444 | [newArray addObject:obj];
1445 | }else{
1446 | id jsonObj = ModelToJSONObjectRecursive(obj);
1447 | if (jsonObj && jsonObj != (id)kCFNull) [newArray addObject:jsonObj];
1448 | }
1449 | }
1450 | return newArray;
1451 | }
1452 |
1453 | if ([model isKindOfClass:[NSArray class]]) {
1454 | if ([NSJSONSerialization isValidJSONObject:model]) return model;
1455 | NSMutableArray *newArray = [NSMutableArray new];
1456 | for (id obj in (NSArray *)model) {
1457 | if ([obj isKindOfClass:[NSString class]] || [obj isKindOfClass:[NSNumber class]]) {
1458 | [newArray addObject:obj];
1459 | }else{
1460 | id jsonObj = ModelToJSONObjectRecursive(obj);
1461 | if (jsonObj && jsonObj != (id)kCFNull) [newArray addObject:jsonObj];
1462 |
1463 | }
1464 | return newArray;
1465 | }
1466 |
1467 | }
1468 |
1469 |
1470 | if ([model isKindOfClass:[NSURL class]]) return ((NSURL *)model).absoluteString;
1471 |
1472 | if ([model isKindOfClass:[NSAttributedString class]]) return ((NSAttributedString *)model).string;
1473 |
1474 | if ([model isKindOfClass:[NSDate class]]) return [ADISODateFormatter() stringFromDate:(id)model];
1475 |
1476 | if ([model isKindOfClass:[NSData class]]) return nil;
1477 |
1478 | _ADModelMeta *modelMeta = [_ADModelMeta metaWithClass:[model class]];
1479 | if (!modelMeta || modelMeta -> _keyMappedCount == 0) return nil;
1480 | NSMutableDictionary *result = [[NSMutableDictionary alloc] initWithCapacity:64];
1481 | __unsafe_unretained NSMutableDictionary *dic = result; // avoid retain and release in block ,避免在Block里retain、release
1482 | [modelMeta->_mapper enumerateKeysAndObjectsUsingBlock:^(NSString *propertyMappedKey, _ADModelPropertyMeta *propertyMeta, BOOL * stop) {
1483 |
1484 | if (!propertyMeta -> _getter) return;
1485 | id value = nil;
1486 | if (propertyMeta -> _isCNumber) {
1487 | value = ModelCreateNumberFromProperty(model, propertyMeta);
1488 | }else if (propertyMeta -> _nsType){
1489 | id v = ((id (*)(id,SEL))(void *)objc_msgSend)((id)model,propertyMeta->_getter);
1490 | value = ModelToJSONObjectRecursive(v);
1491 | }else{
1492 | switch (propertyMeta->_type & ADEncodingTypeMask) {
1493 | case ADEncodingTypeObject:{
1494 | id v = ((id (*)(id,SEL))(void *)objc_msgSend)((id)model,propertyMeta->_getter);
1495 | value = ModelToJSONObjectRecursive(v);
1496 | if (value == (id)kCFNull) value = nil;
1497 | } break;
1498 | case ADEncodingTypeClass:{
1499 | Class v = ((Class(*)(id,SEL))(void *)objc_msgSend)((id)model,propertyMeta->_getter);
1500 | value = v ? NSStringFromClass(v) : nil;
1501 | } break;
1502 |
1503 | case ADEncodingTypeSEL:{
1504 | SEL v = ((SEL(*)(id,SEL))(void *)objc_msgSend)((id)model,propertyMeta->_getter);
1505 | value = v ? NSStringFromSelector(v) : nil;
1506 | }break;
1507 | default: break;
1508 | }
1509 | }
1510 |
1511 | if (!value) return;
1512 |
1513 |
1514 | if (propertyMeta->_mappedToKeyPath) {
1515 | NSMutableDictionary *superDic = dic;
1516 | NSMutableDictionary *subDic = nil;
1517 | for (NSUInteger i = 0, max = propertyMeta -> _mappedToKeyPath.count; i < max ; i++) {
1518 | NSString *key = propertyMeta->_mappedToKeyPath[i];
1519 | if ( i + 1 == max) {//end
1520 | if (!superDic[key]) superDic[key] = value;
1521 | break;
1522 | }
1523 | subDic = superDic[key];
1524 | if (subDic) {
1525 | if ([subDic isKindOfClass:[NSDictionary class]]) {
1526 | subDic = subDic.mutableCopy;
1527 | superDic[key] = subDic;
1528 | }else{
1529 | break;
1530 | }
1531 | } else {
1532 | subDic = [NSMutableDictionary new];
1533 | superDic[key] = subDic;
1534 | }
1535 | superDic = subDic;
1536 | subDic = nil;
1537 | }
1538 | }else{
1539 | if (!dic[propertyMeta->_mappedToKey]) {
1540 | dic[propertyMeta->_mappedToKey] = value;
1541 | }
1542 | }
1543 | }];
1544 |
1545 | if (modelMeta->_hasCustomTransformToDictionary) {
1546 | BOOL suc = [((id)model)modelCustomTransformToDictionary:dic];
1547 | if (!suc) return nil;
1548 | }
1549 | return result;
1550 |
1551 | }
1552 |
1553 |
1554 | /// Add indent to string (exclude first line)
1555 | /// 给string添加缩进(不包括第一行)
1556 | static NSMutableString *ModelDescriptionAddIndent(NSMutableString * desc,NSUInteger indent) {
1557 |
1558 | for (NSUInteger i = 0, max = desc.length; i < max ; i++) {
1559 | unichar c = [desc characterAtIndex:i];
1560 | if (c == '\n') {
1561 | for (NSUInteger j = 0; j < indent; j++) {
1562 | [desc insertString:@" " atIndex:i + 1];
1563 | }
1564 | i += indent * 4;
1565 | max += indent * 4;
1566 | }
1567 | }
1568 | return desc;
1569 | }
1570 |
1571 |
1572 | /// Generaate a description string
1573 | /// 创建一个字符串描述
1574 |
1575 | static NSString *ModelDescription(NSObject *model) {
1576 |
1577 | static const int kDescMaxLength = 100;
1578 | if (!model) return @"";
1579 | if (model == (id)kCFNull) return @"";
1580 | if (![model isKindOfClass:[NSObject class]]) return [NSString stringWithFormat:@"%@",model];
1581 |
1582 | _ADModelMeta *modelMeta = [_ADModelMeta metaWithClass:model.class];
1583 | switch (modelMeta->_nsType) {
1584 | case ADEncodingTypeNSString: case ADEncodingTypeNSMutableString: {
1585 | return [NSString stringWithFormat:@"\"%@\"",model];
1586 | }
1587 |
1588 | case ADEncodingTypeNSValue:
1589 | case ADEncodingTypeNSData: case ADEncodingTypeNSMutableData:{
1590 | NSString *tmp = model.description;
1591 | if (tmp.length > kDescMaxLength) {
1592 | tmp = [tmp substringToIndex:kDescMaxLength];
1593 | tmp = [tmp stringByAppendingString:@"..."];
1594 | }
1595 | return tmp;
1596 | }
1597 |
1598 |
1599 | case ADEncodingTypeNSNumber:
1600 | case ADEncodingTypeNSDecimalNumber:
1601 | case ADEncodingTypeNSDate:
1602 | case ADEncodingTypeNSURL:{
1603 | return [NSString stringWithFormat:@"%@",model];
1604 | }
1605 |
1606 |
1607 | case ADEncodingTypeNSSet: case ADEncodingTypeNSMutableSet: {
1608 | model = ((NSSet *)model).allObjects;
1609 | }//no break
1610 |
1611 | case ADEncodingTypeNSArray: case ADEncodingTypeNSMutableArray:{
1612 | NSArray *array = (id)model;
1613 | NSMutableString *desc = [NSMutableString new];
1614 | if (array.count == 0) {
1615 | return [desc stringByAppendingString:@"[]"];
1616 | }else {
1617 | [desc appendFormat:@"[\n"];
1618 | for (NSUInteger i = 0, max = array.count; i < max; i++) {
1619 | NSObject *obj = array[i];
1620 | [desc appendString:@" "];
1621 | [desc appendString:ModelDescriptionAddIndent(ModelDescription(obj).mutableCopy, 1)];
1622 | [desc appendString:(i + 1 == max) ? @"\n" : @";\n" ];
1623 | }
1624 | [desc appendString:@"]"];
1625 | return desc;
1626 | }
1627 | }
1628 |
1629 | case ADEncodingTypeNSDictionary: case ADEncodingTypeNSMutableDictionary: {
1630 | NSDictionary *dic = (id)model;
1631 | NSMutableString *desc = [NSMutableString new];
1632 | if (dic.count == 0) {
1633 | return [desc stringByAppendingString:@"{}"];
1634 | }else{
1635 | NSArray *keys = dic.allKeys;
1636 | [desc appendFormat:@"{\n"];
1637 | for (NSUInteger i = 0, max = keys.count; i < max; i++) {
1638 | NSString *key = keys[i];
1639 | NSObject *value = dic[key];
1640 | [desc appendString:@" "];
1641 | [desc appendFormat:@"%@ = %@",key,ModelDescriptionAddIndent(ModelDescription(value).mutableCopy, 1)];
1642 | [desc appendString:(i + 1 == max) ? @"\n" : @";\n"];
1643 | }
1644 | [desc appendString:@"}"];
1645 | }
1646 | return desc;
1647 | }
1648 |
1649 | default:{
1650 | NSMutableString *desc = [NSMutableString new];
1651 | [desc appendFormat:@"<%@: %p>",model.class,model];
1652 | if (modelMeta->_allPropertyMetas.count == 0)return desc;
1653 |
1654 | //sort property names ,排序property name
1655 | NSArray *properties = [modelMeta->_allPropertyMetas sortedArrayUsingComparator:^NSComparisonResult(_ADModelPropertyMeta *p1, _ADModelPropertyMeta *p2) {
1656 | return [p1->_name compare:p2->_name];
1657 | }];
1658 | [desc appendFormat:@"{\n"];
1659 | for (NSUInteger i = 0, max = properties.count; i < max; i++) {
1660 | _ADModelPropertyMeta *property = properties[i];
1661 | NSString *propertyDesc;
1662 | if (property->_isCNumber) {
1663 | NSNumber *num = ModelCreateNumberFromProperty(model, property);
1664 | propertyDesc = num.stringValue;
1665 | }else {
1666 |
1667 | switch (property -> _type & ADEncodingTypeMask) {
1668 |
1669 | case ADEncodingTypeObject: {
1670 | id v = ((id(*)(id, SEL))(void *)objc_msgSend)((id)model, property->_getter);
1671 |
1672 | propertyDesc = ModelDescription(v);
1673 | if (!propertyDesc)
1674 | propertyDesc = @"";
1675 | } break;
1676 |
1677 | case ADEncodingTypeClass: {
1678 | id v = ((id(*)(id,SEL))(void *)objc_msgSend)((id)model,property->_getter);
1679 | propertyDesc = ((NSObject *)v).description;
1680 | if (!propertyDesc) propertyDesc = @"";
1681 | } break;
1682 |
1683 | case ADEncodingTypeSEL: {
1684 | SEL sel = ((SEL(*)(id,SEL))(void *)objc_msgSend)((id)model,property->_getter);
1685 | if (sel) propertyDesc = NSStringFromSelector(sel);
1686 | else propertyDesc = @"";
1687 | } break;
1688 |
1689 |
1690 |
1691 | // case YYEncodingTypeStruct: case YYEncodingTypeUnion: {
1692 | // NSValue *value = [model valueForKey:property->_name];
1693 | // propertyDesc = value ? value.description : @"{unknown}";
1694 | // } break;
1695 | // default: propertyDesc = @"";
1696 | // }
1697 | // }
1698 | case ADEncodingTypeBlock:{
1699 | id block = ((id (*)(id,SEL))(void *) objc_msgSend)((id)model,property->_getter);
1700 | propertyDesc = block ? ((NSObject *)block).description : @"";
1701 | }break;
1702 |
1703 | case ADEncodingTypeCArray:
1704 | case ADEncodingTypeCString:
1705 | case ADEncodingTypePointer:{
1706 | void *pointer = ((void* (*)(id, SEL))(void *)objc_msgSend)((id)model,property->_getter);
1707 | propertyDesc = [NSString stringWithFormat:@"%p",pointer];
1708 | }break;
1709 |
1710 | case ADEncodingTypeStruct:
1711 | case ADEncodingTypeUnion:{
1712 | NSValue *value = [model valueForKey:property->_name];
1713 | propertyDesc = value ? value.description :@"{unknown}";
1714 | }break;
1715 |
1716 | default:propertyDesc = @"";
1717 |
1718 | }
1719 | }
1720 | propertyDesc = ModelDescriptionAddIndent(propertyDesc.mutableCopy, 1);
1721 | [desc appendFormat:@" %@ = %@",property->_name,propertyDesc];
1722 | [desc appendString:(i + 1 == max) ? @"\n" : @";\n"];
1723 | }
1724 | [desc appendFormat:@"}"];
1725 | return desc;
1726 | }
1727 | }
1728 | }
1729 |
1730 | @implementation NSObject(ADModel)
1731 |
1732 | //自定义方法
1733 | /**
1734 | _ad_dictionaryWithJSON 类方法,自定义的实现方法并没有相应的方法声明
1735 | 接收到了Json文件后先判断Json文件是否为空,判断有两种方式
1736 | if (!json || json == (id)kCFNull) kCFNull: NSNull的单例,也就是空的意思
1737 | 那为什么不用Null、Nil或nil呢?以下为nil,Nil,Null,NSNull的区别
1738 | Nil:对类进行赋空值
1739 | nil:对对象进行赋空值
1740 | Null:对C指针进行赋空操作,如字符串数组的首地址 char *name = NULL
1741 | NSNull:对组合值,如NSArray,Json而言,其内部有值,但值为空
1742 | 所以判断条件json不存在或json存在,但是其内部值为空,就直接返回nil
1743 | 若son存在且其内部有值,则创建一个空字典(dic)与空NSData(jsonData)值
1744 | 而后再判断,若son是NSDictionary类,就直接赋值给字典
1745 | 若是NSString类,就将其强制转化为NSString,而后用UTF-8编码处理赋值给jsonData
1746 | 若是NSData,就直接赋值给jsonData
1747 | 而后判断,而jsonData存在就代表son值转化为二进制NSData,用官方提供的JSON解析就可获取到所需的值赋值为dic,若发现解析后取到得值不是NSDictionary,就代表值不能为dict,因为不是同一类型值,就让dict为nil
1748 | 最后返回dict,在这个方法里相当于若JSON文件为NSDictionary类型或可解析成dict的NSData、NSString类型就赋值给dict返回,若不能则返回的dict为nil
1749 | */
1750 | +(NSDictionary *)_ad_dictionaryWithJSON:(id)json {
1751 | // kCFNull: NSNull的单例
1752 | if (!json || json == (id)kCFNull) return nil;
1753 | NSDictionary *dic = nil;
1754 | NSData *jsonData = nil;
1755 | if ([json isKindOfClass:[NSDictionary class]]) {
1756 | dic = json;
1757 | }else if([json isKindOfClass:[NSString class]]){
1758 | jsonData = [(NSString *)json dataUsingEncoding:NSUTF8StringEncoding];
1759 | }else if ([json isKindOfClass:[NSData class]]){
1760 | jsonData = json;
1761 | }
1762 |
1763 | if (jsonData) {
1764 | dic = [NSJSONSerialization JSONObjectWithData:jsonData options:kNilOptions error:NULL];
1765 | if (![dic isKindOfClass:[NSDictionary class]]) dic = nil;
1766 | }
1767 | return dic;
1768 | }
1769 |
1770 |
1771 | /**
1772 | Creates and returns a new instance of the receiver from a json.
1773 | This method is thread-safe.
1774 |
1775 | @param json A json object in `NSDictionary`, `NSString` or `NSData`.
1776 |
1777 | @return A new instance created from the json, or nil if an error occurs.
1778 | */
1779 | // 创建并返回一个新的列子,通过收取的一个Json文件
1780 | //这个方法是安全的
1781 | //参数:json Json包含的类型可以是NSDictionary、NSString、NSData
1782 | //返回:通过json创建的新的对象,如果解析错误就返回为空
1783 | +(instancetype)ad_modelWithJSON:(id)json{
1784 | //将json转换成字典
1785 | NSDictionary *dic = [self _ad_dictionaryWithJSON:json];
1786 | //通过字典转换成所需的实例
1787 | return [self ad_modelWithDictionary:dic];
1788 | }
1789 |
1790 | /**
1791 | 创建并返回一个新的列子通过参数的key-value字典
1792 | 这个方法是安全的
1793 | 参数:dictionary 一个key-value字典映射到列子的属性
1794 | 字典中任何一对无效的key-value都将被忽视
1795 | 返回一个新的列子通过字典创建的,如果解析失败返回为nil
1796 | 描述:字典中的key将映射到接收者的property name
1797 | 而值将设置给这个Property,如果这个值类型与property不匹配
1798 | 这个方法将试图转变这个值基于这些结果:
1799 | 结果详情看.h文件
1800 | */
1801 | +(instancetype)ad_modelWithDictionary:(NSDictionary *)dictionary{
1802 | if (!dictionary || dictionary == (id)kCFNull) return nil;
1803 | if (![dictionary isKindOfClass:[NSDictionary class]]) return nil;
1804 |
1805 | Class cls = [self class];
1806 | //_ADModelMeta保存class信息
1807 | _ADModelMeta *modelMeta = [_ADModelMeta metaWithClass:cls];
1808 |
1809 |
1810 | if (modelMeta->_hasCustomClassFromDictionary) {
1811 | cls = [cls modelCustomClassForDictionary:dictionary] ?: cls;
1812 | }
1813 |
1814 | NSObject *one = [cls new];
1815 | if ([one ad_modelSetWithDictionary:dictionary]) return one;
1816 | return nil;
1817 | }
1818 |
1819 | /**
1820 | 通过一个json对象设置调用者的property
1821 | json中任何无效的数据都将被忽视
1822 | 参数:json 一个关于NSDictionary,NSString,NSData的json对象将映射到调用者的property
1823 | 返回:是否成功
1824 |
1825 | */
1826 | -(BOOL)ad_modelSetWithJSON:(id)json{
1827 | NSDictionary *dic = [NSObject _ad_dictionaryWithJSON:json];
1828 | return [self ad_modelSetWithDictionary:dic];
1829 | }
1830 |
1831 |
1832 | /**
1833 | 通过一个key-value字典设置调用者的属性
1834 | 参数:dic 一个Key-Value字典映射到调用者property,字典中任何一对无效的Key-Value都将被忽视
1835 | 描述 dictionary中的Key将被映射到调用者的property name 而这个value将设置给property.
1836 | 如果value类型与property类型不匹配,这个方法将试图转换这个value基于以下这些值:
1837 | 返回 转换是否成功
1838 | */
1839 | -(BOOL)ad_modelSetWithDictionary:(NSDictionary *)dic {
1840 |
1841 | if (!dic || dic == (id)kCFNull) return NO;
1842 | if (![dic isKindOfClass:[NSDictionary class]]) return NO;
1843 |
1844 | _ADModelMeta *modelMeta = [_ADModelMeta metaWithClass:object_getClass(self)];
1845 |
1846 |
1847 | if (modelMeta -> _keyMappedCount == 0) return NO;
1848 |
1849 | if (modelMeta->_hasCustomWillTransformFromDictionary) {
1850 | dic = [((id)self) modelCustomWillTransformFromDictionary:dic];
1851 | if (![dic isKindOfClass:[NSDictionary class]]) return NO;
1852 | }
1853 |
1854 | ModelSetContext context = {0};
1855 | context.modelMeta = (__bridge void *)(modelMeta);
1856 | context.model = (__bridge void *)(self);
1857 | context.dictionary = (__bridge void *)(dic);
1858 |
1859 | if (modelMeta->_keyMappedCount >= CFDictionaryGetCount((CFDictionaryRef)dic)) {
1860 |
1861 | // CFDictionaryApplyFunction 对所有键值执行同一个方法
1862 | // @function CFDictionaryApplyFunction调用一次函数字典中的每个值。
1863 | // @param 字典。如果这个参数不是一个有效的CFDictionary,行为是未定义的。
1864 | // @param 调用字典中每一个值执行一次这个方法。如果这个参数不是一个指针指向一个函数的正确的原型,行为是未定义的。
1865 | // @param 一个用户自定义的上下文指针大小的值,通过第三个参数作用于这个函数,另有没使用此函数的。如果上下文不是预期的应用功能,则这个行为未定义。
1866 | // 第三个参数的意思,感觉像是让字典所有的键值去执行完方法后,保存在这个上下文指针(如自定义结构体)的指针(指向一个地址,所以自定义的结构体要用&取地址符)所指向的地址,也就是自定义的结构体中。如何保存那?就是这个上下文也会传到参数2中。
1867 | // 也就是dic里面的键值对全部执行完参数2的方法后保存在参数3中,其中参数3也会传到参数2的函数中。
1868 |
1869 | CFDictionaryApplyFunction((CFDictionaryRef)dic, ModelSetWithDictionaryFunction, &context);
1870 | if (modelMeta->_keyPathPropertyMetas) {
1871 |
1872 | CFArrayApplyFunction((CFArrayRef)modelMeta->_keyPathPropertyMetas, CFRangeMake(0, CFArrayGetCount((CFArrayRef)modelMeta->_keyPathPropertyMetas)), ModelSetWithPropertyMetaArrayFunction, &context);
1873 |
1874 | }
1875 | if (modelMeta->_multiKeysPropertyMetas) {
1876 | CFArrayApplyFunction((CFArrayRef)modelMeta->_multiKeysPropertyMetas, CFRangeMake(0, CFArrayGetCount((CFArrayRef)modelMeta->_multiKeysPropertyMetas)), ModelSetWithPropertyMetaArrayFunction, &context);
1877 | }
1878 |
1879 | } else {
1880 |
1881 | CFArrayApplyFunction((CFArrayRef)modelMeta->_allPropertyMetas, CFRangeMake(0, modelMeta->_keyMappedCount), ModelSetWithPropertyMetaArrayFunction, &context);
1882 |
1883 | } if (modelMeta->_hasCustomTransformFromDictionary) {
1884 |
1885 | return [((id)self) modelCustomTransformFromDictionary:dic];
1886 | }
1887 |
1888 | return YES;
1889 | }
1890 |
1891 |
1892 | /**
1893 | 产生一个json对象通过调用者的property
1894 | 返回一个NSDictionary或NSArray的json对象,如果解析失败返回一个Nil
1895 | 了解更多消息观看[NSJSONSerialization isValidJSONObject]
1896 | 描述:任何无效的property都将被忽视
1897 | 如果调用者是NSArray,NSDictionary或NSSet,他将转换里面的对象为json对象
1898 | */
1899 | - (id)ad_modelToJSONObject {
1900 | /*
1901 | Apple said:
1902 | The top level object is an NSArray or NSDictionary.
1903 | All objects are instances of NSString, NSNumber, NSArray, NSDictionary, or NSNull.
1904 | All dictionary keys are instances of NSString.
1905 | Numbers are not NaN or infinity.
1906 | */
1907 | /**
1908 | 苹果说:
1909 | 顶端等级的对象是NSArray 或 NSDictionary
1910 | 所有对象是关于NSString,NSNumber,NSArray,NSDictionary或NSNull的列子
1911 | NSString的所有的字典key是列子
1912 | Nunmber并不是NaN或无穷大的
1913 | */
1914 | id jsonObject = ModelToJSONObjectRecursive(self);
1915 | if([jsonObject isKindOfClass:[NSArray class]]) return jsonObject;
1916 | if ([jsonObject isKindOfClass:[NSDictionary class]]) return jsonObject;
1917 | return nil;
1918 |
1919 | }
1920 |
1921 | /**
1922 | 创建一个json string‘s data(json字符串二进制数据)通过调用者的property
1923 | 返回一个json string's data,如果解析失败返回为空
1924 | 描述:任何无效的property都将被忽视
1925 | 如果调用者是一个NSArray,NSDictionary或NSSet,它也将转换内部对象为一个Json字符串
1926 | */
1927 | - (NSData *)ad_modelToJSONData{
1928 | id jsonObject = [self ad_modelToJSONObject];
1929 | if (!jsonObject) return nil;
1930 |
1931 | return [NSJSONSerialization dataWithJSONObject:jsonObject options:0 error:NULL];
1932 | }
1933 |
1934 | /**
1935 | 创建一个json string通过调用者的property
1936 | 返回一个json string,如果错误产生返回一个nil
1937 | 描述 任何无效的property都将被忽视
1938 | 如果调用者是NSArray,NSDictionary或NSSet,它也将转换内部对象为一个json string
1939 | */
1940 | - (NSString *)ad_modelToJSONString{
1941 | NSData *jsonData = [self ad_modelToJSONData];
1942 | if (jsonData.length == 0) return nil;
1943 | return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
1944 |
1945 | }
1946 |
1947 | /**
1948 | copy一个对象通过调用者的properties
1949 | 返回一个copy的对象,如果解析失败则返回为nil
1950 | */
1951 | - (id)ad_modelCopy{
1952 |
1953 | if (self == (id)kCFNull) return self;
1954 | _ADModelMeta *modelMeta = [_ADModelMeta metaWithClass:self.class];
1955 | if (modelMeta->_nsType) return [self copy];
1956 |
1957 | NSObject *one = [self.class new];
1958 |
1959 | for (_ADModelPropertyMeta *propertyMeta in modelMeta->_allPropertyMetas) {
1960 |
1961 | if (!propertyMeta->_getter || !propertyMeta->_setter) continue;
1962 |
1963 | if (propertyMeta->_isCNumber) {
1964 | switch (propertyMeta->_type & ADEncodingTypeMask) {
1965 | case ADEncodingTypeBool:{
1966 | bool num = ((bool(*)(id,SEL))(void *)objc_msgSend)((id)self,propertyMeta->_getter);
1967 |
1968 | ((void(*)(id,SEL,BOOL))(void *)objc_msgSend)((id)one,propertyMeta->_setter,num);
1969 |
1970 | } break;
1971 | case ADEncodingTypeInt8:
1972 | case ADEncodingTypeUInt8:{
1973 | uint8_t num = ((BOOL (*)(id,SEL))(void *)objc_msgSend)((id)self,propertyMeta->_getter);
1974 | ((void (*)(id, SEL, uint8_t))(void *) objc_msgSend)((id)one, propertyMeta->_setter, num);
1975 | }break;
1976 | case ADEncodingTypeInt16:
1977 | case ADEncodingTypeUInt16: {
1978 | uint16_t num = ((uint16_t(*)(id,SEL))(void *)objc_msgSend)((id)self,propertyMeta->_getter);
1979 | ((void (*)(id,SEL,uint16_t))(void *)objc_msgSend)((id)one,propertyMeta->_setter,num);
1980 | }break;
1981 | case ADEncodingTypeInt32:
1982 | case ADEncodingTypeUInt32:{
1983 | uint32_t num = ((uint32_t(*)(id,SEL))(void *)objc_msgSend)((id)self,propertyMeta->_getter);
1984 | ((void (*)(id,SEL,uint32_t))(void *)objc_msgSend)((id)one,propertyMeta->_setter,num);
1985 | }break;
1986 | case ADEncodingTypeInt64:
1987 | case ADEncodingTypeUInt64:{
1988 | uint64_t num = ((uint64_t (*)(id,SEL))(void *)objc_msgSend)((id)self,propertyMeta->_getter);
1989 | ((void (*)(id,SEL,uint64_t))(void *)objc_msgSend)((id)one,propertyMeta->_setter,num);
1990 | }break;
1991 | case ADEncodingTypeFloat:{
1992 | float num = ((float(*)(id,SEL))(void *)objc_msgSend)((id)self,propertyMeta->_getter);
1993 | ((void(*)(id,SEL,float))(void *)objc_msgSend)((id)one,propertyMeta->_setter,num);
1994 | }break;
1995 | case ADEncodingTypeDouble:{
1996 | double num = ((double(*)(id,SEL))(void *)objc_msgSend)((id)self,propertyMeta->_getter);
1997 | ((void (*)(id,SEL,double))(void *)objc_msgSend)((id)one,propertyMeta->_setter,num);
1998 | }break;
1999 | case ADEncodingTypeLongDouble:{
2000 | long double num = ((long double (*)(id,SEL))(void *)objc_msgSend)((id)self,propertyMeta->_getter);
2001 | ((void(*)(id,SEL,long double))(void *)objc_msgSend)((id)one,propertyMeta->_setter,num);
2002 | }//break;commented for code coverage in next line
2003 | default:
2004 | break;
2005 | }
2006 | }else {
2007 | switch (propertyMeta->_type & ADEncodingTypeMask) {
2008 | case ADEncodingTypeObject:
2009 | case ADEncodingTypeClass:
2010 | case ADEncodingTypeBlock:{
2011 | id value = ((id(*)(id,SEL))(void *)objc_msgSend)((id)self,propertyMeta->_getter);
2012 | ((void(*)(id,SEL,id))(void *)objc_msgSend)((id)one,propertyMeta->_setter,value);
2013 | } break;
2014 | case ADEncodingTypeSEL:
2015 | case ADEncodingTypePointer:
2016 | case ADEncodingTypeCString:{
2017 | size_t value = ((size_t(*)(id,SEL))(void *)objc_msgSend)((id)self,propertyMeta->_getter);
2018 | ((void (*)(id,SEL,size_t))(void *)objc_msgSend)((id)one,propertyMeta->_setter,value);
2019 | }break;
2020 | case ADEncodingTypeStruct:
2021 | case ADEncodingTypeUnion:{
2022 | @try {
2023 | NSValue *value = [self valueForKey:NSStringFromSelector(propertyMeta->_getter)];
2024 | if (value) {
2025 | [one setValue:value forKey:propertyMeta->_name];
2026 | }
2027 | } @catch (NSException *exception) { }
2028 | }// break; commented for code coverage in next line
2029 |
2030 | default:
2031 | break;
2032 | }
2033 | }
2034 | }
2035 | return one;
2036 | }
2037 |
2038 | /**
2039 | Encode the receiver's properties to a coder.
2040 |
2041 | @param aCoder An archiver object.
2042 | 将调用者property编码为一个Coder
2043 | 参数 aCoder 一个对象档案
2044 | */
2045 | -(void)ad_modelEncodeWithCoder:(NSCoder *)aCoder{
2046 | if (!aCoder) return;
2047 | if (self == (id)kCFNull) {
2048 | [((id)self)encodeWithCoder:aCoder];
2049 | return;
2050 | }
2051 |
2052 | _ADModelMeta *modelMeta = [_ADModelMeta metaWithClass:self.class];
2053 | if (modelMeta->_nsType) {
2054 | [((id)self)encodeWithCoder:aCoder];
2055 | return;
2056 | }
2057 |
2058 |
2059 | for (_ADModelPropertyMeta *propertyMeta in modelMeta->_allPropertyMetas) {
2060 | if (!propertyMeta->_getter) return;
2061 |
2062 | if (propertyMeta->_isCNumber) {
2063 | NSNumber *value = ModelCreateNumberFromProperty(self, propertyMeta);
2064 | if (value) [aCoder encodeObject:value forKey:propertyMeta->_name];
2065 | }else{
2066 | switch (propertyMeta->_type & ADEncodingTypeMask) {
2067 | case ADEncodingTypeObject:{
2068 | id value = ((id(*)(id,SEL))(void *)objc_msgSend)((id)self,propertyMeta->_getter);
2069 | if (value && (propertyMeta->_nsType || [value respondsToSelector:@selector(encodeWithCoder:)])) {
2070 | if ([value isKindOfClass:[NSValue class]]) {
2071 | if ([value isKindOfClass:[NSNumber class]]) {
2072 | [aCoder encodeObject:value forKey:propertyMeta->_name];
2073 | }
2074 | }else {
2075 | [aCoder encodeObject:value forKey:propertyMeta->_name];
2076 | }
2077 | }
2078 | } break;
2079 | case ADEncodingTypeSEL:{
2080 | SEL value = ((SEL (*)(id,SEL))(void *)objc_msgSend)((id)self,propertyMeta->_getter);
2081 | if (value) {
2082 | NSString *str = NSStringFromSelector(value);
2083 | [aCoder encodeObject:str forKey:propertyMeta->_name];
2084 | }
2085 | }break;
2086 | case ADEncodingTypeStruct:
2087 | case ADEncodingTypeUnion:{
2088 | if (propertyMeta->_isKVCCompatible && propertyMeta->_isStructAvailableForKeyedArchiver) {
2089 | @try {
2090 | NSValue *value = [self valueForKey:NSStringFromSelector(propertyMeta->_getter)];
2091 | [aCoder encodeObject:value forKey:propertyMeta->_name];
2092 | } @catch (NSException *exception) { }
2093 | }
2094 | }break;
2095 | default:
2096 | break;
2097 | }
2098 | }
2099 | }
2100 | }
2101 |
2102 | /**
2103 | Decode the receiver's properties from a decoder.
2104 |
2105 | @param aDecoder An archiver object.
2106 |
2107 | @return self
2108 | 通过一个decoder解码成对象的property
2109 | 参数 aDecoder 一个对象档案
2110 | 返回 调用者自己
2111 | */
2112 | -(id)ad_modelInitWithCoder:(NSCoder *)aDecoder{
2113 | if (!aDecoder) return self;
2114 | if (self == (id)kCFNull) return self;
2115 | _ADModelMeta *modelMeta = [_ADModelMeta metaWithClass:self.class];
2116 | if (modelMeta->_nsType) return self;
2117 |
2118 | for (_ADModelPropertyMeta *propertyMeta in modelMeta->_allPropertyMetas) {
2119 | if (!propertyMeta->_setter) continue;
2120 | if (propertyMeta->_isCNumber) {
2121 | NSNumber *value = [aDecoder decodeObjectForKey:propertyMeta->_name];
2122 | if ([value isKindOfClass:[NSNumber class]]) {
2123 | ModelSetNumberToProperty(self, value, propertyMeta);
2124 | [value class];
2125 | }
2126 | }else {
2127 | ADEncodingType type = propertyMeta->_type & ADEncodingTypeMask;
2128 | switch (type) {
2129 | case ADEncodingTypeObject:{
2130 | id value = [aDecoder decodeObjectForKey:propertyMeta->_name];
2131 | ((void (*)(id,SEL,id))(void *)objc_msgSend)((id)self,propertyMeta->_setter,value);
2132 | }break;
2133 |
2134 | case ADEncodingTypeSEL:{
2135 | NSString *str = [aDecoder decodeObjectForKey:propertyMeta->_name];
2136 | if ([str isKindOfClass:[NSString class]]) {
2137 | SEL sel = NSSelectorFromString(str);
2138 | ((void (*)(id,SEL,SEL))(void *)objc_msgSend)((id)self,propertyMeta->_setter,sel);
2139 | }
2140 | }break;
2141 |
2142 | case ADEncodingTypeStruct:
2143 | case ADEncodingTypeUnion:{
2144 | if (propertyMeta->_isKVCCompatible) {
2145 | @try {
2146 | NSValue *value = [aDecoder decodeObjectForKey:propertyMeta->_name];
2147 | if ((value)) [self setValue:value forKey:propertyMeta->_name];
2148 | } @catch (NSException *exception) { }
2149 | }
2150 | }break;
2151 | default:
2152 | break;
2153 | }
2154 | }
2155 | }
2156 | return self;
2157 |
2158 |
2159 | }
2160 |
2161 | /**
2162 | Get a hash code with the receiver's properties.
2163 |
2164 | @return Hash code.
2165 | 通过调用者Property获取到一个哈希Code
2166 | 返回 hashCode
2167 | */
2168 | -(NSUInteger)ad_modelHash{
2169 | if (self == (id)kCFNull) return [self hash];
2170 | _ADModelMeta *modelMeta = [_ADModelMeta metaWithClass:self.class];
2171 | if (modelMeta->_nsType) return [self hash];
2172 |
2173 | NSUInteger value = 0;
2174 | NSUInteger count = 0;
2175 | for (_ADModelPropertyMeta *properytyMeta in modelMeta->_allPropertyMetas) {
2176 | if (!properytyMeta->_isKVCCompatible) continue;
2177 | value ^= [[self valueForKey:NSStringFromSelector(properytyMeta->_getter)] hash];
2178 | count++;
2179 | }
2180 | if (count == 0) value = (long)((__bridge void *)self);
2181 | return value;
2182 | }
2183 |
2184 | /**
2185 | Compares the receiver with another object for equality, based on properties.
2186 |
2187 | @param model Another object.
2188 |
2189 | @return `YES` if the reciever is equal to the object, otherwise `NO`.
2190 | 比较这个调用者和另一个对象是否相同,基于property
2191 | 参数 model 另一个对象
2192 | 返回 如果两个对象相同则返回YES 否则为NO
2193 | */
2194 | -(BOOL)ad_modelIsEqual:(id)model{
2195 | if (self == model) return YES;
2196 | if (![model isMemberOfClass:self.class]) return NO;
2197 | _ADModelMeta *modelMeta = [_ADModelMeta metaWithClass:self.class];
2198 | if (modelMeta->_nsType) return [self isEqual:model];
2199 | if ([self hash] != [model hash]) return NO;
2200 |
2201 | for (_ADModelPropertyMeta *propertyMeta in modelMeta->_allPropertyMetas) {
2202 | if (!propertyMeta->_isKVCCompatible) continue;
2203 | id this = [self valueForKey:NSStringFromSelector(propertyMeta->_getter)];
2204 | id that = [model valueForKey:NSStringFromSelector(propertyMeta->_getter)];
2205 | if (this == that) continue;
2206 | if (this == nil || that == nil) return NO;
2207 | if (![this isEqual:that]) return NO;
2208 | }
2209 | return YES;
2210 | }
2211 |
2212 |
2213 | /**
2214 | Description method for debugging purposes based on properties.
2215 |
2216 | @return A string that describes the contents of the receiver.
2217 | 描述方法为基于属性的Debug目的(Debug模式中基于属性的描述方法)
2218 | 返回一个字符串描述调用者的内容
2219 | */
2220 | -(NSString *)ad_modelDescription{
2221 | return ModelDescription(self);
2222 | }
2223 |
2224 | @end
2225 |
2226 |
2227 | @implementation NSArray (ADModel)
2228 |
2229 | /**
2230 | 通过一个json-array创建并返回一个数组
2231 | 这个方法是安全的
2232 |
2233 | 参数:cls array中的对象类
2234 | 参数:json 一个json array 关于"NSArray","NSString"或"NSData"
2235 | 列子:[{"name","Mary"},{name:"Joe"}]
2236 | 返回一个数组,如果解析错误则返回nil
2237 | */
2238 | +(NSArray *)ad_modelArrayWithClass:(Class)cls json:(id)json{
2239 | if (!json) return nil;
2240 | NSArray *arr = nil;
2241 | NSData *jsonData = nil;
2242 | if ([json isKindOfClass:[NSArray class]]) {
2243 | arr = json;
2244 | }else if([json isKindOfClass:[NSString class]]){
2245 | jsonData = [(NSString *)json dataUsingEncoding:NSUTF8StringEncoding];
2246 | }else if([json isKindOfClass:[NSData class]]){
2247 | jsonData = json;
2248 | }
2249 | if (jsonData) {
2250 | arr = [NSJSONSerialization JSONObjectWithData:jsonData options:kNilOptions error:NULL];
2251 | if (![arr isKindOfClass:[NSArray class]]) arr = nil;
2252 | }
2253 | return [self ad_modelArrayWithClass:cls array:arr];
2254 |
2255 | }
2256 | //自定义方法,未在.h声明
2257 | + (NSArray *)ad_modelArrayWithClass:(Class)cls array:(NSArray *)arr{
2258 |
2259 | if (!cls || !arr) return nil;
2260 | NSMutableArray *result = [NSMutableArray new];
2261 | for (NSDictionary *dic in arr) {
2262 | if (![dic isKindOfClass:[NSDictionary class]]) continue;
2263 | NSObject *obj = [cls ad_modelWithDictionary:dic];
2264 | if (obj) [result addObject:obj];
2265 | }
2266 | return result;
2267 | }
2268 | @end
2269 |
2270 | @implementation NSDictionary(ADModel)
2271 |
2272 | /**
2273 | @return A dictionary, or nil if an error occurs.
2274 | 通过一个json文件创建并返回一个字典
2275 | 这个方法是安全的
2276 | 参数cls 字典中value的对象class
2277 | 参数json 一个json的字典是"NSDictionary","NSStirng"或"NSData"的
2278 | 列子: {"user1":{"name","Mary"}, "user2": {name:"Joe"}}
2279 | */
2280 | +(NSDictionary *)ad_modelDictionaryWithClass:(Class)cls json:(id)json{
2281 | if (!json) return nil;
2282 | NSDictionary *dic = nil;
2283 | NSData *jsonData = nil;
2284 | if ([json isKindOfClass:[NSDictionary class]]) {
2285 | dic = json;
2286 | }else if([json isKindOfClass:[NSString class]]){
2287 | jsonData = [(NSString *)json dataUsingEncoding:NSUTF8StringEncoding];
2288 | }else if([json isKindOfClass:[NSData class]]){
2289 | jsonData = json;
2290 | }
2291 | if (jsonData) {
2292 | dic = [NSJSONSerialization JSONObjectWithData:jsonData options:kNilOptions error:NULL];
2293 | if (![dic isKindOfClass:[NSDictionary class]]) dic = nil;
2294 | }
2295 | return [self ad_modelDictionaryWithClass:cls dictionary:dic];
2296 | }
2297 | //自定义方法,未在.h声明
2298 | +(NSDictionary *)ad_modelDictionaryWithClass:(Class)cls dictionary:(NSDictionary *)dic {
2299 | if (!cls || !dic) return nil;
2300 | NSMutableDictionary *result = [NSMutableDictionary new];
2301 | for (NSString *key in dic.allKeys) {
2302 | if (![key isKindOfClass:[NSString class]]) continue;
2303 | NSObject *obj = [cls ad_modelWithDictionary:dic[key]];
2304 | if (obj) result[key] = obj;
2305 | }
2306 | return result;
2307 | }
2308 | @end
--------------------------------------------------------------------------------