├── .codecov.yml ├── .gitignore ├── .travis.yml ├── Benchmark ├── ModelBenchmark.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ └── contents.xcworkspacedata ├── ModelBenchmark.xcworkspace │ └── contents.xcworkspacedata ├── ModelBenchmark │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Assets.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── GithubUserModel │ │ ├── GitHubUser.h │ │ ├── GitHubUser.m │ │ ├── SwiftGithubUser.swift │ │ └── user.json │ ├── Info.plist │ ├── ModelBenchmark-Bridging-Header.h │ ├── SwiftModel.swift │ ├── ViewController.h │ ├── ViewController.m │ ├── WeiboModel │ │ ├── DateFormatter.h │ │ ├── DateFormatter.m │ │ ├── FEWeiboModel.h │ │ ├── FEWeiboModel.m │ │ ├── JSWeiboModel.h │ │ ├── JSWeiboModel.m │ │ ├── MJWeiboModel.h │ │ ├── MJWeiboModel.m │ │ ├── MTWeiboModel.h │ │ ├── MTWeiboModel.m │ │ ├── YYWeiboModel.h │ │ ├── YYWeiboModel.m │ │ └── weibo.json │ └── main.m ├── Podfile ├── Result.numbers └── Result.png ├── Framework ├── Info.plist └── YYModel.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ └── contents.xcworkspacedata │ └── xcshareddata │ └── xcschemes │ └── YYModel.xcscheme ├── LICENSE ├── README.md ├── YYModel.podspec ├── YYModel ├── NSObject+YYModel.h ├── NSObject+YYModel.m ├── YYClassInfo.h ├── YYClassInfo.m └── YYModel.h └── YYModelTests ├── Info.plist ├── YYTestAutoTypeConvert.m ├── YYTestBlacklistWhitelist.m ├── YYTestClassInfo.m ├── YYTestCopyingAndCoding.m ├── YYTestCustomClass.m ├── YYTestCustomTransform.m ├── YYTestDescription.m ├── YYTestHelper.h ├── YYTestHelper.m ├── YYTestModelMapper.m ├── YYTestModelToJSON.m └── YYTestNestModel.m /.codecov.yml: -------------------------------------------------------------------------------- 1 | coverage: 2 | ignore: 3 | - YYModelTests/* 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OS X 2 | .DS_Store 3 | 4 | # Xcode 5 | # 6 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 7 | 8 | ## Build generated 9 | build/ 10 | DerivedData/ 11 | 12 | ## Various settings 13 | *.pbxuser 14 | !default.pbxuser 15 | *.mode1v3 16 | !default.mode1v3 17 | *.mode2v3 18 | !default.mode2v3 19 | *.perspectivev3 20 | !default.perspectivev3 21 | xcuserdata/ 22 | 23 | ## Other 24 | *.moved-aside 25 | *.xccheckout 26 | *.xcuserstate 27 | *.xcscmblueprint 28 | 29 | ## Obj-C/Swift specific 30 | *.hmap 31 | *.ipa 32 | *.dSYM.zip 33 | *.dSYM 34 | 35 | # CocoaPods 36 | # 37 | # We recommend against adding the Pods directory to your .gitignore. However 38 | # you should judge for yourself, the pros and cons are mentioned at: 39 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 40 | # 41 | # Pods/ 42 | 43 | # Carthage 44 | # 45 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 46 | # Carthage/Checkouts 47 | 48 | Carthage/Build 49 | 50 | # fastlane 51 | # 52 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 53 | # screenshots whenever they are needed. 54 | # For more information about the recommended setup visit: 55 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 56 | 57 | fastlane/report.xml 58 | fastlane/Preview.html 59 | fastlane/screenshots 60 | fastlane/test_output 61 | 62 | # Code Injection 63 | # 64 | # After new code Injection tools there's a generated folder /iOSInjectionProject 65 | # https://github.com/johnno1962/injectionforxcode 66 | 67 | iOSInjectionProject/ 68 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | osx_image: xcode8 3 | xcode_project: Framework/YYModel.xcodeproj 4 | xcode_scheme: YYModel 5 | 6 | before_install: 7 | - env 8 | - xcodebuild -version 9 | - xcodebuild -showsdks 10 | - xcpretty --version 11 | - xcrun instruments -w 'iPhone 7' || sleep 15 12 | 13 | script: 14 | - set -o pipefail 15 | - xcodebuild clean build -project "$TRAVIS_XCODE_PROJECT" -scheme "$TRAVIS_XCODE_SCHEME" | xcpretty 16 | - xcodebuild test -project "$TRAVIS_XCODE_PROJECT" -scheme "$TRAVIS_XCODE_SCHEME" -destination "name=iPhone 7" -enableCodeCoverage YES | xcpretty 17 | 18 | after_success: 19 | - bash <(curl -s https://codecov.io/bash) 20 | -------------------------------------------------------------------------------- /Benchmark/ModelBenchmark.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Benchmark/ModelBenchmark.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Benchmark/ModelBenchmark/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // ModelBenchmark 4 | // 5 | // Created by ibireme on 15/9/18. 6 | // Copyright (c) 2015 ibireme. 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 | -------------------------------------------------------------------------------- /Benchmark/ModelBenchmark/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // ModelBenchmark 4 | // 5 | // Created by ibireme on 15/9/18. 6 | // Copyright (c) 2015 ibireme. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | @end 13 | 14 | @implementation AppDelegate 15 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 16 | return YES; 17 | } 18 | @end 19 | -------------------------------------------------------------------------------- /Benchmark/ModelBenchmark/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | } 88 | ], 89 | "info" : { 90 | "version" : 1, 91 | "author" : "xcode" 92 | } 93 | } -------------------------------------------------------------------------------- /Benchmark/ModelBenchmark/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 | -------------------------------------------------------------------------------- /Benchmark/ModelBenchmark/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 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /Benchmark/ModelBenchmark/GithubUserModel/GitHubUser.h: -------------------------------------------------------------------------------- 1 | // 2 | // GitHubUser.h 3 | // ModelBenchmark 4 | // 5 | // Created by ibireme on 15/9/18. 6 | // Copyright (c) 2015 ibireme. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import 12 | #import 13 | #import 14 | 15 | // https://api.github.com/users/facebook 16 | 17 | /// manually GHUser 18 | @interface GHUser : NSObject 19 | @property (nonatomic, strong) NSString *login; 20 | @property (nonatomic, assign) UInt64 userID; 21 | @property (nonatomic, strong) NSString *avatarURL; 22 | @property (nonatomic, strong) NSString *gravatarID; 23 | @property (nonatomic, strong) NSString *url; 24 | @property (nonatomic, strong) NSString *htmlURL; 25 | @property (nonatomic, strong) NSString *followersURL; 26 | @property (nonatomic, strong) NSString *followingURL; 27 | @property (nonatomic, strong) NSString *gistsURL; 28 | @property (nonatomic, strong) NSString *starredURL; 29 | @property (nonatomic, strong) NSString *subscriptionsURL; 30 | @property (nonatomic, strong) NSString *organizationsURL; 31 | @property (nonatomic, strong) NSString *reposURL; 32 | @property (nonatomic, strong) NSString *eventsURL; 33 | @property (nonatomic, strong) NSString *receivedEventsURL; 34 | @property (nonatomic, strong) NSString *type; 35 | @property (nonatomic, assign) BOOL siteAdmin; 36 | @property (nonatomic, strong) NSString *name; 37 | @property (nonatomic, strong) NSString *company; 38 | @property (nonatomic, strong) NSString *blog; 39 | @property (nonatomic, strong) NSString *location; 40 | @property (nonatomic, strong) NSString *email; 41 | @property (nonatomic, strong) NSString *hireable; 42 | @property (nonatomic, strong) NSString *bio; 43 | @property (nonatomic, assign) UInt32 publicRepos; 44 | @property (nonatomic, assign) UInt32 publicGists; 45 | @property (nonatomic, assign) UInt32 followers; 46 | @property (nonatomic, assign) UInt32 following; 47 | @property (nonatomic, strong) NSDate *createdAt; 48 | @property (nonatomic, strong) NSDate *updatedAt; 49 | @property (nonatomic, strong) NSValue *test; 50 | - (instancetype)initWithJSONDictionary:(NSDictionary *)dictionary; 51 | - (NSDictionary *)convertToJSONDictionary; 52 | @end 53 | 54 | /// YYModel GHUser 55 | @interface YYGHUser : NSObject 56 | @property (nonatomic, strong) NSString *login; 57 | @property (nonatomic, assign) UInt64 userID; 58 | @property (nonatomic, strong) NSString *avatarURL; 59 | @property (nonatomic, strong) NSString *gravatarID; 60 | @property (nonatomic, strong) NSString *url; 61 | @property (nonatomic, strong) NSString *htmlURL; 62 | @property (nonatomic, strong) NSString *followersURL; 63 | @property (nonatomic, strong) NSString *followingURL; 64 | @property (nonatomic, strong) NSString *gistsURL; 65 | @property (nonatomic, strong) NSString *starredURL; 66 | @property (nonatomic, strong) NSString *subscriptionsURL; 67 | @property (nonatomic, strong) NSString *organizationsURL; 68 | @property (nonatomic, strong) NSString *reposURL; 69 | @property (nonatomic, strong) NSString *eventsURL; 70 | @property (nonatomic, strong) NSString *receivedEventsURL; 71 | @property (nonatomic, strong) NSString *type; 72 | @property (nonatomic, assign) BOOL siteAdmin; 73 | @property (nonatomic, strong) NSString *name; 74 | @property (nonatomic, strong) NSString *company; 75 | @property (nonatomic, strong) NSString *blog; 76 | @property (nonatomic, strong) NSString *location; 77 | @property (nonatomic, strong) NSString *email; 78 | @property (nonatomic, strong) NSString *hireable; 79 | @property (nonatomic, strong) NSString *bio; 80 | @property (nonatomic, assign) UInt32 publicRepos; 81 | @property (nonatomic, assign) UInt32 publicGists; 82 | @property (nonatomic, assign) UInt32 followers; 83 | @property (nonatomic, assign) UInt32 following; 84 | @property (nonatomic, strong) NSDate *createdAt; 85 | @property (nonatomic, strong) NSDate *updatedAt; 86 | @property (nonatomic, strong) NSValue *test; 87 | @end 88 | 89 | /// JSONModel GHUser 90 | @interface JSGHUser : JSONModel 91 | @property (nonatomic, strong) NSString *login; 92 | @property (nonatomic, assign) UInt64 userID; 93 | @property (nonatomic, strong) NSString *avatarURL; 94 | @property (nonatomic, strong) NSString *gravatarID; 95 | @property (nonatomic, strong) NSString *url; 96 | @property (nonatomic, strong) NSString *htmlURL; 97 | @property (nonatomic, strong) NSString *followersURL; 98 | @property (nonatomic, strong) NSString *followingURL; 99 | @property (nonatomic, strong) NSString *gistsURL; 100 | @property (nonatomic, strong) NSString *starredURL; 101 | @property (nonatomic, strong) NSString *subscriptionsURL; 102 | @property (nonatomic, strong) NSString *organizationsURL; 103 | @property (nonatomic, strong) NSString *reposURL; 104 | @property (nonatomic, strong) NSString *eventsURL; 105 | @property (nonatomic, strong) NSString *receivedEventsURL; 106 | @property (nonatomic, strong) NSString *type; 107 | @property (nonatomic, assign) BOOL siteAdmin; 108 | @property (nonatomic, strong) NSString *name; 109 | @property (nonatomic, strong) NSString *company; 110 | @property (nonatomic, strong) NSString *blog; 111 | @property (nonatomic, strong) NSString *location; 112 | @property (nonatomic, strong) NSString *email; 113 | @property (nonatomic, strong) NSString *hireable; 114 | @property (nonatomic, strong) NSString *bio; 115 | @property (nonatomic, assign) unsigned publicRepos; 116 | @property (nonatomic, assign) unsigned publicGists; 117 | @property (nonatomic, assign) unsigned followers; 118 | @property (nonatomic, assign) unsigned following; 119 | @property (nonatomic, strong) NSDate *createdAt; 120 | @property (nonatomic, strong) NSDate *updatedAt; 121 | @property (nonatomic, strong) NSValue *test; 122 | // JSONModel doesn't support UInt32 in armv7... Replace UInt32 with unsigned 123 | @end 124 | 125 | /// Mantle GHUser 126 | @interface MTGHUser : MTLModel 127 | @property (nonatomic, strong) NSString *login; 128 | @property (nonatomic, assign) UInt64 userID; 129 | @property (nonatomic, strong) NSString *avatarURL; 130 | @property (nonatomic, strong) NSString *gravatarID; 131 | @property (nonatomic, strong) NSString *url; 132 | @property (nonatomic, strong) NSString *htmlURL; 133 | @property (nonatomic, strong) NSString *followersURL; 134 | @property (nonatomic, strong) NSString *followingURL; 135 | @property (nonatomic, strong) NSString *gistsURL; 136 | @property (nonatomic, strong) NSString *starredURL; 137 | @property (nonatomic, strong) NSString *subscriptionsURL; 138 | @property (nonatomic, strong) NSString *organizationsURL; 139 | @property (nonatomic, strong) NSString *reposURL; 140 | @property (nonatomic, strong) NSString *eventsURL; 141 | @property (nonatomic, strong) NSString *receivedEventsURL; 142 | @property (nonatomic, strong) NSString *type; 143 | @property (nonatomic, assign) BOOL siteAdmin; 144 | @property (nonatomic, strong) NSString *name; 145 | @property (nonatomic, strong) NSString *company; 146 | @property (nonatomic, strong) NSString *blog; 147 | @property (nonatomic, strong) NSString *location; 148 | @property (nonatomic, strong) NSString *email; 149 | @property (nonatomic, strong) NSString *hireable; 150 | @property (nonatomic, strong) NSString *bio; 151 | @property (nonatomic, assign) UInt32 publicRepos; 152 | @property (nonatomic, assign) UInt32 publicGists; 153 | @property (nonatomic, assign) UInt32 followers; 154 | @property (nonatomic, assign) UInt32 following; 155 | @property (nonatomic, strong) NSDate *createdAt; 156 | @property (nonatomic, strong) NSDate *updatedAt; 157 | @property (nonatomic, strong) NSValue *test; 158 | @end 159 | 160 | /// FastEasyMapping GHUser 161 | @interface FEGHUser : NSObject 162 | @property (nonatomic, strong) NSString *login; 163 | @property (nonatomic, assign) UInt64 userID; 164 | @property (nonatomic, strong) NSString *avatarURL; 165 | @property (nonatomic, strong) NSString *gravatarID; 166 | @property (nonatomic, strong) NSString *url; 167 | @property (nonatomic, strong) NSString *htmlURL; 168 | @property (nonatomic, strong) NSString *followersURL; 169 | @property (nonatomic, strong) NSString *followingURL; 170 | @property (nonatomic, strong) NSString *gistsURL; 171 | @property (nonatomic, strong) NSString *starredURL; 172 | @property (nonatomic, strong) NSString *subscriptionsURL; 173 | @property (nonatomic, strong) NSString *organizationsURL; 174 | @property (nonatomic, strong) NSString *reposURL; 175 | @property (nonatomic, strong) NSString *eventsURL; 176 | @property (nonatomic, strong) NSString *receivedEventsURL; 177 | @property (nonatomic, strong) NSString *type; 178 | @property (nonatomic, assign) BOOL siteAdmin; 179 | @property (nonatomic, strong) NSString *name; 180 | @property (nonatomic, strong) NSString *company; 181 | @property (nonatomic, strong) NSString *blog; 182 | @property (nonatomic, strong) NSString *location; 183 | @property (nonatomic, strong) NSString *email; 184 | @property (nonatomic, strong) NSString *hireable; 185 | @property (nonatomic, strong) NSString *bio; 186 | @property (nonatomic, assign) UInt32 publicRepos; 187 | @property (nonatomic, assign) UInt32 publicGists; 188 | @property (nonatomic, assign) UInt32 followers; 189 | @property (nonatomic, assign) UInt32 following; 190 | @property (nonatomic, strong) NSDate *createdAt; 191 | @property (nonatomic, strong) NSDate *updatedAt; 192 | @property (nonatomic, strong) NSValue *test; 193 | + (FEMMapping *)defaultMapping; 194 | @end 195 | 196 | /// MJExtension GHUser 197 | @interface MJGHUser : NSObject 198 | @property (nonatomic, strong) NSString *login; 199 | @property (nonatomic, assign) UInt64 userID; 200 | @property (nonatomic, strong) NSString *avatarURL; 201 | @property (nonatomic, strong) NSString *gravatarID; 202 | @property (nonatomic, strong) NSString *url; 203 | @property (nonatomic, strong) NSString *htmlURL; 204 | @property (nonatomic, strong) NSString *followersURL; 205 | @property (nonatomic, strong) NSString *followingURL; 206 | @property (nonatomic, strong) NSString *gistsURL; 207 | @property (nonatomic, strong) NSString *starredURL; 208 | @property (nonatomic, strong) NSString *subscriptionsURL; 209 | @property (nonatomic, strong) NSString *organizationsURL; 210 | @property (nonatomic, strong) NSString *reposURL; 211 | @property (nonatomic, strong) NSString *eventsURL; 212 | @property (nonatomic, strong) NSString *receivedEventsURL; 213 | @property (nonatomic, strong) NSString *type; 214 | @property (nonatomic, assign) BOOL siteAdmin; 215 | @property (nonatomic, strong) NSString *name; 216 | @property (nonatomic, strong) NSString *company; 217 | @property (nonatomic, strong) NSString *blog; 218 | @property (nonatomic, strong) NSString *location; 219 | @property (nonatomic, strong) NSString *email; 220 | @property (nonatomic, strong) NSString *hireable; 221 | @property (nonatomic, strong) NSString *bio; 222 | @property (nonatomic, assign) UInt32 publicRepos; 223 | @property (nonatomic, assign) UInt32 publicGists; 224 | @property (nonatomic, assign) UInt32 followers; 225 | @property (nonatomic, assign) UInt32 following; 226 | @property (nonatomic, strong) NSDate *createdAt; 227 | @property (nonatomic, strong) NSDate *updatedAt; 228 | @property (nonatomic, strong) NSValue *test; 229 | @end 230 | -------------------------------------------------------------------------------- /Benchmark/ModelBenchmark/GithubUserModel/GitHubUser.m: -------------------------------------------------------------------------------- 1 | // 2 | // GitHubUser.m 3 | // ModelBenchmark 4 | // 5 | // Created by ibireme on 15/9/18. 6 | // Copyright (c) 2015 ibireme. All rights reserved. 7 | // 8 | 9 | #import "GitHubUser.h" 10 | #import "DateFormatter.h" 11 | 12 | 13 | @implementation GHUser 14 | 15 | #define GHUSER_MANUALLY_SETTER_LIST \ 16 | SET_STR(_login, @"login"); \ 17 | SET_NUM(_userID, @"id"); \ 18 | SET_STR(_avatarURL, @"avatar_url"); \ 19 | SET_STR(_gravatarID, @"gravatar_id"); \ 20 | SET_STR(_url, @"url"); \ 21 | SET_STR(_htmlURL, @"html_url"); \ 22 | SET_STR(_followersURL, @"followers_url"); \ 23 | SET_STR(_followingURL, @"following_url"); \ 24 | SET_STR(_gistsURL, @"gists_url"); \ 25 | SET_STR(_starredURL,@"starred_url"); \ 26 | SET_STR(_subscriptionsURL, @"subscriptions_url"); \ 27 | SET_STR(_organizationsURL, @"organizations_url"); \ 28 | SET_STR(_reposURL, @"repos_url"); \ 29 | SET_STR(_eventsURL, @"events_url"); \ 30 | SET_STR(_receivedEventsURL, @"received_events_url"); \ 31 | SET_STR(_type, @"type"); \ 32 | SET_NUM(_siteAdmin, @"site_admin"); \ 33 | SET_STR(_name, @"name"); \ 34 | SET_STR(_company, @"company"); \ 35 | SET_STR(_blog, @"blog"); \ 36 | SET_STR(_location, @"location"); \ 37 | SET_STR(_email, @"email"); \ 38 | SET_STR(_hireable, @"hireable"); \ 39 | SET_STR(_bio, @"bio"); \ 40 | SET_NUM(_publicRepos, @"public_repos"); \ 41 | SET_NUM(_publicGists, @"public_gists"); \ 42 | SET_NUM(_followers, @"followers"); \ 43 | SET_NUM(_following, @"following"); 44 | 45 | - (instancetype)initWithJSONDictionary:(NSDictionary *)dic { 46 | self = [super init]; 47 | if (![dic isKindOfClass:[NSDictionary class]]) return nil; 48 | 49 | NSString *str; 50 | NSNumber *num; 51 | 52 | #define SET_STR(_IVAR_, _JSON_KEY_) \ 53 | str = dic[_JSON_KEY_]; \ 54 | if ([str isKindOfClass:[NSString class]]) _IVAR_ = str; 55 | 56 | #define SET_NUM(_IVAR_, _JSON_KEY_) \ 57 | num = dic[_JSON_KEY_]; \ 58 | if ([num isKindOfClass:[NSNumber class]]) _IVAR_ = num.unsignedIntValue; 59 | 60 | GHUSER_MANUALLY_SETTER_LIST 61 | 62 | #undef SET_STR 63 | #undef SET_NUM 64 | return self; 65 | } 66 | 67 | - (NSDictionary *)convertToJSONDictionary { 68 | NSMutableDictionary *dic = [NSMutableDictionary new]; 69 | 70 | #define SET_STR(_IVAR_, _JSON_KEY_) \ 71 | if (_IVAR_) dic[_JSON_KEY_] = _IVAR_; 72 | 73 | #define SET_NUM(_IVAR_, _JSON_KEY_) \ 74 | dic[_JSON_KEY_] = @(_IVAR_); 75 | 76 | GHUSER_MANUALLY_SETTER_LIST 77 | 78 | #undef SET_STR 79 | #undef SET_NUM 80 | return dic; 81 | } 82 | 83 | - (void)encodeWithCoder:(NSCoder *)aCoder { 84 | if (!aCoder) return; 85 | 86 | #define SET_STR(_IVAR_, _JSON_KEY_) \ 87 | [aCoder encodeObject:_IVAR_ forKey:(__bridge NSString *)CFSTR(#_IVAR_)]; 88 | 89 | #define SET_NUM(_IVAR_, _JSON_KEY_) \ 90 | [aCoder encodeObject:@(_IVAR_) forKey:(__bridge NSString *)CFSTR(#_IVAR_)]; 91 | 92 | GHUSER_MANUALLY_SETTER_LIST 93 | 94 | #undef SET_STR 95 | #undef SET_NUM 96 | } 97 | 98 | - (id)initWithCoder:(NSCoder *)aDecoder { 99 | self = [self init]; 100 | if (!self) return nil; 101 | 102 | #define SET_STR(_IVAR_, _JSON_KEY_) \ 103 | _IVAR_ = [aDecoder decodeObjectForKey:(__bridge NSString *)CFSTR(#_IVAR_)]; 104 | 105 | #define SET_NUM(_IVAR_, _JSON_KEY_) \ 106 | _IVAR_ = ((NSNumber *)[aDecoder decodeObjectForKey:(__bridge NSString *)CFSTR(#_IVAR_)]).unsignedIntValue; 107 | 108 | GHUSER_MANUALLY_SETTER_LIST 109 | 110 | #undef SET_STR 111 | #undef SET_NUM 112 | return self; 113 | } 114 | 115 | #undef GHUSER_MANUALLY_SETTER_LIST 116 | @end 117 | 118 | 119 | 120 | 121 | 122 | @implementation YYGHUser 123 | + (NSDictionary *)modelCustomPropertyMapper { 124 | return @{ 125 | @"userID" : @"id", 126 | @"avatarURL" : @"avatar_url", 127 | @"gravatarID" : @"gravatar_id", 128 | @"htmlURL" : @"html_url", 129 | @"followersURL" : @"followers_url", 130 | @"followingURL" : @"following_url", 131 | @"gistsURL" : @"gists_url", 132 | @"starredURL" : @"starred_url", 133 | @"subscriptionsURL" : @"subscriptions_url", 134 | @"organizationsURL" : @"organizations_url", 135 | @"reposURL" : @"repos_url", 136 | @"eventsURL" : @"events_url", 137 | @"receivedEventsURL" : @"received_events_url", 138 | @"siteAdmin" : @"site_admin", 139 | @"publicRepos" : @"public_repos", 140 | @"publicGists" : @"public_gists", 141 | @"createdAt" : @"created_at", 142 | @"updatedAt" : @"updated_at", 143 | }; 144 | } 145 | - (void)encodeWithCoder:(NSCoder *)aCoder { [self yy_modelEncodeWithCoder:aCoder]; } 146 | - (id)initWithCoder:(NSCoder *)aDecoder { return [self yy_modelInitWithCoder:aDecoder]; } 147 | @end 148 | 149 | 150 | 151 | 152 | 153 | @implementation JSGHUser 154 | + (JSONKeyMapper *)keyMapper { 155 | return [[JSONKeyMapper alloc] initWithModelToJSONDictionary:@{ 156 | @"userID" : @"id", 157 | @"avatarURL" : @"avatar_url", 158 | @"gravatarID" : @"gravatar_id", 159 | @"htmlURL" : @"html_url", 160 | @"followersURL" : @"followers_url", 161 | @"followingURL" : @"following_url", 162 | @"gistsURL" : @"gists_url", 163 | @"starredURL" : @"starred_url", 164 | @"subscriptionsURL" : @"subscriptions_url", 165 | @"organizationsURL" : @"organizations_url", 166 | @"reposURL" : @"repos_url", 167 | @"eventsURL" : @"events_url", 168 | @"receivedEventsURL" : @"received_events_url", 169 | @"siteAdmin" : @"site_admin", 170 | @"publicRepos" : @"public_repos", 171 | @"publicGists" : @"public_gists", 172 | @"createdAt" : @"created_at", 173 | @"updatedAt" : @"updated_at", 174 | }]; 175 | } 176 | + (BOOL)propertyIsOptional:(NSString *)propertyName { 177 | return YES; 178 | } 179 | @end 180 | 181 | 182 | 183 | 184 | 185 | @implementation MTGHUser 186 | + (NSDictionary *)JSONKeyPathsByPropertyKey { 187 | return @{ 188 | @"login" : @"login", 189 | @"userID" : @"id", 190 | @"avatarURL" : @"avatar_url", 191 | @"gravatarID" : @"gravatar_id", 192 | @"url" : @"url", 193 | @"htmlURL" : @"html_url", 194 | @"followersURL" : @"followers_url", 195 | @"followingURL" : @"following_url", 196 | @"gistsURL" : @"gists_url", 197 | @"starredURL" : @"starred_url", 198 | @"subscriptionsURL" : @"subscriptions_url", 199 | @"organizationsURL" : @"organizations_url", 200 | @"reposURL" : @"repos_url", 201 | @"eventsURL" : @"events_url", 202 | @"receivedEventsURL" : @"received_events_url", 203 | @"type" : @"type", 204 | @"siteAdmin" : @"site_admin", 205 | @"name" : @"name", 206 | @"company" : @"company", 207 | @"blog" : @"blog", 208 | @"location" : @"location", 209 | @"email" : @"email", 210 | @"hireable" : @"hireable", 211 | @"bio" : @"bio", 212 | @"publicRepos" : @"public_repos", 213 | @"publicGists" : @"public_gists", 214 | @"followers" : @"followers", 215 | @"following" : @"following", 216 | @"createdAt" : @"created_at", 217 | @"updatedAt" : @"updated_at", 218 | @"test" : @"test" 219 | }; 220 | } 221 | @end 222 | 223 | 224 | 225 | 226 | 227 | @implementation FEGHUser 228 | + (FEMMapping *)defaultMapping { 229 | FEMMapping *mapping = [[FEMMapping alloc] initWithEntityName:@"FEGHUser"]; 230 | [mapping addAttributesFromDictionary:@{ 231 | @"login" : @"login", 232 | @"userID" : @"id", 233 | @"avatarURL" : @"avatar_url", 234 | @"gravatarID" : @"gravatar_id", 235 | @"url" : @"url", 236 | @"htmlURL" : @"html_url", 237 | @"followersURL" : @"followers_url", 238 | @"followingURL" : @"following_url", 239 | @"gistsURL" : @"gists_url", 240 | @"starredURL" : @"starred_url", 241 | @"subscriptionsURL" : @"subscriptions_url", 242 | @"organizationsURL" : @"organizations_url", 243 | @"reposURL" : @"repos_url", 244 | @"eventsURL" : @"events_url", 245 | @"receivedEventsURL" : @"received_events_url", 246 | @"type" : @"type", 247 | @"siteAdmin" : @"site_admin", 248 | @"name" : @"name", 249 | @"company" : @"company", 250 | @"blog" : @"blog", 251 | @"location" : @"location", 252 | @"email" : @"email", 253 | @"hireable" : @"hireable", 254 | @"bio" : @"bio", 255 | @"publicRepos" : @"public_repos", 256 | @"publicGists" : @"public_gists", 257 | @"followers" : @"followers", 258 | @"following" : @"following", 259 | @"createdAt" : @"created_at", 260 | @"updatedAt" : @"updated_at", 261 | @"test" : @"test" 262 | }]; 263 | return mapping; 264 | } 265 | @end 266 | 267 | 268 | 269 | 270 | 271 | @implementation MJGHUser 272 | + (NSDictionary *)mj_replacedKeyFromPropertyName { 273 | return @{ 274 | @"userID" : @"id", 275 | @"avatarURL" : @"avatar_url", 276 | @"gravatarID" : @"gravatar_id", 277 | @"htmlURL" : @"html_url", 278 | @"followersURL" : @"followers_url", 279 | @"followingURL" : @"following_url", 280 | @"gistsURL" : @"gists_url", 281 | @"starredURL" : @"starred_url", 282 | @"subscriptionsURL" : @"subscriptions_url", 283 | @"organizationsURL" : @"organizations_url", 284 | @"reposURL" : @"repos_url", 285 | @"eventsURL" : @"events_url", 286 | @"receivedEventsURL" : @"received_events_url", 287 | @"siteAdmin" : @"site_admin", 288 | @"publicRepos" : @"public_repos", 289 | @"publicGists" : @"public_gists", 290 | @"createdAt" : @"created_at", 291 | @"updatedAt" : @"updated_at", 292 | }; 293 | } 294 | MJExtensionCodingImplementation 295 | @end 296 | -------------------------------------------------------------------------------- /Benchmark/ModelBenchmark/GithubUserModel/user.json: -------------------------------------------------------------------------------- 1 | { 2 | "login": "facebook", 3 | "id": 69631, 4 | "avatar_url": "https://avatars.githubusercontent.com/u/69631?v=3", 5 | "gravatar_id": "", 6 | "url": "https://api.github.com/users/facebook", 7 | "html_url": "https://github.com/facebook", 8 | "followers_url": "https://api.github.com/users/facebook/followers", 9 | "following_url": "https://api.github.com/users/facebook/following{/other_user}", 10 | "gists_url": "https://api.github.com/users/facebook/gists{/gist_id}", 11 | "starred_url": "https://api.github.com/users/facebook/starred{/owner}{/repo}", 12 | "subscriptions_url": "https://api.github.com/users/facebook/subscriptions", 13 | "organizations_url": "https://api.github.com/users/facebook/orgs", 14 | "repos_url": "https://api.github.com/users/facebook/repos", 15 | "events_url": "https://api.github.com/users/facebook/events{/privacy}", 16 | "received_events_url": "https://api.github.com/users/facebook/received_events", 17 | "type": "Organization", 18 | "site_admin": false, 19 | "name": "Facebook", 20 | "company": null, 21 | "blog": "https://code.facebook.com/projects/", 22 | "location": "Menlo Park, California", 23 | "email": null, 24 | "hireable": null, 25 | "bio": "We work hard to contribute our work back to the web, mobile, big data, & infrastructure communities. NB: members must have two-factor auth.", 26 | "public_repos": 147, 27 | "public_gists": 12, 28 | "followers": 0, 29 | "following": 0 30 | } 31 | -------------------------------------------------------------------------------- /Benchmark/ModelBenchmark/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 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 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /Benchmark/ModelBenchmark/ModelBenchmark-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | 5 | -------------------------------------------------------------------------------- /Benchmark/ModelBenchmark/SwiftModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // ModelBenchmark 4 | // 5 | // Created by ibireme on 2017/8/6. 6 | // Copyright © 2017年 ibireme. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | @objc class GithubUserBenchmark : NSObject { 12 | @objc static public func benchmark() { 13 | 14 | GithubUserObjectMapper.benchmark() 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Benchmark/ModelBenchmark/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // ModelBenchmark 4 | // 5 | // Created by ibireme on 15/9/18. 6 | // Copyright (c) 2015 ibireme. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /Benchmark/ModelBenchmark/WeiboModel/DateFormatter.h: -------------------------------------------------------------------------------- 1 | // 2 | // DateFormatter.h 3 | // ModelBenchmark 4 | // 5 | // Created by ibireme on 15/9/18. 6 | // Copyright (c) 2015 ibireme. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface DateFormatter : NSObject 12 | + (NSDateFormatter *)githubDataFormatter; 13 | + (NSDateFormatter *)weiboDataFormatter; 14 | @end 15 | -------------------------------------------------------------------------------- /Benchmark/ModelBenchmark/WeiboModel/DateFormatter.m: -------------------------------------------------------------------------------- 1 | // 2 | // DateFormatter.m 3 | // ModelBenchmark 4 | // 5 | // Created by ibireme on 15/9/18. 6 | // Copyright (c) 2015 ibireme. All rights reserved. 7 | // 8 | 9 | #import "DateFormatter.h" 10 | #import "JSONModel.h" 11 | 12 | @implementation DateFormatter 13 | + (NSDateFormatter *)githubDataFormatter { 14 | static NSDateFormatter *formatter; 15 | static dispatch_once_t onceToken; 16 | dispatch_once(&onceToken, ^{ 17 | formatter = [[NSDateFormatter alloc] init]; 18 | formatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]; 19 | formatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ssZ"; 20 | }); 21 | return formatter; 22 | } 23 | + (NSDateFormatter *)weiboDataFormatter { 24 | static NSDateFormatter *formatter; 25 | static dispatch_once_t onceToken; 26 | dispatch_once(&onceToken, ^{ 27 | formatter = [[NSDateFormatter alloc] init]; 28 | formatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]; 29 | formatter.dateFormat = @"EEE MMM dd HH:mm:ss Z yyyy"; 30 | }); 31 | return formatter; 32 | } 33 | @end 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /Benchmark/ModelBenchmark/WeiboModel/FEWeiboModel.h: -------------------------------------------------------------------------------- 1 | // 2 | // FEWeiboModel.h 3 | // ModelBenchmark 4 | // 5 | // Created by ibireme on 15/9/18. 6 | // Copyright (c) 2015 ibireme. All rights reserved. 7 | // 8 | 9 | #import "FEMMapping.h" 10 | 11 | 12 | @interface FEWeiboPictureMetadata : NSObject 13 | @property (nonatomic, strong) NSString *url; 14 | @property (nonatomic, assign) int width; 15 | @property (nonatomic, assign) int height; 16 | @property (nonatomic, strong) NSString *type; 17 | @property (nonatomic, assign) int cutType; 18 | + (FEMMapping *)defaultMapping; 19 | @end 20 | 21 | @interface FEWeiboPicture : NSObject 22 | @property (nonatomic, strong) NSString *picID; 23 | @property (nonatomic, strong) NSString *objectID; 24 | @property (nonatomic, assign) int photoTag; 25 | @property (nonatomic, assign) BOOL keepSize; 26 | @property (nonatomic, strong) FEWeiboPictureMetadata *thumbnail; 27 | @property (nonatomic, strong) FEWeiboPictureMetadata *bmiddle; 28 | @property (nonatomic, strong) FEWeiboPictureMetadata *middlePlus; 29 | @property (nonatomic, strong) FEWeiboPictureMetadata *large; 30 | @property (nonatomic, strong) FEWeiboPictureMetadata *largest; 31 | @property (nonatomic, strong) FEWeiboPictureMetadata *original; 32 | + (FEMMapping *)defaultMapping; 33 | @end 34 | 35 | @interface FEWeiboURL : NSObject 36 | @property (nonatomic, assign) BOOL result; 37 | @property (nonatomic, strong) NSString *shortURL; 38 | @property (nonatomic, strong) NSString *oriURL; 39 | @property (nonatomic, strong) NSString *urlTitle; 40 | @property (nonatomic, strong) NSString *urlTypePic; 41 | @property (nonatomic, assign) int32_t urlType; 42 | @property (nonatomic, strong) NSString *log; 43 | @property (nonatomic, strong) NSDictionary *actionLog; 44 | @property (nonatomic, strong) NSString *pageID; 45 | @property (nonatomic, strong) NSString *storageType; 46 | + (FEMMapping *)defaultMapping; 47 | @end 48 | 49 | @interface FEWeiboUser : NSObject 50 | @property (nonatomic, assign) uint64_t userID; 51 | @property (nonatomic, strong) NSString *idString; 52 | @property (nonatomic, strong) NSString *genderString; 53 | @property (nonatomic, strong) NSString *desc; 54 | @property (nonatomic, strong) NSString *domain; 55 | @property (nonatomic, strong) NSString *name; 56 | @property (nonatomic, strong) NSString *screenName; 57 | @property (nonatomic, strong) NSString *remark; 58 | @property (nonatomic, assign) int32_t followersCount; 59 | @property (nonatomic, assign) int32_t friendsCount; 60 | @property (nonatomic, assign) int32_t biFollowersCount; 61 | @property (nonatomic, assign) int32_t favouritesCount; 62 | @property (nonatomic, assign) int32_t statusesCount; 63 | @property (nonatomic, assign) int32_t pagefriendsCount; 64 | @property (nonatomic, assign) BOOL followMe; 65 | @property (nonatomic, assign) BOOL following; 66 | @property (nonatomic, strong) NSString *province; 67 | @property (nonatomic, strong) NSString *city; 68 | @property (nonatomic, strong) NSString *url; 69 | @property (nonatomic, strong) NSString *profileImageURL; 70 | @property (nonatomic, strong) NSString *avatarLarge; 71 | @property (nonatomic, strong) NSString *avatarHD; 72 | @property (nonatomic, strong) NSString *coverImage; 73 | @property (nonatomic, strong) NSString *coverImagePhone; 74 | @property (nonatomic, strong) NSString *profileURL; 75 | @property (nonatomic, assign) int32_t type; 76 | @property (nonatomic, assign) int32_t ptype; 77 | @property (nonatomic, assign) int32_t mbtype; 78 | @property (nonatomic, assign) int32_t urank; 79 | @property (nonatomic, assign) int32_t uclass; 80 | @property (nonatomic, assign) int32_t ulevel; 81 | @property (nonatomic, assign) int32_t mbrank; 82 | @property (nonatomic, assign) int32_t star; 83 | @property (nonatomic, assign) int32_t level; 84 | @property (nonatomic, strong) NSDate *createdAt; 85 | @property (nonatomic, assign) BOOL allowAllActMsg; 86 | @property (nonatomic, assign) BOOL allowAllComment; 87 | @property (nonatomic, assign) BOOL geoEnabled; 88 | @property (nonatomic, assign) int32_t onlineStatus; 89 | @property (nonatomic, strong) NSString *location; 90 | @property (nonatomic, strong) NSArray *icons; 91 | @property (nonatomic, strong) NSString *weihao; 92 | @property (nonatomic, strong) NSString *badgeTop; 93 | @property (nonatomic, assign) int32_t blockWord; 94 | @property (nonatomic, assign) int32_t blockApp; 95 | @property (nonatomic, assign) int32_t hasAbilityTag; 96 | @property (nonatomic, assign) int32_t creditScore; 97 | @property (nonatomic, strong) NSDictionary *badge; 98 | @property (nonatomic, strong) NSString *lang; 99 | @property (nonatomic, assign) int32_t userAbility; 100 | @property (nonatomic, strong) NSDictionary *extend; 101 | @property (nonatomic, assign) BOOL verified; 102 | @property (nonatomic, assign) int32_t verifiedType; 103 | @property (nonatomic, assign) int32_t verifiedLevel; 104 | @property (nonatomic, assign) int32_t verifiedState; 105 | @property (nonatomic, strong) NSString *verifiedContactEmail; 106 | @property (nonatomic, strong) NSString *verifiedContactMobile; 107 | @property (nonatomic, strong) NSString *verifiedTrade; 108 | @property (nonatomic, strong) NSString *verifiedContactName; 109 | @property (nonatomic, strong) NSString *verifiedSource; 110 | @property (nonatomic, strong) NSString *verifiedSourceURL; 111 | @property (nonatomic, strong) NSString *verifiedReason; 112 | @property (nonatomic, strong) NSString *verifiedReasonURL; 113 | @property (nonatomic, strong) NSString *verifiedReasonModified; 114 | + (FEMMapping *)defaultMapping; 115 | @end 116 | 117 | @interface FEWeiboStatus : NSObject 118 | @property (nonatomic, assign) uint64_t statusID; 119 | @property (nonatomic, strong) NSString *idstr; 120 | @property (nonatomic, strong) NSString *mid; 121 | @property (nonatomic, strong) NSString *rid; 122 | @property (nonatomic, strong) NSDate *createdAt; 123 | @property (nonatomic, strong) FEWeiboUser *user; 124 | @property (nonatomic, assign) int32_t userType; 125 | @property (nonatomic, strong) NSString *text; 126 | @property (nonatomic, strong) NSArray *picIds; /// Array 127 | @property (nonatomic, strong) NSDictionary *picInfos; /// Dic 128 | @property (nonatomic, strong) NSArray *urlStruct; ///< Array 129 | @property (nonatomic, assign) BOOL favorited; 130 | @property (nonatomic, assign) BOOL truncated; 131 | @property (nonatomic, assign) int32_t repostsCount; 132 | @property (nonatomic, assign) int32_t commentsCount; 133 | @property (nonatomic, assign) int32_t attitudesCount; 134 | @property (nonatomic, assign) int32_t attitudesStatus; 135 | @property (nonatomic, assign) int32_t recomState; 136 | @property (nonatomic, strong) NSString *inReplyToScreenName; 137 | @property (nonatomic, strong) NSString *inReplyToStatusId; 138 | @property (nonatomic, strong) NSString *inReplyToUserId; 139 | @property (nonatomic, strong) NSString *source; 140 | @property (nonatomic, assign) int32_t sourceType; 141 | @property (nonatomic, assign) int32_t sourceAllowClick; 142 | @property (nonatomic, strong) NSString *geo; 143 | @property (nonatomic, strong) NSArray *annotations; 144 | @property (nonatomic, assign) int32_t bizFeature; 145 | @property (nonatomic, assign) int32_t mlevel; 146 | @property (nonatomic, strong) NSString *mblogid; 147 | @property (nonatomic, strong) NSString *mblogTypeName; 148 | @property (nonatomic, assign) int32_t mblogType; 149 | @property (nonatomic, strong) NSString *scheme; 150 | @property (nonatomic, strong) NSDictionary *visible; 151 | @property (nonatomic, strong) NSArray *darwinTags; 152 | + (FEMMapping *)defaultMapping; 153 | @end 154 | -------------------------------------------------------------------------------- /Benchmark/ModelBenchmark/WeiboModel/FEWeiboModel.m: -------------------------------------------------------------------------------- 1 | // 2 | // FEWeiboModel.m 3 | // ModelBenchmark 4 | // 5 | // Created by ibireme on 15/9/18. 6 | // Copyright (c) 2015 ibireme. All rights reserved. 7 | // 8 | 9 | #import "FEWeiboModel.h" 10 | #import "FEMDeserializer.h" 11 | #import "DateFormatter.h" 12 | #import "FEMSerializer.h" 13 | 14 | @implementation FEWeiboPictureMetadata 15 | + (FEMMapping *)defaultMapping { 16 | static FEMMapping *mapping; 17 | static dispatch_once_t onceToken; 18 | dispatch_once(&onceToken, ^{ 19 | mapping = [[FEMMapping alloc] initWithEntityName:@"FEWeiboPictureMetadata"]; 20 | [mapping addAttributesFromDictionary:@{ 21 | @"url" : @"url", 22 | @"width" : @"width", 23 | @"height" : @"height", 24 | @"type" : @"type", 25 | @"cutType" : @"cut_type" 26 | }]; 27 | 28 | }); 29 | return mapping; 30 | } 31 | @end 32 | 33 | @implementation FEWeiboPicture 34 | + (FEMMapping *)defaultMapping { 35 | static FEMMapping *mapping; 36 | static dispatch_once_t onceToken; 37 | dispatch_once(&onceToken, ^{ 38 | mapping = [[FEMMapping alloc] initWithEntityName:@"FEWeiboPicture"]; 39 | [mapping addAttributesFromDictionary:@{ 40 | @"picID" : @"pic_id", 41 | @"keepSize" : @"keep_size", 42 | @"photoTag" : @"photo_tag", 43 | @"objectID" : @"object_id", 44 | // @"thumbnail" : @"thumbnail", 45 | // @"bmiddle" : @"bmiddle", 46 | // @"middlePlus" : @"middleplus", 47 | // @"large" : @"large", 48 | // @"largest" : @"largest", 49 | // @"original" : @"original" 50 | }]; 51 | 52 | FEMMapBlock map = (id)^(NSDictionary *value) { 53 | if ([value isKindOfClass:[NSDictionary class]]) { 54 | FEMMapping *mapping = [FEWeiboPictureMetadata defaultMapping]; 55 | FEWeiboPictureMetadata *meta = [FEWeiboPictureMetadata new]; 56 | [FEMDeserializer fillObject:meta fromRepresentation:value mapping:mapping]; 57 | return meta; 58 | } 59 | return (FEWeiboPictureMetadata*)nil; 60 | }; 61 | FEMMapBlock reverseMap = (id)^(NSDictionary *value) { 62 | if ([value isKindOfClass:[FEWeiboPictureMetadata class]]) { 63 | FEMMapping *mapping = [FEWeiboPictureMetadata defaultMapping]; 64 | id meta = [FEMSerializer serializeObject:value usingMapping:mapping]; 65 | return meta; 66 | } 67 | return (id)nil; 68 | }; 69 | [mapping addAttribute:[[FEMAttribute alloc] initWithProperty:@"thumbnail" keyPath:@"thumbnail" map:map reverseMap:reverseMap]]; 70 | [mapping addAttribute:[[FEMAttribute alloc] initWithProperty:@"bmiddle" keyPath:@"bmiddle" map:map reverseMap:reverseMap]]; 71 | [mapping addAttribute:[[FEMAttribute alloc] initWithProperty:@"middlePlus" keyPath:@"middleplus" map:map reverseMap:reverseMap]]; 72 | [mapping addAttribute:[[FEMAttribute alloc] initWithProperty:@"large" keyPath:@"large" map:map reverseMap:reverseMap]]; 73 | [mapping addAttribute:[[FEMAttribute alloc] initWithProperty:@"largest" keyPath:@"largest" map:map reverseMap:reverseMap]]; 74 | [mapping addAttribute:[[FEMAttribute alloc] initWithProperty:@"original" keyPath:@"original" map:map reverseMap:reverseMap]]; 75 | }); 76 | return mapping; 77 | } 78 | @end 79 | 80 | @implementation FEWeiboURL 81 | + (FEMMapping *)defaultMapping { 82 | static FEMMapping *mapping; 83 | static dispatch_once_t onceToken; 84 | dispatch_once(&onceToken, ^{ 85 | mapping = [[FEMMapping alloc] initWithEntityName:@"FEWeiboURL"]; 86 | [mapping addAttributesFromDictionary:@{ 87 | @"result" : @"result", 88 | @"log" : @"log", 89 | @"oriURL" : @"ori_url", 90 | @"urlTitle" : @"url_title", 91 | @"urlTypePic" : @"url_type_pic", 92 | @"urlType" : @"url_type", 93 | @"shortURL" : @"short_url", 94 | @"actionLog" : @"actionlog", 95 | @"pageID" : @"page_id", 96 | @"storageType" : @"storage_type" 97 | }]; 98 | 99 | }); 100 | return mapping; 101 | } 102 | @end 103 | 104 | @implementation FEWeiboUser 105 | + (FEMMapping *)defaultMapping { 106 | static FEMMapping *mapping; 107 | static dispatch_once_t onceToken; 108 | dispatch_once(&onceToken, ^{ 109 | mapping = [[FEMMapping alloc] initWithEntityName:@"FEWeiboUser"]; 110 | [mapping addAttributesFromDictionary:@{ 111 | @"userID" : @"id", 112 | @"idString" : @"idstr", 113 | @"genderString" : @"gender", 114 | @"desc" : @"description", 115 | @"domain" : @"domain", 116 | @"name" : @"name", 117 | @"screenName" : @"screen_name", 118 | @"remark" : @"remark", 119 | @"followersCount" : @"followers_count", 120 | @"friendsCount" : @"friends_count", 121 | @"biFollowersCount" : @"bi_followers_count", 122 | @"favouritesCount" : @"favourites_count", 123 | @"statusesCount" : @"statuses_count", 124 | @"pagefriendsCount" : @"pagefriends_count", 125 | @"followMe" : @"follow_me", 126 | @"following" : @"following", 127 | @"province" : @"province", 128 | @"city" : @"city", 129 | @"url" : @"url", 130 | @"profileImageURL" : @"profile_image_url", 131 | @"avatarLarge" : @"avatar_large", 132 | @"avatarHD" : @"avatar_hd", 133 | @"coverImage" : @"cover_image", 134 | @"coverImagePhone" : @"cover_image_phone", 135 | @"profileURL" : @"profile_url", 136 | @"type" : @"type", 137 | @"ptype" : @"ptype", 138 | @"mbtype" : @"mbtype", 139 | @"urank" : @"urank", 140 | @"uclass" : @"class", 141 | @"ulevel" : @"ulevel", 142 | @"mbrank" : @"mbrank", 143 | @"star" : @"star", 144 | @"level" : @"level", 145 | //@"createdAt" : @"created_at", 146 | @"allowAllActMsg" : @"allow_all_act_msg", 147 | @"allowAllComment" : @"allow_all_comment", 148 | @"geoEnabled" : @"geo_enabled", 149 | @"onlineStatus" : @"online_status", 150 | @"location" : @"location", 151 | @"icons" : @"icons", 152 | @"weihao" : @"weihao", 153 | @"badgeTop" : @"badge_top", 154 | @"blockWord" : @"block_word", 155 | @"blockApp" : @"block_app", 156 | @"hasAbilityTag" : @"has_ability_tag", 157 | @"creditScore" : @"credit_score", 158 | @"badge" : @"badge", 159 | @"lang" : @"lang", 160 | @"userAbility" : @"user_ability", 161 | @"extend" : @"extend", 162 | @"verified" : @"verified", 163 | @"verifiedType" : @"verified_type", 164 | @"verifiedLevel" : @"verified_level", 165 | @"verifiedState" : @"verified_state", 166 | @"verifiedContactEmail" : @"verified_contact_email", 167 | @"verifiedContactMobile" : @"verified_contact_mobile", 168 | @"verifiedTrade" : @"verified_trade", 169 | @"verifiedContactName" : @"verified_contact_name", 170 | @"verifiedSource" : @"verified_source", 171 | @"verifiedSourceURL" : @"verified_source_url", 172 | @"verifiedReason" : @"verified_reason", 173 | @"verifiedReasonURL" : @"verified_reason_url", 174 | @"verifiedReasonModified" : @"verified_reason_modified" 175 | }]; 176 | FEMAttribute *createdAt = [[FEMAttribute alloc] initWithProperty:@"createdAt" keyPath:@"created_at" map:(id)^(NSString *value) { 177 | if ([value isKindOfClass:[NSString class]]) { 178 | return [[DateFormatter weiboDataFormatter] dateFromString:value]; 179 | } 180 | return (NSDate*)nil; 181 | } reverseMap:(id)^(id value) { 182 | if ([value isKindOfClass:[NSDate class]]) { 183 | return [[DateFormatter weiboDataFormatter] stringFromDate:value]; 184 | } 185 | return (NSString *)[NSNull null]; 186 | }]; 187 | [mapping addAttribute:createdAt]; 188 | }); 189 | return mapping; 190 | } 191 | @end 192 | 193 | @implementation FEWeiboStatus 194 | + (FEMMapping *)defaultMapping { 195 | static FEMMapping *mapping; 196 | static dispatch_once_t onceToken; 197 | dispatch_once(&onceToken, ^{ 198 | mapping = [[FEMMapping alloc] initWithEntityName:@"FEWeiboStatus"]; 199 | [mapping addAttributesFromDictionary:@{ 200 | @"statusID" : @"id", 201 | @"idstr" : @"idstr", 202 | @"mid" : @"mid", 203 | @"rid" : @"rid", 204 | //@"createdAt" : @"created_at", 205 | //@"user" : @"user", 206 | @"userType" : @"userType", 207 | @"text" : @"text", 208 | @"picIds" : @"pic_ids", 209 | //@"picInfos" : @"pic_infos", 210 | //@"urlStruct" : @"url_struct", 211 | @"favorited" : @"favorited", 212 | @"truncated" : @"truncated", 213 | @"repostsCount" : @"reposts_count", 214 | @"commentsCount" : @"comments_count", 215 | @"attitudesCount" : @"attitudes_count", 216 | @"attitudesStatus" : @"attitudes_status", 217 | @"recomState" : @"recom_state", 218 | @"inReplyToScreenName" : @"in_reply_to_screen_name", 219 | @"inReplyToStatusId" : @"in_reply_to_status_id", 220 | @"inReplyToUserId" : @"in_reply_to_user_id", 221 | @"source" : @"source", 222 | @"sourceType" : @"source_type", 223 | @"sourceAllowClick" : @"source_allowclick", 224 | @"geo" : @"geo", 225 | @"annotations" : @"annotations", 226 | @"bizFeature" : @"biz_feature", 227 | @"mblogid" : @"mblogid", 228 | @"mblogTypeName" : @"mblogtypename", 229 | @"mblogType" : @"mblogtype", 230 | @"scheme" : @"scheme", 231 | @"visible" : @"visible", 232 | @"darwinTags" : @"darwin_tags" 233 | }]; 234 | 235 | FEMAttribute *createdAt = [[FEMAttribute alloc] initWithProperty:@"createdAt" keyPath:@"created_at" map:(id)^(NSString *value) { 236 | if ([value isKindOfClass:[NSString class]]) { 237 | return [[DateFormatter weiboDataFormatter] dateFromString:value]; 238 | } 239 | return (NSDate*)nil; 240 | } reverseMap:(id)^(id value) { 241 | if ([value isKindOfClass:[NSDate class]]) { 242 | return [[DateFormatter weiboDataFormatter] stringFromDate:value]; 243 | } 244 | return (NSString *)[NSNull null]; 245 | }]; 246 | [mapping addAttribute:createdAt]; 247 | 248 | FEMAttribute *user = [[FEMAttribute alloc] initWithProperty:@"user" keyPath:@"user" map:(id)^(NSDictionary *value) { 249 | if ([value isKindOfClass:[NSDictionary class]]) { 250 | FEMMapping *mapping = [FEWeiboUser defaultMapping]; 251 | FEWeiboUser *user = [FEWeiboUser new]; 252 | [FEMDeserializer fillObject:user fromRepresentation:value mapping:mapping]; 253 | return user; 254 | } 255 | return (FEWeiboUser*)nil; 256 | } reverseMap:(id)^(FEWeiboUser *value) { 257 | if ([value isKindOfClass:[FEWeiboUser class]]) { 258 | FEMMapping *mapping = [FEWeiboUser defaultMapping]; 259 | id user = [FEMSerializer serializeObject:value usingMapping:mapping]; 260 | return user; 261 | } 262 | return (id)nil; 263 | }]; 264 | [mapping addAttribute:user]; 265 | 266 | FEMAttribute *picInfos = [[FEMAttribute alloc] initWithProperty:@"picInfos" keyPath:@"pic_infos" map:(id)^(NSDictionary *value) { 267 | if ([value isKindOfClass:[NSDictionary class]]) { 268 | NSMutableDictionary *infos = [NSMutableDictionary new]; 269 | FEMMapping *picMapping = [FEWeiboPicture defaultMapping]; 270 | [value enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { 271 | FEWeiboPicture *pic = [FEWeiboPicture new]; 272 | [FEMDeserializer fillObject:pic fromRepresentation:obj mapping:picMapping]; 273 | infos[key] = pic; 274 | }]; 275 | return (id)infos; 276 | } 277 | return (id)nil; 278 | } reverseMap:(id) ^ (NSDictionary *value) { 279 | FEMMapping *picMapping = [FEWeiboPicture defaultMapping]; 280 | NSMutableDictionary *infos = [NSMutableDictionary new]; 281 | [value enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { 282 | id pic = [FEMSerializer serializeObject:obj usingMapping:picMapping]; 283 | if (pic) infos[key] = pic; 284 | }]; 285 | return infos; 286 | }]; 287 | [mapping addAttribute:picInfos]; 288 | 289 | FEMAttribute *urlStruct = [[FEMAttribute alloc] initWithProperty:@"urlStruct" keyPath:@"url_struct" map:(id)^(NSArray *value) { 290 | if ([value isKindOfClass:[NSArray class]]) { 291 | NSMutableArray *urls = [NSMutableArray new]; 292 | FEMMapping *picMapping = [FEWeiboURL defaultMapping]; 293 | [value enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 294 | FEWeiboURL *url = [FEWeiboURL new]; 295 | [FEMDeserializer fillObject:url fromRepresentation:obj mapping:picMapping]; 296 | [urls addObject:url]; 297 | }]; 298 | return (id)urls; 299 | } 300 | return (id)nil; 301 | } reverseMap:(id) ^ (NSArray *value) { 302 | FEMMapping *picMapping = [FEWeiboURL defaultMapping]; 303 | NSMutableArray *urls = [NSMutableArray new]; 304 | [value enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 305 | id url = [FEMSerializer serializeObject:obj usingMapping:picMapping]; 306 | if (url) [urls addObject:url]; 307 | }]; 308 | return urls; 309 | }]; 310 | [mapping addAttribute:urlStruct]; 311 | 312 | }); 313 | return mapping; 314 | } 315 | 316 | @end -------------------------------------------------------------------------------- /Benchmark/ModelBenchmark/WeiboModel/JSWeiboModel.h: -------------------------------------------------------------------------------- 1 | // 2 | // JSWeiboModel.h 3 | // ModelBenchmark 4 | // 5 | // Created by ibireme on 15/9/18. 6 | // Copyright (c) 2015 ibireme. All rights reserved. 7 | // 8 | 9 | #import "JSONModel.h" 10 | 11 | 12 | @interface JSWeiboPictureMetadata : JSONModel 13 | @property (nonatomic, strong) NSString *url; 14 | @property (nonatomic, assign) int width; 15 | @property (nonatomic, assign) int height; 16 | @property (nonatomic, strong) NSString *type; 17 | @property (nonatomic, assign) int cutType; 18 | @end 19 | 20 | @protocol JSWeiboPicture 21 | @end 22 | @interface JSWeiboPicture : JSONModel 23 | @property (nonatomic, strong) NSString *picID; 24 | @property (nonatomic, strong) NSString *objectID; 25 | @property (nonatomic, assign) int photoTag; 26 | @property (nonatomic, assign) BOOL keepSize; 27 | @property (nonatomic, strong) JSWeiboPictureMetadata *thumbnail; 28 | @property (nonatomic, strong) JSWeiboPictureMetadata *bmiddle; 29 | @property (nonatomic, strong) JSWeiboPictureMetadata *middlePlus; 30 | @property (nonatomic, strong) JSWeiboPictureMetadata *large; 31 | @property (nonatomic, strong) JSWeiboPictureMetadata *largest; 32 | @property (nonatomic, strong) JSWeiboPictureMetadata *original; 33 | @end 34 | 35 | @protocol JSWeiboURL 36 | @end 37 | @interface JSWeiboURL : JSONModel 38 | @property (nonatomic, assign) BOOL result; 39 | @property (nonatomic, strong) NSString *shortURL; 40 | @property (nonatomic, strong) NSString *oriURL; 41 | @property (nonatomic, strong) NSString *urlTitle; 42 | @property (nonatomic, strong) NSString *urlTypePic; 43 | @property (nonatomic, assign) int32_t urlType; 44 | @property (nonatomic, strong) NSString *log; 45 | @property (nonatomic, strong) NSDictionary *actionLog; 46 | @property (nonatomic, strong) NSString *pageID; 47 | @property (nonatomic, strong) NSString *storageType; 48 | @end 49 | 50 | @interface JSWeiboUser : JSONModel 51 | @property (nonatomic, assign) uint64_t userID; 52 | @property (nonatomic, strong) NSString *idString; 53 | @property (nonatomic, strong) NSString *genderString; 54 | @property (nonatomic, strong) NSString *desc; 55 | @property (nonatomic, strong) NSString *domain; 56 | @property (nonatomic, strong) NSString *name; 57 | @property (nonatomic, strong) NSString *screenName; 58 | @property (nonatomic, strong) NSString *remark; 59 | @property (nonatomic, assign) int32_t followersCount; 60 | @property (nonatomic, assign) int32_t friendsCount; 61 | @property (nonatomic, assign) int32_t biFollowersCount; 62 | @property (nonatomic, assign) int32_t favouritesCount; 63 | @property (nonatomic, assign) int32_t statusesCount; 64 | @property (nonatomic, assign) int32_t pagefriendsCount; 65 | @property (nonatomic, assign) BOOL followMe; 66 | @property (nonatomic, assign) BOOL following; 67 | @property (nonatomic, strong) NSString *province; 68 | @property (nonatomic, strong) NSString *city; 69 | @property (nonatomic, strong) NSString *url; 70 | @property (nonatomic, strong) NSString *profileImageURL; 71 | @property (nonatomic, strong) NSString *avatarLarge; 72 | @property (nonatomic, strong) NSString *avatarHD; 73 | @property (nonatomic, strong) NSString *coverImage; 74 | @property (nonatomic, strong) NSString *coverImagePhone; 75 | @property (nonatomic, strong) NSString *profileURL; 76 | @property (nonatomic, assign) int32_t type; 77 | @property (nonatomic, assign) int32_t ptype; 78 | @property (nonatomic, assign) int32_t mbtype; 79 | @property (nonatomic, assign) int32_t urank; 80 | @property (nonatomic, assign) int32_t uclass; 81 | @property (nonatomic, assign) int32_t ulevel; 82 | @property (nonatomic, assign) int32_t mbrank; 83 | @property (nonatomic, assign) int32_t star; 84 | @property (nonatomic, assign) int32_t level; 85 | @property (nonatomic, strong) NSDate *createdAt; 86 | @property (nonatomic, assign) BOOL allowAllActMsg; 87 | @property (nonatomic, assign) BOOL allowAllComment; 88 | @property (nonatomic, assign) BOOL geoEnabled; 89 | @property (nonatomic, assign) int32_t onlineStatus; 90 | @property (nonatomic, strong) NSString *location; 91 | @property (nonatomic, strong) NSArray *icons; 92 | @property (nonatomic, strong) NSString *weihao; 93 | @property (nonatomic, strong) NSString *badgeTop; 94 | @property (nonatomic, assign) int32_t blockWord; 95 | @property (nonatomic, assign) int32_t blockApp; 96 | @property (nonatomic, assign) int32_t hasAbilityTag; 97 | @property (nonatomic, assign) int32_t creditScore; 98 | @property (nonatomic, strong) NSDictionary *badge; 99 | @property (nonatomic, strong) NSString *lang; 100 | @property (nonatomic, assign) int32_t userAbility; 101 | @property (nonatomic, strong) NSDictionary *extend; 102 | @property (nonatomic, assign) BOOL verified; 103 | @property (nonatomic, assign) int32_t verifiedType; 104 | @property (nonatomic, assign) int32_t verifiedLevel; 105 | @property (nonatomic, assign) int32_t verifiedState; 106 | @property (nonatomic, strong) NSString *verifiedContactEmail; 107 | @property (nonatomic, strong) NSString *verifiedContactMobile; 108 | @property (nonatomic, strong) NSString *verifiedTrade; 109 | @property (nonatomic, strong) NSString *verifiedContactName; 110 | @property (nonatomic, strong) NSString *verifiedSource; 111 | @property (nonatomic, strong) NSString *verifiedSourceURL; 112 | @property (nonatomic, strong) NSString *verifiedReason; 113 | @property (nonatomic, strong) NSString *verifiedReasonURL; 114 | @property (nonatomic, strong) NSString *verifiedReasonModified; 115 | @end 116 | 117 | @interface JSWeiboStatus : JSONModel 118 | @property (nonatomic, assign) uint64_t statusID; 119 | @property (nonatomic, strong) NSString *idstr; 120 | @property (nonatomic, strong) NSString *mid; 121 | @property (nonatomic, strong) NSString *rid; 122 | @property (nonatomic, strong) NSDate *createdAt; 123 | @property (nonatomic, strong) JSWeiboUser *user; 124 | @property (nonatomic, assign) int32_t userType; 125 | @property (nonatomic, strong) NSString *text; 126 | @property (nonatomic, strong) NSArray *picIds; /// Array 127 | @property (nonatomic, strong) NSDictionary *picInfos; /// Dic 128 | @property (nonatomic, strong) NSArray *urlStruct; ///< Array 129 | @property (nonatomic, assign) BOOL favorited; 130 | @property (nonatomic, assign) BOOL truncated; 131 | @property (nonatomic, assign) int32_t repostsCount; 132 | @property (nonatomic, assign) int32_t commentsCount; 133 | @property (nonatomic, assign) int32_t attitudesCount; 134 | @property (nonatomic, assign) int32_t attitudesStatus; 135 | @property (nonatomic, assign) int32_t recomState; 136 | @property (nonatomic, strong) NSString *inReplyToScreenName; 137 | @property (nonatomic, strong) NSString *inReplyToStatusId; 138 | @property (nonatomic, strong) NSString *inReplyToUserId; 139 | @property (nonatomic, strong) NSString *source; 140 | @property (nonatomic, assign) int32_t sourceType; 141 | @property (nonatomic, assign) int32_t sourceAllowClick; 142 | @property (nonatomic, strong) NSString *geo; 143 | @property (nonatomic, strong) NSArray *annotations; 144 | @property (nonatomic, assign) int32_t bizFeature; 145 | @property (nonatomic, assign) int32_t mlevel; 146 | @property (nonatomic, strong) NSString *mblogid; 147 | @property (nonatomic, strong) NSString *mblogTypeName; 148 | @property (nonatomic, assign) int32_t mblogType; 149 | @property (nonatomic, strong) NSString *scheme; 150 | @property (nonatomic, strong) NSDictionary *visible; 151 | @property (nonatomic, strong) NSArray *darwinTags; 152 | @end 153 | 154 | -------------------------------------------------------------------------------- /Benchmark/ModelBenchmark/WeiboModel/JSWeiboModel.m: -------------------------------------------------------------------------------- 1 | // 2 | // JSWeiboModel.m 3 | // ModelBenchmark 4 | // 5 | // Created by ibireme on 15/9/18. 6 | // Copyright (c) 2015 ibireme. All rights reserved. 7 | // 8 | 9 | #import "JSWeiboModel.h" 10 | #import "DateFormatter.h" 11 | 12 | @implementation JSWeiboPictureMetadata 13 | + (JSONKeyMapper *)keyMapper { 14 | return [[JSONKeyMapper alloc] initWithModelToJSONDictionary:@{ @"cut_type" : @"cutType" }]; 15 | } 16 | +(BOOL)propertyIsOptional:(NSString*)propertyName { 17 | return YES; 18 | } 19 | @end 20 | 21 | @implementation JSWeiboPicture 22 | + (JSONKeyMapper *)keyMapper { 23 | return [[JSONKeyMapper alloc] initWithModelToJSONDictionary:@{ 24 | @"url" : @"url", 25 | @"width" : @"width", 26 | @"height" : @"height", 27 | @"type" : @"type", 28 | @"cutType" : @"cut_type" 29 | }]; 30 | } 31 | +(BOOL)propertyIsOptional:(NSString*)propertyName { 32 | return YES; 33 | } 34 | @end 35 | 36 | @implementation JSWeiboURL 37 | + (JSONKeyMapper *)keyMapper { 38 | return [[JSONKeyMapper alloc] initWithModelToJSONDictionary:@{ 39 | @"oriURL" : @"ori_url", 40 | @"urlTitle" : @"url_title", 41 | @"urlTypePic" : @"url_type_pic", 42 | @"urlType" : @"url_type", 43 | @"shortURL" : @"short_url", 44 | @"actionLog" : @"actionlog", 45 | @"pageID" : @"page_id", 46 | @"storageType" : @"storage_type" 47 | }]; 48 | } 49 | +(BOOL)propertyIsOptional:(NSString*)propertyName { 50 | return YES; 51 | } 52 | @end 53 | 54 | @implementation JSWeiboUser 55 | + (JSONKeyMapper *)keyMapper { 56 | return [[JSONKeyMapper alloc] initWithModelToJSONDictionary:@{ 57 | @"userID" : @"id", 58 | @"idString" : @"idstr", 59 | @"genderString" : @"gender", 60 | @"biFollowersCount" : @"bi_followers_count", 61 | @"profileImageURL" : @"profile_image_url", 62 | @"uclass" : @"class", 63 | @"verifiedContactEmail" : @"verified_contact_email", 64 | @"statusesCount" : @"statuses_count", 65 | @"geoEnabled" : @"geo_enabled", 66 | @"followMe" : @"follow_me", 67 | @"coverImagePhone" : @"cover_image_phone", 68 | @"desc" : @"description", 69 | @"followersCount" : @"followers_count", 70 | @"verifiedContactMobile" : @"verified_contact_mobile", 71 | @"avatarLarge" : @"avatar_large", 72 | @"verifiedTrade" : @"verified_trade", 73 | @"profileURL" : @"profile_url", 74 | @"coverImage" : @"cover_image", 75 | @"onlineStatus" : @"online_status", 76 | @"badgeTop" : @"badge_top", 77 | @"verifiedContactName" : @"verified_contact_name", 78 | @"screenName" : @"screen_name", 79 | @"verifiedSourceURL" : @"verified_source_url", 80 | @"pagefriendsCount" : @"pagefriends_count", 81 | @"verifiedReason" : @"verified_reason", 82 | @"friendsCount" : @"friends_count", 83 | @"blockApp" : @"block_app", 84 | @"hasAbilityTag" : @"has_ability_tag", 85 | @"avatarHD" : @"avatar_hd", 86 | @"creditScore" : @"credit_score", 87 | @"createdAt" : @"created_at", 88 | @"blockWord" : @"block_word", 89 | @"allowAllActMsg" : @"allow_all_act_msg", 90 | @"verifiedState" : @"verified_state", 91 | @"verifiedReasonModified" : @"verified_reason_modified", 92 | @"allowAllComment" : @"allow_all_comment", 93 | @"verifiedLevel" : @"verified_level", 94 | @"verifiedReasonURL" : @"verified_reason_url", 95 | @"favouritesCount" : @"favourites_count", 96 | @"verifiedType" : @"verified_type", 97 | @"verifiedSource" : @"verified_source", 98 | @"userAbility" : @"user_ability"}]; 99 | } 100 | +(BOOL)propertyIsOptional:(NSString*)propertyName { 101 | return YES; 102 | } 103 | @end 104 | 105 | @implementation JSWeiboStatus 106 | + (JSONKeyMapper *)keyMapper { 107 | return [[JSONKeyMapper alloc] initWithModelToJSONDictionary:@{ 108 | @"statusID" : @"id", 109 | @"createdAt" : @"created_at", 110 | @"attitudesStatus" : @"attitudes_status", 111 | @"inReplyToScreenName" : @"in_reply_to_screen_name", 112 | @"sourceType" : @"source_type", 113 | @"commentsCount" : @"comments_count", 114 | @"urlStruct" : @"url_struct", 115 | @"recomState" : @"recom_state", 116 | @"sourceAllowClick" : @"source_allowclick", 117 | @"bizFeature" : @"biz_feature", 118 | @"mblogTypeName" : @"mblogtypename", 119 | @"mblogType" : @"mblogtype", 120 | @"inReplyToStatusId" : @"in_reply_to_status_id", 121 | @"picIds" : @"pic_ids", 122 | @"repostsCount" : @"reposts_count", 123 | @"attitudesCount" : @"attitudes_count", 124 | @"darwinTags" : @"darwin_tags", 125 | @"userType" : @"userType", 126 | @"picInfos" : @"pic_infos", 127 | @"inReplyToUserId" : @"in_reply_to_user_id" 128 | }]; 129 | } 130 | +(BOOL)propertyIsOptional:(NSString*)propertyName { 131 | return YES; 132 | } 133 | @end 134 | 135 | 136 | 137 | 138 | 139 | /** 140 | JSONModel use a global transformer to transform value... 141 | But how to create a custom transformer for a specified model ?!!!! 142 | */ 143 | @interface JSONValueTransformer(CustomDate) 144 | -(NSDate*)__NSDateFromNSString:(NSString*)string; 145 | @end 146 | 147 | #pragma clang diagnostic push 148 | #pragma clang diagnostic ignored "-Wincomplete-implementation" 149 | @implementation JSONValueTransformer (CustomDate) 150 | - (NSDate *)NSDateFromNSString:(NSString *)string { 151 | if (string.length == 30) { 152 | return [[DateFormatter weiboDataFormatter] dateFromString:string]; 153 | } else { 154 | return [self __NSDateFromNSString:string]; 155 | } 156 | } 157 | @end 158 | #pragma clang diagnostic pop 159 | -------------------------------------------------------------------------------- /Benchmark/ModelBenchmark/WeiboModel/MJWeiboModel.h: -------------------------------------------------------------------------------- 1 | // 2 | // MJWeiboModel.h 3 | // ModelBenchmark 4 | // 5 | // Created by ibireme on 15/9/18. 6 | // Copyright (c) 2015 ibireme. All rights reserved. 7 | // 8 | 9 | #import "MJExtension.h" 10 | 11 | 12 | @interface MJWeiboPictureMetadata : NSObject 13 | @property (nonatomic, strong) NSString *url; 14 | @property (nonatomic, assign) int width; 15 | @property (nonatomic, assign) int height; 16 | @property (nonatomic, strong) NSString *type; 17 | @property (nonatomic, assign) int cutType; 18 | @end 19 | 20 | @interface MJWeiboPicture : NSObject 21 | @property (nonatomic, strong) NSString *picID; 22 | @property (nonatomic, strong) NSString *objectID; 23 | @property (nonatomic, assign) int photoTag; 24 | @property (nonatomic, assign) BOOL keepSize; 25 | @property (nonatomic, strong) MJWeiboPictureMetadata *thumbnail; 26 | @property (nonatomic, strong) MJWeiboPictureMetadata *bmiddle; 27 | @property (nonatomic, strong) MJWeiboPictureMetadata *middlePlus; 28 | @property (nonatomic, strong) MJWeiboPictureMetadata *large; 29 | @property (nonatomic, strong) MJWeiboPictureMetadata *largest; 30 | @property (nonatomic, strong) MJWeiboPictureMetadata *original; 31 | @end 32 | 33 | @interface MJWeiboURL : NSObject 34 | @property (nonatomic, assign) BOOL result; 35 | @property (nonatomic, strong) NSString *shortURL; 36 | @property (nonatomic, strong) NSString *oriURL; 37 | @property (nonatomic, strong) NSString *urlTitle; 38 | @property (nonatomic, strong) NSString *urlTypePic; 39 | @property (nonatomic, assign) int32_t urlType; 40 | @property (nonatomic, strong) NSString *log; 41 | @property (nonatomic, strong) NSDictionary *actionLog; 42 | @property (nonatomic, strong) NSString *pageID; 43 | @property (nonatomic, strong) NSString *storageType; 44 | @end 45 | 46 | @interface MJWeiboUser : NSObject 47 | @property (nonatomic, assign) uint64_t userID; 48 | @property (nonatomic, strong) NSString *idString; 49 | @property (nonatomic, strong) NSString *genderString; 50 | @property (nonatomic, strong) NSString *desc; 51 | @property (nonatomic, strong) NSString *domain; 52 | @property (nonatomic, strong) NSString *name; 53 | @property (nonatomic, strong) NSString *screenName; 54 | @property (nonatomic, strong) NSString *remark; 55 | @property (nonatomic, assign) int32_t followersCount; 56 | @property (nonatomic, assign) int32_t friendsCount; 57 | @property (nonatomic, assign) int32_t biFollowersCount; 58 | @property (nonatomic, assign) int32_t favouritesCount; 59 | @property (nonatomic, assign) int32_t statusesCount; 60 | @property (nonatomic, assign) int32_t pagefriendsCount; 61 | @property (nonatomic, assign) BOOL followMe; 62 | @property (nonatomic, assign) BOOL following; 63 | @property (nonatomic, strong) NSString *province; 64 | @property (nonatomic, strong) NSString *city; 65 | @property (nonatomic, strong) NSString *url; 66 | @property (nonatomic, strong) NSString *profileImageURL; 67 | @property (nonatomic, strong) NSString *avatarLarge; 68 | @property (nonatomic, strong) NSString *avatarHD; 69 | @property (nonatomic, strong) NSString *coverImage; 70 | @property (nonatomic, strong) NSString *coverImagePhone; 71 | @property (nonatomic, strong) NSString *profileURL; 72 | @property (nonatomic, assign) int32_t type; 73 | @property (nonatomic, assign) int32_t ptype; 74 | @property (nonatomic, assign) int32_t mbtype; 75 | @property (nonatomic, assign) int32_t urank; 76 | @property (nonatomic, assign) int32_t uclass; 77 | @property (nonatomic, assign) int32_t ulevel; 78 | @property (nonatomic, assign) int32_t mbrank; 79 | @property (nonatomic, assign) int32_t star; 80 | @property (nonatomic, assign) int32_t level; 81 | @property (nonatomic, strong) NSDate *createdAt; 82 | @property (nonatomic, assign) BOOL allowAllActMsg; 83 | @property (nonatomic, assign) BOOL allowAllComment; 84 | @property (nonatomic, assign) BOOL geoEnabled; 85 | @property (nonatomic, assign) int32_t onlineStatus; 86 | @property (nonatomic, strong) NSString *location; 87 | @property (nonatomic, strong) NSArray *icons; 88 | @property (nonatomic, strong) NSString *weihao; 89 | @property (nonatomic, strong) NSString *badgeTop; 90 | @property (nonatomic, assign) int32_t blockWord; 91 | @property (nonatomic, assign) int32_t blockApp; 92 | @property (nonatomic, assign) int32_t hasAbilityTag; 93 | @property (nonatomic, assign) int32_t creditScore; 94 | @property (nonatomic, strong) NSDictionary *badge; 95 | @property (nonatomic, strong) NSString *lang; 96 | @property (nonatomic, assign) int32_t userAbility; 97 | @property (nonatomic, strong) NSDictionary *extend; 98 | @property (nonatomic, assign) BOOL verified; 99 | @property (nonatomic, assign) int32_t verifiedType; 100 | @property (nonatomic, assign) int32_t verifiedLevel; 101 | @property (nonatomic, assign) int32_t verifiedState; 102 | @property (nonatomic, strong) NSString *verifiedContactEmail; 103 | @property (nonatomic, strong) NSString *verifiedContactMobile; 104 | @property (nonatomic, strong) NSString *verifiedTrade; 105 | @property (nonatomic, strong) NSString *verifiedContactName; 106 | @property (nonatomic, strong) NSString *verifiedSource; 107 | @property (nonatomic, strong) NSString *verifiedSourceURL; 108 | @property (nonatomic, strong) NSString *verifiedReason; 109 | @property (nonatomic, strong) NSString *verifiedReasonURL; 110 | @property (nonatomic, strong) NSString *verifiedReasonModified; 111 | @end 112 | 113 | @interface MJWeiboStatus : NSObject 114 | @property (nonatomic, assign) uint64_t statusID; 115 | @property (nonatomic, strong) NSString *idstr; 116 | @property (nonatomic, strong) NSString *mid; 117 | @property (nonatomic, strong) NSString *rid; 118 | @property (nonatomic, strong) NSDate *createdAt; 119 | @property (nonatomic, strong) MJWeiboUser *user; 120 | @property (nonatomic, assign) int32_t userType; 121 | @property (nonatomic, strong) NSString *text; 122 | @property (nonatomic, strong) NSArray *picIds; /// Array 123 | @property (nonatomic, strong) NSDictionary *picInfos; /// Dic 124 | @property (nonatomic, strong) NSArray *urlStruct; ///< Array 125 | @property (nonatomic, assign) BOOL favorited; 126 | @property (nonatomic, assign) BOOL truncated; 127 | @property (nonatomic, assign) int32_t repostsCount; 128 | @property (nonatomic, assign) int32_t commentsCount; 129 | @property (nonatomic, assign) int32_t attitudesCount; 130 | @property (nonatomic, assign) int32_t attitudesStatus; 131 | @property (nonatomic, assign) int32_t recomState; 132 | @property (nonatomic, strong) NSString *inReplyToScreenName; 133 | @property (nonatomic, strong) NSString *inReplyToStatusId; 134 | @property (nonatomic, strong) NSString *inReplyToUserId; 135 | @property (nonatomic, strong) NSString *source; 136 | @property (nonatomic, assign) int32_t sourceType; 137 | @property (nonatomic, assign) int32_t sourceAllowClick; 138 | @property (nonatomic, strong) NSString *geo; 139 | @property (nonatomic, strong) NSArray *annotations; 140 | @property (nonatomic, assign) int32_t bizFeature; 141 | @property (nonatomic, assign) int32_t mlevel; 142 | @property (nonatomic, strong) NSString *mblogid; 143 | @property (nonatomic, strong) NSString *mblogTypeName; 144 | @property (nonatomic, assign) int32_t mblogType; 145 | @property (nonatomic, strong) NSString *scheme; 146 | @property (nonatomic, strong) NSDictionary *visible; 147 | @property (nonatomic, strong) NSArray *darwinTags; 148 | @end 149 | -------------------------------------------------------------------------------- /Benchmark/ModelBenchmark/WeiboModel/MJWeiboModel.m: -------------------------------------------------------------------------------- 1 | // 2 | // MJWeiboModel.m 3 | // ModelBenchmark 4 | // 5 | // Created by ibireme on 15/9/18. 6 | // Copyright (c) 2015 ibireme. All rights reserved. 7 | // 8 | 9 | #import "MJWeiboModel.h" 10 | #import "DateFormatter.h" 11 | 12 | @implementation MJWeiboPictureMetadata 13 | MJExtensionCodingImplementation 14 | + (NSDictionary *)replacedKeyFromPropertyName { 15 | return @{@"cutType" : @"cut_type"}; 16 | } 17 | 18 | @end 19 | 20 | @implementation MJWeiboPicture 21 | MJExtensionCodingImplementation 22 | + (NSDictionary *)mj_replacedKeyFromPropertyName { 23 | return @{@"picID" : @"pic_id", 24 | @"keepSize" : @"keep_size", 25 | @"photoTag" : @"photo_tag", 26 | @"objectID" : @"object_id", 27 | @"middlePlus" : @"middleplus"}; 28 | } 29 | 30 | @end 31 | 32 | @implementation MJWeiboURL 33 | MJExtensionCodingImplementation 34 | + (NSDictionary *)mj_replacedKeyFromPropertyName { 35 | return @{@"oriURL" : @"ori_url", 36 | @"urlTitle" : @"url_title", 37 | @"urlTypePic" : @"url_type_pic", 38 | @"urlType" : @"url_type", 39 | @"shortURL" : @"short_url", 40 | @"actionLog" : @"actionlog", 41 | @"pageID" : @"page_id", 42 | @"storageType" : @"storage_type"}; 43 | } 44 | 45 | @end 46 | 47 | @implementation MJWeiboUser 48 | MJExtensionCodingImplementation 49 | + (NSDictionary *)mj_replacedKeyFromPropertyName { 50 | return @{@"userID" : @"id", 51 | @"idString" : @"idstr", 52 | @"genderString" : @"gender", 53 | @"biFollowersCount" : @"bi_followers_count", 54 | @"profileImageURL" : @"profile_image_url", 55 | @"uclass" : @"class", 56 | @"verifiedContactEmail" : @"verified_contact_email", 57 | @"statusesCount" : @"statuses_count", 58 | @"geoEnabled" : @"geo_enabled", 59 | @"followMe" : @"follow_me", 60 | @"coverImagePhone" : @"cover_image_phone", 61 | @"desc" : @"description", 62 | @"followersCount" : @"followers_count", 63 | @"verifiedContactMobile" : @"verified_contact_mobile", 64 | @"avatarLarge" : @"avatar_large", 65 | @"verifiedTrade" : @"verified_trade", 66 | @"profileURL" : @"profile_url", 67 | @"coverImage" : @"cover_image", 68 | @"onlineStatus" : @"online_status", 69 | @"badgeTop" : @"badge_top", 70 | @"verifiedContactName" : @"verified_contact_name", 71 | @"screenName" : @"screen_name", 72 | @"verifiedSourceURL" : @"verified_source_url", 73 | @"pagefriendsCount" : @"pagefriends_count", 74 | @"verifiedReason" : @"verified_reason", 75 | @"friendsCount" : @"friends_count", 76 | @"blockApp" : @"block_app", 77 | @"hasAbilityTag" : @"has_ability_tag", 78 | @"avatarHD" : @"avatar_hd", 79 | @"creditScore" : @"credit_score", 80 | @"createdAt" : @"created_at", 81 | @"blockWord" : @"block_word", 82 | @"allowAllActMsg" : @"allow_all_act_msg", 83 | @"verifiedState" : @"verified_state", 84 | @"verifiedReasonModified" : @"verified_reason_modified", 85 | @"allowAllComment" : @"allow_all_comment", 86 | @"verifiedLevel" : @"verified_level", 87 | @"verifiedReasonURL" : @"verified_reason_url", 88 | @"favouritesCount" : @"favourites_count", 89 | @"verifiedType" : @"verified_type", 90 | @"verifiedSource" : @"verified_source", 91 | @"userAbility" : @"user_ability"}; 92 | } 93 | - (id)mj_newValueFromOldValue:(id)oldValue property:(MJProperty *)property { 94 | if ([property.name isEqualToString:@"createdAt"]) { 95 | return [[DateFormatter weiboDataFormatter] dateFromString:oldValue]; 96 | } 97 | return oldValue; 98 | } 99 | @end 100 | 101 | @implementation MJWeiboStatus 102 | MJExtensionCodingImplementation 103 | + (NSDictionary *)mj_replacedKeyFromPropertyName { 104 | return @{@"statusID" : @"id", 105 | @"createdAt" : @"created_at", 106 | @"attitudesStatus" : @"attitudes_status", 107 | @"inReplyToScreenName" : @"in_reply_to_screen_name", 108 | @"sourceType" : @"source_type", 109 | @"commentsCount" : @"comments_count", 110 | @"urlStruct" : @"url_struct", 111 | @"recomState" : @"recom_state", 112 | @"sourceAllowClick" : @"source_allowclick", 113 | @"bizFeature" : @"biz_feature", 114 | @"mblogTypeName" : @"mblogtypename", 115 | @"mblogType" : @"mblogtype", 116 | @"inReplyToStatusId" : @"in_reply_to_status_id", 117 | @"picIds" : @"pic_ids", 118 | @"repostsCount" : @"reposts_count", 119 | @"attitudesCount" : @"attitudes_count", 120 | @"darwinTags" : @"darwin_tags", 121 | @"userType" : @"userType", 122 | @"picInfos" : @"pic_infos", 123 | @"inReplyToUserId" : @"in_reply_to_user_id"}; 124 | } 125 | + (NSDictionary *)mj_objectClassInArray { 126 | return @{@"picIds" : @"NSString", 127 | @"urlStruct" : @"MJWeiboURL"}; 128 | } 129 | - (id)mj_newValueFromOldValue:(id)oldValue property:(MJProperty *)property { 130 | if ([property.name isEqualToString:@"createdAt"]) { 131 | if ([oldValue isKindOfClass:[NSString class]]) { 132 | return [[DateFormatter weiboDataFormatter] dateFromString:oldValue]; 133 | } 134 | return nil; 135 | } else if ([property.name isEqualToString:@"picInfos"]) { 136 | if (!oldValue || oldValue == [NSNull null]) return nil; 137 | if (![oldValue isKindOfClass:[NSDictionary class]]) return nil; 138 | NSMutableDictionary *pics = [NSMutableDictionary new]; 139 | [((NSDictionary *)oldValue) enumerateKeysAndObjectsUsingBlock:^(id key, NSDictionary *obj, BOOL *stop) { 140 | if ([obj isKindOfClass:[NSDictionary class]]) { 141 | MJWeiboPicture *pic = [MJWeiboPicture mj_objectWithKeyValues:obj]; 142 | if (pic) pics[key] = pic; 143 | } 144 | }]; 145 | return pics; 146 | } 147 | return oldValue; 148 | } 149 | @end 150 | -------------------------------------------------------------------------------- /Benchmark/ModelBenchmark/WeiboModel/MTWeiboModel.h: -------------------------------------------------------------------------------- 1 | // 2 | // MTWeiboModel.h 3 | // ModelBenchmark 4 | // 5 | // Created by ibireme on 15/9/18. 6 | // Copyright (c) 2015 ibireme. All rights reserved. 7 | // 8 | 9 | #import "Mantle.h" 10 | 11 | @interface MTWeiboPictureMetadata : MTLModel 12 | @property (nonatomic, strong) NSString *url; 13 | @property (nonatomic, assign) int width; 14 | @property (nonatomic, assign) int height; 15 | @property (nonatomic, strong) NSString *type; 16 | @property (nonatomic, assign) int cutType; 17 | @end 18 | 19 | @interface MTWeiboPicture : MTLModel 20 | @property (nonatomic, strong) NSString *picID; 21 | @property (nonatomic, strong) NSString *objectID; 22 | @property (nonatomic, assign) int photoTag; 23 | @property (nonatomic, assign) BOOL keepSize; 24 | @property (nonatomic, strong) MTWeiboPictureMetadata *thumbnail; 25 | @property (nonatomic, strong) MTWeiboPictureMetadata *bmiddle; 26 | @property (nonatomic, strong) MTWeiboPictureMetadata *middlePlus; 27 | @property (nonatomic, strong) MTWeiboPictureMetadata *large; 28 | @property (nonatomic, strong) MTWeiboPictureMetadata *largest; 29 | @property (nonatomic, strong) MTWeiboPictureMetadata *original; 30 | @end 31 | 32 | @interface MTWeiboURL : MTLModel 33 | @property (nonatomic, assign) BOOL result; 34 | @property (nonatomic, strong) NSString *shortURL; 35 | @property (nonatomic, strong) NSString *oriURL; 36 | @property (nonatomic, strong) NSString *urlTitle; 37 | @property (nonatomic, strong) NSString *urlTypePic; 38 | @property (nonatomic, assign) int32_t urlType; 39 | @property (nonatomic, strong) NSString *log; 40 | @property (nonatomic, strong) NSDictionary *actionLog; 41 | @property (nonatomic, strong) NSString *pageID; 42 | @property (nonatomic, strong) NSString *storageType; 43 | @end 44 | 45 | @interface MTWeiboUser : MTLModel 46 | @property (nonatomic, assign) uint64_t userID; 47 | @property (nonatomic, strong) NSString *idString; 48 | @property (nonatomic, strong) NSString *genderString; 49 | @property (nonatomic, strong) NSString *desc; 50 | @property (nonatomic, strong) NSString *domain; 51 | @property (nonatomic, strong) NSString *name; 52 | @property (nonatomic, strong) NSString *screenName; 53 | @property (nonatomic, strong) NSString *remark; 54 | @property (nonatomic, assign) int32_t followersCount; 55 | @property (nonatomic, assign) int32_t friendsCount; 56 | @property (nonatomic, assign) int32_t biFollowersCount; 57 | @property (nonatomic, assign) int32_t favouritesCount; 58 | @property (nonatomic, assign) int32_t statusesCount; 59 | @property (nonatomic, assign) int32_t pagefriendsCount; 60 | @property (nonatomic, assign) BOOL followMe; 61 | @property (nonatomic, assign) BOOL following; 62 | @property (nonatomic, strong) NSString *province; 63 | @property (nonatomic, strong) NSString *city; 64 | @property (nonatomic, strong) NSString *url; 65 | @property (nonatomic, strong) NSString *profileImageURL; 66 | @property (nonatomic, strong) NSString *avatarLarge; 67 | @property (nonatomic, strong) NSString *avatarHD; 68 | @property (nonatomic, strong) NSString *coverImage; 69 | @property (nonatomic, strong) NSString *coverImagePhone; 70 | @property (nonatomic, strong) NSString *profileURL; 71 | @property (nonatomic, assign) int32_t type; 72 | @property (nonatomic, assign) int32_t ptype; 73 | @property (nonatomic, assign) int32_t mbtype; 74 | @property (nonatomic, assign) int32_t urank; 75 | @property (nonatomic, assign) int32_t uclass; 76 | @property (nonatomic, assign) int32_t ulevel; 77 | @property (nonatomic, assign) int32_t mbrank; 78 | @property (nonatomic, assign) int32_t star; 79 | @property (nonatomic, assign) int32_t level; 80 | @property (nonatomic, strong) NSDate *createdAt; 81 | @property (nonatomic, assign) BOOL allowAllActMsg; 82 | @property (nonatomic, assign) BOOL allowAllComment; 83 | @property (nonatomic, assign) BOOL geoEnabled; 84 | @property (nonatomic, assign) int32_t onlineStatus; 85 | @property (nonatomic, strong) NSString *location; 86 | @property (nonatomic, strong) NSArray *icons; 87 | @property (nonatomic, strong) NSString *weihao; 88 | @property (nonatomic, strong) NSString *badgeTop; 89 | @property (nonatomic, assign) int32_t blockWord; 90 | @property (nonatomic, assign) int32_t blockApp; 91 | @property (nonatomic, assign) int32_t hasAbilityTag; 92 | @property (nonatomic, assign) int32_t creditScore; 93 | @property (nonatomic, strong) NSDictionary *badge; 94 | @property (nonatomic, strong) NSString *lang; 95 | @property (nonatomic, assign) int32_t userAbility; 96 | @property (nonatomic, strong) NSDictionary *extend; 97 | @property (nonatomic, assign) BOOL verified; 98 | @property (nonatomic, assign) int32_t verifiedType; 99 | @property (nonatomic, assign) int32_t verifiedLevel; 100 | @property (nonatomic, assign) int32_t verifiedState; 101 | @property (nonatomic, strong) NSString *verifiedContactEmail; 102 | @property (nonatomic, strong) NSString *verifiedContactMobile; 103 | @property (nonatomic, strong) NSString *verifiedTrade; 104 | @property (nonatomic, strong) NSString *verifiedContactName; 105 | @property (nonatomic, strong) NSString *verifiedSource; 106 | @property (nonatomic, strong) NSString *verifiedSourceURL; 107 | @property (nonatomic, strong) NSString *verifiedReason; 108 | @property (nonatomic, strong) NSString *verifiedReasonURL; 109 | @property (nonatomic, strong) NSString *verifiedReasonModified; 110 | @end 111 | 112 | @interface MTWeiboStatus : MTLModel 113 | @property (nonatomic, assign) uint64_t statusID; 114 | @property (nonatomic, strong) NSString *idstr; 115 | @property (nonatomic, strong) NSString *mid; 116 | @property (nonatomic, strong) NSString *rid; 117 | @property (nonatomic, strong) NSDate *createdAt; 118 | @property (nonatomic, strong) MTWeiboUser *user; 119 | @property (nonatomic, assign) int32_t userType; 120 | @property (nonatomic, strong) NSString *text; 121 | @property (nonatomic, strong) NSArray *picIds; /// Array 122 | @property (nonatomic, strong) NSDictionary *picInfos; /// Dic 123 | @property (nonatomic, strong) NSArray *urlStruct; ///< Array 124 | @property (nonatomic, assign) BOOL favorited; 125 | @property (nonatomic, assign) BOOL truncated; 126 | @property (nonatomic, assign) int32_t repostsCount; 127 | @property (nonatomic, assign) int32_t commentsCount; 128 | @property (nonatomic, assign) int32_t attitudesCount; 129 | @property (nonatomic, assign) int32_t attitudesStatus; 130 | @property (nonatomic, assign) int32_t recomState; 131 | @property (nonatomic, strong) NSString *inReplyToScreenName; 132 | @property (nonatomic, strong) NSString *inReplyToStatusId; 133 | @property (nonatomic, strong) NSString *inReplyToUserId; 134 | @property (nonatomic, strong) NSString *source; 135 | @property (nonatomic, assign) int32_t sourceType; 136 | @property (nonatomic, assign) int32_t sourceAllowClick; 137 | @property (nonatomic, strong) NSString *geo; 138 | @property (nonatomic, strong) NSArray *annotations; 139 | @property (nonatomic, assign) int32_t bizFeature; 140 | @property (nonatomic, assign) int32_t mlevel; 141 | @property (nonatomic, strong) NSString *mblogid; 142 | @property (nonatomic, strong) NSString *mblogTypeName; 143 | @property (nonatomic, assign) int32_t mblogType; 144 | @property (nonatomic, strong) NSString *scheme; 145 | @property (nonatomic, strong) NSDictionary *visible; 146 | @property (nonatomic, strong) NSArray *darwinTags; 147 | @end 148 | -------------------------------------------------------------------------------- /Benchmark/ModelBenchmark/WeiboModel/MTWeiboModel.m: -------------------------------------------------------------------------------- 1 | // 2 | // MTWeiboModel.m 3 | // ModelBenchmark 4 | // 5 | // Created by ibireme on 15/9/18. 6 | // Copyright (c) 2015 ibireme. All rights reserved. 7 | // 8 | 9 | #import "MTWeiboModel.h" 10 | #import "DateFormatter.h" 11 | 12 | @implementation MTWeiboPictureMetadata 13 | + (NSDictionary *)JSONKeyPathsByPropertyKey { 14 | return @{@"url" : @"url", 15 | @"width" : @"width", 16 | @"height" : @"height", 17 | @"type" : @"type", 18 | @"cutType" : @"cut_type"}; 19 | } 20 | + (NSValueTransformer *)widthJSONTransformer { 21 | return [MTLValueTransformer transformerUsingForwardBlock:^id(NSNumber *num, BOOL *success, NSError *__autoreleasing *error) { 22 | if ([num isKindOfClass:[NSString class]]) { 23 | num = @([((NSString *)num) integerValue]); 24 | } 25 | return num; 26 | } reverseBlock:^id(NSNumber *num, BOOL *success, NSError *__autoreleasing *error) { 27 | return num; 28 | }]; 29 | return [MTLJSONAdapter dictionaryTransformerWithModelClass:MTWeiboPicture.class]; 30 | } 31 | + (NSValueTransformer *)heightJSONTransformer { 32 | return [MTLValueTransformer transformerUsingForwardBlock:^id(NSNumber *num, BOOL *success, NSError *__autoreleasing *error) { 33 | if ([num isKindOfClass:[NSString class]]) { 34 | num = @([((NSString *)num) integerValue]); 35 | } 36 | return num; 37 | } reverseBlock:^id(NSNumber *num, BOOL *success, NSError *__autoreleasing *error) { 38 | return num; 39 | }]; 40 | return [MTLJSONAdapter dictionaryTransformerWithModelClass:MTWeiboPicture.class]; 41 | } 42 | @end 43 | 44 | @implementation MTWeiboPicture 45 | + (NSDictionary *)JSONKeyPathsByPropertyKey { 46 | return @{@"picID" : @"pic_id", 47 | @"keepSize" : @"keep_size", 48 | @"photoTag" : @"photo_tag", 49 | @"objectID" : @"object_id", 50 | @"thumbnail" : @"thumbnail", 51 | @"bmiddle" : @"bmiddle", 52 | @"middlePlus" : @"middleplus", 53 | @"large" : @"large", 54 | @"largest" : @"largest", 55 | @"original" : @"original"}; 56 | } 57 | @end 58 | 59 | @implementation MTWeiboURL 60 | + (NSDictionary *)JSONKeyPathsByPropertyKey { 61 | return @{@"result" : @"result", 62 | @"log" : @"log", 63 | @"oriURL" : @"ori_url", 64 | @"urlTitle" : @"url_title", 65 | @"urlTypePic" : @"url_type_pic", 66 | @"urlType" : @"url_type", 67 | @"shortURL" : @"short_url", 68 | @"actionLog" : @"actionlog", 69 | @"pageID" : @"page_id", 70 | @"storageType" : @"storage_type"}; 71 | } 72 | @end 73 | 74 | @implementation MTWeiboUser 75 | + (NSDictionary *)JSONKeyPathsByPropertyKey { 76 | return @{@"userID" : @"id", 77 | @"idString" : @"idstr", 78 | @"genderString" : @"gender", 79 | @"desc" : @"description", 80 | @"domain" : @"domain", 81 | @"name" : @"name", 82 | @"screenName" : @"screen_name", 83 | @"remark" : @"remark", 84 | @"followersCount" : @"followers_count", 85 | @"friendsCount" : @"friends_count", 86 | @"biFollowersCount" : @"bi_followers_count", 87 | @"favouritesCount" : @"favourites_count", 88 | @"statusesCount" : @"statuses_count", 89 | @"pagefriendsCount" : @"pagefriends_count", 90 | @"followMe" : @"follow_me", 91 | @"following" : @"following", 92 | @"province" : @"province", 93 | @"city" : @"city", 94 | @"url" : @"url", 95 | @"profileImageURL" : @"profile_image_url", 96 | @"avatarLarge" : @"avatar_large", 97 | @"avatarHD" : @"avatar_hd", 98 | @"coverImage" : @"cover_image", 99 | @"coverImagePhone" : @"cover_image_phone", 100 | @"profileURL" : @"profile_url", 101 | @"type" : @"type", 102 | @"ptype" : @"ptype", 103 | @"mbtype" : @"mbtype", 104 | @"urank" : @"urank", 105 | @"uclass" : @"class", 106 | @"ulevel" : @"ulevel", 107 | @"mbrank" : @"mbrank", 108 | @"star" : @"star", 109 | @"level" : @"level", 110 | @"createdAt" : @"created_at", 111 | @"allowAllActMsg" : @"allow_all_act_msg", 112 | @"allowAllComment" : @"allow_all_comment", 113 | @"geoEnabled" : @"geo_enabled", 114 | @"onlineStatus" : @"online_status", 115 | @"location" : @"location", 116 | @"icons" : @"icons", 117 | @"weihao" : @"weihao", 118 | @"badgeTop" : @"badge_top", 119 | @"blockWord" : @"block_word", 120 | @"blockApp" : @"block_app", 121 | @"hasAbilityTag" : @"has_ability_tag", 122 | @"creditScore" : @"credit_score", 123 | @"badge" : @"badge", 124 | @"lang" : @"lang", 125 | @"userAbility" : @"user_ability", 126 | @"extend" : @"extend", 127 | @"verified" : @"verified", 128 | @"verifiedType" : @"verified_type", 129 | @"verifiedLevel" : @"verified_level", 130 | @"verifiedState" : @"verified_state", 131 | @"verifiedContactEmail" : @"verified_contact_email", 132 | @"verifiedContactMobile" : @"verified_contact_mobile", 133 | @"verifiedTrade" : @"verified_trade", 134 | @"verifiedContactName" : @"verified_contact_name", 135 | @"verifiedSource" : @"verified_source", 136 | @"verifiedSourceURL" : @"verified_source_url", 137 | @"verifiedReason" : @"verified_reason", 138 | @"verifiedReasonURL" : @"verified_reason_url", 139 | @"verifiedReasonModified" : @"verified_reason_modified"}; 140 | } 141 | + (NSValueTransformer *)createdAtJSONTransformer { 142 | return [MTLValueTransformer transformerUsingForwardBlock:^id(NSString *dateString, BOOL *success, NSError *__autoreleasing *error) { 143 | return [[DateFormatter weiboDataFormatter] dateFromString:dateString]; 144 | } reverseBlock:^id(NSDate *date, BOOL *success, NSError *__autoreleasing *error) { 145 | return [[DateFormatter weiboDataFormatter] stringFromDate:date]; 146 | }]; 147 | } 148 | @end 149 | 150 | @implementation MTWeiboStatus 151 | + (NSDictionary *)JSONKeyPathsByPropertyKey { 152 | return @{@"statusID" : @"id", 153 | @"idstr" : @"idstr", 154 | @"mid" : @"mid", 155 | @"rid" : @"rid", 156 | @"createdAt" : @"created_at", 157 | @"user" : @"user", 158 | @"userType" : @"userType", 159 | @"text" : @"text", 160 | @"picIds" : @"pic_ids", 161 | @"picInfos" : @"pic_infos", 162 | @"urlStruct" : @"url_struct", 163 | @"favorited" : @"favorited", 164 | @"truncated" : @"truncated", 165 | @"repostsCount" : @"reposts_count", 166 | @"commentsCount" : @"comments_count", 167 | @"attitudesCount" : @"attitudes_count", 168 | @"attitudesStatus" : @"attitudes_status", 169 | @"recomState" : @"recom_state", 170 | @"inReplyToScreenName" : @"in_reply_to_screen_name", 171 | @"inReplyToStatusId" : @"in_reply_to_status_id", 172 | @"inReplyToUserId" : @"in_reply_to_user_id", 173 | @"source" : @"source", 174 | @"sourceType" : @"source_type", 175 | @"sourceAllowClick" : @"source_allowclick", 176 | @"geo" : @"geo", 177 | @"annotations" : @"annotations", 178 | @"bizFeature" : @"biz_feature", 179 | @"mblogid" : @"mblogid", 180 | @"mblogTypeName" : @"mblogtypename", 181 | @"mblogType" : @"mblogtype", 182 | @"scheme" : @"scheme", 183 | @"visible" : @"visible", 184 | @"darwinTags" : @"darwin_tags"}; 185 | } 186 | + (NSValueTransformer *)picInfosJSONTransformer { 187 | static MTLJSONAdapter *pictureAdapter; 188 | static dispatch_once_t onceToken; 189 | dispatch_once(&onceToken, ^{ 190 | pictureAdapter = [[MTLJSONAdapter alloc] initWithModelClass:[MTWeiboPicture class]]; 191 | }); 192 | 193 | return [MTLValueTransformer transformerUsingForwardBlock:^id(NSDictionary *dic, BOOL *success, NSError *__autoreleasing *error) { 194 | NSMutableDictionary *pics = [NSMutableDictionary new]; 195 | [dic enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { 196 | MTWeiboPicture *pic = [pictureAdapter modelFromJSONDictionary:obj error:nil]; 197 | if (pic) pics[key] = pic; 198 | }]; 199 | return pics; 200 | } reverseBlock:^id(NSDictionary *dic, BOOL *success, NSError *__autoreleasing *error) { 201 | NSMutableDictionary *ret = [NSMutableDictionary new]; 202 | [dic enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { 203 | if ([obj isKindOfClass:[MTWeiboPicture class]]) { 204 | NSDictionary *one = [pictureAdapter JSONDictionaryFromModel:obj error:nil]; 205 | if (one) ret[key] = one; 206 | } 207 | }]; 208 | return ret; 209 | }]; 210 | return [MTLJSONAdapter dictionaryTransformerWithModelClass:MTWeiboPicture.class]; 211 | } 212 | + (NSValueTransformer *)urlStructJSONTransformer { 213 | return [MTLJSONAdapter arrayTransformerWithModelClass:MTWeiboURL.class]; 214 | } 215 | + (NSValueTransformer *)createdAtJSONTransformer { 216 | return [MTLValueTransformer transformerUsingForwardBlock:^id(NSString *dateString, BOOL *success, NSError *__autoreleasing *error) { 217 | return [[DateFormatter weiboDataFormatter] dateFromString:dateString]; 218 | } reverseBlock:^id(NSDate *date, BOOL *success, NSError *__autoreleasing *error) { 219 | return [[DateFormatter weiboDataFormatter] stringFromDate:date]; 220 | }]; 221 | } 222 | @end 223 | 224 | -------------------------------------------------------------------------------- /Benchmark/ModelBenchmark/WeiboModel/YYWeiboModel.h: -------------------------------------------------------------------------------- 1 | // 2 | // WBModel.h 3 | // ModelBenchmark 4 | // 5 | // Created by ibireme on 15/9/18. 6 | // Copyright (c) 2015 ibireme. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface YYWeiboPictureMetadata : NSObject 12 | @property (nonatomic, strong) NSString *url; 13 | @property (nonatomic, assign) int width; 14 | @property (nonatomic, assign) int height; 15 | @property (nonatomic, strong) NSString *type; 16 | @property (nonatomic, assign) int cutType; 17 | @end 18 | 19 | @interface YYWeiboPicture : NSObject 20 | @property (nonatomic, strong) NSString *picID; 21 | @property (nonatomic, strong) NSString *objectID; 22 | @property (nonatomic, assign) int photoTag; 23 | @property (nonatomic, assign) BOOL keepSize; 24 | @property (nonatomic, strong) YYWeiboPictureMetadata *thumbnail; 25 | @property (nonatomic, strong) YYWeiboPictureMetadata *bmiddle; 26 | @property (nonatomic, strong) YYWeiboPictureMetadata *middlePlus; 27 | @property (nonatomic, strong) YYWeiboPictureMetadata *large; 28 | @property (nonatomic, strong) YYWeiboPictureMetadata *largest; 29 | @property (nonatomic, strong) YYWeiboPictureMetadata *original; 30 | @end 31 | 32 | @interface YYWeiboURL : NSObject 33 | @property (nonatomic, assign) BOOL result; 34 | @property (nonatomic, strong) NSString *shortURL; 35 | @property (nonatomic, strong) NSString *oriURL; 36 | @property (nonatomic, strong) NSString *urlTitle; 37 | @property (nonatomic, strong) NSString *urlTypePic; 38 | @property (nonatomic, assign) int32_t urlType; 39 | @property (nonatomic, strong) NSString *log; 40 | @property (nonatomic, strong) NSDictionary *actionLog; 41 | @property (nonatomic, strong) NSString *pageID; 42 | @property (nonatomic, strong) NSString *storageType; 43 | @end 44 | 45 | @interface YYWeiboUser : NSObject 46 | @property (nonatomic, assign) uint64_t userID; 47 | @property (nonatomic, strong) NSString *idString; 48 | @property (nonatomic, strong) NSString *genderString; 49 | @property (nonatomic, strong) NSString *desc; 50 | @property (nonatomic, strong) NSString *domain; 51 | @property (nonatomic, strong) NSString *name; 52 | @property (nonatomic, strong) NSString *screenName; 53 | @property (nonatomic, strong) NSString *remark; 54 | @property (nonatomic, assign) int32_t followersCount; 55 | @property (nonatomic, assign) int32_t friendsCount; 56 | @property (nonatomic, assign) int32_t biFollowersCount; 57 | @property (nonatomic, assign) int32_t favouritesCount; 58 | @property (nonatomic, assign) int32_t statusesCount; 59 | @property (nonatomic, assign) int32_t pagefriendsCount; 60 | @property (nonatomic, assign) BOOL followMe; 61 | @property (nonatomic, assign) BOOL following; 62 | @property (nonatomic, strong) NSString *province; 63 | @property (nonatomic, strong) NSString *city; 64 | @property (nonatomic, strong) NSString *url; 65 | @property (nonatomic, strong) NSString *profileImageURL; 66 | @property (nonatomic, strong) NSString *avatarLarge; 67 | @property (nonatomic, strong) NSString *avatarHD; 68 | @property (nonatomic, strong) NSString *coverImage; 69 | @property (nonatomic, strong) NSString *coverImagePhone; 70 | @property (nonatomic, strong) NSString *profileURL; 71 | @property (nonatomic, assign) int32_t type; 72 | @property (nonatomic, assign) int32_t ptype; 73 | @property (nonatomic, assign) int32_t mbtype; 74 | @property (nonatomic, assign) int32_t urank; 75 | @property (nonatomic, assign) int32_t uclass; 76 | @property (nonatomic, assign) int32_t ulevel; 77 | @property (nonatomic, assign) int32_t mbrank; 78 | @property (nonatomic, assign) int32_t star; 79 | @property (nonatomic, assign) int32_t level; 80 | @property (nonatomic, strong) NSDate *createdAt; 81 | @property (nonatomic, assign) BOOL allowAllActMsg; 82 | @property (nonatomic, assign) BOOL allowAllComment; 83 | @property (nonatomic, assign) BOOL geoEnabled; 84 | @property (nonatomic, assign) int32_t onlineStatus; 85 | @property (nonatomic, strong) NSString *location; 86 | @property (nonatomic, strong) NSArray *icons; 87 | @property (nonatomic, strong) NSString *weihao; 88 | @property (nonatomic, strong) NSString *badgeTop; 89 | @property (nonatomic, assign) int32_t blockWord; 90 | @property (nonatomic, assign) int32_t blockApp; 91 | @property (nonatomic, assign) int32_t hasAbilityTag; 92 | @property (nonatomic, assign) int32_t creditScore; 93 | @property (nonatomic, strong) NSDictionary *badge; 94 | @property (nonatomic, strong) NSString *lang; 95 | @property (nonatomic, assign) int32_t userAbility; 96 | @property (nonatomic, strong) NSDictionary *extend; 97 | @property (nonatomic, assign) BOOL verified; 98 | @property (nonatomic, assign) int32_t verifiedType; 99 | @property (nonatomic, assign) int32_t verifiedLevel; 100 | @property (nonatomic, assign) int32_t verifiedState; 101 | @property (nonatomic, strong) NSString *verifiedContactEmail; 102 | @property (nonatomic, strong) NSString *verifiedContactMobile; 103 | @property (nonatomic, strong) NSString *verifiedTrade; 104 | @property (nonatomic, strong) NSString *verifiedContactName; 105 | @property (nonatomic, strong) NSString *verifiedSource; 106 | @property (nonatomic, strong) NSString *verifiedSourceURL; 107 | @property (nonatomic, strong) NSString *verifiedReason; 108 | @property (nonatomic, strong) NSString *verifiedReasonURL; 109 | @property (nonatomic, strong) NSString *verifiedReasonModified; 110 | @end 111 | 112 | @interface YYWeiboStatus : NSObject 113 | @property (nonatomic, assign) uint64_t statusID; 114 | @property (nonatomic, strong) NSString *idstr; 115 | @property (nonatomic, strong) NSString *mid; 116 | @property (nonatomic, strong) NSString *rid; 117 | @property (nonatomic, strong) NSDate *createdAt; 118 | @property (nonatomic, strong) YYWeiboUser *user; 119 | @property (nonatomic, assign) int32_t userType; 120 | @property (nonatomic, strong) NSString *text; 121 | @property (nonatomic, strong) NSArray *picIds; /// Array 122 | @property (nonatomic, strong) NSDictionary *picInfos; /// Dic 123 | @property (nonatomic, strong) NSArray *urlStruct; ///< Array 124 | @property (nonatomic, assign) BOOL favorited; 125 | @property (nonatomic, assign) BOOL truncated; 126 | @property (nonatomic, assign) int32_t repostsCount; 127 | @property (nonatomic, assign) int32_t commentsCount; 128 | @property (nonatomic, assign) int32_t attitudesCount; 129 | @property (nonatomic, assign) int32_t attitudesStatus; 130 | @property (nonatomic, assign) int32_t recomState; 131 | @property (nonatomic, strong) NSString *inReplyToScreenName; 132 | @property (nonatomic, strong) NSString *inReplyToStatusId; 133 | @property (nonatomic, strong) NSString *inReplyToUserId; 134 | @property (nonatomic, strong) NSString *source; 135 | @property (nonatomic, assign) int32_t sourceType; 136 | @property (nonatomic, assign) int32_t sourceAllowClick; 137 | @property (nonatomic, strong) NSString *geo; 138 | @property (nonatomic, strong) NSArray *annotations; 139 | @property (nonatomic, assign) int32_t bizFeature; 140 | @property (nonatomic, assign) int32_t mlevel; 141 | @property (nonatomic, strong) NSString *mblogid; 142 | @property (nonatomic, strong) NSString *mblogTypeName; 143 | @property (nonatomic, assign) int32_t mblogType; 144 | @property (nonatomic, strong) NSString *scheme; 145 | @property (nonatomic, strong) NSDictionary *visible; 146 | @property (nonatomic, strong) NSArray *darwinTags; 147 | @end 148 | -------------------------------------------------------------------------------- /Benchmark/ModelBenchmark/WeiboModel/YYWeiboModel.m: -------------------------------------------------------------------------------- 1 | // 2 | // WBModel.m 3 | // ModelBenchmark 4 | // 5 | // Created by ibireme on 15/9/18. 6 | // Copyright (c) 2015 ibireme. All rights reserved. 7 | // 8 | 9 | #import "YYWeiboModel.h" 10 | 11 | #define YYModelSynthCoderAndHash \ 12 | - (void)encodeWithCoder:(NSCoder *)aCoder { [self yy_modelEncodeWithCoder:aCoder]; } \ 13 | - (id)initWithCoder:(NSCoder *)aDecoder { return [self yy_modelInitWithCoder:aDecoder]; } \ 14 | - (id)copyWithZone:(NSZone *)zone { return [self yy_modelCopy]; } \ 15 | - (NSUInteger)hash { return [self yy_modelHash]; } \ 16 | - (BOOL)isEqual:(id)object { return [self yy_modelIsEqual:object]; } 17 | 18 | 19 | @implementation YYWeiboPictureMetadata 20 | + (NSDictionary *)modelCustomPropertyMapper { 21 | return @{@"cutType" : @"cut_type"}; 22 | } 23 | YYModelSynthCoderAndHash 24 | @end 25 | 26 | @implementation YYWeiboPicture 27 | + (NSDictionary *)modelCustomPropertyMapper { 28 | return @{@"picID" : @"pic_id", 29 | @"keepSize" : @"keep_size", 30 | @"photoTag" : @"photo_tag", 31 | @"objectID" : @"object_id", 32 | @"middlePlus" : @"middleplus"}; 33 | } 34 | YYModelSynthCoderAndHash 35 | @end 36 | 37 | @implementation YYWeiboURL 38 | + (NSDictionary *)modelCustomPropertyMapper { 39 | return @{@"oriURL" : @"ori_url", 40 | @"urlTitle" : @"url_title", 41 | @"urlTypePic" : @"url_type_pic", 42 | @"urlType" : @"url_type", 43 | @"shortURL" : @"short_url", 44 | @"actionLog" : @"actionlog", 45 | @"pageID" : @"page_id", 46 | @"storageType" : @"storage_type"}; 47 | } 48 | YYModelSynthCoderAndHash 49 | @end 50 | 51 | @implementation YYWeiboUser 52 | + (NSDictionary *)modelCustomPropertyMapper { 53 | return @{@"userID" : @"id", 54 | @"idString" : @"idstr", 55 | @"genderString" : @"gender", 56 | @"biFollowersCount" : @"bi_followers_count", 57 | @"profileImageURL" : @"profile_image_url", 58 | @"uclass" : @"class", 59 | @"verifiedContactEmail" : @"verified_contact_email", 60 | @"statusesCount" : @"statuses_count", 61 | @"geoEnabled" : @"geo_enabled", 62 | @"followMe" : @"follow_me", 63 | @"coverImagePhone" : @"cover_image_phone", 64 | @"desc" : @"description", 65 | @"followersCount" : @"followers_count", 66 | @"verifiedContactMobile" : @"verified_contact_mobile", 67 | @"avatarLarge" : @"avatar_large", 68 | @"verifiedTrade" : @"verified_trade", 69 | @"profileURL" : @"profile_url", 70 | @"coverImage" : @"cover_image", 71 | @"onlineStatus" : @"online_status", 72 | @"badgeTop" : @"badge_top", 73 | @"verifiedContactName" : @"verified_contact_name", 74 | @"screenName" : @"screen_name", 75 | @"verifiedSourceURL" : @"verified_source_url", 76 | @"pagefriendsCount" : @"pagefriends_count", 77 | @"verifiedReason" : @"verified_reason", 78 | @"friendsCount" : @"friends_count", 79 | @"blockApp" : @"block_app", 80 | @"hasAbilityTag" : @"has_ability_tag", 81 | @"avatarHD" : @"avatar_hd", 82 | @"creditScore" : @"credit_score", 83 | @"createdAt" : @"created_at", 84 | @"blockWord" : @"block_word", 85 | @"allowAllActMsg" : @"allow_all_act_msg", 86 | @"verifiedState" : @"verified_state", 87 | @"verifiedReasonModified" : @"verified_reason_modified", 88 | @"allowAllComment" : @"allow_all_comment", 89 | @"verifiedLevel" : @"verified_level", 90 | @"verifiedReasonURL" : @"verified_reason_url", 91 | @"favouritesCount" : @"favourites_count", 92 | @"verifiedType" : @"verified_type", 93 | @"verifiedSource" : @"verified_source", 94 | @"userAbility" : @"user_ability"}; 95 | } 96 | YYModelSynthCoderAndHash 97 | @end 98 | 99 | @implementation YYWeiboStatus 100 | + (NSDictionary *)modelCustomPropertyMapper { 101 | return @{@"statusID" : @"id", 102 | @"createdAt" : @"created_at", 103 | @"attitudesStatus" : @"attitudes_status", 104 | @"inReplyToScreenName" : @"in_reply_to_screen_name", 105 | @"sourceType" : @"source_type", 106 | @"commentsCount" : @"comments_count", 107 | @"recomState" : @"recom_state", 108 | @"urlStruct" : @"url_struct", 109 | @"sourceAllowClick" : @"source_allowclick", 110 | @"bizFeature" : @"biz_feature", 111 | @"mblogTypeName" : @"mblogtypename", 112 | @"mblogType" : @"mblogtype", 113 | @"inReplyToStatusId" : @"in_reply_to_status_id", 114 | @"picIds" : @"pic_ids", 115 | @"repostsCount" : @"reposts_count", 116 | @"attitudesCount" : @"attitudes_count", 117 | @"darwinTags" : @"darwin_tags", 118 | @"userType" : @"userType", 119 | @"picInfos" : @"pic_infos", 120 | @"inReplyToUserId" : @"in_reply_to_user_id"}; 121 | } 122 | + (NSDictionary *)modelContainerPropertyGenericClass { 123 | return @{@"picIds" : [NSString class], 124 | @"picInfos" : [YYWeiboPicture class], 125 | @"urlStruct" : [YYWeiboURL class]}; 126 | } 127 | YYModelSynthCoderAndHash 128 | @end 129 | -------------------------------------------------------------------------------- /Benchmark/ModelBenchmark/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // ModelBenchmark 4 | // 5 | // Created by ibireme on 15/9/18. 6 | // Copyright (c) 2015 ibireme. 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 | -------------------------------------------------------------------------------- /Benchmark/Podfile: -------------------------------------------------------------------------------- 1 | platform :ios, '8.0' 2 | use_frameworks! 3 | 4 | target 'ModelBenchmark' do 5 | 6 | # Objective-C 7 | pod 'FastEasyMapping' 8 | pod 'JSONModel' 9 | pod 'Mantle' 10 | pod 'MJExtension' 11 | pod 'YYModel' 12 | 13 | # Swift 14 | pod 'Argo' 15 | pod 'Gloss' 16 | pod 'HandyJSON' 17 | pod 'ObjectMapper' 18 | pod 'SwiftyJSON' 19 | 20 | # Generator 21 | # https://github.com/Ahmed-Ali/JSONExport 22 | # https://github.com/nixzhu/Coolie 23 | # https://github.com/johnlui/JSONNeverDie 24 | 25 | end 26 | 27 | 28 | -------------------------------------------------------------------------------- /Benchmark/Result.numbers: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibireme/YYModel/1230e605c1abdcd34bf0adb371d89783ff39a856/Benchmark/Result.numbers -------------------------------------------------------------------------------- /Benchmark/Result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibireme/YYModel/1230e605c1abdcd34bf0adb371d89783ff39a856/Benchmark/Result.png -------------------------------------------------------------------------------- /Framework/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.4 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Framework/YYModel.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Framework/YYModel.xcodeproj/xcshareddata/xcschemes/YYModel.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 34 | 40 | 41 | 42 | 43 | 44 | 50 | 51 | 52 | 53 | 54 | 55 | 65 | 66 | 72 | 73 | 74 | 75 | 76 | 77 | 83 | 84 | 90 | 91 | 92 | 93 | 95 | 96 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 ibireme 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /YYModel.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'YYModel' 3 | s.summary = 'High performance model framework for iOS/OSX.' 4 | s.version = '1.0.4' 5 | s.license = { :type => 'MIT', :file => 'LICENSE' } 6 | s.authors = { 'ibireme' => 'ibireme@gmail.com' } 7 | s.social_media_url = 'http://blog.ibireme.com' 8 | s.homepage = 'https://github.com/ibireme/YYModel' 9 | 10 | s.ios.deployment_target = '6.0' 11 | s.osx.deployment_target = '10.7' 12 | s.watchos.deployment_target = '2.0' 13 | s.tvos.deployment_target = '9.0' 14 | 15 | s.source = { :git => 'https://github.com/ibireme/YYModel.git', :tag => s.version.to_s } 16 | 17 | s.requires_arc = true 18 | s.source_files = 'YYModel/*.{h,m}' 19 | s.public_header_files = 'YYModel/*.{h}' 20 | 21 | s.frameworks = 'Foundation', 'CoreFoundation' 22 | 23 | end 24 | -------------------------------------------------------------------------------- /YYModel/NSObject+YYModel.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+YYModel.h 3 | // YYModel 4 | // 5 | // Created by ibireme on 15/5/10. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | /** 17 | Provide some data-model method: 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 | 23 | See `YYModel` protocol for custom methods. 24 | 25 | 26 | Sample Code: 27 | 28 | ********************** json convertor ********************* 29 | @code 30 | @interface YYAuthor : NSObject 31 | @property (nonatomic, strong) NSString *name; 32 | @property (nonatomic, assign) NSDate *birthday; 33 | @end 34 | @implementation YYAuthor 35 | @end 36 | 37 | @interface YYBook : NSObject 38 | @property (nonatomic, copy) NSString *name; 39 | @property (nonatomic, assign) NSUInteger pages; 40 | @property (nonatomic, strong) YYAuthor *author; 41 | @end 42 | @implementation YYBook 43 | @end 44 | 45 | int main() { 46 | // create model from json 47 | YYBook *book = [YYBook yy_modelWithJSON:@"{\"name\": \"Harry Potter\", \"pages\": 256, \"author\": {\"name\": \"J.K.Rowling\", \"birthday\": \"1965-07-31\" }}"]; 48 | 49 | // convert model to json 50 | NSString *json = [book yy_modelToJSONString]; 51 | // {"author":{"name":"J.K.Rowling","birthday":"1965-07-31T00:00:00+0000"},"name":"Harry Potter","pages":256} 52 | } 53 | @endcode 54 | 55 | ********************** Coding/Copying/hash/equal ********************* 56 | @code 57 | @interface YYShadow :NSObject 58 | @property (nonatomic, copy) NSString *name; 59 | @property (nonatomic, assign) CGSize size; 60 | @end 61 | 62 | @implementation YYShadow 63 | - (void)encodeWithCoder:(NSCoder *)aCoder { [self yy_modelEncodeWithCoder:aCoder]; } 64 | - (id)initWithCoder:(NSCoder *)aDecoder { self = [super init]; return [self yy_modelInitWithCoder:aDecoder]; } 65 | - (id)copyWithZone:(NSZone *)zone { return [self yy_modelCopy]; } 66 | - (NSUInteger)hash { return [self yy_modelHash]; } 67 | - (BOOL)isEqual:(id)object { return [self yy_modelIsEqual:object]; } 68 | @end 69 | @endcode 70 | 71 | */ 72 | @interface NSObject (YYModel) 73 | 74 | /** 75 | Creates and returns a new instance of the receiver from a json. 76 | This method is thread-safe. 77 | 78 | @param json A json object in `NSDictionary`, `NSString` or `NSData`. 79 | 80 | @return A new instance created from the json, or nil if an error occurs. 81 | */ 82 | + (nullable instancetype)yy_modelWithJSON:(id)json; 83 | 84 | /** 85 | Creates and returns a new instance of the receiver from a key-value dictionary. 86 | This method is thread-safe. 87 | 88 | @param dictionary A key-value dictionary mapped to the instance's properties. 89 | Any invalid key-value pair in dictionary will be ignored. 90 | 91 | @return A new instance created from the dictionary, or nil if an error occurs. 92 | 93 | @discussion The key in `dictionary` will mapped to the reciever's property name, 94 | and the value will set to the property. If the value's type does not match the 95 | property, this method will try to convert the value based on these rules: 96 | 97 | `NSString` or `NSNumber` -> c number, such as BOOL, int, long, float, NSUInteger... 98 | `NSString` -> NSDate, parsed with format "yyyy-MM-dd'T'HH:mm:ssZ", "yyyy-MM-dd HH:mm:ss" or "yyyy-MM-dd". 99 | `NSString` -> NSURL. 100 | `NSValue` -> struct or union, such as CGRect, CGSize, ... 101 | `NSString` -> SEL, Class. 102 | */ 103 | + (nullable instancetype)yy_modelWithDictionary:(NSDictionary *)dictionary; 104 | 105 | /** 106 | Set the receiver's properties with a json object. 107 | 108 | @discussion Any invalid data in json will be ignored. 109 | 110 | @param json A json object of `NSDictionary`, `NSString` or `NSData`, mapped to the 111 | receiver's properties. 112 | 113 | @return Whether succeed. 114 | */ 115 | - (BOOL)yy_modelSetWithJSON:(id)json; 116 | 117 | /** 118 | Set the receiver's properties with a key-value dictionary. 119 | 120 | @param dic A key-value dictionary mapped to the receiver's properties. 121 | Any invalid key-value pair in dictionary will be ignored. 122 | 123 | @discussion The key in `dictionary` will mapped to the reciever's property name, 124 | and the value will set to the property. If the value's type doesn't match the 125 | property, this method will try to convert the value based on these rules: 126 | 127 | `NSString`, `NSNumber` -> c number, such as BOOL, int, long, float, NSUInteger... 128 | `NSString` -> NSDate, parsed with format "yyyy-MM-dd'T'HH:mm:ssZ", "yyyy-MM-dd HH:mm:ss" or "yyyy-MM-dd". 129 | `NSString` -> NSURL. 130 | `NSValue` -> struct or union, such as CGRect, CGSize, ... 131 | `NSString` -> SEL, Class. 132 | 133 | @return Whether succeed. 134 | */ 135 | - (BOOL)yy_modelSetWithDictionary:(NSDictionary *)dic; 136 | 137 | /** 138 | Generate a json object from the receiver's properties. 139 | 140 | @return A json object in `NSDictionary` or `NSArray`, or nil if an error occurs. 141 | See [NSJSONSerialization isValidJSONObject] for more information. 142 | 143 | @discussion Any of the invalid property is ignored. 144 | If the reciver is `NSArray`, `NSDictionary` or `NSSet`, it just convert 145 | the inner object to json object. 146 | */ 147 | - (nullable id)yy_modelToJSONObject; 148 | 149 | /** 150 | Generate a json string's data from the receiver's properties. 151 | 152 | @return A json string's data, or nil if an error occurs. 153 | 154 | @discussion Any of the invalid property is ignored. 155 | If the reciver is `NSArray`, `NSDictionary` or `NSSet`, it will also convert the 156 | inner object to json string. 157 | */ 158 | - (nullable NSData *)yy_modelToJSONData; 159 | 160 | /** 161 | Generate a json string from the receiver's properties. 162 | 163 | @return A json string, or nil if an error occurs. 164 | 165 | @discussion Any of the invalid property is ignored. 166 | If the reciver is `NSArray`, `NSDictionary` or `NSSet`, it will also convert the 167 | inner object to json string. 168 | */ 169 | - (nullable NSString *)yy_modelToJSONString; 170 | 171 | /** 172 | Copy a instance with the receiver's properties. 173 | 174 | @return A copied instance, or nil if an error occurs. 175 | */ 176 | - (nullable id)yy_modelCopy; 177 | 178 | /** 179 | Encode the receiver's properties to a coder. 180 | 181 | @param aCoder An archiver object. 182 | */ 183 | - (void)yy_modelEncodeWithCoder:(NSCoder *)aCoder; 184 | 185 | /** 186 | Decode the receiver's properties from a decoder. 187 | 188 | @param aDecoder An archiver object. 189 | 190 | @return self 191 | */ 192 | - (id)yy_modelInitWithCoder:(NSCoder *)aDecoder; 193 | 194 | /** 195 | Get a hash code with the receiver's properties. 196 | 197 | @return Hash code. 198 | */ 199 | - (NSUInteger)yy_modelHash; 200 | 201 | /** 202 | Compares the receiver with another object for equality, based on properties. 203 | 204 | @param model Another object. 205 | 206 | @return `YES` if the reciever is equal to the object, otherwise `NO`. 207 | */ 208 | - (BOOL)yy_modelIsEqual:(id)model; 209 | 210 | /** 211 | Description method for debugging purposes based on properties. 212 | 213 | @return A string that describes the contents of the receiver. 214 | */ 215 | - (NSString *)yy_modelDescription; 216 | 217 | @end 218 | 219 | 220 | 221 | /** 222 | Provide some data-model method for NSArray. 223 | */ 224 | @interface NSArray (YYModel) 225 | 226 | /** 227 | Creates and returns an array from a json-array. 228 | This method is thread-safe. 229 | 230 | @param cls The instance's class in array. 231 | @param json A json array of `NSArray`, `NSString` or `NSData`. 232 | Example: [{"name":"Mary"},{name:"Joe"}] 233 | 234 | @return A array, or nil if an error occurs. 235 | */ 236 | + (nullable NSArray *)yy_modelArrayWithClass:(Class)cls json:(id)json; 237 | 238 | @end 239 | 240 | 241 | 242 | /** 243 | Provide some data-model method for NSDictionary. 244 | */ 245 | @interface NSDictionary (YYModel) 246 | 247 | /** 248 | Creates and returns a dictionary from a json. 249 | This method is thread-safe. 250 | 251 | @param cls The value instance's class in dictionary. 252 | @param json A json dictionary of `NSDictionary`, `NSString` or `NSData`. 253 | Example: {"user1":{"name","Mary"}, "user2": {name:"Joe"}} 254 | 255 | @return A dictionary, or nil if an error occurs. 256 | */ 257 | + (nullable NSDictionary *)yy_modelDictionaryWithClass:(Class)cls json:(id)json; 258 | @end 259 | 260 | 261 | 262 | /** 263 | If the default model transform does not fit to your model class, implement one or 264 | more method in this protocol to change the default key-value transform process. 265 | There's no need to add '' to your class header. 266 | */ 267 | @protocol YYModel 268 | @optional 269 | 270 | /** 271 | Custom property mapper. 272 | 273 | @discussion If the key in JSON/Dictionary does not match to the model's property name, 274 | implements this method and returns the additional mapper. 275 | 276 | Example: 277 | 278 | json: 279 | { 280 | "n":"Harry Pottery", 281 | "p": 256, 282 | "ext" : { 283 | "desc" : "A book written by J.K.Rowling." 284 | }, 285 | "ID" : 100010 286 | } 287 | 288 | model: 289 | @code 290 | @interface YYBook : NSObject 291 | @property NSString *name; 292 | @property NSInteger page; 293 | @property NSString *desc; 294 | @property NSString *bookID; 295 | @end 296 | 297 | @implementation YYBook 298 | + (NSDictionary *)modelCustomPropertyMapper { 299 | return @{@"name" : @"n", 300 | @"page" : @"p", 301 | @"desc" : @"ext.desc", 302 | @"bookID": @[@"id", @"ID", @"book_id"]}; 303 | } 304 | @end 305 | @endcode 306 | 307 | @return A custom mapper for properties. 308 | */ 309 | + (nullable NSDictionary *)modelCustomPropertyMapper; 310 | 311 | /** 312 | The generic class mapper for container properties. 313 | 314 | @discussion If the property is a container object, such as NSArray/NSSet/NSDictionary, 315 | implements this method and returns a property->class mapper, tells which kind of 316 | object will be add to the array/set/dictionary. 317 | 318 | Example: 319 | @code 320 | @class YYShadow, YYBorder, YYAttachment; 321 | 322 | @interface YYAttributes 323 | @property NSString *name; 324 | @property NSArray *shadows; 325 | @property NSSet *borders; 326 | @property NSDictionary *attachments; 327 | @end 328 | 329 | @implementation YYAttributes 330 | + (NSDictionary *)modelContainerPropertyGenericClass { 331 | return @{@"shadows" : [YYShadow class], 332 | @"borders" : YYBorder.class, 333 | @"attachments" : @"YYAttachment" }; 334 | } 335 | @end 336 | @endcode 337 | 338 | @return A class mapper. 339 | */ 340 | + (nullable NSDictionary *)modelContainerPropertyGenericClass; 341 | 342 | /** 343 | If you need to create instances of different classes during json->object transform, 344 | use the method to choose custom class based on dictionary data. 345 | 346 | @discussion If the model implements this method, it will be called to determine resulting class 347 | during `+modelWithJSON:`, `+modelWithDictionary:`, conveting object of properties of parent objects 348 | (both singular and containers via `+modelContainerPropertyGenericClass`). 349 | 350 | Example: 351 | @code 352 | @class YYCircle, YYRectangle, YYLine; 353 | 354 | @implementation YYShape 355 | 356 | + (Class)modelCustomClassForDictionary:(NSDictionary*)dictionary { 357 | if (dictionary[@"radius"] != nil) { 358 | return [YYCircle class]; 359 | } else if (dictionary[@"width"] != nil) { 360 | return [YYRectangle class]; 361 | } else if (dictionary[@"y2"] != nil) { 362 | return [YYLine class]; 363 | } else { 364 | return [self class]; 365 | } 366 | } 367 | 368 | @end 369 | @endcode 370 | 371 | @param dictionary The json/kv dictionary. 372 | 373 | @return Class to create from this dictionary, `nil` to use current class. 374 | 375 | */ 376 | + (nullable Class)modelCustomClassForDictionary:(NSDictionary *)dictionary; 377 | 378 | /** 379 | All the properties in blacklist will be ignored in model transform process. 380 | Returns nil to ignore this feature. 381 | 382 | @return An array of property's name. 383 | */ 384 | + (nullable NSArray *)modelPropertyBlacklist; 385 | 386 | /** 387 | If a property is not in the whitelist, it will be ignored in model transform process. 388 | Returns nil to ignore this feature. 389 | 390 | @return An array of property's name. 391 | */ 392 | + (nullable NSArray *)modelPropertyWhitelist; 393 | 394 | /** 395 | This method's behavior is similar to `- (BOOL)modelCustomTransformFromDictionary:(NSDictionary *)dic;`, 396 | but be called before the model transform. 397 | 398 | @discussion If the model implements this method, it will be called before 399 | `+modelWithJSON:`, `+modelWithDictionary:`, `-modelSetWithJSON:` and `-modelSetWithDictionary:`. 400 | If this method returns nil, the transform process will ignore this model. 401 | 402 | @param dic The json/kv dictionary. 403 | 404 | @return Returns the modified dictionary, or nil to ignore this model. 405 | */ 406 | - (NSDictionary *)modelCustomWillTransformFromDictionary:(NSDictionary *)dic; 407 | 408 | /** 409 | If the default json-to-model transform does not fit to your model object, implement 410 | this method to do additional process. You can also use this method to validate the 411 | model's properties. 412 | 413 | @discussion If the model implements this method, it will be called at the end of 414 | `+modelWithJSON:`, `+modelWithDictionary:`, `-modelSetWithJSON:` and `-modelSetWithDictionary:`. 415 | If this method returns NO, the transform process will ignore this model. 416 | 417 | @param dic The json/kv dictionary. 418 | 419 | @return Returns YES if the model is valid, or NO to ignore this model. 420 | */ 421 | - (BOOL)modelCustomTransformFromDictionary:(NSDictionary *)dic; 422 | 423 | /** 424 | If the default model-to-json transform does not fit to your model class, implement 425 | this method to do additional process. You can also use this method to validate the 426 | json dictionary. 427 | 428 | @discussion If the model implements this method, it will be called at the end of 429 | `-modelToJSONObject` and `-modelToJSONString`. 430 | If this method returns NO, the transform process will ignore this json dictionary. 431 | 432 | @param dic The json dictionary. 433 | 434 | @return Returns YES if the model is valid, or NO to ignore this model. 435 | */ 436 | - (BOOL)modelCustomTransformToDictionary:(NSMutableDictionary *)dic; 437 | 438 | @end 439 | 440 | NS_ASSUME_NONNULL_END 441 | -------------------------------------------------------------------------------- /YYModel/YYClassInfo.h: -------------------------------------------------------------------------------- 1 | // 2 | // YYClassInfo.h 3 | // YYModel 4 | // 5 | // Created by ibireme on 15/5/9. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | #import 14 | 15 | NS_ASSUME_NONNULL_BEGIN 16 | 17 | /** 18 | Type encoding's type. 19 | */ 20 | typedef NS_OPTIONS(NSUInteger, YYEncodingType) { 21 | YYEncodingTypeMask = 0xFF, ///< mask of type value 22 | YYEncodingTypeUnknown = 0, ///< unknown 23 | YYEncodingTypeVoid = 1, ///< void 24 | YYEncodingTypeBool = 2, ///< bool 25 | YYEncodingTypeInt8 = 3, ///< char / BOOL 26 | YYEncodingTypeUInt8 = 4, ///< unsigned char 27 | YYEncodingTypeInt16 = 5, ///< short 28 | YYEncodingTypeUInt16 = 6, ///< unsigned short 29 | YYEncodingTypeInt32 = 7, ///< int 30 | YYEncodingTypeUInt32 = 8, ///< unsigned int 31 | YYEncodingTypeInt64 = 9, ///< long long 32 | YYEncodingTypeUInt64 = 10, ///< unsigned long long 33 | YYEncodingTypeFloat = 11, ///< float 34 | YYEncodingTypeDouble = 12, ///< double 35 | YYEncodingTypeLongDouble = 13, ///< long double 36 | YYEncodingTypeObject = 14, ///< id 37 | YYEncodingTypeClass = 15, ///< Class 38 | YYEncodingTypeSEL = 16, ///< SEL 39 | YYEncodingTypeBlock = 17, ///< block 40 | YYEncodingTypePointer = 18, ///< void* 41 | YYEncodingTypeStruct = 19, ///< struct 42 | YYEncodingTypeUnion = 20, ///< union 43 | YYEncodingTypeCString = 21, ///< char* 44 | YYEncodingTypeCArray = 22, ///< char[10] (for example) 45 | 46 | YYEncodingTypeQualifierMask = 0xFF00, ///< mask of qualifier 47 | YYEncodingTypeQualifierConst = 1 << 8, ///< const 48 | YYEncodingTypeQualifierIn = 1 << 9, ///< in 49 | YYEncodingTypeQualifierInout = 1 << 10, ///< inout 50 | YYEncodingTypeQualifierOut = 1 << 11, ///< out 51 | YYEncodingTypeQualifierBycopy = 1 << 12, ///< bycopy 52 | YYEncodingTypeQualifierByref = 1 << 13, ///< byref 53 | YYEncodingTypeQualifierOneway = 1 << 14, ///< oneway 54 | 55 | YYEncodingTypePropertyMask = 0xFF0000, ///< mask of property 56 | YYEncodingTypePropertyReadonly = 1 << 16, ///< readonly 57 | YYEncodingTypePropertyCopy = 1 << 17, ///< copy 58 | YYEncodingTypePropertyRetain = 1 << 18, ///< retain 59 | YYEncodingTypePropertyNonatomic = 1 << 19, ///< nonatomic 60 | YYEncodingTypePropertyWeak = 1 << 20, ///< weak 61 | YYEncodingTypePropertyCustomGetter = 1 << 21, ///< getter= 62 | YYEncodingTypePropertyCustomSetter = 1 << 22, ///< setter= 63 | YYEncodingTypePropertyDynamic = 1 << 23, ///< @dynamic 64 | }; 65 | 66 | /** 67 | Get the type from a Type-Encoding string. 68 | 69 | @discussion See also: 70 | https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html 71 | https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html 72 | 73 | @param typeEncoding A Type-Encoding string. 74 | @return The encoding type. 75 | */ 76 | YYEncodingType YYEncodingGetType(const char *typeEncoding); 77 | 78 | 79 | /** 80 | Instance variable information. 81 | */ 82 | @interface YYClassIvarInfo : NSObject 83 | @property (nonatomic, assign, readonly) Ivar ivar; ///< ivar opaque struct 84 | @property (nonatomic, strong, readonly) NSString *name; ///< Ivar's name 85 | @property (nonatomic, assign, readonly) ptrdiff_t offset; ///< Ivar's offset 86 | @property (nonatomic, strong, readonly) NSString *typeEncoding; ///< Ivar's type encoding 87 | @property (nonatomic, assign, readonly) YYEncodingType type; ///< Ivar's type 88 | 89 | /** 90 | Creates and returns an ivar info object. 91 | 92 | @param ivar ivar opaque struct 93 | @return A new object, or nil if an error occurs. 94 | */ 95 | - (instancetype)initWithIvar:(Ivar)ivar; 96 | @end 97 | 98 | 99 | /** 100 | Method information. 101 | */ 102 | @interface YYClassMethodInfo : NSObject 103 | @property (nonatomic, assign, readonly) Method method; ///< method opaque struct 104 | @property (nonatomic, strong, readonly) NSString *name; ///< method name 105 | @property (nonatomic, assign, readonly) SEL sel; ///< method's selector 106 | @property (nonatomic, assign, readonly) IMP imp; ///< method's implementation 107 | @property (nonatomic, strong, readonly) NSString *typeEncoding; ///< method's parameter and return types 108 | @property (nonatomic, strong, readonly) NSString *returnTypeEncoding; ///< return value's type 109 | @property (nullable, nonatomic, strong, readonly) NSArray *argumentTypeEncodings; ///< array of arguments' type 110 | 111 | /** 112 | Creates and returns a method info object. 113 | 114 | @param method method opaque struct 115 | @return A new object, or nil if an error occurs. 116 | */ 117 | - (instancetype)initWithMethod:(Method)method; 118 | @end 119 | 120 | 121 | /** 122 | Property information. 123 | */ 124 | @interface YYClassPropertyInfo : NSObject 125 | @property (nonatomic, assign, readonly) objc_property_t property; ///< property's opaque struct 126 | @property (nonatomic, strong, readonly) NSString *name; ///< property's name 127 | @property (nonatomic, assign, readonly) YYEncodingType type; ///< property's type 128 | @property (nonatomic, strong, readonly) NSString *typeEncoding; ///< property's encoding value 129 | @property (nonatomic, strong, readonly) NSString *ivarName; ///< property's ivar name 130 | @property (nullable, nonatomic, assign, readonly) Class cls; ///< may be nil 131 | @property (nullable, nonatomic, strong, readonly) NSArray *protocols; ///< may nil 132 | @property (nonatomic, assign, readonly) SEL getter; ///< getter (nonnull) 133 | @property (nonatomic, assign, readonly) SEL setter; ///< setter (nonnull) 134 | 135 | /** 136 | Creates and returns a property info object. 137 | 138 | @param property property opaque struct 139 | @return A new object, or nil if an error occurs. 140 | */ 141 | - (instancetype)initWithProperty:(objc_property_t)property; 142 | @end 143 | 144 | 145 | /** 146 | Class information for a class. 147 | */ 148 | @interface YYClassInfo : NSObject 149 | @property (nonatomic, assign, readonly) Class cls; ///< class object 150 | @property (nullable, nonatomic, assign, readonly) Class superCls; ///< super class object 151 | @property (nullable, nonatomic, assign, readonly) Class metaCls; ///< class's meta class object 152 | @property (nonatomic, readonly) BOOL isMeta; ///< whether this class is meta class 153 | @property (nonatomic, strong, readonly) NSString *name; ///< class name 154 | @property (nullable, nonatomic, strong, readonly) YYClassInfo *superClassInfo; ///< super class's class info 155 | @property (nullable, nonatomic, strong, readonly) NSDictionary *ivarInfos; ///< ivars 156 | @property (nullable, nonatomic, strong, readonly) NSDictionary *methodInfos; ///< methods 157 | @property (nullable, nonatomic, strong, readonly) NSDictionary *propertyInfos; ///< properties 158 | 159 | /** 160 | If the class is changed (for example: you add a method to this class with 161 | 'class_addMethod()'), you should call this method to refresh the class info cache. 162 | 163 | After called this method, `needUpdate` will returns `YES`, and you should call 164 | 'classInfoWithClass' or 'classInfoWithClassName' to get the updated class info. 165 | */ 166 | - (void)setNeedUpdate; 167 | 168 | /** 169 | If this method returns `YES`, you should stop using this instance and call 170 | `classInfoWithClass` or `classInfoWithClassName` to get the updated class info. 171 | 172 | @return Whether this class info need update. 173 | */ 174 | - (BOOL)needUpdate; 175 | 176 | /** 177 | Get the class info of a specified Class. 178 | 179 | @discussion This method will cache the class info and super-class info 180 | at the first access to the Class. This method is thread-safe. 181 | 182 | @param cls A class. 183 | @return A class info, or nil if an error occurs. 184 | */ 185 | + (nullable instancetype)classInfoWithClass:(Class)cls; 186 | 187 | /** 188 | Get the class info of a specified Class. 189 | 190 | @discussion This method will cache the class info and super-class info 191 | at the first access to the Class. This method is thread-safe. 192 | 193 | @param className A class name. 194 | @return A class info, or nil if an error occurs. 195 | */ 196 | + (nullable instancetype)classInfoWithClassName:(NSString *)className; 197 | 198 | @end 199 | 200 | NS_ASSUME_NONNULL_END 201 | -------------------------------------------------------------------------------- /YYModel/YYClassInfo.m: -------------------------------------------------------------------------------- 1 | // 2 | // YYClassInfo.m 3 | // YYModel 4 | // 5 | // Created by ibireme on 15/5/9. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import "YYClassInfo.h" 13 | #import 14 | 15 | YYEncodingType YYEncodingGetType(const char *typeEncoding) { 16 | char *type = (char *)typeEncoding; 17 | if (!type) return YYEncodingTypeUnknown; 18 | size_t len = strlen(type); 19 | if (len == 0) return YYEncodingTypeUnknown; 20 | 21 | YYEncodingType qualifier = 0; 22 | bool prefix = true; 23 | while (prefix) { 24 | switch (*type) { 25 | case 'r': { 26 | qualifier |= YYEncodingTypeQualifierConst; 27 | type++; 28 | } break; 29 | case 'n': { 30 | qualifier |= YYEncodingTypeQualifierIn; 31 | type++; 32 | } break; 33 | case 'N': { 34 | qualifier |= YYEncodingTypeQualifierInout; 35 | type++; 36 | } break; 37 | case 'o': { 38 | qualifier |= YYEncodingTypeQualifierOut; 39 | type++; 40 | } break; 41 | case 'O': { 42 | qualifier |= YYEncodingTypeQualifierBycopy; 43 | type++; 44 | } break; 45 | case 'R': { 46 | qualifier |= YYEncodingTypeQualifierByref; 47 | type++; 48 | } break; 49 | case 'V': { 50 | qualifier |= YYEncodingTypeQualifierOneway; 51 | type++; 52 | } break; 53 | default: { prefix = false; } break; 54 | } 55 | } 56 | 57 | len = strlen(type); 58 | if (len == 0) return YYEncodingTypeUnknown | qualifier; 59 | 60 | switch (*type) { 61 | case 'v': return YYEncodingTypeVoid | qualifier; 62 | case 'B': return YYEncodingTypeBool | qualifier; 63 | case 'c': return YYEncodingTypeInt8 | qualifier; 64 | case 'C': return YYEncodingTypeUInt8 | qualifier; 65 | case 's': return YYEncodingTypeInt16 | qualifier; 66 | case 'S': return YYEncodingTypeUInt16 | qualifier; 67 | case 'i': return YYEncodingTypeInt32 | qualifier; 68 | case 'I': return YYEncodingTypeUInt32 | qualifier; 69 | case 'l': return YYEncodingTypeInt32 | qualifier; 70 | case 'L': return YYEncodingTypeUInt32 | qualifier; 71 | case 'q': return YYEncodingTypeInt64 | qualifier; 72 | case 'Q': return YYEncodingTypeUInt64 | qualifier; 73 | case 'f': return YYEncodingTypeFloat | qualifier; 74 | case 'd': return YYEncodingTypeDouble | qualifier; 75 | case 'D': return YYEncodingTypeLongDouble | qualifier; 76 | case '#': return YYEncodingTypeClass | qualifier; 77 | case ':': return YYEncodingTypeSEL | qualifier; 78 | case '*': return YYEncodingTypeCString | qualifier; 79 | case '^': return YYEncodingTypePointer | qualifier; 80 | case '[': return YYEncodingTypeCArray | qualifier; 81 | case '(': return YYEncodingTypeUnion | qualifier; 82 | case '{': return YYEncodingTypeStruct | qualifier; 83 | case '@': { 84 | if (len == 2 && *(type + 1) == '?') 85 | return YYEncodingTypeBlock | qualifier; 86 | else 87 | return YYEncodingTypeObject | qualifier; 88 | } 89 | default: return YYEncodingTypeUnknown | qualifier; 90 | } 91 | } 92 | 93 | @implementation YYClassIvarInfo 94 | 95 | - (instancetype)initWithIvar:(Ivar)ivar { 96 | if (!ivar) return nil; 97 | self = [super init]; 98 | _ivar = ivar; 99 | const char *name = ivar_getName(ivar); 100 | if (name) { 101 | _name = [NSString stringWithUTF8String:name]; 102 | } 103 | _offset = ivar_getOffset(ivar); 104 | const char *typeEncoding = ivar_getTypeEncoding(ivar); 105 | if (typeEncoding) { 106 | _typeEncoding = [NSString stringWithUTF8String:typeEncoding]; 107 | _type = YYEncodingGetType(typeEncoding); 108 | } 109 | return self; 110 | } 111 | 112 | @end 113 | 114 | @implementation YYClassMethodInfo 115 | 116 | - (instancetype)initWithMethod:(Method)method { 117 | if (!method) return nil; 118 | self = [super init]; 119 | _method = method; 120 | _sel = method_getName(method); 121 | _imp = method_getImplementation(method); 122 | const char *name = sel_getName(_sel); 123 | if (name) { 124 | _name = [NSString stringWithUTF8String:name]; 125 | } 126 | const char *typeEncoding = method_getTypeEncoding(method); 127 | if (typeEncoding) { 128 | _typeEncoding = [NSString stringWithUTF8String:typeEncoding]; 129 | } 130 | char *returnType = method_copyReturnType(method); 131 | if (returnType) { 132 | _returnTypeEncoding = [NSString stringWithUTF8String:returnType]; 133 | free(returnType); 134 | } 135 | unsigned int argumentCount = method_getNumberOfArguments(method); 136 | if (argumentCount > 0) { 137 | NSMutableArray *argumentTypes = [NSMutableArray new]; 138 | for (unsigned int i = 0; i < argumentCount; i++) { 139 | char *argumentType = method_copyArgumentType(method, i); 140 | NSString *type = argumentType ? [NSString stringWithUTF8String:argumentType] : nil; 141 | [argumentTypes addObject:type ? type : @""]; 142 | if (argumentType) free(argumentType); 143 | } 144 | _argumentTypeEncodings = argumentTypes; 145 | } 146 | return self; 147 | } 148 | 149 | @end 150 | 151 | @implementation YYClassPropertyInfo 152 | 153 | - (instancetype)initWithProperty:(objc_property_t)property { 154 | if (!property) return nil; 155 | self = [super init]; 156 | _property = property; 157 | const char *name = property_getName(property); 158 | if (name) { 159 | _name = [NSString stringWithUTF8String:name]; 160 | } 161 | 162 | YYEncodingType type = 0; 163 | unsigned int attrCount; 164 | objc_property_attribute_t *attrs = property_copyAttributeList(property, &attrCount); 165 | for (unsigned int i = 0; i < attrCount; i++) { 166 | switch (attrs[i].name[0]) { 167 | case 'T': { // Type encoding 168 | if (attrs[i].value) { 169 | _typeEncoding = [NSString stringWithUTF8String:attrs[i].value]; 170 | type = YYEncodingGetType(attrs[i].value); 171 | 172 | if ((type & YYEncodingTypeMask) == YYEncodingTypeObject && _typeEncoding.length) { 173 | NSScanner *scanner = [NSScanner scannerWithString:_typeEncoding]; 174 | if (![scanner scanString:@"@\"" intoString:NULL]) continue; 175 | 176 | NSString *clsName = nil; 177 | if ([scanner scanUpToCharactersFromSet: [NSCharacterSet characterSetWithCharactersInString:@"\"<"] intoString:&clsName]) { 178 | if (clsName.length) _cls = objc_getClass(clsName.UTF8String); 179 | } 180 | 181 | NSMutableArray *protocols = nil; 182 | while ([scanner scanString:@"<" intoString:NULL]) { 183 | NSString* protocol = nil; 184 | if ([scanner scanUpToString:@">" intoString: &protocol]) { 185 | if (protocol.length) { 186 | if (!protocols) protocols = [NSMutableArray new]; 187 | [protocols addObject:protocol]; 188 | } 189 | } 190 | [scanner scanString:@">" intoString:NULL]; 191 | } 192 | _protocols = protocols; 193 | } 194 | } 195 | } break; 196 | case 'V': { // Instance variable 197 | if (attrs[i].value) { 198 | _ivarName = [NSString stringWithUTF8String:attrs[i].value]; 199 | } 200 | } break; 201 | case 'R': { 202 | type |= YYEncodingTypePropertyReadonly; 203 | } break; 204 | case 'C': { 205 | type |= YYEncodingTypePropertyCopy; 206 | } break; 207 | case '&': { 208 | type |= YYEncodingTypePropertyRetain; 209 | } break; 210 | case 'N': { 211 | type |= YYEncodingTypePropertyNonatomic; 212 | } break; 213 | case 'D': { 214 | type |= YYEncodingTypePropertyDynamic; 215 | } break; 216 | case 'W': { 217 | type |= YYEncodingTypePropertyWeak; 218 | } break; 219 | case 'G': { 220 | type |= YYEncodingTypePropertyCustomGetter; 221 | if (attrs[i].value) { 222 | _getter = NSSelectorFromString([NSString stringWithUTF8String:attrs[i].value]); 223 | } 224 | } break; 225 | case 'S': { 226 | type |= YYEncodingTypePropertyCustomSetter; 227 | if (attrs[i].value) { 228 | _setter = NSSelectorFromString([NSString stringWithUTF8String:attrs[i].value]); 229 | } 230 | } // break; commented for code coverage in next line 231 | default: break; 232 | } 233 | } 234 | if (attrs) { 235 | free(attrs); 236 | attrs = NULL; 237 | } 238 | 239 | _type = type; 240 | if (_name.length) { 241 | if (!_getter) { 242 | _getter = NSSelectorFromString(_name); 243 | } 244 | if (!_setter) { 245 | _setter = NSSelectorFromString([NSString stringWithFormat:@"set%@%@:", [_name substringToIndex:1].uppercaseString, [_name substringFromIndex:1]]); 246 | } 247 | } 248 | return self; 249 | } 250 | 251 | @end 252 | 253 | @implementation YYClassInfo { 254 | BOOL _needUpdate; 255 | } 256 | 257 | - (instancetype)initWithClass:(Class)cls { 258 | if (!cls) return nil; 259 | self = [super init]; 260 | _cls = cls; 261 | _superCls = class_getSuperclass(cls); 262 | _isMeta = class_isMetaClass(cls); 263 | if (!_isMeta) { 264 | _metaCls = objc_getMetaClass(class_getName(cls)); 265 | } 266 | _name = NSStringFromClass(cls); 267 | [self _update]; 268 | 269 | _superClassInfo = [self.class classInfoWithClass:_superCls]; 270 | return self; 271 | } 272 | 273 | - (void)_update { 274 | _ivarInfos = nil; 275 | _methodInfos = nil; 276 | _propertyInfos = nil; 277 | 278 | Class cls = self.cls; 279 | unsigned int methodCount = 0; 280 | Method *methods = class_copyMethodList(cls, &methodCount); 281 | if (methods) { 282 | NSMutableDictionary *methodInfos = [NSMutableDictionary new]; 283 | _methodInfos = methodInfos; 284 | for (unsigned int i = 0; i < methodCount; i++) { 285 | YYClassMethodInfo *info = [[YYClassMethodInfo alloc] initWithMethod:methods[i]]; 286 | if (info.name) methodInfos[info.name] = info; 287 | } 288 | free(methods); 289 | } 290 | unsigned int propertyCount = 0; 291 | objc_property_t *properties = class_copyPropertyList(cls, &propertyCount); 292 | if (properties) { 293 | NSMutableDictionary *propertyInfos = [NSMutableDictionary new]; 294 | _propertyInfos = propertyInfos; 295 | for (unsigned int i = 0; i < propertyCount; i++) { 296 | YYClassPropertyInfo *info = [[YYClassPropertyInfo alloc] initWithProperty:properties[i]]; 297 | if (info.name) propertyInfos[info.name] = info; 298 | } 299 | free(properties); 300 | } 301 | 302 | unsigned int ivarCount = 0; 303 | Ivar *ivars = class_copyIvarList(cls, &ivarCount); 304 | if (ivars) { 305 | NSMutableDictionary *ivarInfos = [NSMutableDictionary new]; 306 | _ivarInfos = ivarInfos; 307 | for (unsigned int i = 0; i < ivarCount; i++) { 308 | YYClassIvarInfo *info = [[YYClassIvarInfo alloc] initWithIvar:ivars[i]]; 309 | if (info.name) ivarInfos[info.name] = info; 310 | } 311 | free(ivars); 312 | } 313 | 314 | if (!_ivarInfos) _ivarInfos = @{}; 315 | if (!_methodInfos) _methodInfos = @{}; 316 | if (!_propertyInfos) _propertyInfos = @{}; 317 | 318 | _needUpdate = NO; 319 | } 320 | 321 | - (void)setNeedUpdate { 322 | _needUpdate = YES; 323 | } 324 | 325 | - (BOOL)needUpdate { 326 | return _needUpdate; 327 | } 328 | 329 | + (instancetype)classInfoWithClass:(Class)cls { 330 | if (!cls) return nil; 331 | static CFMutableDictionaryRef classCache; 332 | static CFMutableDictionaryRef metaCache; 333 | static dispatch_once_t onceToken; 334 | static dispatch_semaphore_t lock; 335 | dispatch_once(&onceToken, ^{ 336 | classCache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 337 | metaCache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 338 | lock = dispatch_semaphore_create(1); 339 | }); 340 | dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER); 341 | YYClassInfo *info = CFDictionaryGetValue(class_isMetaClass(cls) ? metaCache : classCache, (__bridge const void *)(cls)); 342 | if (info && info->_needUpdate) { 343 | [info _update]; 344 | } 345 | dispatch_semaphore_signal(lock); 346 | if (!info) { 347 | info = [[YYClassInfo alloc] initWithClass:cls]; 348 | if (info) { 349 | dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER); 350 | CFDictionarySetValue(info.isMeta ? metaCache : classCache, (__bridge const void *)(cls), (__bridge const void *)(info)); 351 | dispatch_semaphore_signal(lock); 352 | } 353 | } 354 | return info; 355 | } 356 | 357 | + (instancetype)classInfoWithClassName:(NSString *)className { 358 | Class cls = NSClassFromString(className); 359 | return [self classInfoWithClass:cls]; 360 | } 361 | 362 | @end 363 | -------------------------------------------------------------------------------- /YYModel/YYModel.h: -------------------------------------------------------------------------------- 1 | // 2 | // YYModel.h 3 | // YYModel 4 | // 5 | // Created by ibireme on 15/5/10. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | 14 | #if __has_include() 15 | FOUNDATION_EXPORT double YYModelVersionNumber; 16 | FOUNDATION_EXPORT const unsigned char YYModelVersionString[]; 17 | #import 18 | #import 19 | #else 20 | #import "NSObject+YYModel.h" 21 | #import "YYClassInfo.h" 22 | #endif 23 | -------------------------------------------------------------------------------- /YYModelTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 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 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /YYModelTests/YYTestBlacklistWhitelist.m: -------------------------------------------------------------------------------- 1 | // 2 | // YYTestBlacklistWhitelist.m 3 | // YYModel 4 | // 5 | // Created by ibireme on 15/11/29. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | #import "YYModel.h" 14 | 15 | 16 | @interface YYTestBlacklistModel : NSObject 17 | @property (nonatomic, strong) NSString *a; 18 | @property (nonatomic, strong) NSString *b; 19 | @property (nonatomic, strong) NSString *c; 20 | @end 21 | 22 | @implementation YYTestBlacklistModel 23 | + (NSArray *)modelPropertyBlacklist { 24 | return @[@"a", @"d"]; 25 | } 26 | @end 27 | 28 | @interface YYTestWhitelistModel : NSObject 29 | @property (nonatomic, strong) NSString *a; 30 | @property (nonatomic, strong) NSString *b; 31 | @property (nonatomic, strong) NSString *c; 32 | @end 33 | 34 | @implementation YYTestWhitelistModel 35 | + (NSArray *)modelPropertyWhitelist { 36 | return @[@"a", @"d"]; 37 | } 38 | @end 39 | 40 | 41 | @interface YYTestBlackWhitelistModel : NSObject 42 | @property (nonatomic, strong) NSString *a; 43 | @property (nonatomic, strong) NSString *b; 44 | @property (nonatomic, strong) NSString *c; 45 | @end 46 | 47 | @implementation YYTestBlackWhitelistModel 48 | + (NSArray *)modelPropertyBlacklist { 49 | return @[@"a", @"d"]; 50 | } 51 | + (NSArray *)modelPropertyWhitelist { 52 | return @[@"a", @"b", @"d"]; 53 | } 54 | @end 55 | 56 | 57 | 58 | 59 | @interface YYTestBlacklistWhitelist : XCTestCase 60 | 61 | @end 62 | 63 | @implementation YYTestBlacklistWhitelist 64 | 65 | - (void)testBlacklist { 66 | NSString *json = @"{\"a\":\"A\", \"b\":\"B\", \"c\":\"C\", \"d\":\"D\"}"; 67 | YYTestBlacklistModel *model = [YYTestBlacklistModel yy_modelWithJSON:json]; 68 | XCTAssert(model.a == nil); 69 | XCTAssert(model.b != nil); 70 | XCTAssert(model.c != nil); 71 | 72 | NSDictionary *dic = [model yy_modelToJSONObject]; 73 | XCTAssert(dic[@"a"] == nil); 74 | XCTAssert(dic[@"b"] != nil); 75 | XCTAssert(dic[@"c"] != nil); 76 | } 77 | 78 | - (void)testWhitelist { 79 | NSString *json = @"{\"a\":\"A\", \"b\":\"B\", \"c\":\"C\", \"d\":\"D\"}"; 80 | YYTestWhitelistModel *model = [YYTestWhitelistModel yy_modelWithJSON:json]; 81 | XCTAssert(model.a != nil); 82 | XCTAssert(model.b == nil); 83 | XCTAssert(model.c == nil); 84 | 85 | NSDictionary *dic = [model yy_modelToJSONObject]; 86 | XCTAssert(dic[@"a"] != nil); 87 | XCTAssert(dic[@"b"] == nil); 88 | XCTAssert(dic[@"c"] == nil); 89 | } 90 | 91 | 92 | - (void)testBlackWhitelist { 93 | NSString *json = @"{\"a\":\"A\", \"b\":\"B\", \"c\":\"C\", \"d\":\"D\"}"; 94 | YYTestBlackWhitelistModel *model = [YYTestBlackWhitelistModel yy_modelWithJSON:json]; 95 | XCTAssert(model.a == nil); 96 | XCTAssert(model.b != nil); 97 | XCTAssert(model.c == nil); 98 | 99 | NSDictionary *dic = [model yy_modelToJSONObject]; 100 | XCTAssert(dic[@"a"] == nil); 101 | XCTAssert(dic[@"b"] != nil); 102 | XCTAssert(dic[@"c"] == nil); 103 | } 104 | 105 | @end 106 | -------------------------------------------------------------------------------- /YYModelTests/YYTestClassInfo.m: -------------------------------------------------------------------------------- 1 | // 2 | // YYTestClassInfo.m 3 | // YYModel 4 | // 5 | // Created by ibireme on 15/11/27. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | #import 14 | #import "YYModel.h" 15 | 16 | typedef union yy_union{ char a; int b;} yy_union; 17 | 18 | @interface YYTestPropertyModel : NSObject 19 | @property bool boolValue; 20 | @property BOOL BOOLValue; 21 | @property char charValue; 22 | @property unsigned char unsignedCharValue; 23 | @property short shortValue; 24 | @property unsigned short unsignedShortValue; 25 | @property int intValue; 26 | @property unsigned int unsignedIntValue; 27 | @property long longValue; 28 | @property unsigned long unsignedLongValue; 29 | @property long long longLongValue; 30 | @property unsigned long long unsignedLongLongValue; 31 | @property float floatValue; 32 | @property double doubleValue; 33 | @property long double longDoubleValue; 34 | @property (strong) NSObject *objectValue; 35 | @property (strong) NSArray *arrayValue; 36 | @property (strong) Class classValue; 37 | @property SEL selectorValue; 38 | @property (copy) void (^blockValue)(); 39 | @property void *pointerValue; 40 | @property CFArrayEqualCallBack functionPointerValue; 41 | @property CGRect structValue; 42 | @property yy_union unionValue; 43 | @property char *cStringValue; 44 | 45 | @property (nonatomic) NSObject *nonatomicValue; 46 | @property (copy) NSObject *aCopyValue; 47 | @property (assign) NSObject *assignValue; 48 | @property (strong) NSObject *strongValue; 49 | @property (retain) NSObject *retainValue; 50 | @property (weak) NSObject *weakValue; 51 | @property (readonly) NSObject *readonlyValue; 52 | @property (nonatomic) NSObject *dynamicValue; 53 | @property (unsafe_unretained) NSObject *unsafeValue; 54 | @property (nonatomic, getter=getValue) NSObject *getterValue; 55 | @property (nonatomic, setter=setValue:) NSObject *setterValue; 56 | @end 57 | 58 | @implementation YYTestPropertyModel { 59 | const NSObject *_constValue; 60 | } 61 | 62 | @dynamic dynamicValue; 63 | 64 | - (NSObject *)getValue { 65 | return _getterValue; 66 | } 67 | 68 | - (void)setValue:(NSObject *)value { 69 | _setterValue = value; 70 | } 71 | 72 | - (void)testConst:(const NSObject *)value {} 73 | - (void)testIn:(in NSObject *)value {} 74 | - (void)testOut:(out NSObject *)value {} 75 | - (void)testInout:(inout NSObject *)value {} 76 | - (void)testBycopy:(bycopy NSObject *)value {} 77 | - (void)testByref:(byref NSObject *)value {} 78 | - (void)testOneway:(oneway NSObject *)value {} 79 | @end 80 | 81 | 82 | 83 | 84 | 85 | 86 | @interface YYTestClassInfo : XCTestCase 87 | @end 88 | 89 | @implementation YYTestClassInfo 90 | 91 | - (void)testClassInfoCache { 92 | YYClassInfo *info1 = [YYClassInfo classInfoWithClass:[YYTestPropertyModel class]]; 93 | [info1 setNeedUpdate]; 94 | YYClassInfo *info2 = [YYClassInfo classInfoWithClassName:@"YYTestPropertyModel"]; 95 | XCTAssertNotNil(info1); 96 | XCTAssertNotNil(info2); 97 | XCTAssertEqual(info1, info2); 98 | } 99 | 100 | - (void)testClassMeta { 101 | YYClassInfo *classInfo = [YYClassInfo classInfoWithClass:[YYTestPropertyModel class]]; 102 | XCTAssertNotNil(classInfo); 103 | XCTAssertEqual(classInfo.cls, [YYTestPropertyModel class]); 104 | XCTAssertEqual(classInfo.superCls, [NSObject class]); 105 | XCTAssertEqual(classInfo.metaCls, objc_getMetaClass("YYTestPropertyModel")); 106 | XCTAssertEqual(classInfo.isMeta, NO); 107 | 108 | Class meta = object_getClass([YYTestPropertyModel class]); 109 | YYClassInfo *metaClassInfo = [YYClassInfo classInfoWithClass:meta]; 110 | XCTAssertNotNil(metaClassInfo); 111 | XCTAssertEqual(metaClassInfo.cls, meta); 112 | XCTAssertEqual(metaClassInfo.superCls, object_getClass([NSObject class])); 113 | XCTAssertEqual(metaClassInfo.metaCls, nil); 114 | XCTAssertEqual(metaClassInfo.isMeta, YES); 115 | } 116 | 117 | - (void)testClassInfo { 118 | YYClassInfo *info = [YYClassInfo classInfoWithClass:[YYTestPropertyModel class]]; 119 | XCTAssertEqual([self getType:info name:@"boolValue"] & YYEncodingTypeMask, YYEncodingTypeBool); 120 | #ifdef OBJC_BOOL_IS_BOOL 121 | XCTAssertEqual([self getType:info name:@"BOOLValue"] & YYEncodingTypeMask, YYEncodingTypeBool); 122 | #else 123 | XCTAssertEqual([self getType:info name:@"BOOLValue"] & YYEncodingTypeMask, YYEncodingTypeInt8); 124 | #endif 125 | XCTAssertEqual([self getType:info name:@"charValue"] & YYEncodingTypeMask, YYEncodingTypeInt8); 126 | XCTAssertEqual([self getType:info name:@"unsignedCharValue"] & YYEncodingTypeMask, YYEncodingTypeUInt8); 127 | XCTAssertEqual([self getType:info name:@"shortValue"] & YYEncodingTypeMask, YYEncodingTypeInt16); 128 | XCTAssertEqual([self getType:info name:@"unsignedShortValue"] & YYEncodingTypeMask, YYEncodingTypeUInt16); 129 | XCTAssertEqual([self getType:info name:@"intValue"] & YYEncodingTypeMask, YYEncodingTypeInt32); 130 | XCTAssertEqual([self getType:info name:@"unsignedIntValue"] & YYEncodingTypeMask, YYEncodingTypeUInt32); 131 | #ifdef __LP64__ 132 | XCTAssertEqual([self getType:info name:@"longValue"] & YYEncodingTypeMask, YYEncodingTypeInt64); 133 | XCTAssertEqual([self getType:info name:@"unsignedLongValue"] & YYEncodingTypeMask, YYEncodingTypeUInt64); 134 | XCTAssertEqual(YYEncodingGetType("l") & YYEncodingTypeMask, YYEncodingTypeInt32); // long in 32 bit system 135 | XCTAssertEqual(YYEncodingGetType("L") & YYEncodingTypeMask, YYEncodingTypeUInt32); // unsingle long in 32 bit system 136 | #else 137 | XCTAssertEqual([self getType:info name:@"longValue"] & YYEncodingTypeMask, YYEncodingTypeInt32); 138 | XCTAssertEqual([self getType:info name:@"unsignedLongValue"] & YYEncodingTypeMask, YYEncodingTypeUInt32); 139 | #endif 140 | XCTAssertEqual([self getType:info name:@"longLongValue"] & YYEncodingTypeMask, YYEncodingTypeInt64); 141 | XCTAssertEqual([self getType:info name:@"unsignedLongLongValue"] & YYEncodingTypeMask, YYEncodingTypeUInt64); 142 | XCTAssertEqual([self getType:info name:@"floatValue"] & YYEncodingTypeMask, YYEncodingTypeFloat); 143 | XCTAssertEqual([self getType:info name:@"doubleValue"] & YYEncodingTypeMask, YYEncodingTypeDouble); 144 | XCTAssertEqual([self getType:info name:@"longDoubleValue"] & YYEncodingTypeMask, YYEncodingTypeLongDouble); 145 | 146 | XCTAssertEqual([self getType:info name:@"objectValue"] & YYEncodingTypeMask, YYEncodingTypeObject); 147 | XCTAssertEqual([self getType:info name:@"arrayValue"] & YYEncodingTypeMask, YYEncodingTypeObject); 148 | XCTAssertEqual([self getType:info name:@"classValue"] & YYEncodingTypeMask, YYEncodingTypeClass); 149 | XCTAssertEqual([self getType:info name:@"selectorValue"] & YYEncodingTypeMask, YYEncodingTypeSEL); 150 | XCTAssertEqual([self getType:info name:@"blockValue"] & YYEncodingTypeMask, YYEncodingTypeBlock); 151 | XCTAssertEqual([self getType:info name:@"pointerValue"] & YYEncodingTypeMask, YYEncodingTypePointer); 152 | XCTAssertEqual([self getType:info name:@"functionPointerValue"] & YYEncodingTypeMask, YYEncodingTypePointer); 153 | XCTAssertEqual([self getType:info name:@"structValue"] & YYEncodingTypeMask, YYEncodingTypeStruct); 154 | XCTAssertEqual([self getType:info name:@"unionValue"] & YYEncodingTypeMask, YYEncodingTypeUnion); 155 | XCTAssertEqual([self getType:info name:@"cStringValue"] & YYEncodingTypeMask, YYEncodingTypeCString); 156 | 157 | XCTAssertEqual(YYEncodingGetType(@encode(void)) & YYEncodingTypeMask, YYEncodingTypeVoid); 158 | XCTAssertEqual(YYEncodingGetType(@encode(int[10])) & YYEncodingTypeMask, YYEncodingTypeCArray); 159 | XCTAssertEqual(YYEncodingGetType("") & YYEncodingTypeMask, YYEncodingTypeUnknown); 160 | XCTAssertEqual(YYEncodingGetType(".") & YYEncodingTypeMask, YYEncodingTypeUnknown); 161 | XCTAssertEqual(YYEncodingGetType("ri") & YYEncodingTypeQualifierMask, YYEncodingTypeQualifierConst); 162 | XCTAssertEqual([self getMethodTypeWithName:@"testIn:"] & YYEncodingTypeQualifierMask, YYEncodingTypeQualifierIn); 163 | XCTAssertEqual([self getMethodTypeWithName:@"testOut:"] & YYEncodingTypeQualifierMask, YYEncodingTypeQualifierOut); 164 | XCTAssertEqual([self getMethodTypeWithName:@"testInout:"] & YYEncodingTypeQualifierMask, YYEncodingTypeQualifierInout); 165 | XCTAssertEqual([self getMethodTypeWithName:@"testBycopy:"] & YYEncodingTypeQualifierMask, YYEncodingTypeQualifierBycopy); 166 | XCTAssertEqual([self getMethodTypeWithName:@"testByref:"] & YYEncodingTypeQualifierMask, YYEncodingTypeQualifierByref); 167 | XCTAssertEqual([self getMethodTypeWithName:@"testOneway:"] & YYEncodingTypeQualifierMask, YYEncodingTypeQualifierOneway); 168 | 169 | XCTAssert([self getType:info name:@"nonatomicValue"] & YYEncodingTypePropertyMask &YYEncodingTypePropertyNonatomic); 170 | XCTAssert([self getType:info name:@"aCopyValue"] & YYEncodingTypePropertyMask & YYEncodingTypePropertyCopy); 171 | XCTAssert([self getType:info name:@"strongValue"] & YYEncodingTypePropertyMask & YYEncodingTypePropertyRetain); 172 | XCTAssert([self getType:info name:@"retainValue"] & YYEncodingTypePropertyMask & YYEncodingTypePropertyRetain); 173 | XCTAssert([self getType:info name:@"weakValue"] & YYEncodingTypePropertyMask & YYEncodingTypePropertyWeak); 174 | XCTAssert([self getType:info name:@"readonlyValue"] & YYEncodingTypePropertyMask & YYEncodingTypePropertyReadonly); 175 | XCTAssert([self getType:info name:@"dynamicValue"] & YYEncodingTypePropertyMask & YYEncodingTypePropertyDynamic); 176 | XCTAssert([self getType:info name:@"getterValue"] & YYEncodingTypePropertyMask &YYEncodingTypePropertyCustomGetter); 177 | XCTAssert([self getType:info name:@"setterValue"] & YYEncodingTypePropertyMask & YYEncodingTypePropertyCustomSetter); 178 | } 179 | 180 | - (YYEncodingType)getType:(YYClassInfo *)info name:(NSString *)name { 181 | return ((YYClassPropertyInfo *)info.propertyInfos[name]).type; 182 | } 183 | 184 | - (YYEncodingType)getMethodTypeWithName:(NSString *)name { 185 | YYTestPropertyModel *model = [YYTestPropertyModel new]; 186 | NSMethodSignature *sig = [model methodSignatureForSelector:NSSelectorFromString(name)]; 187 | const char *typeName = [sig getArgumentTypeAtIndex:2]; 188 | return YYEncodingGetType(typeName); 189 | } 190 | 191 | @end 192 | -------------------------------------------------------------------------------- /YYModelTests/YYTestCopyingAndCoding.m: -------------------------------------------------------------------------------- 1 | // 2 | // YYTestCopyingAndCoding.m 3 | // YYModel 4 | // 5 | // Created by ibireme on 15/11/29. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | #import "YYModel.h" 14 | 15 | 16 | typedef struct my_struct { 17 | int a; 18 | double b; 19 | long double c; 20 | } my_struct; 21 | 22 | @interface YYTestModelHashModel : NSObject 23 | @property bool boolValue; 24 | @property BOOL BOOLValue; 25 | @property char charValue; 26 | @property unsigned char unsignedCharValue; 27 | @property short shortValue; 28 | @property unsigned short unsignedShortValue; 29 | @property int intValue; 30 | @property unsigned int unsignedIntValue; 31 | @property long longValue; 32 | @property unsigned long unsignedLongValue; 33 | @property long long longLongValue; 34 | @property unsigned long long unsignedLongLongValue; 35 | @property float floatValue; 36 | @property double doubleValue; 37 | @property long double longDoubleValue; 38 | @property (strong) Class classValue; 39 | @property SEL selectorValue; 40 | @property (copy) void (^blockValue)(); 41 | @property void *pointerValue; 42 | @property CFArrayRef cfArrayValue; 43 | @property NSValue *valueValue; 44 | 45 | @property CGSize sizeValue; 46 | @property CGPoint pointValue; 47 | @property CGRect rectValue; 48 | @property CGAffineTransform transformValue; 49 | @property UIEdgeInsets insetsValue; 50 | @property UIOffset offsetValue; 51 | @property CATransform3D transform3DValue; // invalid for NSKeyedArchiver/Unarchiver 52 | @property my_struct myStructValue; // invalid for NSKeyedArchiver/Unarchiver 53 | 54 | 55 | 56 | @property (nonatomic, strong) NSObject *object; 57 | @property (nonatomic, strong) NSNumber *number; 58 | @property (nonatomic, strong) NSDecimalNumber *decimal; 59 | @property (nonatomic, strong) NSString *string; 60 | @property (nonatomic, strong) NSString *string2; 61 | @property (nonatomic, strong) NSMutableString *mString; 62 | @property (nonatomic, strong) NSData *data; 63 | @property (nonatomic, strong) NSMutableData *mData; 64 | @property (nonatomic, strong) NSDate *date; 65 | @property (nonatomic, strong) NSValue *value; 66 | @property (nonatomic, strong) NSURL *url; 67 | 68 | @property (nonatomic, strong) NSArray *array; 69 | @property (nonatomic, strong) NSMutableArray *mArray; 70 | @property (nonatomic, strong) NSDictionary *dict; 71 | @property (nonatomic, strong) NSMutableDictionary *mDict; 72 | @property (nonatomic, strong) NSSet *set; 73 | @property (nonatomic, strong) NSMutableSet *mSet; 74 | @end 75 | 76 | @implementation YYTestModelHashModel 77 | - (void)encodeWithCoder:(NSCoder *)aCoder { [self yy_modelEncodeWithCoder:aCoder]; } 78 | - (id)initWithCoder:(NSCoder *)aDecoder { self = [super init]; return [self yy_modelInitWithCoder:aDecoder]; } 79 | - (id)copyWithZone:(NSZone *)zone { return [self yy_modelCopy]; } 80 | - (NSUInteger)hash { return [self yy_modelHash]; } 81 | - (BOOL)isEqual:(id)object { return [self yy_modelIsEqual:object]; } 82 | @end 83 | 84 | 85 | 86 | 87 | @interface YYTestEqualAndHash : XCTestCase 88 | 89 | @end 90 | 91 | @implementation YYTestEqualAndHash 92 | 93 | - (void)testHash { 94 | YYTestModelHashModel *model1 = [YYTestModelHashModel new]; 95 | YYTestModelHashModel *model2 = [YYTestModelHashModel new]; 96 | 97 | XCTAssertTrue([model1 isEqual:model2]); 98 | 99 | model1.intValue = 1; 100 | XCTAssertFalse([model1 isEqual:model2]); 101 | 102 | model2.intValue = 1; 103 | XCTAssertTrue([model1 isEqual:model2]); 104 | 105 | model1.string = @"Apple"; 106 | XCTAssertFalse([model1 isEqual:model2]); 107 | 108 | model2.string = @"Apple"; 109 | XCTAssertTrue([model1 isEqual:model2]); 110 | 111 | my_struct my = {0}; 112 | my.b = 12.34; 113 | 114 | model1.myStructValue = my; 115 | XCTAssertFalse([model1 isEqual:model2]); 116 | 117 | model2.myStructValue = my; 118 | XCTAssertTrue([model1 isEqual:model2]); 119 | 120 | model1.string = @"Apple"; 121 | model1.string2 = @"Apple"; 122 | model2.string = @"Steve Jobs"; 123 | model2.string2 = @"Steve Jobs"; 124 | XCTAssertTrue(model1.hash == model2.hash); 125 | XCTAssertFalse([model1 isEqual:model2]); 126 | } 127 | 128 | - (void)testCopying { 129 | YYTestModelHashModel *model1 = [YYTestModelHashModel new]; 130 | YYTestModelHashModel *model2 = nil; 131 | 132 | model1.intValue = 1; 133 | model1.floatValue = 12.34; 134 | model1.myStructValue = (my_struct){.b = 56.78}; 135 | model1.string = @"Apple"; 136 | model2 = model1.copy; 137 | 138 | XCTAssertEqual(model1.intValue, model2.intValue); 139 | XCTAssertEqual(model1.floatValue, model2.floatValue); 140 | XCTAssertEqual(model1.myStructValue.b, model2.myStructValue.b); 141 | XCTAssertTrue([model1.string isEqualToString:model2.string]); 142 | } 143 | 144 | - (void)testCoding { 145 | NSData *data = nil; 146 | YYTestModelHashModel *model1 = [YYTestModelHashModel new]; 147 | YYTestModelHashModel *model2 = nil; 148 | 149 | model1.intValue = 1; 150 | model1.floatValue = 12.34; 151 | model1.number = @(1234); 152 | model1.string = @"Apple"; 153 | model1.sizeValue = CGSizeMake(12, 34); 154 | model1.selectorValue = @selector(stringWithFormat:); 155 | model1.myStructValue = (my_struct){.b = 56.78}; 156 | model1.valueValue = [NSValue valueWithCATransform3D:CATransform3DIdentity]; 157 | 158 | data = [NSKeyedArchiver archivedDataWithRootObject:model1]; 159 | model2 = [NSKeyedUnarchiver unarchiveObjectWithData:data]; 160 | 161 | XCTAssertEqual(model1.intValue, model2.intValue); 162 | XCTAssertEqual(model1.floatValue, model2.floatValue); 163 | XCTAssertTrue([model1.number isEqual:model2.number]); 164 | XCTAssertTrue([model1.string isEqualToString:model2.string]); 165 | XCTAssertTrue(CGSizeEqualToSize(model1.sizeValue, model2.sizeValue)); 166 | XCTAssertTrue(model2.selectorValue == @selector(stringWithFormat:)); 167 | XCTAssertTrue(model2.myStructValue.b == 0); // ignore in NSKeyedArchiver 168 | XCTAssertTrue(model2.valueValue == nil); // ignore in NSKeyedArchiver 169 | 170 | // for code coverage 171 | NSArray *array = @[model1, model2]; 172 | NSMutableData *mutableData = [NSMutableData new]; 173 | NSKeyedArchiver *coder = [[NSKeyedArchiver alloc] initForWritingWithMutableData:mutableData]; 174 | [array yy_modelEncodeWithCoder:coder]; 175 | [coder finishEncoding]; 176 | XCTAssertTrue(mutableData.length > 0); 177 | 178 | mutableData = [NSMutableData new]; 179 | coder = [[NSKeyedArchiver alloc] initForWritingWithMutableData:mutableData]; 180 | [[NSNull null] yy_modelEncodeWithCoder:coder]; 181 | [coder finishEncoding]; 182 | XCTAssertTrue(mutableData.length > 0); 183 | } 184 | 185 | @end 186 | -------------------------------------------------------------------------------- /YYModelTests/YYTestCustomClass.m: -------------------------------------------------------------------------------- 1 | // 2 | // YYTestCustomClass.m 3 | // YYModel 4 | // 5 | // Created by ibireme on 15/11/29. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | #import "YYModel.h" 14 | 15 | @interface YYBaseUser : NSObject 16 | @property uint64_t uid; 17 | @property NSString *name; 18 | @end 19 | 20 | 21 | @interface YYLocalUser : YYBaseUser 22 | @property NSString *localName; 23 | @end 24 | @implementation YYLocalUser 25 | @end 26 | 27 | @interface YYRemoteUser : YYBaseUser 28 | @property NSString *remoteName; 29 | @end 30 | @implementation YYRemoteUser 31 | @end 32 | 33 | 34 | @implementation YYBaseUser 35 | + (Class)modelCustomClassForDictionary:(NSDictionary*)dictionary { 36 | if (dictionary[@"localName"]) { 37 | return [YYLocalUser class]; 38 | } else if (dictionary[@"remoteName"]) { 39 | return [YYRemoteUser class]; 40 | } 41 | return [YYBaseUser class]; 42 | } 43 | @end 44 | 45 | @interface YYTestCustomClassModel : NSObject 46 | @property (nonatomic, strong) NSArray *users; 47 | @property (nonatomic, strong) NSDictionary *userDict; 48 | @property (nonatomic, strong) NSSet *userSet; 49 | @property (nonatomic, strong) YYBaseUser *user; 50 | @end 51 | 52 | @implementation YYTestCustomClassModel 53 | 54 | + (NSDictionary *)modelContainerPropertyGenericClass { 55 | return @{@"users" : YYBaseUser.class, 56 | @"userDict" : YYBaseUser.class, 57 | @"userSet" : YYBaseUser.class}; 58 | } 59 | + (Class)modelCustomClassForDictionary:(NSDictionary*)dictionary { 60 | if (dictionary[@"localName"]) { 61 | return [YYLocalUser class]; 62 | } else if (dictionary[@"remoteName"]) { 63 | return [YYRemoteUser class]; 64 | } 65 | return nil; 66 | } 67 | @end 68 | 69 | 70 | @interface YYTestCustomClass : XCTestCase 71 | 72 | @end 73 | 74 | @implementation YYTestCustomClass 75 | 76 | - (void)test { 77 | YYTestCustomClassModel *model; 78 | YYBaseUser *user; 79 | 80 | NSDictionary *jsonUserBase = @{@"uid" : @123, @"name" : @"Harry"}; 81 | NSDictionary *jsonUserLocal = @{@"uid" : @123, @"name" : @"Harry", @"localName" : @"HarryLocal"}; 82 | NSDictionary *jsonUserRemote = @{@"uid" : @123, @"name" : @"Harry", @"remoteName" : @"HarryRemote"}; 83 | 84 | user = [YYBaseUser yy_modelWithDictionary:jsonUserBase]; 85 | XCTAssert([user isMemberOfClass:[YYBaseUser class]]); 86 | 87 | user = [YYBaseUser yy_modelWithDictionary:jsonUserLocal]; 88 | XCTAssert([user isMemberOfClass:[YYLocalUser class]]); 89 | 90 | user = [YYBaseUser yy_modelWithDictionary:jsonUserRemote]; 91 | XCTAssert([user isMemberOfClass:[YYRemoteUser class]]); 92 | 93 | 94 | model = [YYTestCustomClassModel yy_modelWithJSON:@{@"user" : jsonUserLocal}]; 95 | XCTAssert([model.user isMemberOfClass:[YYLocalUser class]]); 96 | 97 | model = [YYTestCustomClassModel yy_modelWithJSON:@{@"users" : @[jsonUserBase, jsonUserLocal, jsonUserRemote]}]; 98 | XCTAssert([model.users[0] isMemberOfClass:[YYBaseUser class]]); 99 | XCTAssert([model.users[1] isMemberOfClass:[YYLocalUser class]]); 100 | XCTAssert([model.users[2] isMemberOfClass:[YYRemoteUser class]]); 101 | 102 | model = [YYTestCustomClassModel yy_modelWithJSON:@{@"userDict" : @{@"a" : jsonUserBase, @"b" : jsonUserLocal, @"c" : jsonUserRemote}}]; 103 | XCTAssert([model.userDict[@"a"] isKindOfClass:[YYBaseUser class]]); 104 | XCTAssert([model.userDict[@"b"] isKindOfClass:[YYLocalUser class]]); 105 | XCTAssert([model.userDict[@"c"] isKindOfClass:[YYRemoteUser class]]); 106 | 107 | model = [YYTestCustomClassModel yy_modelWithJSON:@{@"userSet" : @[jsonUserBase, jsonUserLocal, jsonUserRemote]}]; 108 | XCTAssert([model.userSet.anyObject isKindOfClass:[YYBaseUser class]]); 109 | } 110 | 111 | @end 112 | -------------------------------------------------------------------------------- /YYModelTests/YYTestCustomTransform.m: -------------------------------------------------------------------------------- 1 | // 2 | // YYTestCustomTransform.m 3 | // YYModel 4 | // 5 | // Created by ibireme on 15/11/29. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | #import "YYModel.h" 14 | 15 | @interface YYTestCustomTransformModel : NSObject 16 | @property uint64_t id; 17 | @property NSString *content; 18 | @property NSDate *time; 19 | @end 20 | 21 | @implementation YYTestCustomTransformModel 22 | 23 | 24 | -(NSDictionary *)modelCustomWillTransformFromDictionary:(NSDictionary *)dic{ 25 | if (dic) { 26 | NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithDictionary:dic]; 27 | if (dict[@"date"]) { 28 | dict[@"time"] = dict[@"date"]; 29 | } 30 | return dict; 31 | } 32 | return dic; 33 | } 34 | 35 | - (BOOL)modelCustomTransformFromDictionary:(NSDictionary *)dic { 36 | NSNumber *time = dic[@"time"]; 37 | if ([time isKindOfClass:[NSNumber class]] && time.unsignedLongLongValue != 0) { 38 | _time = [NSDate dateWithTimeIntervalSince1970:time.unsignedLongLongValue / 1000.0]; 39 | return YES; 40 | } else { 41 | return NO; 42 | } 43 | } 44 | 45 | - (BOOL)modelCustomTransformToDictionary:(NSMutableDictionary *)dic { 46 | if (_time) { 47 | dic[@"time"] = @((uint64_t)(_time.timeIntervalSince1970 * 1000)); 48 | return YES; 49 | } else { 50 | return NO; 51 | } 52 | } 53 | 54 | @end 55 | 56 | 57 | 58 | @interface YYTestCustomTransform : XCTestCase 59 | 60 | @end 61 | 62 | @implementation YYTestCustomTransform 63 | 64 | 65 | - (void)test { 66 | NSString *json; 67 | YYTestCustomTransformModel *model; 68 | NSDictionary *jsonObject; 69 | 70 | json = @"{\"id\":5472746497,\"content\":\"Hello\",\"time\":1401234567000}"; 71 | model = [YYTestCustomTransformModel yy_modelWithJSON:json]; 72 | XCTAssert(model.time != nil); 73 | 74 | json = @"{\"id\":5472746497,\"content\":\"Hello\"}"; 75 | model = [YYTestCustomTransformModel yy_modelWithJSON:json]; 76 | XCTAssert(model == nil); 77 | 78 | model = [YYTestCustomTransformModel yy_modelWithDictionary:@{@"id":@5472746497,@"content":@"Hello"}]; 79 | XCTAssert(model == nil); 80 | 81 | json = @"{\"id\":5472746497,\"content\":\"Hello\",\"time\":1401234567000}"; 82 | model = [YYTestCustomTransformModel yy_modelWithJSON:json]; 83 | jsonObject = [model yy_modelToJSONObject]; 84 | XCTAssert([jsonObject[@"time"] isKindOfClass:[NSNumber class]]); 85 | 86 | model.time = nil; 87 | jsonObject = [model yy_modelToJSONObject]; 88 | XCTAssert(jsonObject == nil); 89 | 90 | json = @"{\"id\":5472746497,\"content\":\"Hello\",\"date\":1401234567000}"; 91 | model = [YYTestCustomTransformModel yy_modelWithJSON:json]; 92 | XCTAssert(model.time != nil); 93 | 94 | } 95 | 96 | @end 97 | -------------------------------------------------------------------------------- /YYModelTests/YYTestDescription.m: -------------------------------------------------------------------------------- 1 | // 2 | // YYTestDescription.m 3 | // YYModel 4 | // 5 | // Created by ibireme on 16/1/3. 6 | // Copyright (c) 2016 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | #import "YYModel.h" 14 | 15 | 16 | typedef struct my_struct { 17 | int a; 18 | double b; 19 | long double c; 20 | } my_struct; 21 | 22 | typedef struct my_union { 23 | int a; 24 | double b; 25 | long double c; 26 | } my_union; 27 | 28 | 29 | 30 | @interface YYTestDescriptionModel : NSObject 31 | @property bool boolValue; 32 | @property BOOL BOOLValue; 33 | @property char charValue; 34 | @property unsigned char unsignedCharValue; 35 | @property short shortValue; 36 | @property unsigned short unsignedShortValue; 37 | @property int intValue; 38 | @property unsigned int unsignedIntValue; 39 | @property long longValue; 40 | @property unsigned long unsignedLongValue; 41 | @property long long longLongValue; 42 | @property unsigned long long unsignedLongLongValue; 43 | @property float floatValue; 44 | @property double doubleValue; 45 | @property long double longDoubleValue; 46 | @property (strong) Class classValue; 47 | @property SEL selectorValue; 48 | @property (copy) void (^blockValue)(); 49 | @property void *pointerValue; 50 | @property char *cString; 51 | @property CFArrayRef cfArrayValue; 52 | @property NSValue *valueValue; 53 | 54 | @property CGSize sizeValue; 55 | @property CGPoint pointValue; 56 | @property CGRect rectValue; 57 | @property CGAffineTransform transformValue; 58 | @property UIEdgeInsets insetsValue; 59 | @property UIOffset offsetValue; 60 | @property my_struct myStructValue; // invalid for NSKeyedArchiver/Unarchiver 61 | @property my_union myUnionValue; // invalid for NSKeyedArchiver/Unarchiver 62 | 63 | 64 | @property (nonatomic, strong) NSObject *object; 65 | @property (nonatomic, strong) NSNumber *number; 66 | @property (nonatomic, strong) NSDecimalNumber *decimal; 67 | @property (nonatomic, strong) NSString *string; 68 | @property (nonatomic, strong) NSString *string2; 69 | @property (nonatomic, strong) NSMutableString *mString; 70 | @property (nonatomic, strong) NSData *data; 71 | @property (nonatomic, strong) NSMutableData *mData; 72 | @property (nonatomic, strong) NSDate *date; 73 | @property (nonatomic, strong) NSValue *value; 74 | @property (nonatomic, strong) NSURL *url; 75 | 76 | @property (nonatomic, strong) NSArray *array; 77 | @property (nonatomic, strong) NSMutableArray *mArray; 78 | @property (nonatomic, strong) NSDictionary *dict; 79 | @property (nonatomic, strong) NSMutableDictionary *mDict; 80 | @property (nonatomic, strong) NSSet *set; 81 | @property (nonatomic, strong) NSMutableSet *mSet; 82 | @end 83 | 84 | @implementation YYTestDescriptionModel 85 | - (NSString *)description { 86 | return [self yy_modelDescription]; 87 | } 88 | @end 89 | 90 | 91 | 92 | 93 | @interface YYTestDescription : XCTestCase 94 | 95 | @end 96 | 97 | @implementation YYTestDescription 98 | 99 | - (void)testDescription { 100 | YYTestDescriptionModel *model = [YYTestDescriptionModel new]; 101 | model.string = @"test"; 102 | model.intValue = 100; 103 | 104 | model.number = @(123); 105 | model.decimal = [NSDecimalNumber decimalNumberWithString:@"456"]; 106 | model.value = [NSValue valueWithRange:NSMakeRange(10, 5)]; 107 | model.date = [NSDate new]; 108 | model.blockValue = ^{}; 109 | model.mData = [NSMutableData data]; 110 | for (int i = 0; i < 1024; i++) { 111 | [model.mData appendBytes:&i length:sizeof(int)]; 112 | } 113 | model.array = @[]; 114 | model.dict = @{}; 115 | model.set = [NSSet new]; 116 | model.mArray = [NSMutableArray new]; 117 | model.mDict = [NSMutableDictionary new]; 118 | model.mSet = [NSMutableSet new]; 119 | 120 | for (int i = 0; i < 2; i++) { 121 | YYTestDescriptionModel *sub = [YYTestDescriptionModel new]; 122 | sub.intValue = i; 123 | [model.mArray addObject:sub]; 124 | model.mDict[@(i).description] = sub; 125 | [model.mSet addObject:sub]; 126 | } 127 | 128 | NSLog(@"%@",model.description); 129 | } 130 | 131 | @end 132 | -------------------------------------------------------------------------------- /YYModelTests/YYTestHelper.h: -------------------------------------------------------------------------------- 1 | // 2 | // YYTestHelper.h 3 | // YYModel 4 | // 5 | // Created by ibireme on 15/11/28. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | 14 | @interface YYTestHelper : NSObject 15 | + (NSString *)jsonStringFromData:(NSData *)data; 16 | + (NSString *)jsonStringFromObject:(id)object; 17 | + (id)jsonObjectFromData:(NSData *)data; 18 | + (id)jsonObjectFromString:(NSString *)string; 19 | + (NSData *)jsonDataFromString:(NSString *)string; 20 | + (NSData *)jsonDataFromObject:(id)object; 21 | @end 22 | -------------------------------------------------------------------------------- /YYModelTests/YYTestHelper.m: -------------------------------------------------------------------------------- 1 | // 2 | // YYTestHelper.m 3 | // YYModel 4 | // 5 | // Created by ibireme on 15/11/28. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import "YYTestHelper.h" 13 | 14 | @implementation YYTestHelper 15 | 16 | + (NSString *)jsonStringFromData:(NSData *)data { 17 | return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; 18 | } 19 | 20 | + (NSString *)jsonStringFromObject:(id)object { 21 | NSData *data = [NSJSONSerialization dataWithJSONObject:object options:0 error:NULL]; 22 | return [self jsonStringFromData:data]; 23 | } 24 | 25 | + (id)jsonObjectFromData:(NSData *)data { 26 | return [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:NULL]; 27 | } 28 | 29 | + (id)jsonObjectFromString:(NSString *)string { 30 | NSData *data = [self jsonDataFromString:string]; 31 | return [self jsonObjectFromData:data]; 32 | } 33 | 34 | + (NSData *)jsonDataFromString:(NSString *)string { 35 | return [string dataUsingEncoding:NSUTF8StringEncoding]; 36 | } 37 | 38 | + (NSData *)jsonDataFromObject:(id)object { 39 | NSString *string = [self jsonStringFromObject:object]; 40 | return [self jsonDataFromString:string]; 41 | } 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /YYModelTests/YYTestModelMapper.m: -------------------------------------------------------------------------------- 1 | // 2 | // YYTestModelMapper.m 3 | // YYModel 4 | // 5 | // Created by ibireme on 15/11/27. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | #import "YYModel.h" 14 | 15 | 16 | @interface YYTestPropertyMapperModelAuto : NSObject 17 | @property (nonatomic, assign) NSString *name; 18 | @property (nonatomic, assign) NSNumber *count; 19 | @end 20 | 21 | @implementation YYTestPropertyMapperModelAuto 22 | @end 23 | 24 | @interface YYTestPropertyMapperModelCustom : NSObject 25 | @property (nonatomic, assign) NSString *name; 26 | @property (nonatomic, assign) NSNumber *count; 27 | @property (nonatomic, assign) NSString *desc1; 28 | @property (nonatomic, assign) NSString *desc2; 29 | @property (nonatomic, assign) NSString *desc3; 30 | @property (nonatomic, assign) NSString *desc4; 31 | @property (nonatomic, assign) NSString *modelID; 32 | @end 33 | 34 | @implementation YYTestPropertyMapperModelCustom 35 | + (NSDictionary *)modelCustomPropertyMapper { 36 | return @{ @"name" : @"n", 37 | @"count" : @"ext.c", 38 | @"desc1" : @"ext.d", // mapped to same key path 39 | @"desc2" : @"ext.d", // mapped to same key path 40 | @"desc3" : @"ext.d.e", 41 | @"desc4" : @".ext", 42 | @"modelID" : @[@"ID", @"Id", @"id", @"ext.id"]}; 43 | } 44 | @end 45 | 46 | @interface YYTestPropertyMapperModelWarn : NSObject { 47 | NSString *_description; 48 | } 49 | @property (nonatomic, strong) NSString *description; 50 | @property (nonatomic, strong) NSNumber *id; 51 | @end 52 | 53 | @implementation YYTestPropertyMapperModelWarn 54 | @synthesize description = _description; 55 | @end 56 | 57 | 58 | 59 | 60 | 61 | 62 | @protocol YYTestPropertyMapperModelAuto 63 | @end 64 | 65 | @protocol YYTestPropertyMapperModelCustom 66 | @end 67 | 68 | @protocol YYSimpleProtocol 69 | @end 70 | 71 | 72 | @interface YYTestPropertyMapperModelContainer : NSObject 73 | @property (nonatomic, strong) NSArray *array; 74 | @property (nonatomic, strong) NSMutableArray *mArray; 75 | @property (nonatomic, strong) NSDictionary *dict; 76 | @property (nonatomic, strong) NSMutableDictionary *mDict; 77 | @property (nonatomic, strong) NSSet *set; 78 | @property (nonatomic, strong) NSMutableSet *mSet; 79 | 80 | @property (nonatomic, strong) NSArray *pArray1; 81 | @property (nonatomic, strong) NSArray *pArray2; 82 | @property (nonatomic, strong) NSArray *pArray3; 83 | @end 84 | 85 | @implementation YYTestPropertyMapperModelContainer 86 | @end 87 | 88 | @interface YYTestPropertyMapperModelContainerGeneric : YYTestPropertyMapperModelContainer 89 | @end 90 | 91 | @implementation YYTestPropertyMapperModelContainerGeneric 92 | + (NSDictionary *)modelCustomPropertyMapper { 93 | return @{ @"mArray" : @"array", 94 | @"mDict" : @"dict", 95 | @"mSet" : @"set", 96 | @"pArray1" : @"array", 97 | @"pArray2" : @"array", 98 | @"pArray3" : @"array"}; 99 | } 100 | + (NSDictionary *)modelContainerPropertyGenericClass { 101 | return @{@"array" : YYTestPropertyMapperModelAuto.class, 102 | @"mArray" : YYTestPropertyMapperModelAuto.class, 103 | @"dict" : YYTestPropertyMapperModelAuto.class, 104 | @"mDict" : YYTestPropertyMapperModelAuto.class, 105 | @"set" : @"YYTestPropertyMapperModelAuto", 106 | @"mSet" : @"YYTestPropertyMapperModelAuto", 107 | @"pArray3" : @"YYTestPropertyMapperModelAuto"}; 108 | } 109 | @end 110 | 111 | 112 | 113 | @interface YYTestModelPropertyMapper : XCTestCase 114 | 115 | @end 116 | 117 | @implementation YYTestModelPropertyMapper 118 | 119 | - (void)testAuto { 120 | NSString *json; 121 | YYTestPropertyMapperModelAuto *model; 122 | 123 | json = @"{\"name\":\"Apple\",\"count\":12}"; 124 | model = [YYTestPropertyMapperModelAuto yy_modelWithJSON:json]; 125 | XCTAssertTrue([model.name isEqualToString:@"Apple"]); 126 | XCTAssertTrue([model.count isEqual:@12]); 127 | 128 | json = @"{\"n\":\"Apple\",\"count\":12, \"description\":\"hehe\"}"; 129 | model = [YYTestPropertyMapperModelAuto yy_modelWithJSON:json]; 130 | XCTAssertTrue(model.name == nil); 131 | XCTAssertTrue([model.count isEqual:@12]); 132 | } 133 | 134 | - (void)testCustom { 135 | NSString *json; 136 | NSDictionary *jsonObject; 137 | YYTestPropertyMapperModelCustom *model; 138 | 139 | json = @"{\"n\":\"Apple\",\"ext\":{\"c\":12}}"; 140 | model = [YYTestPropertyMapperModelCustom yy_modelWithJSON:json]; 141 | XCTAssertTrue([model.name isEqualToString:@"Apple"]); 142 | XCTAssertTrue([model.count isEqual:@12]); 143 | 144 | json = @"{\"n\":\"Apple\",\"count\":12}"; 145 | model = [YYTestPropertyMapperModelCustom yy_modelWithJSON:json]; 146 | XCTAssertTrue(model.count == nil); 147 | 148 | json = @"{\"n\":\"Apple\",\"ext\":12}"; 149 | model = [YYTestPropertyMapperModelCustom yy_modelWithJSON:json]; 150 | XCTAssertTrue(model.count == nil); 151 | 152 | json = @"{\"n\":\"Apple\",\"ext\":@{}}"; 153 | model = [YYTestPropertyMapperModelCustom yy_modelWithJSON:json]; 154 | XCTAssertTrue(model.count == nil); 155 | 156 | json = @"{\"ext\":{\"d\":\"Apple\"}}"; 157 | model = [YYTestPropertyMapperModelCustom yy_modelWithJSON:json]; 158 | XCTAssertTrue([model.desc1 isEqualToString:@"Apple"]); 159 | XCTAssertTrue([model.desc2 isEqualToString:@"Apple"]); 160 | 161 | jsonObject = [model yy_modelToJSONObject]; 162 | XCTAssertTrue([((NSDictionary *)jsonObject[@"ext"])[@"d"] isEqualToString:@"Apple"]); 163 | 164 | json = @"{\"ext\":{\"d\":{ \"e\" : \"Apple\"}}}"; 165 | model = [YYTestPropertyMapperModelCustom yy_modelWithJSON:json]; 166 | XCTAssertTrue([model.desc3 isEqualToString:@"Apple"]); 167 | 168 | json = @"{\".ext\":\"Apple\"}"; 169 | model = [YYTestPropertyMapperModelCustom yy_modelWithJSON:json]; 170 | XCTAssertTrue([model.desc4 isEqualToString:@"Apple"]); 171 | 172 | json = @"{\".ext\":\"Apple\", \"name\":\"Apple\", \"count\":\"10\", \"desc1\":\"Apple\", \"desc2\":\"Apple\", \"desc3\":\"Apple\", \"desc4\":\"Apple\", \"modelID\":\"Apple\"}"; 173 | model = [YYTestPropertyMapperModelCustom yy_modelWithJSON:json]; 174 | XCTAssertTrue([model.desc4 isEqualToString:@"Apple"]); 175 | 176 | json = @"{\"id\":\"abcd\"}"; 177 | model = [YYTestPropertyMapperModelCustom yy_modelWithJSON:json]; 178 | XCTAssertTrue([model.modelID isEqualToString:@"abcd"]); 179 | 180 | json = @"{\"ext\":{\"id\":\"abcd\"}}"; 181 | model = [YYTestPropertyMapperModelCustom yy_modelWithJSON:json]; 182 | XCTAssertTrue([model.modelID isEqualToString:@"abcd"]); 183 | 184 | json = @"{\"id\":\"abcd\",\"ID\":\"ABCD\",\"Id\":\"Abcd\"}"; 185 | model = [YYTestPropertyMapperModelCustom yy_modelWithJSON:json]; 186 | XCTAssertTrue([model.modelID isEqualToString:@"ABCD"]); 187 | 188 | jsonObject = [model yy_modelToJSONObject]; 189 | XCTAssertTrue(jsonObject[@"id"] == nil); 190 | XCTAssertTrue([jsonObject[@"ID"] isEqualToString:@"ABCD"]); 191 | } 192 | 193 | - (void)testWarn { 194 | NSString *json = @"{\"description\":\"Apple\",\"id\":12345}"; 195 | YYTestPropertyMapperModelWarn *model = [YYTestPropertyMapperModelWarn yy_modelWithJSON:json]; 196 | XCTAssertTrue([model.description isEqualToString:@"Apple"]); 197 | XCTAssertTrue([model.id isEqual:@12345]); 198 | } 199 | 200 | - (void)testContainer { 201 | NSString *json; 202 | NSDictionary *jsonObject = nil; 203 | YYTestPropertyMapperModelContainer *model; 204 | 205 | json = @"{\"array\":[\n {\"name\":\"Apple\", \"count\":10},\n {\"name\":\"Banana\", \"count\":11},\n {\"name\":\"Pear\", \"count\":12},\n null\n]}"; 206 | 207 | model = [YYTestPropertyMapperModelContainer yy_modelWithJSON:json]; 208 | XCTAssertTrue([model.array isKindOfClass:[NSArray class]]); 209 | XCTAssertTrue(model.array.count == 4); 210 | 211 | jsonObject = [model yy_modelToJSONObject]; 212 | XCTAssertTrue([jsonObject[@"array"] isKindOfClass:[NSArray class]]); 213 | 214 | model = [YYTestPropertyMapperModelContainerGeneric yy_modelWithJSON:json]; 215 | XCTAssertTrue([model.array isKindOfClass:[NSArray class]]); 216 | XCTAssertTrue(model.array.count == 3); 217 | XCTAssertTrue([((YYTestPropertyMapperModelAuto *)model.array[0]).name isEqualToString:@"Apple"]); 218 | XCTAssertTrue([((YYTestPropertyMapperModelAuto *)model.array[0]).count isEqual:@10]); 219 | XCTAssertTrue([((YYTestPropertyMapperModelAuto *)model.array[2]).name isEqualToString:@"Pear"]); 220 | XCTAssertTrue([((YYTestPropertyMapperModelAuto *)model.array[2]).count isEqual:@12]); 221 | XCTAssertTrue([model.mArray isKindOfClass:[NSMutableArray class]]); 222 | 223 | XCTAssertTrue(model.pArray1.count == 3); 224 | XCTAssertTrue([((YYTestPropertyMapperModelAuto *)model.pArray1[0]).name isEqualToString:@"Apple"]); 225 | XCTAssertTrue([((YYTestPropertyMapperModelAuto *)model.pArray1[0]).count isEqual:@10]); 226 | XCTAssertTrue(model.pArray2.count == 3); 227 | XCTAssertTrue([((YYTestPropertyMapperModelAuto *)model.pArray2[0]).name isEqualToString:@"Apple"]); 228 | XCTAssertTrue([((YYTestPropertyMapperModelAuto *)model.pArray2[0]).count isEqual:@10]); 229 | XCTAssertTrue(model.pArray3.count == 3); 230 | XCTAssertTrue([((YYTestPropertyMapperModelAuto *)model.pArray3[0]).name isEqualToString:@"Apple"]); 231 | XCTAssertTrue([((YYTestPropertyMapperModelAuto *)model.pArray3[0]).count isEqual:@10]); 232 | 233 | 234 | jsonObject = [model yy_modelToJSONObject]; 235 | XCTAssertTrue([jsonObject[@"array"] isKindOfClass:[NSArray class]]); 236 | 237 | json = @"{\"dict\":{\n \"A\":{\"name\":\"Apple\", \"count\":10},\n \"B\":{\"name\":\"Banana\", \"count\":11},\n \"P\":{\"name\":\"Pear\", \"count\":12},\n \"N\":null\n}}"; 238 | 239 | model = [YYTestPropertyMapperModelContainer yy_modelWithJSON:json]; 240 | XCTAssertTrue([model.dict isKindOfClass:[NSDictionary class]]); 241 | XCTAssertTrue(model.dict.count == 4); 242 | 243 | jsonObject = [model yy_modelToJSONObject]; 244 | XCTAssertTrue(jsonObject != nil); 245 | 246 | model = [YYTestPropertyMapperModelContainerGeneric yy_modelWithJSON:json]; 247 | XCTAssertTrue([model.dict isKindOfClass:[NSDictionary class]]); 248 | XCTAssertTrue(model.dict.count == 3); 249 | XCTAssertTrue([((YYTestPropertyMapperModelAuto *)model.dict[@"A"]).name isEqualToString:@"Apple"]); 250 | XCTAssertTrue([((YYTestPropertyMapperModelAuto *)model.dict[@"A"]).count isEqual:@10]); 251 | XCTAssertTrue([((YYTestPropertyMapperModelAuto *)model.dict[@"P"]).name isEqualToString:@"Pear"]); 252 | XCTAssertTrue([((YYTestPropertyMapperModelAuto *)model.dict[@"P"]).count isEqual:@12]); 253 | XCTAssertTrue([model.mDict isKindOfClass:[NSMutableDictionary class]]); 254 | 255 | jsonObject = [model yy_modelToJSONObject]; 256 | XCTAssertTrue(jsonObject != nil); 257 | 258 | json = @"{\"set\":[\n {\"name\":\"Apple\", \"count\":10},\n {\"name\":\"Banana\", \"count\":11},\n {\"name\":\"Pear\", \"count\":12},\n null\n]}"; 259 | 260 | model = [YYTestPropertyMapperModelContainer yy_modelWithJSON:json]; 261 | XCTAssertTrue([model.set isKindOfClass:[NSSet class]]); 262 | XCTAssertTrue(model.set.count == 4); 263 | 264 | jsonObject = [model yy_modelToJSONObject]; 265 | XCTAssertTrue(jsonObject != nil); 266 | 267 | model = [YYTestPropertyMapperModelContainerGeneric yy_modelWithJSON:json]; 268 | XCTAssertTrue([model.set isKindOfClass:[NSSet class]]); 269 | XCTAssertTrue(model.set.count == 3); 270 | XCTAssertTrue([((YYTestPropertyMapperModelAuto *)model.set.anyObject).name isKindOfClass:[NSString class]]); 271 | XCTAssertTrue([model.mSet isKindOfClass:[NSMutableSet class]]); 272 | 273 | jsonObject = [model yy_modelToJSONObject]; 274 | XCTAssertTrue(jsonObject != nil); 275 | 276 | model = [YYTestPropertyMapperModelContainerGeneric yy_modelWithJSON:@{@"set" : @[[YYTestPropertyMapperModelAuto new]]}]; 277 | XCTAssertTrue([model.set isKindOfClass:[NSSet class]]); 278 | XCTAssertTrue([[model.set anyObject] isKindOfClass:[YYTestPropertyMapperModelAuto class]]); 279 | 280 | model = [YYTestPropertyMapperModelContainerGeneric yy_modelWithJSON:@{@"array" : [NSSet setWithArray:@[[YYTestPropertyMapperModelAuto new]]]}]; 281 | XCTAssertTrue([model.array isKindOfClass:[NSArray class]]); 282 | XCTAssertTrue([[model.array firstObject] isKindOfClass:[YYTestPropertyMapperModelAuto class]]); 283 | 284 | model = [YYTestPropertyMapperModelContainer yy_modelWithJSON:@{@"mArray" : @[[YYTestPropertyMapperModelAuto new]]}]; 285 | XCTAssertTrue([model.mArray isKindOfClass:[NSMutableArray class]]); 286 | XCTAssertTrue([[model.mArray firstObject] isKindOfClass:[YYTestPropertyMapperModelAuto class]]); 287 | 288 | model = [YYTestPropertyMapperModelContainer yy_modelWithJSON:@{@"mArray" : [NSSet setWithArray:@[[YYTestPropertyMapperModelAuto new]]]}]; 289 | XCTAssertTrue([model.mArray isKindOfClass:[NSMutableArray class]]); 290 | XCTAssertTrue([[model.mArray firstObject] isKindOfClass:[YYTestPropertyMapperModelAuto class]]); 291 | } 292 | 293 | @end 294 | -------------------------------------------------------------------------------- /YYModelTests/YYTestModelToJSON.m: -------------------------------------------------------------------------------- 1 | // 2 | // YYTestModelToJSON.m 3 | // YYModel 4 | // 5 | // Created by ibireme on 15/11/29. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | #import 14 | #import "YYModel.h" 15 | #import "YYTestHelper.h" 16 | 17 | @interface YYTestModelToJSONModel : NSObject 18 | @property bool boolValue; 19 | @property BOOL BOOLValue; 20 | @property char charValue; 21 | @property unsigned char unsignedCharValue; 22 | @property short shortValue; 23 | @property unsigned short unsignedShortValue; 24 | @property int intValue; 25 | @property unsigned int unsignedIntValue; 26 | @property long longValue; 27 | @property unsigned long unsignedLongValue; 28 | @property long long longLongValue; 29 | @property unsigned long long unsignedLongLongValue; 30 | @property float floatValue; 31 | @property double doubleValue; 32 | @property long double longDoubleValue; 33 | @property (strong) Class classValue; 34 | @property SEL selectorValue; 35 | @property (copy) void (^blockValue)(); 36 | @property void *pointerValue; 37 | @property CGRect structValue; 38 | @property CGPoint pointValue; 39 | 40 | @property (nonatomic, strong) NSObject *object; 41 | @property (nonatomic, strong) NSNumber *number; 42 | @property (nonatomic, strong) NSDecimalNumber *decimal; 43 | @property (nonatomic, strong) NSString *string; 44 | @property (nonatomic, strong) NSMutableString *mString; 45 | @property (nonatomic, strong) NSData *data; 46 | @property (nonatomic, strong) NSMutableData *mData; 47 | @property (nonatomic, strong) NSDate *date; 48 | @property (nonatomic, strong) NSValue *value; 49 | @property (nonatomic, strong) NSURL *url; 50 | 51 | @property (nonatomic, strong) NSArray *array; 52 | @property (nonatomic, strong) NSMutableArray *mArray; 53 | @property (nonatomic, strong) NSDictionary *dict; 54 | @property (nonatomic, strong) NSMutableDictionary *mDict; 55 | @property (nonatomic, strong) NSSet *set; 56 | @property (nonatomic, strong) NSMutableSet *mSet; 57 | @property (nonatomic, strong) UIColor *color; 58 | @end 59 | 60 | @implementation YYTestModelToJSONModel 61 | + (NSDictionary *)modelCustomPropertyMapper { 62 | return @{ 63 | @"intValue" : @"int", 64 | @"longValue" : @"long", // mapped to same key 65 | @"unsignedLongLongValue" : @"long", // mapped to same key 66 | @"shortValue" : @"ext.short" // mapped to key path 67 | }; 68 | } 69 | @end 70 | 71 | 72 | 73 | @interface YYTestKeyPathModelToJSONModel : NSObject 74 | @property (nonatomic, strong) NSString *a; 75 | @property (nonatomic, strong) NSString *b; 76 | @property (nonatomic, strong) NSString *c; 77 | @property (nonatomic, strong) NSString *d; 78 | @property (nonatomic, strong) NSString *e; 79 | @property (nonatomic, strong) NSDictionary *f; 80 | @property (nonatomic, strong) NSString *g; 81 | @end 82 | 83 | @implementation YYTestKeyPathModelToJSONModel 84 | + (NSDictionary *)modelCustomPropertyMapper { 85 | return @{ 86 | @"a" : @"ext.a", 87 | @"b" : @"ext.b", 88 | @"c" : @"ext.a", 89 | @"e" : @"d.e", 90 | @"g" : @"f.g.g" 91 | }; 92 | } 93 | @end 94 | 95 | 96 | 97 | @interface YYTestModelToJSON : XCTestCase 98 | 99 | @end 100 | 101 | @implementation YYTestModelToJSON 102 | 103 | 104 | - (void)testToJSON { 105 | YYTestModelToJSONModel *model = [YYTestModelToJSONModel new]; 106 | model.intValue = 1; 107 | model.longValue = 2; 108 | model.unsignedLongLongValue = 3; 109 | model.shortValue = 4; 110 | model.array = @[@1,@"2",[NSURL URLWithString:@"https://github.com"]]; 111 | model.set = [NSSet setWithArray:model.array]; 112 | model.color = [UIColor colorWithRed:1 green:0 blue:0 alpha:0.5]; 113 | 114 | NSDictionary *jsonObject = [model yy_modelToJSONObject]; 115 | XCTAssert([jsonObject isKindOfClass:[NSDictionary class]]); 116 | XCTAssert([jsonObject[@"int"] isEqual:@(1)]); 117 | XCTAssert([jsonObject[@"long"] isEqual:@(2)] || [jsonObject[@"long"] isEqual:@(3)]); 118 | XCTAssert([ ((NSDictionary *)jsonObject[@"ext"])[@"short"] isEqual:@(4)]); 119 | XCTAssert(jsonObject[@"color"] != nil); 120 | 121 | NSString *jsonString = [model yy_modelToJSONString]; 122 | XCTAssert([[YYTestHelper jsonObjectFromString:jsonString] isKindOfClass:[NSDictionary class]]); 123 | 124 | NSData *jsonData = [model yy_modelToJSONData]; 125 | XCTAssert([[YYTestHelper jsonObjectFromData:jsonData] isKindOfClass:[NSDictionary class]]); 126 | 127 | model = [YYTestModelToJSONModel yy_modelWithJSON:jsonData]; 128 | XCTAssert(model.intValue == 1); 129 | } 130 | 131 | - (void)testKeyPath { 132 | YYTestKeyPathModelToJSONModel *model = [YYTestKeyPathModelToJSONModel new]; 133 | model.a = @"a"; 134 | model.b = @"b"; 135 | model.c = @"c"; 136 | model.d = @"d"; 137 | model.e = @"e"; 138 | model.f = @{}; 139 | 140 | NSDictionary *dic = [model yy_modelToJSONObject]; 141 | NSDictionary *ext = dic[@"ext"]; 142 | XCTAssert([ext[@"b"] isEqualToString:@"b"]); 143 | XCTAssert([ext[@"a"] isEqualToString:@"a"] || [ext[@"a"] isEqualToString:@"c"]); 144 | 145 | model.f = @{@"g" : @""}; 146 | dic = [model yy_modelToJSONObject]; 147 | 148 | } 149 | 150 | - (void)testDate { 151 | NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; 152 | formatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]; 153 | formatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ssZ"; 154 | 155 | NSDate *date = [NSDate dateWithTimeIntervalSince1970:100000000]; 156 | NSString *dateString = [formatter stringFromDate:date]; 157 | 158 | YYTestModelToJSONModel *model = [YYTestModelToJSONModel new]; 159 | model.date = date; 160 | 161 | NSDictionary *jsonObject = [model yy_modelToJSONObject]; 162 | XCTAssert([jsonObject[@"date"] isEqual:dateString]); 163 | 164 | NSString *jsonString = [model yy_modelToJSONString]; 165 | YYTestModelToJSONModel *newModel = [YYTestModelToJSONModel yy_modelWithJSON:jsonString]; 166 | XCTAssert([newModel.date isEqualToDate:date]); 167 | } 168 | 169 | @end 170 | -------------------------------------------------------------------------------- /YYModelTests/YYTestNestModel.m: -------------------------------------------------------------------------------- 1 | // 2 | // YYTestNestModel.m 3 | // YYModel 4 | // 5 | // Created by ibireme on 15/11/29. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | #import "YYModel.h" 14 | 15 | 16 | @interface YYTestNestUser : NSObject 17 | @property uint64_t uid; 18 | @property NSString *name; 19 | @end 20 | @implementation YYTestNestUser 21 | @end 22 | 23 | @interface YYTestNestRepo : NSObject 24 | @property uint64_t repoID; 25 | @property NSString *name; 26 | @property YYTestNestUser *user; 27 | @end 28 | @implementation YYTestNestRepo 29 | @end 30 | 31 | 32 | 33 | @interface YYTestNestModel : XCTestCase 34 | 35 | @end 36 | 37 | @implementation YYTestNestModel 38 | 39 | - (void)test { 40 | NSString *json = @"{\"repoID\":1234,\"name\":\"YYModel\",\"user\":{\"uid\":5678,\"name\":\"ibireme\"}}"; 41 | YYTestNestRepo *repo = [YYTestNestRepo yy_modelWithJSON:json]; 42 | XCTAssert(repo.repoID == 1234); 43 | XCTAssert([repo.name isEqualToString:@"YYModel"]); 44 | XCTAssert(repo.user.uid == 5678); 45 | XCTAssert([repo.user.name isEqualToString:@"ibireme"]); 46 | 47 | NSDictionary *jsonObject = [repo yy_modelToJSONObject]; 48 | XCTAssert([((NSString *)jsonObject[@"name"]) isEqualToString:@"YYModel"]); 49 | XCTAssert([((NSString *)((NSDictionary *)jsonObject[@"user"])[@"name"]) isEqualToString:@"ibireme"]); 50 | 51 | [repo yy_modelSetWithJSON:@{@"name" : @"YYImage", @"user" : @{@"name": @"bot"}}]; 52 | XCTAssert(repo.repoID == 1234); 53 | XCTAssert([repo.name isEqualToString:@"YYImage"]); 54 | XCTAssert(repo.user.uid == 5678); 55 | XCTAssert([repo.user.name isEqualToString:@"bot"]); 56 | } 57 | 58 | @end 59 | --------------------------------------------------------------------------------