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