├── README.md ├── TBModelTool.podspec ├── TBModelTool.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings │ └── xcuserdata │ │ ├── ducktobey.xcuserdatad │ │ ├── UserInterfaceState.xcuserstate │ │ └── WorkspaceSettings.xcsettings │ │ └── tangbin.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ ├── ducktobey.xcuserdatad │ ├── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ │ ├── TBModelTool.xcscheme │ │ └── xcschememanagement.plist │ └── tangbin.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ ├── TBModelTool.xcscheme │ └── xcschememanagement.plist ├── TBModelTool ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── Info.plist ├── MJExtension │ ├── Info.plist │ ├── MJExtension.h │ ├── MJExtensionConst.h │ ├── MJExtensionConst.m │ ├── MJFoundation.h │ ├── MJFoundation.m │ ├── MJProperty.h │ ├── MJProperty.m │ ├── MJPropertyKey.h │ ├── MJPropertyKey.m │ ├── MJPropertyType.h │ ├── MJPropertyType.m │ ├── NSObject+MJClass.h │ ├── NSObject+MJClass.m │ ├── NSObject+MJCoding.h │ ├── NSObject+MJCoding.m │ ├── NSObject+MJKeyValue.h │ ├── NSObject+MJKeyValue.m │ ├── NSObject+MJProperty.h │ ├── NSObject+MJProperty.m │ ├── NSString+MJExtension.h │ └── NSString+MJExtension.m ├── Model │ ├── NSObject+YYModel.h │ ├── NSObject+YYModel.m │ ├── YYClassInfo.h │ └── YYClassInfo.m ├── TBModelTool │ ├── TBModelTool.h │ └── TBModelTool.m ├── ViewController.h ├── ViewController.m ├── json.json └── main.m ├── TBModelToolTests ├── Info.plist └── TBModelToolTests.m └── TBModelToolUITests ├── Info.plist └── TBModelToolUITests.m /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # 欢迎使用 TBModelTool 模型生成工具 5 | 6 | ------ 7 | 8 | > TBModelTool是一个简单的工具,可以帮助大家快速的根据 json 数据生成对应的 Model 文件,支持多层嵌套的 json 解析,极大的简化了通过json创建对应模型对象的步骤,现在你只需要通过以下步骤即可快速创建模型对象; 9 | 10 | #### 使用方法: 11 | 12 | 首先要指定文件生成的位置:TBModelTool.h的MODEL_FILE_PATH宏定义中指定你的文件生成位置 13 | 14 | - 如果你是通过KVC的方式解析数据模型,你大概只需要在获得json数据的地方,通过这种方式: 15 | 16 | ```objective-c 17 | [TBModelTool KVC_createModelClassFileWithResource:jsonData andModelClassFileName:@"Your Model Name" andReplacedKeyFromPropertyName:nil]; 18 | ``` 19 | 20 | 21 | 22 | - 如果你是通过MJExtension来解析你的数据模型,你可以在获得json数据的地方,通过这种方式生成你期望的模型: 23 | 24 | ```objective-c 25 | [TBModelTool MJ_createModelClassFileWithResource:jsonData andModelClassFileName:@"Your Model Name" andReplacedKeyFromPropertyName:nil]; 26 | ``` 27 | 28 | 29 | 30 | - 如果你是通过YYModel来解析力的数据模型,你可以在获得json数据的地方,通过这种方式获得你期望的数据模型: 31 | 32 | ```objective-c 33 | [TBModelTool YYModel_createModelClassFileWithResource:jsonData andModelClassFileName:@"Your Model Name" andReplacedKeyFromPropertyName:nil]; 34 | ``` 35 | 36 | #### 然后到指定的目录下 导入该文件到项目即可!至此,模型生成就完成了 37 | 38 | - ⚠️在使用中有需要注意的地方: 39 | 40 | 1. andReplacedKeyFromPropertyName: 41 | 该参数是自定义某些字段转换为另外一个字段进行解析的情况,如json数据中存在id字段,但是在object-C中,id是关键字,因此可以通过传入`@{@"id":@"Id"}`后,解析出来的模型就会自动将需要替换的字段替换为期望的字段,并在解析的字段中也替换了,因此你不需要任何操作; 42 | 2. TBModelTool 这个工具生成model文件时,需要再模拟器中运行,因为在真机中获取到的文件目录是真机中的目录,无法获取Mac中的目录; 43 | 44 | 如果你在使用中遇到任何不满足的需求或者使用不便的地方,欢迎 Issues 或者 Q 我 QQ:2446959228 (*^__^*) 45 | 46 | ##### 如果你觉得这个工具对你有帮助,请帮我点一下右上角的小星星,谢谢对我的支持,我将持续为大家提供更好用的工具。 47 | 48 | -------------------------------------------------------------------------------- /TBModelTool.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod spec lint TBModelTool.podspec' to ensure this is a 3 | # valid spec and to remove all comments including this before submitting the spec. 4 | # 5 | # To learn more about Podspec attributes see https://docs.cocoapods.org/specification.html 6 | # To see working Podspecs in the CocoaPods repo see https://github.com/CocoaPods/Specs/ 7 | # 8 | 9 | Pod::Spec.new do |spec| 10 | 11 | # ――― Spec Metadata ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 12 | # 13 | # These will help people to find your library, and whilst it 14 | # can feel like a chore to fill in it's definitely to your advantage. The 15 | # summary should be tweet-length, and the description more in depth. 16 | # 17 | 18 | spec.name = "TBModelTool" 19 | spec.version = "0.0.1" 20 | spec.summary = "Help you fast create a data model for Objective-C." 21 | 22 | # This description is used to generate tags and improve search results. 23 | # * Think: What does it do? Why did you write it? What is the focus? 24 | # * Try to keep it short, snappy and to the point. 25 | # * Write the description between the DESC delimiters below. 26 | # * Finally, don't worry about the indent, CocoaPods strips it! 27 | spec.description = <<-DESC 28 | help you fast create a data model for Objective-C 29 | DESC 30 | 31 | spec.homepage = "http://github.com/MSTGit/TBModelTool" 32 | # spec.screenshots = "www.example.com/screenshots_1.gif", "www.example.com/screenshots_2.gif" 33 | 34 | 35 | # ――― Spec License ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 36 | # 37 | # Licensing your code is important. See https://choosealicense.com for more info. 38 | # CocoaPods will detect a license file if there is a named LICENSE* 39 | # Popular ones are 'MIT', 'BSD' and 'Apache License, Version 2.0'. 40 | # 41 | 42 | spec.license = "MIT" 43 | # spec.license = { :type => "MIT", :file => "FILE_LICENSE" } 44 | 45 | 46 | # ――― Author Metadata ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 47 | # 48 | # Specify the authors of the library, with email addresses. Email addresses 49 | # of the authors are extracted from the SCM log. E.g. $ git log. CocoaPods also 50 | # accepts just a name if you'd rather not provide an email address. 51 | # 52 | # Specify a social_media_url where others can refer to, for example a twitter 53 | # profile URL. 54 | # 55 | 56 | spec.author = { "MRT" => "ducktobey@gmail.com" } 57 | # Or just: spec.author = "MRT" 58 | # spec.authors = { "MRT" => "" } 59 | # spec.social_media_url = "https://twitter.com/MRT" 60 | 61 | # ――― Platform Specifics ――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 62 | # 63 | # If this Pod runs only on iOS or OS X, then specify the platform and 64 | # the deployment target. You can optionally include the target after the platform. 65 | # 66 | 67 | spec.platform = :ios 68 | spec.platform = :ios, "5.0" 69 | 70 | # When using multiple platforms 71 | spec.ios.deployment_target = "8.0" 72 | # spec.osx.deployment_target = "10.7" 73 | # spec.watchos.deployment_target = "2.0" 74 | # spec.tvos.deployment_target = "9.0" 75 | 76 | 77 | # ――― Source Location ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 78 | # 79 | # Specify the location from where the source should be retrieved. 80 | # Supports git, hg, bzr, svn and HTTP. 81 | # 82 | 83 | spec.source = { :git => "http://github.com/MSTGit/TBModelTool.git", :tag => "#{spec.version}" } 84 | 85 | 86 | # ――― Source Code ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 87 | # 88 | # CocoaPods is smart about how it includes source code. For source files 89 | # giving a folder will include any swift, h, m, mm, c & cpp files. 90 | # For header files it will include any header in the folder. 91 | # Not including the public_header_files will make all headers public. 92 | # 93 | 94 | spec.source_files = "TBModelTool/TBModelTool/**/*.{h,m}" 95 | #spec.exclude_files = "Classes/Exclude" 96 | 97 | # spec.public_header_files = "Classes/**/*.h" 98 | 99 | 100 | # ――― Resources ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 101 | # 102 | # A list of resources included with the Pod. These are copied into the 103 | # target bundle with a build phase script. Anything else will be cleaned. 104 | # You can preserve files from being cleaned, please don't preserve 105 | # non-essential files like tests, examples and documentation. 106 | # 107 | 108 | # spec.resource = "icon.png" 109 | # spec.resources = "Resources/*.png" 110 | 111 | # spec.preserve_paths = "FilesToSave", "MoreFilesToSave" 112 | 113 | 114 | # ――― Project Linking ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 115 | # 116 | # Link your library with frameworks, or libraries. Libraries do not include 117 | # the lib prefix of their name. 118 | # 119 | 120 | # spec.framework = "SomeFramework" 121 | # spec.frameworks = "SomeFramework", "AnotherFramework" 122 | 123 | # spec.library = "iconv" 124 | # spec.libraries = "iconv", "xml2" 125 | 126 | 127 | # ――― Project Settings ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 128 | # 129 | # If your library depends on compiler flags you can set them in the xcconfig hash 130 | # where they will only apply to your library. If you depend on other Podspecs 131 | # you can include multiple dependencies to ensure it works. 132 | 133 | spec.requires_arc = true 134 | 135 | # spec.xcconfig = { "HEADER_SEARCH_PATHS" => "$(SDKROOT)/usr/include/libxml2" } 136 | # spec.dependency "JSONKit", "~> 1.4" 137 | 138 | end 139 | -------------------------------------------------------------------------------- /TBModelTool.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 67FEFB101E558252009E485F /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 67FEFB0F1E558252009E485F /* main.m */; }; 11 | 67FEFB131E558252009E485F /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 67FEFB121E558252009E485F /* AppDelegate.m */; }; 12 | 67FEFB161E558252009E485F /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 67FEFB151E558252009E485F /* ViewController.m */; }; 13 | 67FEFB191E558252009E485F /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 67FEFB171E558252009E485F /* Main.storyboard */; }; 14 | 67FEFB1B1E558252009E485F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 67FEFB1A1E558252009E485F /* Assets.xcassets */; }; 15 | 67FEFB1E1E558252009E485F /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 67FEFB1C1E558252009E485F /* LaunchScreen.storyboard */; }; 16 | 67FEFB291E558252009E485F /* TBModelToolTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 67FEFB281E558252009E485F /* TBModelToolTests.m */; }; 17 | 67FEFB341E558252009E485F /* TBModelToolUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = 67FEFB331E558252009E485F /* TBModelToolUITests.m */; }; 18 | 67FEFB611E558268009E485F /* json.json in Resources */ = {isa = PBXBuildFile; fileRef = 67FEFB411E558268009E485F /* json.json */; }; 19 | 67FEFB621E558268009E485F /* Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 67FEFB431E558268009E485F /* Info.plist */; }; 20 | 67FEFB631E558268009E485F /* MJExtensionConst.m in Sources */ = {isa = PBXBuildFile; fileRef = 67FEFB461E558268009E485F /* MJExtensionConst.m */; }; 21 | 67FEFB641E558268009E485F /* MJFoundation.m in Sources */ = {isa = PBXBuildFile; fileRef = 67FEFB481E558268009E485F /* MJFoundation.m */; }; 22 | 67FEFB651E558268009E485F /* MJProperty.m in Sources */ = {isa = PBXBuildFile; fileRef = 67FEFB4A1E558268009E485F /* MJProperty.m */; }; 23 | 67FEFB661E558268009E485F /* MJPropertyKey.m in Sources */ = {isa = PBXBuildFile; fileRef = 67FEFB4C1E558268009E485F /* MJPropertyKey.m */; }; 24 | 67FEFB671E558268009E485F /* MJPropertyType.m in Sources */ = {isa = PBXBuildFile; fileRef = 67FEFB4E1E558268009E485F /* MJPropertyType.m */; }; 25 | 67FEFB681E558268009E485F /* NSObject+MJClass.m in Sources */ = {isa = PBXBuildFile; fileRef = 67FEFB501E558268009E485F /* NSObject+MJClass.m */; }; 26 | 67FEFB691E558268009E485F /* NSObject+MJCoding.m in Sources */ = {isa = PBXBuildFile; fileRef = 67FEFB521E558268009E485F /* NSObject+MJCoding.m */; }; 27 | 67FEFB6A1E558268009E485F /* NSObject+MJKeyValue.m in Sources */ = {isa = PBXBuildFile; fileRef = 67FEFB541E558268009E485F /* NSObject+MJKeyValue.m */; }; 28 | 67FEFB6B1E558268009E485F /* NSObject+MJProperty.m in Sources */ = {isa = PBXBuildFile; fileRef = 67FEFB561E558268009E485F /* NSObject+MJProperty.m */; }; 29 | 67FEFB6C1E558268009E485F /* NSString+MJExtension.m in Sources */ = {isa = PBXBuildFile; fileRef = 67FEFB581E558268009E485F /* NSString+MJExtension.m */; }; 30 | 67FEFB6D1E558268009E485F /* NSObject+YYModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 67FEFB5B1E558268009E485F /* NSObject+YYModel.m */; }; 31 | 67FEFB6E1E558268009E485F /* YYClassInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 67FEFB5D1E558268009E485F /* YYClassInfo.m */; }; 32 | 67FEFB731E558306009E485F /* TBModelTool.m in Sources */ = {isa = PBXBuildFile; fileRef = 67FEFB721E558306009E485F /* TBModelTool.m */; }; 33 | /* End PBXBuildFile section */ 34 | 35 | /* Begin PBXContainerItemProxy section */ 36 | 67FEFB251E558252009E485F /* PBXContainerItemProxy */ = { 37 | isa = PBXContainerItemProxy; 38 | containerPortal = 67FEFB031E558252009E485F /* Project object */; 39 | proxyType = 1; 40 | remoteGlobalIDString = 67FEFB0A1E558252009E485F; 41 | remoteInfo = TBModelTool; 42 | }; 43 | 67FEFB301E558252009E485F /* PBXContainerItemProxy */ = { 44 | isa = PBXContainerItemProxy; 45 | containerPortal = 67FEFB031E558252009E485F /* Project object */; 46 | proxyType = 1; 47 | remoteGlobalIDString = 67FEFB0A1E558252009E485F; 48 | remoteInfo = TBModelTool; 49 | }; 50 | /* End PBXContainerItemProxy section */ 51 | 52 | /* Begin PBXFileReference section */ 53 | 67FEFB0B1E558252009E485F /* TBModelTool.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TBModelTool.app; sourceTree = BUILT_PRODUCTS_DIR; }; 54 | 67FEFB0F1E558252009E485F /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 55 | 67FEFB111E558252009E485F /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 56 | 67FEFB121E558252009E485F /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 57 | 67FEFB141E558252009E485F /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 58 | 67FEFB151E558252009E485F /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 59 | 67FEFB181E558252009E485F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 60 | 67FEFB1A1E558252009E485F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 61 | 67FEFB1D1E558252009E485F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 62 | 67FEFB1F1E558252009E485F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 63 | 67FEFB241E558252009E485F /* TBModelToolTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TBModelToolTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 64 | 67FEFB281E558252009E485F /* TBModelToolTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TBModelToolTests.m; sourceTree = ""; }; 65 | 67FEFB2A1E558252009E485F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 66 | 67FEFB2F1E558252009E485F /* TBModelToolUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TBModelToolUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 67 | 67FEFB331E558252009E485F /* TBModelToolUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TBModelToolUITests.m; sourceTree = ""; }; 68 | 67FEFB351E558252009E485F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 69 | 67FEFB411E558268009E485F /* json.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = json.json; sourceTree = ""; }; 70 | 67FEFB431E558268009E485F /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 71 | 67FEFB441E558268009E485F /* MJExtension.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MJExtension.h; sourceTree = ""; }; 72 | 67FEFB451E558268009E485F /* MJExtensionConst.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MJExtensionConst.h; sourceTree = ""; }; 73 | 67FEFB461E558268009E485F /* MJExtensionConst.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MJExtensionConst.m; sourceTree = ""; }; 74 | 67FEFB471E558268009E485F /* MJFoundation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MJFoundation.h; sourceTree = ""; }; 75 | 67FEFB481E558268009E485F /* MJFoundation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MJFoundation.m; sourceTree = ""; }; 76 | 67FEFB491E558268009E485F /* MJProperty.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MJProperty.h; sourceTree = ""; }; 77 | 67FEFB4A1E558268009E485F /* MJProperty.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MJProperty.m; sourceTree = ""; }; 78 | 67FEFB4B1E558268009E485F /* MJPropertyKey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MJPropertyKey.h; sourceTree = ""; }; 79 | 67FEFB4C1E558268009E485F /* MJPropertyKey.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MJPropertyKey.m; sourceTree = ""; }; 80 | 67FEFB4D1E558268009E485F /* MJPropertyType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MJPropertyType.h; sourceTree = ""; }; 81 | 67FEFB4E1E558268009E485F /* MJPropertyType.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MJPropertyType.m; sourceTree = ""; }; 82 | 67FEFB4F1E558268009E485F /* NSObject+MJClass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+MJClass.h"; sourceTree = ""; }; 83 | 67FEFB501E558268009E485F /* NSObject+MJClass.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+MJClass.m"; sourceTree = ""; }; 84 | 67FEFB511E558268009E485F /* NSObject+MJCoding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+MJCoding.h"; sourceTree = ""; }; 85 | 67FEFB521E558268009E485F /* NSObject+MJCoding.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+MJCoding.m"; sourceTree = ""; }; 86 | 67FEFB531E558268009E485F /* NSObject+MJKeyValue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+MJKeyValue.h"; sourceTree = ""; }; 87 | 67FEFB541E558268009E485F /* NSObject+MJKeyValue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+MJKeyValue.m"; sourceTree = ""; }; 88 | 67FEFB551E558268009E485F /* NSObject+MJProperty.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+MJProperty.h"; sourceTree = ""; }; 89 | 67FEFB561E558268009E485F /* NSObject+MJProperty.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+MJProperty.m"; sourceTree = ""; }; 90 | 67FEFB571E558268009E485F /* NSString+MJExtension.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+MJExtension.h"; sourceTree = ""; }; 91 | 67FEFB581E558268009E485F /* NSString+MJExtension.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+MJExtension.m"; sourceTree = ""; }; 92 | 67FEFB5A1E558268009E485F /* NSObject+YYModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+YYModel.h"; sourceTree = ""; }; 93 | 67FEFB5B1E558268009E485F /* NSObject+YYModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+YYModel.m"; sourceTree = ""; }; 94 | 67FEFB5C1E558268009E485F /* YYClassInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YYClassInfo.h; sourceTree = ""; }; 95 | 67FEFB5D1E558268009E485F /* YYClassInfo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = YYClassInfo.m; sourceTree = ""; }; 96 | 67FEFB711E558306009E485F /* TBModelTool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TBModelTool.h; sourceTree = ""; }; 97 | 67FEFB721E558306009E485F /* TBModelTool.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TBModelTool.m; sourceTree = ""; }; 98 | /* End PBXFileReference section */ 99 | 100 | /* Begin PBXFrameworksBuildPhase section */ 101 | 67FEFB081E558252009E485F /* Frameworks */ = { 102 | isa = PBXFrameworksBuildPhase; 103 | buildActionMask = 2147483647; 104 | files = ( 105 | ); 106 | runOnlyForDeploymentPostprocessing = 0; 107 | }; 108 | 67FEFB211E558252009E485F /* Frameworks */ = { 109 | isa = PBXFrameworksBuildPhase; 110 | buildActionMask = 2147483647; 111 | files = ( 112 | ); 113 | runOnlyForDeploymentPostprocessing = 0; 114 | }; 115 | 67FEFB2C1E558252009E485F /* Frameworks */ = { 116 | isa = PBXFrameworksBuildPhase; 117 | buildActionMask = 2147483647; 118 | files = ( 119 | ); 120 | runOnlyForDeploymentPostprocessing = 0; 121 | }; 122 | /* End PBXFrameworksBuildPhase section */ 123 | 124 | /* Begin PBXGroup section */ 125 | 67FEFB021E558252009E485F = { 126 | isa = PBXGroup; 127 | children = ( 128 | 67FEFB0D1E558252009E485F /* TBModelTool */, 129 | 67FEFB271E558252009E485F /* TBModelToolTests */, 130 | 67FEFB321E558252009E485F /* TBModelToolUITests */, 131 | 67FEFB0C1E558252009E485F /* Products */, 132 | ); 133 | sourceTree = ""; 134 | }; 135 | 67FEFB0C1E558252009E485F /* Products */ = { 136 | isa = PBXGroup; 137 | children = ( 138 | 67FEFB0B1E558252009E485F /* TBModelTool.app */, 139 | 67FEFB241E558252009E485F /* TBModelToolTests.xctest */, 140 | 67FEFB2F1E558252009E485F /* TBModelToolUITests.xctest */, 141 | ); 142 | name = Products; 143 | sourceTree = ""; 144 | }; 145 | 67FEFB0D1E558252009E485F /* TBModelTool */ = { 146 | isa = PBXGroup; 147 | children = ( 148 | 67FEFB411E558268009E485F /* json.json */, 149 | D96654831E9F571A00A908F8 /* ModelToolFiles */, 150 | 67FEFB701E558306009E485F /* TBModelTool */, 151 | 67FEFB421E558268009E485F /* MJExtension */, 152 | 67FEFB591E558268009E485F /* Model */, 153 | 67FEFB111E558252009E485F /* AppDelegate.h */, 154 | 67FEFB121E558252009E485F /* AppDelegate.m */, 155 | 67FEFB141E558252009E485F /* ViewController.h */, 156 | 67FEFB151E558252009E485F /* ViewController.m */, 157 | 67FEFB171E558252009E485F /* Main.storyboard */, 158 | 67FEFB1A1E558252009E485F /* Assets.xcassets */, 159 | 67FEFB1C1E558252009E485F /* LaunchScreen.storyboard */, 160 | 67FEFB1F1E558252009E485F /* Info.plist */, 161 | 67FEFB0E1E558252009E485F /* Supporting Files */, 162 | ); 163 | path = TBModelTool; 164 | sourceTree = ""; 165 | }; 166 | 67FEFB0E1E558252009E485F /* Supporting Files */ = { 167 | isa = PBXGroup; 168 | children = ( 169 | 67FEFB0F1E558252009E485F /* main.m */, 170 | ); 171 | name = "Supporting Files"; 172 | sourceTree = ""; 173 | }; 174 | 67FEFB271E558252009E485F /* TBModelToolTests */ = { 175 | isa = PBXGroup; 176 | children = ( 177 | 67FEFB281E558252009E485F /* TBModelToolTests.m */, 178 | 67FEFB2A1E558252009E485F /* Info.plist */, 179 | ); 180 | path = TBModelToolTests; 181 | sourceTree = ""; 182 | }; 183 | 67FEFB321E558252009E485F /* TBModelToolUITests */ = { 184 | isa = PBXGroup; 185 | children = ( 186 | 67FEFB331E558252009E485F /* TBModelToolUITests.m */, 187 | 67FEFB351E558252009E485F /* Info.plist */, 188 | ); 189 | path = TBModelToolUITests; 190 | sourceTree = ""; 191 | }; 192 | 67FEFB421E558268009E485F /* MJExtension */ = { 193 | isa = PBXGroup; 194 | children = ( 195 | 67FEFB431E558268009E485F /* Info.plist */, 196 | 67FEFB441E558268009E485F /* MJExtension.h */, 197 | 67FEFB451E558268009E485F /* MJExtensionConst.h */, 198 | 67FEFB461E558268009E485F /* MJExtensionConst.m */, 199 | 67FEFB471E558268009E485F /* MJFoundation.h */, 200 | 67FEFB481E558268009E485F /* MJFoundation.m */, 201 | 67FEFB491E558268009E485F /* MJProperty.h */, 202 | 67FEFB4A1E558268009E485F /* MJProperty.m */, 203 | 67FEFB4B1E558268009E485F /* MJPropertyKey.h */, 204 | 67FEFB4C1E558268009E485F /* MJPropertyKey.m */, 205 | 67FEFB4D1E558268009E485F /* MJPropertyType.h */, 206 | 67FEFB4E1E558268009E485F /* MJPropertyType.m */, 207 | 67FEFB4F1E558268009E485F /* NSObject+MJClass.h */, 208 | 67FEFB501E558268009E485F /* NSObject+MJClass.m */, 209 | 67FEFB511E558268009E485F /* NSObject+MJCoding.h */, 210 | 67FEFB521E558268009E485F /* NSObject+MJCoding.m */, 211 | 67FEFB531E558268009E485F /* NSObject+MJKeyValue.h */, 212 | 67FEFB541E558268009E485F /* NSObject+MJKeyValue.m */, 213 | 67FEFB551E558268009E485F /* NSObject+MJProperty.h */, 214 | 67FEFB561E558268009E485F /* NSObject+MJProperty.m */, 215 | 67FEFB571E558268009E485F /* NSString+MJExtension.h */, 216 | 67FEFB581E558268009E485F /* NSString+MJExtension.m */, 217 | ); 218 | path = MJExtension; 219 | sourceTree = ""; 220 | }; 221 | 67FEFB591E558268009E485F /* Model */ = { 222 | isa = PBXGroup; 223 | children = ( 224 | 67FEFB5A1E558268009E485F /* NSObject+YYModel.h */, 225 | 67FEFB5B1E558268009E485F /* NSObject+YYModel.m */, 226 | 67FEFB5C1E558268009E485F /* YYClassInfo.h */, 227 | 67FEFB5D1E558268009E485F /* YYClassInfo.m */, 228 | ); 229 | path = Model; 230 | sourceTree = ""; 231 | }; 232 | 67FEFB701E558306009E485F /* TBModelTool */ = { 233 | isa = PBXGroup; 234 | children = ( 235 | 67FEFB711E558306009E485F /* TBModelTool.h */, 236 | 67FEFB721E558306009E485F /* TBModelTool.m */, 237 | ); 238 | path = TBModelTool; 239 | sourceTree = ""; 240 | }; 241 | D96654831E9F571A00A908F8 /* ModelToolFiles */ = { 242 | isa = PBXGroup; 243 | children = ( 244 | ); 245 | path = ModelToolFiles; 246 | sourceTree = ""; 247 | }; 248 | /* End PBXGroup section */ 249 | 250 | /* Begin PBXNativeTarget section */ 251 | 67FEFB0A1E558252009E485F /* TBModelTool */ = { 252 | isa = PBXNativeTarget; 253 | buildConfigurationList = 67FEFB381E558252009E485F /* Build configuration list for PBXNativeTarget "TBModelTool" */; 254 | buildPhases = ( 255 | 67FEFB071E558252009E485F /* Sources */, 256 | 67FEFB081E558252009E485F /* Frameworks */, 257 | 67FEFB091E558252009E485F /* Resources */, 258 | ); 259 | buildRules = ( 260 | ); 261 | dependencies = ( 262 | ); 263 | name = TBModelTool; 264 | productName = TBModelTool; 265 | productReference = 67FEFB0B1E558252009E485F /* TBModelTool.app */; 266 | productType = "com.apple.product-type.application"; 267 | }; 268 | 67FEFB231E558252009E485F /* TBModelToolTests */ = { 269 | isa = PBXNativeTarget; 270 | buildConfigurationList = 67FEFB3B1E558252009E485F /* Build configuration list for PBXNativeTarget "TBModelToolTests" */; 271 | buildPhases = ( 272 | 67FEFB201E558252009E485F /* Sources */, 273 | 67FEFB211E558252009E485F /* Frameworks */, 274 | 67FEFB221E558252009E485F /* Resources */, 275 | ); 276 | buildRules = ( 277 | ); 278 | dependencies = ( 279 | 67FEFB261E558252009E485F /* PBXTargetDependency */, 280 | ); 281 | name = TBModelToolTests; 282 | productName = TBModelToolTests; 283 | productReference = 67FEFB241E558252009E485F /* TBModelToolTests.xctest */; 284 | productType = "com.apple.product-type.bundle.unit-test"; 285 | }; 286 | 67FEFB2E1E558252009E485F /* TBModelToolUITests */ = { 287 | isa = PBXNativeTarget; 288 | buildConfigurationList = 67FEFB3E1E558252009E485F /* Build configuration list for PBXNativeTarget "TBModelToolUITests" */; 289 | buildPhases = ( 290 | 67FEFB2B1E558252009E485F /* Sources */, 291 | 67FEFB2C1E558252009E485F /* Frameworks */, 292 | 67FEFB2D1E558252009E485F /* Resources */, 293 | ); 294 | buildRules = ( 295 | ); 296 | dependencies = ( 297 | 67FEFB311E558252009E485F /* PBXTargetDependency */, 298 | ); 299 | name = TBModelToolUITests; 300 | productName = TBModelToolUITests; 301 | productReference = 67FEFB2F1E558252009E485F /* TBModelToolUITests.xctest */; 302 | productType = "com.apple.product-type.bundle.ui-testing"; 303 | }; 304 | /* End PBXNativeTarget section */ 305 | 306 | /* Begin PBXProject section */ 307 | 67FEFB031E558252009E485F /* Project object */ = { 308 | isa = PBXProject; 309 | attributes = { 310 | LastUpgradeCheck = 0820; 311 | ORGANIZATIONNAME = tangbin; 312 | TargetAttributes = { 313 | 67FEFB0A1E558252009E485F = { 314 | CreatedOnToolsVersion = 8.2.1; 315 | DevelopmentTeam = 676W74C3D4; 316 | ProvisioningStyle = Automatic; 317 | }; 318 | 67FEFB231E558252009E485F = { 319 | CreatedOnToolsVersion = 8.2.1; 320 | DevelopmentTeam = 2Q4XA4B6CU; 321 | ProvisioningStyle = Automatic; 322 | TestTargetID = 67FEFB0A1E558252009E485F; 323 | }; 324 | 67FEFB2E1E558252009E485F = { 325 | CreatedOnToolsVersion = 8.2.1; 326 | DevelopmentTeam = 2Q4XA4B6CU; 327 | ProvisioningStyle = Automatic; 328 | TestTargetID = 67FEFB0A1E558252009E485F; 329 | }; 330 | }; 331 | }; 332 | buildConfigurationList = 67FEFB061E558252009E485F /* Build configuration list for PBXProject "TBModelTool" */; 333 | compatibilityVersion = "Xcode 3.2"; 334 | developmentRegion = English; 335 | hasScannedForEncodings = 0; 336 | knownRegions = ( 337 | English, 338 | en, 339 | Base, 340 | ); 341 | mainGroup = 67FEFB021E558252009E485F; 342 | productRefGroup = 67FEFB0C1E558252009E485F /* Products */; 343 | projectDirPath = ""; 344 | projectRoot = ""; 345 | targets = ( 346 | 67FEFB0A1E558252009E485F /* TBModelTool */, 347 | 67FEFB231E558252009E485F /* TBModelToolTests */, 348 | 67FEFB2E1E558252009E485F /* TBModelToolUITests */, 349 | ); 350 | }; 351 | /* End PBXProject section */ 352 | 353 | /* Begin PBXResourcesBuildPhase section */ 354 | 67FEFB091E558252009E485F /* Resources */ = { 355 | isa = PBXResourcesBuildPhase; 356 | buildActionMask = 2147483647; 357 | files = ( 358 | 67FEFB1E1E558252009E485F /* LaunchScreen.storyboard in Resources */, 359 | 67FEFB1B1E558252009E485F /* Assets.xcassets in Resources */, 360 | 67FEFB191E558252009E485F /* Main.storyboard in Resources */, 361 | 67FEFB621E558268009E485F /* Info.plist in Resources */, 362 | 67FEFB611E558268009E485F /* json.json in Resources */, 363 | ); 364 | runOnlyForDeploymentPostprocessing = 0; 365 | }; 366 | 67FEFB221E558252009E485F /* Resources */ = { 367 | isa = PBXResourcesBuildPhase; 368 | buildActionMask = 2147483647; 369 | files = ( 370 | ); 371 | runOnlyForDeploymentPostprocessing = 0; 372 | }; 373 | 67FEFB2D1E558252009E485F /* Resources */ = { 374 | isa = PBXResourcesBuildPhase; 375 | buildActionMask = 2147483647; 376 | files = ( 377 | ); 378 | runOnlyForDeploymentPostprocessing = 0; 379 | }; 380 | /* End PBXResourcesBuildPhase section */ 381 | 382 | /* Begin PBXSourcesBuildPhase section */ 383 | 67FEFB071E558252009E485F /* Sources */ = { 384 | isa = PBXSourcesBuildPhase; 385 | buildActionMask = 2147483647; 386 | files = ( 387 | 67FEFB681E558268009E485F /* NSObject+MJClass.m in Sources */, 388 | 67FEFB631E558268009E485F /* MJExtensionConst.m in Sources */, 389 | 67FEFB671E558268009E485F /* MJPropertyType.m in Sources */, 390 | 67FEFB661E558268009E485F /* MJPropertyKey.m in Sources */, 391 | 67FEFB6A1E558268009E485F /* NSObject+MJKeyValue.m in Sources */, 392 | 67FEFB691E558268009E485F /* NSObject+MJCoding.m in Sources */, 393 | 67FEFB6C1E558268009E485F /* NSString+MJExtension.m in Sources */, 394 | 67FEFB651E558268009E485F /* MJProperty.m in Sources */, 395 | 67FEFB6B1E558268009E485F /* NSObject+MJProperty.m in Sources */, 396 | 67FEFB6D1E558268009E485F /* NSObject+YYModel.m in Sources */, 397 | 67FEFB161E558252009E485F /* ViewController.m in Sources */, 398 | 67FEFB131E558252009E485F /* AppDelegate.m in Sources */, 399 | 67FEFB641E558268009E485F /* MJFoundation.m in Sources */, 400 | 67FEFB731E558306009E485F /* TBModelTool.m in Sources */, 401 | 67FEFB6E1E558268009E485F /* YYClassInfo.m in Sources */, 402 | 67FEFB101E558252009E485F /* main.m in Sources */, 403 | ); 404 | runOnlyForDeploymentPostprocessing = 0; 405 | }; 406 | 67FEFB201E558252009E485F /* Sources */ = { 407 | isa = PBXSourcesBuildPhase; 408 | buildActionMask = 2147483647; 409 | files = ( 410 | 67FEFB291E558252009E485F /* TBModelToolTests.m in Sources */, 411 | ); 412 | runOnlyForDeploymentPostprocessing = 0; 413 | }; 414 | 67FEFB2B1E558252009E485F /* Sources */ = { 415 | isa = PBXSourcesBuildPhase; 416 | buildActionMask = 2147483647; 417 | files = ( 418 | 67FEFB341E558252009E485F /* TBModelToolUITests.m in Sources */, 419 | ); 420 | runOnlyForDeploymentPostprocessing = 0; 421 | }; 422 | /* End PBXSourcesBuildPhase section */ 423 | 424 | /* Begin PBXTargetDependency section */ 425 | 67FEFB261E558252009E485F /* PBXTargetDependency */ = { 426 | isa = PBXTargetDependency; 427 | target = 67FEFB0A1E558252009E485F /* TBModelTool */; 428 | targetProxy = 67FEFB251E558252009E485F /* PBXContainerItemProxy */; 429 | }; 430 | 67FEFB311E558252009E485F /* PBXTargetDependency */ = { 431 | isa = PBXTargetDependency; 432 | target = 67FEFB0A1E558252009E485F /* TBModelTool */; 433 | targetProxy = 67FEFB301E558252009E485F /* PBXContainerItemProxy */; 434 | }; 435 | /* End PBXTargetDependency section */ 436 | 437 | /* Begin PBXVariantGroup section */ 438 | 67FEFB171E558252009E485F /* Main.storyboard */ = { 439 | isa = PBXVariantGroup; 440 | children = ( 441 | 67FEFB181E558252009E485F /* Base */, 442 | ); 443 | name = Main.storyboard; 444 | sourceTree = ""; 445 | }; 446 | 67FEFB1C1E558252009E485F /* LaunchScreen.storyboard */ = { 447 | isa = PBXVariantGroup; 448 | children = ( 449 | 67FEFB1D1E558252009E485F /* Base */, 450 | ); 451 | name = LaunchScreen.storyboard; 452 | sourceTree = ""; 453 | }; 454 | /* End PBXVariantGroup section */ 455 | 456 | /* Begin XCBuildConfiguration section */ 457 | 67FEFB361E558252009E485F /* Debug */ = { 458 | isa = XCBuildConfiguration; 459 | buildSettings = { 460 | ALWAYS_SEARCH_USER_PATHS = NO; 461 | CLANG_ANALYZER_NONNULL = YES; 462 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 463 | CLANG_CXX_LIBRARY = "libc++"; 464 | CLANG_ENABLE_MODULES = YES; 465 | CLANG_ENABLE_OBJC_ARC = YES; 466 | CLANG_WARN_BOOL_CONVERSION = YES; 467 | CLANG_WARN_CONSTANT_CONVERSION = YES; 468 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 469 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 470 | CLANG_WARN_EMPTY_BODY = YES; 471 | CLANG_WARN_ENUM_CONVERSION = YES; 472 | CLANG_WARN_INFINITE_RECURSION = YES; 473 | CLANG_WARN_INT_CONVERSION = YES; 474 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 475 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 476 | CLANG_WARN_UNREACHABLE_CODE = YES; 477 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 478 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 479 | COPY_PHASE_STRIP = NO; 480 | DEBUG_INFORMATION_FORMAT = dwarf; 481 | ENABLE_STRICT_OBJC_MSGSEND = YES; 482 | ENABLE_TESTABILITY = YES; 483 | GCC_C_LANGUAGE_STANDARD = gnu99; 484 | GCC_DYNAMIC_NO_PIC = NO; 485 | GCC_NO_COMMON_BLOCKS = YES; 486 | GCC_OPTIMIZATION_LEVEL = 0; 487 | GCC_PREPROCESSOR_DEFINITIONS = ( 488 | "DEBUG=1", 489 | "$(inherited)", 490 | ); 491 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 492 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 493 | GCC_WARN_UNDECLARED_SELECTOR = YES; 494 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 495 | GCC_WARN_UNUSED_FUNCTION = YES; 496 | GCC_WARN_UNUSED_VARIABLE = YES; 497 | IPHONEOS_DEPLOYMENT_TARGET = 10.2; 498 | MTL_ENABLE_DEBUG_INFO = YES; 499 | ONLY_ACTIVE_ARCH = YES; 500 | SDKROOT = iphoneos; 501 | }; 502 | name = Debug; 503 | }; 504 | 67FEFB371E558252009E485F /* Release */ = { 505 | isa = XCBuildConfiguration; 506 | buildSettings = { 507 | ALWAYS_SEARCH_USER_PATHS = NO; 508 | CLANG_ANALYZER_NONNULL = YES; 509 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 510 | CLANG_CXX_LIBRARY = "libc++"; 511 | CLANG_ENABLE_MODULES = YES; 512 | CLANG_ENABLE_OBJC_ARC = YES; 513 | CLANG_WARN_BOOL_CONVERSION = YES; 514 | CLANG_WARN_CONSTANT_CONVERSION = YES; 515 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 516 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 517 | CLANG_WARN_EMPTY_BODY = YES; 518 | CLANG_WARN_ENUM_CONVERSION = YES; 519 | CLANG_WARN_INFINITE_RECURSION = YES; 520 | CLANG_WARN_INT_CONVERSION = YES; 521 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 522 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 523 | CLANG_WARN_UNREACHABLE_CODE = YES; 524 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 525 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 526 | COPY_PHASE_STRIP = NO; 527 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 528 | ENABLE_NS_ASSERTIONS = NO; 529 | ENABLE_STRICT_OBJC_MSGSEND = YES; 530 | GCC_C_LANGUAGE_STANDARD = gnu99; 531 | GCC_NO_COMMON_BLOCKS = YES; 532 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 533 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 534 | GCC_WARN_UNDECLARED_SELECTOR = YES; 535 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 536 | GCC_WARN_UNUSED_FUNCTION = YES; 537 | GCC_WARN_UNUSED_VARIABLE = YES; 538 | IPHONEOS_DEPLOYMENT_TARGET = 10.2; 539 | MTL_ENABLE_DEBUG_INFO = NO; 540 | SDKROOT = iphoneos; 541 | VALIDATE_PRODUCT = YES; 542 | }; 543 | name = Release; 544 | }; 545 | 67FEFB391E558252009E485F /* Debug */ = { 546 | isa = XCBuildConfiguration; 547 | buildSettings = { 548 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 549 | DEVELOPMENT_TEAM = 676W74C3D4; 550 | INFOPLIST_FILE = TBModelTool/Info.plist; 551 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 552 | PRODUCT_BUNDLE_IDENTIFIER = com.xd.666; 553 | PRODUCT_NAME = "$(TARGET_NAME)"; 554 | }; 555 | name = Debug; 556 | }; 557 | 67FEFB3A1E558252009E485F /* Release */ = { 558 | isa = XCBuildConfiguration; 559 | buildSettings = { 560 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 561 | DEVELOPMENT_TEAM = 676W74C3D4; 562 | INFOPLIST_FILE = TBModelTool/Info.plist; 563 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 564 | PRODUCT_BUNDLE_IDENTIFIER = com.xd.666; 565 | PRODUCT_NAME = "$(TARGET_NAME)"; 566 | }; 567 | name = Release; 568 | }; 569 | 67FEFB3C1E558252009E485F /* Debug */ = { 570 | isa = XCBuildConfiguration; 571 | buildSettings = { 572 | BUNDLE_LOADER = "$(TEST_HOST)"; 573 | DEVELOPMENT_TEAM = 2Q4XA4B6CU; 574 | INFOPLIST_FILE = TBModelToolTests/Info.plist; 575 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 576 | PRODUCT_BUNDLE_IDENTIFIER = Posun.TBModelToolTests; 577 | PRODUCT_NAME = "$(TARGET_NAME)"; 578 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/TBModelTool.app/TBModelTool"; 579 | }; 580 | name = Debug; 581 | }; 582 | 67FEFB3D1E558252009E485F /* Release */ = { 583 | isa = XCBuildConfiguration; 584 | buildSettings = { 585 | BUNDLE_LOADER = "$(TEST_HOST)"; 586 | DEVELOPMENT_TEAM = 2Q4XA4B6CU; 587 | INFOPLIST_FILE = TBModelToolTests/Info.plist; 588 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 589 | PRODUCT_BUNDLE_IDENTIFIER = Posun.TBModelToolTests; 590 | PRODUCT_NAME = "$(TARGET_NAME)"; 591 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/TBModelTool.app/TBModelTool"; 592 | }; 593 | name = Release; 594 | }; 595 | 67FEFB3F1E558252009E485F /* Debug */ = { 596 | isa = XCBuildConfiguration; 597 | buildSettings = { 598 | DEVELOPMENT_TEAM = 2Q4XA4B6CU; 599 | INFOPLIST_FILE = TBModelToolUITests/Info.plist; 600 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 601 | PRODUCT_BUNDLE_IDENTIFIER = Posun.TBModelToolUITests; 602 | PRODUCT_NAME = "$(TARGET_NAME)"; 603 | TEST_TARGET_NAME = TBModelTool; 604 | }; 605 | name = Debug; 606 | }; 607 | 67FEFB401E558252009E485F /* Release */ = { 608 | isa = XCBuildConfiguration; 609 | buildSettings = { 610 | DEVELOPMENT_TEAM = 2Q4XA4B6CU; 611 | INFOPLIST_FILE = TBModelToolUITests/Info.plist; 612 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 613 | PRODUCT_BUNDLE_IDENTIFIER = Posun.TBModelToolUITests; 614 | PRODUCT_NAME = "$(TARGET_NAME)"; 615 | TEST_TARGET_NAME = TBModelTool; 616 | }; 617 | name = Release; 618 | }; 619 | /* End XCBuildConfiguration section */ 620 | 621 | /* Begin XCConfigurationList section */ 622 | 67FEFB061E558252009E485F /* Build configuration list for PBXProject "TBModelTool" */ = { 623 | isa = XCConfigurationList; 624 | buildConfigurations = ( 625 | 67FEFB361E558252009E485F /* Debug */, 626 | 67FEFB371E558252009E485F /* Release */, 627 | ); 628 | defaultConfigurationIsVisible = 0; 629 | defaultConfigurationName = Release; 630 | }; 631 | 67FEFB381E558252009E485F /* Build configuration list for PBXNativeTarget "TBModelTool" */ = { 632 | isa = XCConfigurationList; 633 | buildConfigurations = ( 634 | 67FEFB391E558252009E485F /* Debug */, 635 | 67FEFB3A1E558252009E485F /* Release */, 636 | ); 637 | defaultConfigurationIsVisible = 0; 638 | defaultConfigurationName = Release; 639 | }; 640 | 67FEFB3B1E558252009E485F /* Build configuration list for PBXNativeTarget "TBModelToolTests" */ = { 641 | isa = XCConfigurationList; 642 | buildConfigurations = ( 643 | 67FEFB3C1E558252009E485F /* Debug */, 644 | 67FEFB3D1E558252009E485F /* Release */, 645 | ); 646 | defaultConfigurationIsVisible = 0; 647 | defaultConfigurationName = Release; 648 | }; 649 | 67FEFB3E1E558252009E485F /* Build configuration list for PBXNativeTarget "TBModelToolUITests" */ = { 650 | isa = XCConfigurationList; 651 | buildConfigurations = ( 652 | 67FEFB3F1E558252009E485F /* Debug */, 653 | 67FEFB401E558252009E485F /* Release */, 654 | ); 655 | defaultConfigurationIsVisible = 0; 656 | defaultConfigurationName = Release; 657 | }; 658 | /* End XCConfigurationList section */ 659 | }; 660 | rootObject = 67FEFB031E558252009E485F /* Project object */; 661 | } 662 | -------------------------------------------------------------------------------- /TBModelTool.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /TBModelTool.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /TBModelTool.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BuildSystemType 6 | Original 7 | 8 | 9 | -------------------------------------------------------------------------------- /TBModelTool.xcodeproj/project.xcworkspace/xcuserdata/ducktobey.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MSTGit/TBModelTool/902619addc1d0cd1152274c912362de76c043452/TBModelTool.xcodeproj/project.xcworkspace/xcuserdata/ducktobey.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /TBModelTool.xcodeproj/project.xcworkspace/xcuserdata/ducktobey.xcuserdatad/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BuildLocationStyle 6 | UseAppPreferences 7 | CustomBuildLocationType 8 | RelativeToDerivedData 9 | DerivedDataLocationStyle 10 | Default 11 | EnabledFullIndexStoreVisibility 12 | 13 | IssueFilterStyle 14 | ShowActiveSchemeOnly 15 | LiveSourceIssuesEnabled 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /TBModelTool.xcodeproj/project.xcworkspace/xcuserdata/tangbin.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MSTGit/TBModelTool/902619addc1d0cd1152274c912362de76c043452/TBModelTool.xcodeproj/project.xcworkspace/xcuserdata/tangbin.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /TBModelTool.xcodeproj/xcuserdata/ducktobey.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /TBModelTool.xcodeproj/xcuserdata/ducktobey.xcuserdatad/xcschemes/TBModelTool.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 | -------------------------------------------------------------------------------- /TBModelTool.xcodeproj/xcuserdata/ducktobey.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | TBModelTool.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 67FEFB0A1E558252009E485F 16 | 17 | primary 18 | 19 | 20 | 67FEFB231E558252009E485F 21 | 22 | primary 23 | 24 | 25 | 67FEFB2E1E558252009E485F 26 | 27 | primary 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /TBModelTool.xcodeproj/xcuserdata/tangbin.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /TBModelTool.xcodeproj/xcuserdata/tangbin.xcuserdatad/xcschemes/TBModelTool.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 | -------------------------------------------------------------------------------- /TBModelTool.xcodeproj/xcuserdata/tangbin.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | TBModelTool.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 67FEFB0A1E558252009E485F 16 | 17 | primary 18 | 19 | 20 | 67FEFB231E558252009E485F 21 | 22 | primary 23 | 24 | 25 | 67FEFB2E1E558252009E485F 26 | 27 | primary 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /TBModelTool/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // TBModelTool 4 | // 5 | // Created by tangbin on 2017/2/16. 6 | // Copyright © 2017年 tangbin. 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 | -------------------------------------------------------------------------------- /TBModelTool/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // TBModelTool 4 | // 5 | // Created by tangbin on 2017/2/16. 6 | // Copyright © 2017年 tangbin. 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 | 24 | - (void)applicationWillResignActive:(UIApplication *)application { 25 | // 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. 26 | } 27 | 28 | 29 | - (void)applicationDidEnterBackground:(UIApplication *)application { 30 | // 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. 31 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 32 | } 33 | 34 | 35 | - (void)applicationWillEnterForeground:(UIApplication *)application { 36 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 37 | } 38 | 39 | 40 | - (void)applicationDidBecomeActive:(UIApplication *)application { 41 | // 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. 42 | } 43 | 44 | 45 | - (void)applicationWillTerminate:(UIApplication *)application { 46 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 47 | } 48 | 49 | 50 | @end 51 | -------------------------------------------------------------------------------- /TBModelTool/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 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /TBModelTool/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 | -------------------------------------------------------------------------------- /TBModelTool/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 | 34 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /TBModelTool/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 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /TBModelTool/MJExtension/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 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /TBModelTool/MJExtension/MJExtension.h: -------------------------------------------------------------------------------- 1 | // 2 | // MJExtension.h 3 | // MJExtension 4 | // 5 | // Created by mj on 14-1-15. 6 | // Copyright (c) 2014年 小码哥. All rights reserved. 7 | // 代码地址:https://github.com/CoderMJLee/MJExtension 8 | // 代码地址:http://code4app.com/ios/%E5%AD%97%E5%85%B8-JSON-%E4%B8%8E%E6%A8%A1%E5%9E%8B%E7%9A%84%E8%BD%AC%E6%8D%A2/5339992a933bf062608b4c57 9 | 10 | #import "NSObject+MJCoding.h" 11 | #import "NSObject+MJProperty.h" 12 | #import "NSObject+MJClass.h" 13 | #import "NSObject+MJKeyValue.h" 14 | #import "NSString+MJExtension.h" 15 | #import "MJExtensionConst.h" -------------------------------------------------------------------------------- /TBModelTool/MJExtension/MJExtensionConst.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef __MJExtensionConst__H__ 3 | #define __MJExtensionConst__H__ 4 | 5 | #import 6 | 7 | // 过期 8 | #define MJExtensionDeprecated(instead) NS_DEPRECATED(2_0, 2_0, 2_0, 2_0, instead) 9 | 10 | // 构建错误 11 | #define MJExtensionBuildError(clazz, msg) \ 12 | NSError *error = [NSError errorWithDomain:msg code:250 userInfo:nil]; \ 13 | [clazz setMj_error:error]; 14 | 15 | // 日志输出 16 | #ifdef DEBUG 17 | #define MJExtensionLog(...) NSLog(__VA_ARGS__) 18 | #else 19 | #define MJExtensionLog(...) 20 | #endif 21 | 22 | /** 23 | * 断言 24 | * @param condition 条件 25 | * @param returnValue 返回值 26 | */ 27 | #define MJExtensionAssertError(condition, returnValue, clazz, msg) \ 28 | [clazz setMj_error:nil]; \ 29 | if ((condition) == NO) { \ 30 | MJExtensionBuildError(clazz, msg); \ 31 | return returnValue;\ 32 | } 33 | 34 | #define MJExtensionAssert2(condition, returnValue) \ 35 | if ((condition) == NO) return returnValue; 36 | 37 | /** 38 | * 断言 39 | * @param condition 条件 40 | */ 41 | #define MJExtensionAssert(condition) MJExtensionAssert2(condition, ) 42 | 43 | /** 44 | * 断言 45 | * @param param 参数 46 | * @param returnValue 返回值 47 | */ 48 | #define MJExtensionAssertParamNotNil2(param, returnValue) \ 49 | MJExtensionAssert2((param) != nil, returnValue) 50 | 51 | /** 52 | * 断言 53 | * @param param 参数 54 | */ 55 | #define MJExtensionAssertParamNotNil(param) MJExtensionAssertParamNotNil2(param, ) 56 | 57 | /** 58 | * 打印所有的属性 59 | */ 60 | #define MJLogAllIvars \ 61 | -(NSString *)description \ 62 | { \ 63 | return [self mj_keyValues].description; \ 64 | } 65 | #define MJExtensionLogAllProperties MJLogAllIvars 66 | 67 | /** 68 | * 类型(属性类型) 69 | */ 70 | extern NSString *const MJPropertyTypeInt; 71 | extern NSString *const MJPropertyTypeShort; 72 | extern NSString *const MJPropertyTypeFloat; 73 | extern NSString *const MJPropertyTypeDouble; 74 | extern NSString *const MJPropertyTypeLong; 75 | extern NSString *const MJPropertyTypeLongLong; 76 | extern NSString *const MJPropertyTypeChar; 77 | extern NSString *const MJPropertyTypeBOOL1; 78 | extern NSString *const MJPropertyTypeBOOL2; 79 | extern NSString *const MJPropertyTypePointer; 80 | 81 | extern NSString *const MJPropertyTypeIvar; 82 | extern NSString *const MJPropertyTypeMethod; 83 | extern NSString *const MJPropertyTypeBlock; 84 | extern NSString *const MJPropertyTypeClass; 85 | extern NSString *const MJPropertyTypeSEL; 86 | extern NSString *const MJPropertyTypeId; 87 | 88 | #endif -------------------------------------------------------------------------------- /TBModelTool/MJExtension/MJExtensionConst.m: -------------------------------------------------------------------------------- 1 | #ifndef __MJExtensionConst__M__ 2 | #define __MJExtensionConst__M__ 3 | 4 | #import 5 | 6 | /** 7 | * 成员变量类型(属性类型) 8 | */ 9 | NSString *const MJPropertyTypeInt = @"i"; 10 | NSString *const MJPropertyTypeShort = @"s"; 11 | NSString *const MJPropertyTypeFloat = @"f"; 12 | NSString *const MJPropertyTypeDouble = @"d"; 13 | NSString *const MJPropertyTypeLong = @"l"; 14 | NSString *const MJPropertyTypeLongLong = @"q"; 15 | NSString *const MJPropertyTypeChar = @"c"; 16 | NSString *const MJPropertyTypeBOOL1 = @"c"; 17 | NSString *const MJPropertyTypeBOOL2 = @"b"; 18 | NSString *const MJPropertyTypePointer = @"*"; 19 | 20 | NSString *const MJPropertyTypeIvar = @"^{objc_ivar=}"; 21 | NSString *const MJPropertyTypeMethod = @"^{objc_method=}"; 22 | NSString *const MJPropertyTypeBlock = @"@?"; 23 | NSString *const MJPropertyTypeClass = @"#"; 24 | NSString *const MJPropertyTypeSEL = @":"; 25 | NSString *const MJPropertyTypeId = @"@"; 26 | 27 | #endif -------------------------------------------------------------------------------- /TBModelTool/MJExtension/MJFoundation.h: -------------------------------------------------------------------------------- 1 | // 2 | // MJFoundation.h 3 | // MJExtensionExample 4 | // 5 | // Created by MJ Lee on 14/7/16. 6 | // Copyright (c) 2014年 小码哥. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface MJFoundation : NSObject 12 | + (BOOL)isClassFromFoundation:(Class)c; 13 | @end 14 | -------------------------------------------------------------------------------- /TBModelTool/MJExtension/MJFoundation.m: -------------------------------------------------------------------------------- 1 | // 2 | // MJFoundation.m 3 | // MJExtensionExample 4 | // 5 | // Created by MJ Lee on 14/7/16. 6 | // Copyright (c) 2014年 小码哥. All rights reserved. 7 | // 8 | 9 | #import "MJFoundation.h" 10 | #import "MJExtensionConst.h" 11 | #import 12 | 13 | static NSSet *foundationClasses_; 14 | 15 | @implementation MJFoundation 16 | 17 | + (NSSet *)foundationClasses 18 | { 19 | if (foundationClasses_ == nil) { 20 | // 集合中没有NSObject,因为几乎所有的类都是继承自NSObject,具体是不是NSObject需要特殊判断 21 | foundationClasses_ = [NSSet setWithObjects: 22 | [NSURL class], 23 | [NSDate class], 24 | [NSValue class], 25 | [NSData class], 26 | [NSError class], 27 | [NSArray class], 28 | [NSDictionary class], 29 | [NSString class], 30 | [NSAttributedString class], nil]; 31 | } 32 | return foundationClasses_; 33 | } 34 | 35 | + (BOOL)isClassFromFoundation:(Class)c 36 | { 37 | if (c == [NSObject class] || c == [NSManagedObject class]) return YES; 38 | 39 | __block BOOL result = NO; 40 | [[self foundationClasses] enumerateObjectsUsingBlock:^(Class foundationClass, BOOL *stop) { 41 | if ([c isSubclassOfClass:foundationClass]) { 42 | result = YES; 43 | *stop = YES; 44 | } 45 | }]; 46 | return result; 47 | } 48 | @end 49 | -------------------------------------------------------------------------------- /TBModelTool/MJExtension/MJProperty.h: -------------------------------------------------------------------------------- 1 | // 2 | // MJProperty.h 3 | // MJExtensionExample 4 | // 5 | // Created by MJ Lee on 15/4/17. 6 | // Copyright (c) 2015年 小码哥. All rights reserved. 7 | // 包装一个成员属性 8 | 9 | #import 10 | #import 11 | #import "MJPropertyType.h" 12 | #import "MJPropertyKey.h" 13 | 14 | /** 15 | * 包装一个成员 16 | */ 17 | @interface MJProperty : NSObject 18 | /** 成员属性 */ 19 | @property (nonatomic, assign) objc_property_t property; 20 | /** 成员属性的名字 */ 21 | @property (nonatomic, readonly) NSString *name; 22 | 23 | /** 成员属性的类型 */ 24 | @property (nonatomic, readonly) MJPropertyType *type; 25 | /** 成员属性来源于哪个类(可能是父类) */ 26 | @property (nonatomic, assign) Class srcClass; 27 | 28 | /**** 同一个成员属性 - 父类和子类的行为可能不一致(originKey、propertyKeys、objectClassInArray) ****/ 29 | /** 设置最原始的key */ 30 | - (void)setOriginKey:(id)originKey forClass:(Class)c; 31 | /** 对应着字典中的多级key(里面存放的数组,数组里面都是MJPropertyKey对象) */ 32 | - (NSArray *)propertyKeysForClass:(Class)c; 33 | 34 | /** 模型数组中的模型类型 */ 35 | - (void)setObjectClassInArray:(Class)objectClass forClass:(Class)c; 36 | - (Class)objectClassInArrayForClass:(Class)c; 37 | /**** 同一个成员变量 - 父类和子类的行为可能不一致(key、keys、objectClassInArray) ****/ 38 | 39 | /** 40 | * 设置object的成员变量值 41 | */ 42 | - (void)setValue:(id)value forObject:(id)object; 43 | /** 44 | * 得到object的成员属性值 45 | */ 46 | - (id)valueForObject:(id)object; 47 | 48 | /** 49 | * 初始化 50 | */ 51 | + (instancetype)cachedPropertyWithProperty:(objc_property_t)property; 52 | 53 | @end 54 | -------------------------------------------------------------------------------- /TBModelTool/MJExtension/MJProperty.m: -------------------------------------------------------------------------------- 1 | // 2 | // MJProperty.m 3 | // MJExtensionExample 4 | // 5 | // Created by MJ Lee on 15/4/17. 6 | // Copyright (c) 2015年 小码哥. All rights reserved. 7 | // 8 | 9 | #import "MJProperty.h" 10 | #import "MJFoundation.h" 11 | #import "MJExtensionConst.h" 12 | #import 13 | 14 | @interface MJProperty() 15 | @property (strong, nonatomic) NSMutableDictionary *propertyKeysDict; 16 | @property (strong, nonatomic) NSMutableDictionary *objectClassInArrayDict; 17 | @end 18 | 19 | @implementation MJProperty 20 | 21 | #pragma mark - 初始化 22 | - (instancetype)init 23 | { 24 | if (self = [super init]) { 25 | _propertyKeysDict = [NSMutableDictionary dictionary]; 26 | _objectClassInArrayDict = [NSMutableDictionary dictionary]; 27 | } 28 | return self; 29 | } 30 | 31 | #pragma mark - 缓存 32 | + (instancetype)cachedPropertyWithProperty:(objc_property_t)property 33 | { 34 | MJProperty *propertyObj = objc_getAssociatedObject(self, property); 35 | if (propertyObj == nil) { 36 | propertyObj = [[self alloc] init]; 37 | propertyObj.property = property; 38 | objc_setAssociatedObject(self, property, propertyObj, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 39 | } 40 | return propertyObj; 41 | } 42 | 43 | #pragma mark - 公共方法 44 | - (void)setProperty:(objc_property_t)property 45 | { 46 | _property = property; 47 | 48 | MJExtensionAssertParamNotNil(property); 49 | 50 | // 1.属性名 51 | _name = @(property_getName(property)); 52 | 53 | // 2.成员类型 54 | NSString *attrs = @(property_getAttributes(property)); 55 | NSUInteger dotLoc = [attrs rangeOfString:@","].location; 56 | NSString *code = nil; 57 | NSUInteger loc = 1; 58 | if (dotLoc == NSNotFound) { // 没有, 59 | code = [attrs substringFromIndex:loc]; 60 | } else { 61 | code = [attrs substringWithRange:NSMakeRange(loc, dotLoc - loc)]; 62 | } 63 | _type = [MJPropertyType cachedTypeWithCode:code]; 64 | } 65 | 66 | /** 67 | * 获得成员变量的值 68 | */ 69 | - (id)valueForObject:(id)object 70 | { 71 | if (self.type.KVCDisabled) return [NSNull null]; 72 | return [object valueForKey:self.name]; 73 | } 74 | 75 | /** 76 | * 设置成员变量的值 77 | */ 78 | - (void)setValue:(id)value forObject:(id)object 79 | { 80 | if (self.type.KVCDisabled || value == nil) return; 81 | [object setValue:value forKey:self.name]; 82 | } 83 | 84 | /** 85 | * 通过字符串key创建对应的keys 86 | */ 87 | - (NSArray *)propertyKeysWithStringKey:(NSString *)stringKey 88 | { 89 | if (stringKey.length == 0) return nil; 90 | 91 | NSMutableArray *propertyKeys = [NSMutableArray array]; 92 | // 如果有多级映射 93 | NSArray *oldKeys = [stringKey componentsSeparatedByString:@"."]; 94 | 95 | for (NSString *oldKey in oldKeys) { 96 | NSUInteger start = [oldKey rangeOfString:@"["].location; 97 | if (start != NSNotFound) { // 有索引的key 98 | NSString *prefixKey = [oldKey substringToIndex:start]; 99 | NSString *indexKey = prefixKey; 100 | if (prefixKey.length) { 101 | MJPropertyKey *propertyKey = [[MJPropertyKey alloc] init]; 102 | propertyKey.name = prefixKey; 103 | [propertyKeys addObject:propertyKey]; 104 | 105 | indexKey = [oldKey stringByReplacingOccurrencesOfString:prefixKey withString:@""]; 106 | } 107 | 108 | /** 解析索引 **/ 109 | // 元素 110 | NSArray *cmps = [[indexKey stringByReplacingOccurrencesOfString:@"[" withString:@""] componentsSeparatedByString:@"]"]; 111 | for (NSInteger i = 0; i 10 | 11 | typedef enum { 12 | MJPropertyKeyTypeDictionary = 0, // 字典的key 13 | MJPropertyKeyTypeArray // 数组的key 14 | } MJPropertyKeyType; 15 | 16 | /** 17 | * 属性的key 18 | */ 19 | @interface MJPropertyKey : NSObject 20 | /** key的名字 */ 21 | @property (copy, nonatomic) NSString *name; 22 | /** key的种类,可能是@"10",可能是@"age" */ 23 | @property (assign, nonatomic) MJPropertyKeyType type; 24 | 25 | /** 26 | * 根据当前的key,也就是name,从object(字典或者数组)中取值 27 | */ 28 | - (id)valueInObject:(id)object; 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /TBModelTool/MJExtension/MJPropertyKey.m: -------------------------------------------------------------------------------- 1 | // 2 | // MJPropertyKey.m 3 | // MJExtensionExample 4 | // 5 | // Created by MJ Lee on 15/8/11. 6 | // Copyright (c) 2015年 小码哥. All rights reserved. 7 | // 8 | 9 | #import "MJPropertyKey.h" 10 | 11 | @implementation MJPropertyKey 12 | 13 | - (id)valueInObject:(id)object 14 | { 15 | if ([object isKindOfClass:[NSDictionary class]] && self.type == MJPropertyKeyTypeDictionary) { 16 | return object[self.name]; 17 | } else if ([object isKindOfClass:[NSArray class]] && self.type == MJPropertyKeyTypeArray) { 18 | NSArray *array = object; 19 | NSUInteger index = self.name.intValue; 20 | if (index < array.count) return array[index]; 21 | return nil; 22 | } 23 | return nil; 24 | } 25 | @end 26 | -------------------------------------------------------------------------------- /TBModelTool/MJExtension/MJPropertyType.h: -------------------------------------------------------------------------------- 1 | // 2 | // MJPropertyType.h 3 | // MJExtension 4 | // 5 | // Created by mj on 14-1-15. 6 | // Copyright (c) 2014年 小码哥. All rights reserved. 7 | // 包装一种类型 8 | 9 | #import 10 | 11 | /** 12 | * 包装一种类型 13 | */ 14 | @interface MJPropertyType : NSObject 15 | /** 类型标识符 */ 16 | @property (nonatomic, copy) NSString *code; 17 | 18 | /** 是否为id类型 */ 19 | @property (nonatomic, readonly, getter=isIdType) BOOL idType; 20 | 21 | /** 是否为基本数字类型:int、float等 */ 22 | @property (nonatomic, readonly, getter=isNumberType) BOOL numberType; 23 | 24 | /** 是否为BOOL类型 */ 25 | @property (nonatomic, readonly, getter=isBoolType) BOOL boolType; 26 | 27 | /** 对象类型(如果是基本数据类型,此值为nil) */ 28 | @property (nonatomic, readonly) Class typeClass; 29 | 30 | /** 类型是否来自于Foundation框架,比如NSString、NSArray */ 31 | @property (nonatomic, readonly, getter = isFromFoundation) BOOL fromFoundation; 32 | /** 类型是否不支持KVC */ 33 | @property (nonatomic, readonly, getter = isKVCDisabled) BOOL KVCDisabled; 34 | 35 | /** 36 | * 获得缓存的类型对象 37 | */ 38 | + (instancetype)cachedTypeWithCode:(NSString *)code; 39 | @end -------------------------------------------------------------------------------- /TBModelTool/MJExtension/MJPropertyType.m: -------------------------------------------------------------------------------- 1 | // 2 | // MJPropertyType.m 3 | // MJExtension 4 | // 5 | // Created by mj on 14-1-15. 6 | // Copyright (c) 2014年 小码哥. All rights reserved. 7 | // 8 | 9 | #import "MJPropertyType.h" 10 | #import "MJExtension.h" 11 | #import "MJFoundation.h" 12 | #import "MJExtensionConst.h" 13 | 14 | @implementation MJPropertyType 15 | 16 | static NSMutableDictionary *types_; 17 | + (void)initialize 18 | { 19 | types_ = [NSMutableDictionary dictionary]; 20 | } 21 | 22 | + (instancetype)cachedTypeWithCode:(NSString *)code 23 | { 24 | MJExtensionAssertParamNotNil2(code, nil); 25 | @synchronized (self) { 26 | MJPropertyType *type = types_[code]; 27 | if (type == nil) { 28 | type = [[self alloc] init]; 29 | type.code = code; 30 | types_[code] = type; 31 | } 32 | return type; 33 | } 34 | } 35 | 36 | #pragma mark - 公共方法 37 | - (void)setCode:(NSString *)code 38 | { 39 | _code = code; 40 | 41 | MJExtensionAssertParamNotNil(code); 42 | 43 | if ([code isEqualToString:MJPropertyTypeId]) { 44 | _idType = YES; 45 | } else if (code.length == 0) { 46 | _KVCDisabled = YES; 47 | } else if (code.length > 3 && [code hasPrefix:@"@\""]) { 48 | // 去掉@"和",截取中间的类型名称 49 | _code = [code substringWithRange:NSMakeRange(2, code.length - 3)]; 50 | _typeClass = NSClassFromString(_code); 51 | _fromFoundation = [MJFoundation isClassFromFoundation:_typeClass]; 52 | _numberType = [_typeClass isSubclassOfClass:[NSNumber class]]; 53 | 54 | } else if ([code isEqualToString:MJPropertyTypeSEL] || 55 | [code isEqualToString:MJPropertyTypeIvar] || 56 | [code isEqualToString:MJPropertyTypeMethod]) { 57 | _KVCDisabled = YES; 58 | } 59 | 60 | // 是否为数字类型 61 | NSString *lowerCode = _code.lowercaseString; 62 | NSArray *numberTypes = @[MJPropertyTypeInt, MJPropertyTypeShort, MJPropertyTypeBOOL1, MJPropertyTypeBOOL2, MJPropertyTypeFloat, MJPropertyTypeDouble, MJPropertyTypeLong, MJPropertyTypeLongLong, MJPropertyTypeChar]; 63 | if ([numberTypes containsObject:lowerCode]) { 64 | _numberType = YES; 65 | 66 | if ([lowerCode isEqualToString:MJPropertyTypeBOOL1] 67 | || [lowerCode isEqualToString:MJPropertyTypeBOOL2]) { 68 | _boolType = YES; 69 | } 70 | } 71 | } 72 | @end 73 | -------------------------------------------------------------------------------- /TBModelTool/MJExtension/NSObject+MJClass.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+MJClass.h 3 | // MJExtensionExample 4 | // 5 | // Created by MJ Lee on 15/8/11. 6 | // Copyright (c) 2015年 小码哥. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | /** 12 | * 遍历所有类的block(父类) 13 | */ 14 | typedef void (^MJClassesEnumeration)(Class c, BOOL *stop); 15 | 16 | /** 这个数组中的属性名才会进行字典和模型的转换 */ 17 | typedef NSArray * (^MJAllowedPropertyNames)(); 18 | /** 这个数组中的属性名才会进行归档 */ 19 | typedef NSArray * (^MJAllowedCodingPropertyNames)(); 20 | 21 | /** 这个数组中的属性名将会被忽略:不进行字典和模型的转换 */ 22 | typedef NSArray * (^MJIgnoredPropertyNames)(); 23 | /** 这个数组中的属性名将会被忽略:不进行归档 */ 24 | typedef NSArray * (^MJIgnoredCodingPropertyNames)(); 25 | 26 | /** 27 | * 类相关的扩展 28 | */ 29 | @interface NSObject (MJClass) 30 | /** 31 | * 遍历所有的类 32 | */ 33 | + (void)mj_enumerateClasses:(MJClassesEnumeration)enumeration; 34 | + (void)mj_enumerateAllClasses:(MJClassesEnumeration)enumeration; 35 | 36 | #pragma mark - 属性白名单配置 37 | /** 38 | * 这个数组中的属性名才会进行字典和模型的转换 39 | * 40 | * @param allowedPropertyNames 这个数组中的属性名才会进行字典和模型的转换 41 | */ 42 | + (void)mj_setupAllowedPropertyNames:(MJAllowedPropertyNames)allowedPropertyNames; 43 | 44 | /** 45 | * 这个数组中的属性名才会进行字典和模型的转换 46 | */ 47 | + (NSMutableArray *)mj_totalAllowedPropertyNames; 48 | 49 | #pragma mark - 属性黑名单配置 50 | /** 51 | * 这个数组中的属性名将会被忽略:不进行字典和模型的转换 52 | * 53 | * @param ignoredPropertyNames 这个数组中的属性名将会被忽略:不进行字典和模型的转换 54 | */ 55 | + (void)mj_setupIgnoredPropertyNames:(MJIgnoredPropertyNames)ignoredPropertyNames; 56 | 57 | /** 58 | * 这个数组中的属性名将会被忽略:不进行字典和模型的转换 59 | */ 60 | + (NSMutableArray *)mj_totalIgnoredPropertyNames; 61 | 62 | #pragma mark - 归档属性白名单配置 63 | /** 64 | * 这个数组中的属性名才会进行归档 65 | * 66 | * @param allowedCodingPropertyNames 这个数组中的属性名才会进行归档 67 | */ 68 | + (void)mj_setupAllowedCodingPropertyNames:(MJAllowedCodingPropertyNames)allowedCodingPropertyNames; 69 | 70 | /** 71 | * 这个数组中的属性名才会进行字典和模型的转换 72 | */ 73 | + (NSMutableArray *)mj_totalAllowedCodingPropertyNames; 74 | 75 | #pragma mark - 归档属性黑名单配置 76 | /** 77 | * 这个数组中的属性名将会被忽略:不进行归档 78 | * 79 | * @param ignoredCodingPropertyNames 这个数组中的属性名将会被忽略:不进行归档 80 | */ 81 | + (void)mj_setupIgnoredCodingPropertyNames:(MJIgnoredCodingPropertyNames)ignoredCodingPropertyNames; 82 | 83 | /** 84 | * 这个数组中的属性名将会被忽略:不进行归档 85 | */ 86 | + (NSMutableArray *)mj_totalIgnoredCodingPropertyNames; 87 | 88 | #pragma mark - 内部使用 89 | + (void)mj_setupBlockReturnValue:(id (^)())block key:(const char *)key; 90 | @end 91 | -------------------------------------------------------------------------------- /TBModelTool/MJExtension/NSObject+MJClass.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+MJClass.m 3 | // MJExtensionExample 4 | // 5 | // Created by MJ Lee on 15/8/11. 6 | // Copyright (c) 2015年 小码哥. All rights reserved. 7 | // 8 | 9 | #import "NSObject+MJClass.h" 10 | #import "NSObject+MJCoding.h" 11 | #import "NSObject+MJKeyValue.h" 12 | #import "MJFoundation.h" 13 | #import 14 | 15 | static const char MJAllowedPropertyNamesKey = '\0'; 16 | static const char MJIgnoredPropertyNamesKey = '\0'; 17 | static const char MJAllowedCodingPropertyNamesKey = '\0'; 18 | static const char MJIgnoredCodingPropertyNamesKey = '\0'; 19 | 20 | static NSMutableDictionary *allowedPropertyNamesDict_; 21 | static NSMutableDictionary *ignoredPropertyNamesDict_; 22 | static NSMutableDictionary *allowedCodingPropertyNamesDict_; 23 | static NSMutableDictionary *ignoredCodingPropertyNamesDict_; 24 | 25 | @implementation NSObject (MJClass) 26 | 27 | + (void)load 28 | { 29 | allowedPropertyNamesDict_ = [NSMutableDictionary dictionary]; 30 | ignoredPropertyNamesDict_ = [NSMutableDictionary dictionary]; 31 | allowedCodingPropertyNamesDict_ = [NSMutableDictionary dictionary]; 32 | ignoredCodingPropertyNamesDict_ = [NSMutableDictionary dictionary]; 33 | } 34 | 35 | + (NSMutableDictionary *)dictForKey:(const void *)key 36 | { 37 | @synchronized (self) { 38 | if (key == &MJAllowedPropertyNamesKey) return allowedPropertyNamesDict_; 39 | if (key == &MJIgnoredPropertyNamesKey) return ignoredPropertyNamesDict_; 40 | if (key == &MJAllowedCodingPropertyNamesKey) return allowedCodingPropertyNamesDict_; 41 | if (key == &MJIgnoredCodingPropertyNamesKey) return ignoredCodingPropertyNamesDict_; 42 | return nil; 43 | } 44 | } 45 | 46 | + (void)mj_enumerateClasses:(MJClassesEnumeration)enumeration 47 | { 48 | // 1.没有block就直接返回 49 | if (enumeration == nil) return; 50 | 51 | // 2.停止遍历的标记 52 | BOOL stop = NO; 53 | 54 | // 3.当前正在遍历的类 55 | Class c = self; 56 | 57 | // 4.开始遍历每一个类 58 | while (c && !stop) { 59 | // 4.1.执行操作 60 | enumeration(c, &stop); 61 | 62 | // 4.2.获得父类 63 | c = class_getSuperclass(c); 64 | 65 | if ([MJFoundation isClassFromFoundation:c]) break; 66 | } 67 | } 68 | 69 | + (void)mj_enumerateAllClasses:(MJClassesEnumeration)enumeration 70 | { 71 | // 1.没有block就直接返回 72 | if (enumeration == nil) return; 73 | 74 | // 2.停止遍历的标记 75 | BOOL stop = NO; 76 | 77 | // 3.当前正在遍历的类 78 | Class c = self; 79 | 80 | // 4.开始遍历每一个类 81 | while (c && !stop) { 82 | // 4.1.执行操作 83 | enumeration(c, &stop); 84 | 85 | // 4.2.获得父类 86 | c = class_getSuperclass(c); 87 | } 88 | } 89 | 90 | #pragma mark - 属性黑名单配置 91 | + (void)mj_setupIgnoredPropertyNames:(MJIgnoredPropertyNames)ignoredPropertyNames 92 | { 93 | [self mj_setupBlockReturnValue:ignoredPropertyNames key:&MJIgnoredPropertyNamesKey]; 94 | } 95 | 96 | + (NSMutableArray *)mj_totalIgnoredPropertyNames 97 | { 98 | return [self mj_totalObjectsWithSelector:@selector(mj_ignoredPropertyNames) key:&MJIgnoredPropertyNamesKey]; 99 | } 100 | 101 | #pragma mark - 归档属性黑名单配置 102 | + (void)mj_setupIgnoredCodingPropertyNames:(MJIgnoredCodingPropertyNames)ignoredCodingPropertyNames 103 | { 104 | [self mj_setupBlockReturnValue:ignoredCodingPropertyNames key:&MJIgnoredCodingPropertyNamesKey]; 105 | } 106 | 107 | + (NSMutableArray *)mj_totalIgnoredCodingPropertyNames 108 | { 109 | return [self mj_totalObjectsWithSelector:@selector(mj_ignoredCodingPropertyNames) key:&MJIgnoredCodingPropertyNamesKey]; 110 | } 111 | 112 | #pragma mark - 属性白名单配置 113 | + (void)mj_setupAllowedPropertyNames:(MJAllowedPropertyNames)allowedPropertyNames; 114 | { 115 | [self mj_setupBlockReturnValue:allowedPropertyNames key:&MJAllowedPropertyNamesKey]; 116 | } 117 | 118 | + (NSMutableArray *)mj_totalAllowedPropertyNames 119 | { 120 | return [self mj_totalObjectsWithSelector:@selector(mj_allowedPropertyNames) key:&MJAllowedPropertyNamesKey]; 121 | } 122 | 123 | #pragma mark - 归档属性白名单配置 124 | + (void)mj_setupAllowedCodingPropertyNames:(MJAllowedCodingPropertyNames)allowedCodingPropertyNames 125 | { 126 | [self mj_setupBlockReturnValue:allowedCodingPropertyNames key:&MJAllowedCodingPropertyNamesKey]; 127 | } 128 | 129 | + (NSMutableArray *)mj_totalAllowedCodingPropertyNames 130 | { 131 | return [self mj_totalObjectsWithSelector:@selector(mj_allowedCodingPropertyNames) key:&MJAllowedCodingPropertyNamesKey]; 132 | } 133 | #pragma mark - block和方法处理:存储block的返回值 134 | + (void)mj_setupBlockReturnValue:(id (^)())block key:(const char *)key 135 | { 136 | if (block) { 137 | objc_setAssociatedObject(self, key, block(), OBJC_ASSOCIATION_RETAIN_NONATOMIC); 138 | } else { 139 | objc_setAssociatedObject(self, key, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 140 | } 141 | 142 | // 清空数据 143 | [[self dictForKey:key] removeAllObjects]; 144 | } 145 | 146 | + (NSMutableArray *)mj_totalObjectsWithSelector:(SEL)selector key:(const char *)key 147 | { 148 | NSMutableArray *array = [self dictForKey:key][NSStringFromClass(self)]; 149 | if (array) return array; 150 | 151 | // 创建、存储 152 | [self dictForKey:key][NSStringFromClass(self)] = array = [NSMutableArray array]; 153 | 154 | if ([self respondsToSelector:selector]) { 155 | #pragma clang diagnostic push 156 | #pragma clang diagnostic ignored "-Warc-performSelector-leaks" 157 | NSArray *subArray = [self performSelector:selector]; 158 | #pragma clang diagnostic pop 159 | if (subArray) { 160 | [array addObjectsFromArray:subArray]; 161 | } 162 | } 163 | 164 | [self mj_enumerateAllClasses:^(__unsafe_unretained Class c, BOOL *stop) { 165 | NSArray *subArray = objc_getAssociatedObject(c, key); 166 | [array addObjectsFromArray:subArray]; 167 | }]; 168 | return array; 169 | } 170 | @end 171 | -------------------------------------------------------------------------------- /TBModelTool/MJExtension/NSObject+MJCoding.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+MJCoding.h 3 | // MJExtension 4 | // 5 | // Created by mj on 14-1-15. 6 | // Copyright (c) 2014年 小码哥. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "MJExtensionConst.h" 11 | 12 | /** 13 | * Codeing协议 14 | */ 15 | @protocol MJCoding 16 | @optional 17 | /** 18 | * 这个数组中的属性名才会进行归档 19 | */ 20 | + (NSArray *)mj_allowedCodingPropertyNames; 21 | /** 22 | * 这个数组中的属性名将会被忽略:不进行归档 23 | */ 24 | + (NSArray *)mj_ignoredCodingPropertyNames; 25 | @end 26 | 27 | @interface NSObject (MJCoding) 28 | /** 29 | * 解码(从文件中解析对象) 30 | */ 31 | - (void)mj_decode:(NSCoder *)decoder; 32 | /** 33 | * 编码(将对象写入文件中) 34 | */ 35 | - (void)mj_encode:(NSCoder *)encoder; 36 | @end 37 | 38 | /** 39 | 归档的实现 40 | */ 41 | #define MJCodingImplementation \ 42 | - (id)initWithCoder:(NSCoder *)decoder \ 43 | { \ 44 | if (self = [super init]) { \ 45 | [self mj_decode:decoder]; \ 46 | } \ 47 | return self; \ 48 | } \ 49 | \ 50 | - (void)encodeWithCoder:(NSCoder *)encoder \ 51 | { \ 52 | [self mj_encode:encoder]; \ 53 | } 54 | 55 | #define MJExtensionCodingImplementation MJCodingImplementation -------------------------------------------------------------------------------- /TBModelTool/MJExtension/NSObject+MJCoding.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+MJCoding.m 3 | // MJExtension 4 | // 5 | // Created by mj on 14-1-15. 6 | // Copyright (c) 2014年 小码哥. All rights reserved. 7 | // 8 | 9 | #import "NSObject+MJCoding.h" 10 | #import "NSObject+MJClass.h" 11 | #import "NSObject+MJProperty.h" 12 | #import "MJProperty.h" 13 | 14 | @implementation NSObject (MJCoding) 15 | 16 | - (void)mj_encode:(NSCoder *)encoder 17 | { 18 | Class clazz = [self class]; 19 | 20 | NSArray *allowedCodingPropertyNames = [clazz mj_totalAllowedCodingPropertyNames]; 21 | NSArray *ignoredCodingPropertyNames = [clazz mj_totalIgnoredCodingPropertyNames]; 22 | 23 | [clazz mj_enumerateProperties:^(MJProperty *property, BOOL *stop) { 24 | // 检测是否被忽略 25 | if (allowedCodingPropertyNames.count && ![allowedCodingPropertyNames containsObject:property.name]) return; 26 | if ([ignoredCodingPropertyNames containsObject:property.name]) return; 27 | 28 | id value = [property valueForObject:self]; 29 | if (value == nil) return; 30 | [encoder encodeObject:value forKey:property.name]; 31 | }]; 32 | } 33 | 34 | - (void)mj_decode:(NSCoder *)decoder 35 | { 36 | Class clazz = [self class]; 37 | 38 | NSArray *allowedCodingPropertyNames = [clazz mj_totalAllowedCodingPropertyNames]; 39 | NSArray *ignoredCodingPropertyNames = [clazz mj_totalIgnoredCodingPropertyNames]; 40 | 41 | [clazz mj_enumerateProperties:^(MJProperty *property, BOOL *stop) { 42 | // 检测是否被忽略 43 | if (allowedCodingPropertyNames.count && ![allowedCodingPropertyNames containsObject:property.name]) return; 44 | if ([ignoredCodingPropertyNames containsObject:property.name]) return; 45 | 46 | id value = [decoder decodeObjectForKey:property.name]; 47 | if (value == nil) { // 兼容以前的MJExtension版本 48 | value = [decoder decodeObjectForKey:[@"_" stringByAppendingString:property.name]]; 49 | } 50 | if (value == nil) return; 51 | [property setValue:value forObject:self]; 52 | }]; 53 | } 54 | @end 55 | -------------------------------------------------------------------------------- /TBModelTool/MJExtension/NSObject+MJKeyValue.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+MJKeyValue.h 3 | // MJExtension 4 | // 5 | // Created by mj on 13-8-24. 6 | // Copyright (c) 2013年 小码哥. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "MJExtensionConst.h" 11 | #import 12 | #import "MJProperty.h" 13 | 14 | /** 15 | * KeyValue协议 16 | */ 17 | @protocol MJKeyValue 18 | @optional 19 | /** 20 | * 只有这个数组中的属性名才允许进行字典和模型的转换 21 | */ 22 | + (NSArray *)mj_allowedPropertyNames; 23 | 24 | /** 25 | * 这个数组中的属性名将会被忽略:不进行字典和模型的转换 26 | */ 27 | + (NSArray *)mj_ignoredPropertyNames; 28 | 29 | /** 30 | * 将属性名换为其他key去字典中取值 31 | * 32 | * @return 字典中的key是属性名,value是从字典中取值用的key 33 | */ 34 | + (NSDictionary *)mj_replacedKeyFromPropertyName; 35 | 36 | /** 37 | * 将属性名换为其他key去字典中取值 38 | * 39 | * @return 从字典中取值用的key 40 | */ 41 | + (id)mj_replacedKeyFromPropertyName121:(NSString *)propertyName; 42 | 43 | /** 44 | * 数组中需要转换的模型类 45 | * 46 | * @return 字典中的key是数组属性名,value是数组中存放模型的Class(Class类型或者NSString类型) 47 | */ 48 | + (NSDictionary *)mj_objectClassInArray; 49 | 50 | /** 51 | * 旧值换新值,用于过滤字典中的值 52 | * 53 | * @param oldValue 旧值 54 | * 55 | * @return 新值 56 | */ 57 | - (id)mj_newValueFromOldValue:(id)oldValue property:(MJProperty *)property; 58 | 59 | /** 60 | * 当字典转模型完毕时调用 61 | */ 62 | - (void)mj_keyValuesDidFinishConvertingToObject; 63 | 64 | /** 65 | * 当模型转字典完毕时调用 66 | */ 67 | - (void)mj_objectDidFinishConvertingToKeyValues; 68 | @end 69 | 70 | @interface NSObject (MJKeyValue) 71 | #pragma mark - 类方法 72 | /** 73 | * 字典转模型过程中遇到的错误 74 | */ 75 | + (NSError *)mj_error; 76 | 77 | /** 78 | * 模型转字典时,字典的key是否参考replacedKeyFromPropertyName等方法(父类设置了,子类也会继承下来) 79 | */ 80 | + (void)mj_referenceReplacedKeyWhenCreatingKeyValues:(BOOL)reference; 81 | 82 | #pragma mark - 对象方法 83 | /** 84 | * 将字典的键值对转成模型属性 85 | * @param keyValues 字典(可以是NSDictionary、NSData、NSString) 86 | */ 87 | - (instancetype)mj_setKeyValues:(id)keyValues; 88 | 89 | /** 90 | * 将字典的键值对转成模型属性 91 | * @param keyValues 字典(可以是NSDictionary、NSData、NSString) 92 | * @param context CoreData上下文 93 | */ 94 | - (instancetype)mj_setKeyValues:(id)keyValues context:(NSManagedObjectContext *)context; 95 | 96 | /** 97 | * 将模型转成字典 98 | * @return 字典 99 | */ 100 | - (NSMutableDictionary *)mj_keyValues; 101 | - (NSMutableDictionary *)mj_keyValuesWithKeys:(NSArray *)keys; 102 | - (NSMutableDictionary *)mj_keyValuesWithIgnoredKeys:(NSArray *)ignoredKeys; 103 | 104 | /** 105 | * 通过模型数组来创建一个字典数组 106 | * @param objectArray 模型数组 107 | * @return 字典数组 108 | */ 109 | + (NSMutableArray *)mj_keyValuesArrayWithObjectArray:(NSArray *)objectArray; 110 | + (NSMutableArray *)mj_keyValuesArrayWithObjectArray:(NSArray *)objectArray keys:(NSArray *)keys; 111 | + (NSMutableArray *)mj_keyValuesArrayWithObjectArray:(NSArray *)objectArray ignoredKeys:(NSArray *)ignoredKeys; 112 | 113 | #pragma mark - 字典转模型 114 | /** 115 | * 通过字典来创建一个模型 116 | * @param keyValues 字典(可以是NSDictionary、NSData、NSString) 117 | * @return 新建的对象 118 | */ 119 | + (instancetype)mj_objectWithKeyValues:(id)keyValues; 120 | 121 | /** 122 | * 通过字典来创建一个CoreData模型 123 | * @param keyValues 字典(可以是NSDictionary、NSData、NSString) 124 | * @param context CoreData上下文 125 | * @return 新建的对象 126 | */ 127 | + (instancetype)mj_objectWithKeyValues:(id)keyValues context:(NSManagedObjectContext *)context; 128 | 129 | /** 130 | * 通过plist来创建一个模型 131 | * @param filename 文件名(仅限于mainBundle中的文件) 132 | * @return 新建的对象 133 | */ 134 | + (instancetype)mj_objectWithFilename:(NSString *)filename; 135 | 136 | /** 137 | * 通过plist来创建一个模型 138 | * @param file 文件全路径 139 | * @return 新建的对象 140 | */ 141 | + (instancetype)mj_objectWithFile:(NSString *)file; 142 | 143 | #pragma mark - 字典数组转模型数组 144 | /** 145 | * 通过字典数组来创建一个模型数组 146 | * @param keyValuesArray 字典数组(可以是NSDictionary、NSData、NSString) 147 | * @return 模型数组 148 | */ 149 | + (NSMutableArray *)mj_objectArrayWithKeyValuesArray:(id)keyValuesArray; 150 | 151 | /** 152 | * 通过字典数组来创建一个模型数组 153 | * @param keyValuesArray 字典数组(可以是NSDictionary、NSData、NSString) 154 | * @param context CoreData上下文 155 | * @return 模型数组 156 | */ 157 | + (NSMutableArray *)mj_objectArrayWithKeyValuesArray:(id)keyValuesArray context:(NSManagedObjectContext *)context; 158 | 159 | /** 160 | * 通过plist来创建一个模型数组 161 | * @param filename 文件名(仅限于mainBundle中的文件) 162 | * @return 模型数组 163 | */ 164 | + (NSMutableArray *)mj_objectArrayWithFilename:(NSString *)filename; 165 | 166 | /** 167 | * 通过plist来创建一个模型数组 168 | * @param file 文件全路径 169 | * @return 模型数组 170 | */ 171 | + (NSMutableArray *)mj_objectArrayWithFile:(NSString *)file; 172 | 173 | #pragma mark - 转换为JSON 174 | /** 175 | * 转换为JSON Data 176 | */ 177 | - (NSData *)mj_JSONData; 178 | /** 179 | * 转换为字典或者数组 180 | */ 181 | - (id)mj_JSONObject; 182 | /** 183 | * 转换为JSON 字符串 184 | */ 185 | - (NSString *)mj_JSONString; 186 | @end 187 | 188 | @interface NSObject (MJKeyValueDeprecated_v_2_5_16) 189 | - (instancetype)setKeyValues:(id)keyValue MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 190 | - (instancetype)setKeyValues:(id)keyValues error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 191 | - (instancetype)setKeyValues:(id)keyValues context:(NSManagedObjectContext *)context MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 192 | - (instancetype)setKeyValues:(id)keyValues context:(NSManagedObjectContext *)context error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 193 | + (void)referenceReplacedKeyWhenCreatingKeyValues:(BOOL)reference MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 194 | - (NSMutableDictionary *)keyValues MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 195 | - (NSMutableDictionary *)keyValuesWithError:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 196 | - (NSMutableDictionary *)keyValuesWithKeys:(NSArray *)keys MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 197 | - (NSMutableDictionary *)keyValuesWithKeys:(NSArray *)keys error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 198 | - (NSMutableDictionary *)keyValuesWithIgnoredKeys:(NSArray *)ignoredKeys MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 199 | - (NSMutableDictionary *)keyValuesWithIgnoredKeys:(NSArray *)ignoredKeys error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 200 | + (NSMutableArray *)keyValuesArrayWithObjectArray:(NSArray *)objectArray MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 201 | + (NSMutableArray *)keyValuesArrayWithObjectArray:(NSArray *)objectArray error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 202 | + (NSMutableArray *)keyValuesArrayWithObjectArray:(NSArray *)objectArray keys:(NSArray *)keys MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 203 | + (NSMutableArray *)keyValuesArrayWithObjectArray:(NSArray *)objectArray keys:(NSArray *)keys error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 204 | + (NSMutableArray *)keyValuesArrayWithObjectArray:(NSArray *)objectArray ignoredKeys:(NSArray *)ignoredKeys MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 205 | + (NSMutableArray *)keyValuesArrayWithObjectArray:(NSArray *)objectArray ignoredKeys:(NSArray *)ignoredKeys error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 206 | + (instancetype)objectWithKeyValues:(id)keyValues MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 207 | + (instancetype)objectWithKeyValues:(id)keyValues error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 208 | + (instancetype)objectWithKeyValues:(id)keyValues context:(NSManagedObjectContext *)context MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 209 | + (instancetype)objectWithKeyValues:(id)keyValues context:(NSManagedObjectContext *)context error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 210 | + (instancetype)objectWithFilename:(NSString *)filename MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 211 | + (instancetype)objectWithFilename:(NSString *)filename error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 212 | + (instancetype)objectWithFile:(NSString *)file MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 213 | + (instancetype)objectWithFile:(NSString *)file error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 214 | + (NSMutableArray *)objectArrayWithKeyValuesArray:(id)keyValuesArray MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 215 | + (NSMutableArray *)objectArrayWithKeyValuesArray:(id)keyValuesArray error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 216 | + (NSMutableArray *)objectArrayWithKeyValuesArray:(id)keyValuesArray context:(NSManagedObjectContext *)context MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 217 | + (NSMutableArray *)objectArrayWithKeyValuesArray:(id)keyValuesArray context:(NSManagedObjectContext *)context error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 218 | + (NSMutableArray *)objectArrayWithFilename:(NSString *)filename MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 219 | + (NSMutableArray *)objectArrayWithFilename:(NSString *)filename error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 220 | + (NSMutableArray *)objectArrayWithFile:(NSString *)file MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 221 | + (NSMutableArray *)objectArrayWithFile:(NSString *)file error:(NSError **)error MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 222 | - (NSData *)JSONData MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 223 | - (id)JSONObject MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 224 | - (NSString *)JSONString MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 225 | @end 226 | -------------------------------------------------------------------------------- /TBModelTool/MJExtension/NSObject+MJKeyValue.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+MJKeyValue.m 3 | // MJExtension 4 | // 5 | // Created by mj on 13-8-24. 6 | // Copyright (c) 2013年 小码哥. All rights reserved. 7 | // 8 | 9 | #import "NSObject+MJKeyValue.h" 10 | #import "NSObject+MJProperty.h" 11 | #import "NSString+MJExtension.h" 12 | #import "MJProperty.h" 13 | #import "MJPropertyType.h" 14 | #import "MJExtensionConst.h" 15 | #import "MJFoundation.h" 16 | #import "NSString+MJExtension.h" 17 | #import "NSObject+MJClass.h" 18 | 19 | @implementation NSObject (MJKeyValue) 20 | 21 | #pragma mark - 错误 22 | static const char MJErrorKey = '\0'; 23 | + (NSError *)mj_error 24 | { 25 | return objc_getAssociatedObject(self, &MJErrorKey); 26 | } 27 | 28 | + (void)setMj_error:(NSError *)error 29 | { 30 | objc_setAssociatedObject(self, &MJErrorKey, error, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 31 | } 32 | 33 | #pragma mark - 模型 -> 字典时的参考 34 | /** 模型转字典时,字典的key是否参考replacedKeyFromPropertyName等方法(父类设置了,子类也会继承下来) */ 35 | static const char MJReferenceReplacedKeyWhenCreatingKeyValuesKey = '\0'; 36 | 37 | + (void)mj_referenceReplacedKeyWhenCreatingKeyValues:(BOOL)reference 38 | { 39 | objc_setAssociatedObject(self, &MJReferenceReplacedKeyWhenCreatingKeyValuesKey, @(reference), OBJC_ASSOCIATION_ASSIGN); 40 | } 41 | 42 | + (BOOL)mj_isReferenceReplacedKeyWhenCreatingKeyValues 43 | { 44 | __block id value = objc_getAssociatedObject(self, &MJReferenceReplacedKeyWhenCreatingKeyValuesKey); 45 | if (!value) { 46 | [self mj_enumerateAllClasses:^(__unsafe_unretained Class c, BOOL *stop) { 47 | value = objc_getAssociatedObject(c, &MJReferenceReplacedKeyWhenCreatingKeyValuesKey); 48 | 49 | if (value) *stop = YES; 50 | }]; 51 | } 52 | return [value boolValue]; 53 | } 54 | 55 | #pragma mark - --常用的对象-- 56 | static NSNumberFormatter *numberFormatter_; 57 | + (void)load 58 | { 59 | numberFormatter_ = [[NSNumberFormatter alloc] init]; 60 | 61 | // 默认设置 62 | [self mj_referenceReplacedKeyWhenCreatingKeyValues:YES]; 63 | } 64 | 65 | #pragma mark - --公共方法-- 66 | #pragma mark - 字典 -> 模型 67 | - (instancetype)mj_setKeyValues:(id)keyValues 68 | { 69 | return [self mj_setKeyValues:keyValues context:nil]; 70 | } 71 | 72 | /** 73 | 核心代码: 74 | */ 75 | - (instancetype)mj_setKeyValues:(id)keyValues context:(NSManagedObjectContext *)context 76 | { 77 | // 获得JSON对象 78 | keyValues = [keyValues mj_JSONObject]; 79 | 80 | MJExtensionAssertError([keyValues isKindOfClass:[NSDictionary class]], self, [self class], @"keyValues参数不是一个字典"); 81 | 82 | Class clazz = [self class]; 83 | NSArray *allowedPropertyNames = [clazz mj_totalAllowedPropertyNames]; 84 | NSArray *ignoredPropertyNames = [clazz mj_totalIgnoredPropertyNames]; 85 | 86 | //通过封装的方法回调一个通过运行时编写的,用于返回属性列表的方法。 87 | [clazz mj_enumerateProperties:^(MJProperty *property, BOOL *stop) { 88 | @try { 89 | // 0.检测是否被忽略 90 | if (allowedPropertyNames.count && ![allowedPropertyNames containsObject:property.name]) return; 91 | if ([ignoredPropertyNames containsObject:property.name]) return; 92 | 93 | // 1.取出属性值 94 | id value; 95 | NSArray *propertyKeyses = [property propertyKeysForClass:clazz]; 96 | for (NSArray *propertyKeys in propertyKeyses) { 97 | value = keyValues; 98 | for (MJPropertyKey *propertyKey in propertyKeys) { 99 | value = [propertyKey valueInObject:value]; 100 | } 101 | if (value) break; 102 | } 103 | 104 | // 值的过滤 105 | id newValue = [clazz mj_getNewValueFromObject:self oldValue:value property:property]; 106 | if (newValue != value) { // 有过滤后的新值 107 | [property setValue:newValue forObject:self]; 108 | return; 109 | } 110 | 111 | // 如果没有值,就直接返回 112 | if (!value || value == [NSNull null]) return; 113 | 114 | // 2.复杂处理 115 | MJPropertyType *type = property.type; 116 | Class propertyClass = type.typeClass; 117 | Class objectClass = [property objectClassInArrayForClass:[self class]]; 118 | 119 | // 不可变 -> 可变处理 120 | if (propertyClass == [NSMutableArray class] && [value isKindOfClass:[NSArray class]]) { 121 | value = [NSMutableArray arrayWithArray:value]; 122 | } else if (propertyClass == [NSMutableDictionary class] && [value isKindOfClass:[NSDictionary class]]) { 123 | value = [NSMutableDictionary dictionaryWithDictionary:value]; 124 | } else if (propertyClass == [NSMutableString class] && [value isKindOfClass:[NSString class]]) { 125 | value = [NSMutableString stringWithString:value]; 126 | } else if (propertyClass == [NSMutableData class] && [value isKindOfClass:[NSData class]]) { 127 | value = [NSMutableData dataWithData:value]; 128 | } 129 | 130 | if (!type.isFromFoundation && propertyClass) { // 模型属性 131 | value = [propertyClass mj_objectWithKeyValues:value context:context]; 132 | } else if (objectClass) { 133 | if (objectClass == [NSURL class] && [value isKindOfClass:[NSArray class]]) { 134 | // string array -> url array 135 | NSMutableArray *urlArray = [NSMutableArray array]; 136 | for (NSString *string in value) { 137 | if (![string isKindOfClass:[NSString class]]) continue; 138 | [urlArray addObject:string.mj_url]; 139 | } 140 | value = urlArray; 141 | } else { // 字典数组-->模型数组 142 | value = [objectClass mj_objectArrayWithKeyValuesArray:value context:context]; 143 | } 144 | } else { 145 | if (propertyClass == [NSString class]) { 146 | if ([value isKindOfClass:[NSNumber class]]) { 147 | // NSNumber -> NSString 148 | value = [value description]; 149 | } else if ([value isKindOfClass:[NSURL class]]) { 150 | // NSURL -> NSString 151 | value = [value absoluteString]; 152 | } 153 | } else if ([value isKindOfClass:[NSString class]]) { 154 | if (propertyClass == [NSURL class]) { 155 | // NSString -> NSURL 156 | // 字符串转码 157 | value = [value mj_url]; 158 | } else if (type.isNumberType) { 159 | NSString *oldValue = value; 160 | 161 | // NSString -> NSNumber 162 | if (type.typeClass == [NSDecimalNumber class]) { 163 | value = [NSDecimalNumber decimalNumberWithString:oldValue]; 164 | } else { 165 | value = [numberFormatter_ numberFromString:oldValue]; 166 | } 167 | 168 | // 如果是BOOL 169 | if (type.isBoolType) { 170 | // 字符串转BOOL(字符串没有charValue方法) 171 | // 系统会调用字符串的charValue转为BOOL类型 172 | NSString *lower = [oldValue lowercaseString]; 173 | if ([lower isEqualToString:@"yes"] || [lower isEqualToString:@"true"]) { 174 | value = @YES; 175 | } else if ([lower isEqualToString:@"no"] || [lower isEqualToString:@"false"]) { 176 | value = @NO; 177 | } 178 | } 179 | } 180 | } 181 | 182 | // value和property类型不匹配 183 | if (propertyClass && ![value isKindOfClass:propertyClass]) { 184 | value = nil; 185 | } 186 | } 187 | 188 | // 3.赋值 189 | [property setValue:value forObject:self]; 190 | } @catch (NSException *exception) { 191 | MJExtensionBuildError([self class], exception.reason); 192 | MJExtensionLog(@"%@", exception); 193 | } 194 | }]; 195 | 196 | // 转换完毕 197 | if ([self respondsToSelector:@selector(mj_keyValuesDidFinishConvertingToObject)]) { 198 | [self mj_keyValuesDidFinishConvertingToObject]; 199 | } 200 | return self; 201 | } 202 | 203 | + (instancetype)mj_objectWithKeyValues:(id)keyValues 204 | { 205 | return [self mj_objectWithKeyValues:keyValues context:nil]; 206 | } 207 | 208 | + (instancetype)mj_objectWithKeyValues:(id)keyValues context:(NSManagedObjectContext *)context 209 | { 210 | // 获得JSON对象 211 | keyValues = [keyValues mj_JSONObject]; 212 | MJExtensionAssertError([keyValues isKindOfClass:[NSDictionary class]], nil, [self class], @"keyValues参数不是一个字典"); 213 | 214 | if ([self isSubclassOfClass:[NSManagedObject class]] && context) { 215 | NSString *entityName = [NSStringFromClass(self) componentsSeparatedByString:@"."].lastObject; 216 | return [[NSEntityDescription insertNewObjectForEntityForName:entityName inManagedObjectContext:context] mj_setKeyValues:keyValues context:context]; 217 | } 218 | return [[[self alloc] init] mj_setKeyValues:keyValues]; 219 | } 220 | 221 | + (instancetype)mj_objectWithFilename:(NSString *)filename 222 | { 223 | MJExtensionAssertError(filename != nil, nil, [self class], @"filename参数为nil"); 224 | 225 | return [self mj_objectWithFile:[[NSBundle mainBundle] pathForResource:filename ofType:nil]]; 226 | } 227 | 228 | + (instancetype)mj_objectWithFile:(NSString *)file 229 | { 230 | MJExtensionAssertError(file != nil, nil, [self class], @"file参数为nil"); 231 | 232 | return [self mj_objectWithKeyValues:[NSDictionary dictionaryWithContentsOfFile:file]]; 233 | } 234 | 235 | #pragma mark - 字典数组 -> 模型数组 236 | + (NSMutableArray *)mj_objectArrayWithKeyValuesArray:(NSArray *)keyValuesArray 237 | { 238 | return [self mj_objectArrayWithKeyValuesArray:keyValuesArray context:nil]; 239 | } 240 | 241 | + (NSMutableArray *)mj_objectArrayWithKeyValuesArray:(id)keyValuesArray context:(NSManagedObjectContext *)context 242 | { 243 | // 如果是JSON字符串 244 | keyValuesArray = [keyValuesArray mj_JSONObject]; 245 | 246 | // 1.判断真实性 247 | MJExtensionAssertError([keyValuesArray isKindOfClass:[NSArray class]], nil, [self class], @"keyValuesArray参数不是一个数组"); 248 | 249 | // 如果数组里面放的是NSString、NSNumber等数据 250 | if ([MJFoundation isClassFromFoundation:self]) return [NSMutableArray arrayWithArray:keyValuesArray]; 251 | 252 | 253 | // 2.创建数组 254 | NSMutableArray *modelArray = [NSMutableArray array]; 255 | 256 | // 3.遍历 257 | for (NSDictionary *keyValues in keyValuesArray) { 258 | if ([keyValues isKindOfClass:[NSArray class]]){ 259 | [modelArray addObject:[self mj_objectArrayWithKeyValuesArray:keyValues context:context]]; 260 | } else { 261 | id model = [self mj_objectWithKeyValues:keyValues context:context]; 262 | if (model) [modelArray addObject:model]; 263 | } 264 | } 265 | 266 | return modelArray; 267 | } 268 | 269 | + (NSMutableArray *)mj_objectArrayWithFilename:(NSString *)filename 270 | { 271 | MJExtensionAssertError(filename != nil, nil, [self class], @"filename参数为nil"); 272 | 273 | return [self mj_objectArrayWithFile:[[NSBundle mainBundle] pathForResource:filename ofType:nil]]; 274 | } 275 | 276 | + (NSMutableArray *)mj_objectArrayWithFile:(NSString *)file 277 | { 278 | MJExtensionAssertError(file != nil, nil, [self class], @"file参数为nil"); 279 | 280 | return [self mj_objectArrayWithKeyValuesArray:[NSArray arrayWithContentsOfFile:file]]; 281 | } 282 | 283 | #pragma mark - 模型 -> 字典 284 | - (NSMutableDictionary *)mj_keyValues 285 | { 286 | return [self mj_keyValuesWithKeys:nil ignoredKeys:nil]; 287 | } 288 | 289 | - (NSMutableDictionary *)mj_keyValuesWithKeys:(NSArray *)keys 290 | { 291 | return [self mj_keyValuesWithKeys:keys ignoredKeys:nil]; 292 | } 293 | 294 | - (NSMutableDictionary *)mj_keyValuesWithIgnoredKeys:(NSArray *)ignoredKeys 295 | { 296 | return [self mj_keyValuesWithKeys:nil ignoredKeys:ignoredKeys]; 297 | } 298 | 299 | - (NSMutableDictionary *)mj_keyValuesWithKeys:(NSArray *)keys ignoredKeys:(NSArray *)ignoredKeys 300 | { 301 | // 如果自己不是模型类, 那就返回自己 302 | MJExtensionAssertError(![MJFoundation isClassFromFoundation:[self class]], (NSMutableDictionary *)self, [self class], @"不是自定义的模型类") 303 | 304 | id keyValues = [NSMutableDictionary dictionary]; 305 | 306 | Class clazz = [self class]; 307 | NSArray *allowedPropertyNames = [clazz mj_totalAllowedPropertyNames]; 308 | NSArray *ignoredPropertyNames = [clazz mj_totalIgnoredPropertyNames]; 309 | 310 | [clazz mj_enumerateProperties:^(MJProperty *property, BOOL *stop) { 311 | @try { 312 | // 0.检测是否被忽略 313 | if (allowedPropertyNames.count && ![allowedPropertyNames containsObject:property.name]) return; 314 | if ([ignoredPropertyNames containsObject:property.name]) return; 315 | if (keys.count && ![keys containsObject:property.name]) return; 316 | if ([ignoredKeys containsObject:property.name]) return; 317 | 318 | // 1.取出属性值 319 | id value = [property valueForObject:self]; 320 | if (!value) return; 321 | 322 | // 2.如果是模型属性 323 | MJPropertyType *type = property.type; 324 | Class propertyClass = type.typeClass; 325 | if (!type.isFromFoundation && propertyClass) { 326 | value = [value mj_keyValues]; 327 | } else if ([value isKindOfClass:[NSArray class]]) { 328 | // 3.处理数组里面有模型的情况 329 | value = [NSObject mj_keyValuesArrayWithObjectArray:value]; 330 | } else if (propertyClass == [NSURL class]) { 331 | value = [value absoluteString]; 332 | } 333 | 334 | // 4.赋值 335 | if ([clazz mj_isReferenceReplacedKeyWhenCreatingKeyValues]) { 336 | NSArray *propertyKeys = [[property propertyKeysForClass:clazz] firstObject]; 337 | NSUInteger keyCount = propertyKeys.count; 338 | // 创建字典 339 | __block id innerContainer = keyValues; 340 | [propertyKeys enumerateObjectsUsingBlock:^(MJPropertyKey *propertyKey, NSUInteger idx, BOOL *stop) { 341 | // 下一个属性 342 | MJPropertyKey *nextPropertyKey = nil; 343 | if (idx != keyCount - 1) { 344 | nextPropertyKey = propertyKeys[idx + 1]; 345 | } 346 | 347 | if (nextPropertyKey) { // 不是最后一个key 348 | // 当前propertyKey对应的字典或者数组 349 | id tempInnerContainer = [propertyKey valueInObject:innerContainer]; 350 | if (tempInnerContainer == nil || [tempInnerContainer isKindOfClass:[NSNull class]]) { 351 | if (nextPropertyKey.type == MJPropertyKeyTypeDictionary) { 352 | tempInnerContainer = [NSMutableDictionary dictionary]; 353 | } else { 354 | tempInnerContainer = [NSMutableArray array]; 355 | } 356 | if (propertyKey.type == MJPropertyKeyTypeDictionary) { 357 | innerContainer[propertyKey.name] = tempInnerContainer; 358 | } else { 359 | innerContainer[propertyKey.name.intValue] = tempInnerContainer; 360 | } 361 | } 362 | 363 | if ([tempInnerContainer isKindOfClass:[NSMutableArray class]]) { 364 | NSMutableArray *tempInnerContainerArray = tempInnerContainer; 365 | int index = nextPropertyKey.name.intValue; 366 | while (tempInnerContainerArray.count < index + 1) { 367 | [tempInnerContainerArray addObject:[NSNull null]]; 368 | } 369 | } 370 | 371 | innerContainer = tempInnerContainer; 372 | } else { // 最后一个key 373 | if (propertyKey.type == MJPropertyKeyTypeDictionary) { 374 | innerContainer[propertyKey.name] = value; 375 | } else { 376 | innerContainer[propertyKey.name.intValue] = value; 377 | } 378 | } 379 | }]; 380 | } else { 381 | keyValues[property.name] = value; 382 | } 383 | } @catch (NSException *exception) { 384 | MJExtensionBuildError([self class], exception.reason); 385 | MJExtensionLog(@"%@", exception); 386 | } 387 | }]; 388 | 389 | // 转换完毕 390 | if ([self respondsToSelector:@selector(mj_objectDidFinishConvertingToKeyValues)]) { 391 | [self mj_objectDidFinishConvertingToKeyValues]; 392 | } 393 | 394 | return keyValues; 395 | } 396 | #pragma mark - 模型数组 -> 字典数组 397 | + (NSMutableArray *)mj_keyValuesArrayWithObjectArray:(NSArray *)objectArray 398 | { 399 | return [self mj_keyValuesArrayWithObjectArray:objectArray keys:nil ignoredKeys:nil]; 400 | } 401 | 402 | + (NSMutableArray *)mj_keyValuesArrayWithObjectArray:(NSArray *)objectArray keys:(NSArray *)keys 403 | { 404 | return [self mj_keyValuesArrayWithObjectArray:objectArray keys:keys ignoredKeys:nil]; 405 | } 406 | 407 | + (NSMutableArray *)mj_keyValuesArrayWithObjectArray:(NSArray *)objectArray ignoredKeys:(NSArray *)ignoredKeys 408 | { 409 | return [self mj_keyValuesArrayWithObjectArray:objectArray keys:nil ignoredKeys:ignoredKeys]; 410 | } 411 | 412 | + (NSMutableArray *)mj_keyValuesArrayWithObjectArray:(NSArray *)objectArray keys:(NSArray *)keys ignoredKeys:(NSArray *)ignoredKeys 413 | { 414 | // 0.判断真实性 415 | MJExtensionAssertError([objectArray isKindOfClass:[NSArray class]], nil, [self class], @"objectArray参数不是一个数组"); 416 | 417 | // 1.创建数组 418 | NSMutableArray *keyValuesArray = [NSMutableArray array]; 419 | for (id object in objectArray) { 420 | if (keys) { 421 | [keyValuesArray addObject:[object mj_keyValuesWithKeys:keys]]; 422 | } else { 423 | [keyValuesArray addObject:[object mj_keyValuesWithIgnoredKeys:ignoredKeys]]; 424 | } 425 | } 426 | return keyValuesArray; 427 | } 428 | 429 | #pragma mark - 转换为JSON 430 | - (NSData *)mj_JSONData 431 | { 432 | if ([self isKindOfClass:[NSString class]]) { 433 | return [((NSString *)self) dataUsingEncoding:NSUTF8StringEncoding]; 434 | } else if ([self isKindOfClass:[NSData class]]) { 435 | return (NSData *)self; 436 | } 437 | 438 | return [NSJSONSerialization dataWithJSONObject:[self mj_JSONObject] options:kNilOptions error:nil]; 439 | } 440 | 441 | - (id)mj_JSONObject 442 | { 443 | if ([self isKindOfClass:[NSString class]]) { 444 | return [NSJSONSerialization JSONObjectWithData:[((NSString *)self) dataUsingEncoding:NSUTF8StringEncoding] options:kNilOptions error:nil]; 445 | } else if ([self isKindOfClass:[NSData class]]) { 446 | return [NSJSONSerialization JSONObjectWithData:(NSData *)self options:kNilOptions error:nil]; 447 | } 448 | 449 | return self.mj_keyValues; 450 | } 451 | 452 | - (NSString *)mj_JSONString 453 | { 454 | if ([self isKindOfClass:[NSString class]]) { 455 | return (NSString *)self; 456 | } else if ([self isKindOfClass:[NSData class]]) { 457 | return [[NSString alloc] initWithData:(NSData *)self encoding:NSUTF8StringEncoding]; 458 | } 459 | 460 | return [[NSString alloc] initWithData:[self mj_JSONData] encoding:NSUTF8StringEncoding]; 461 | } 462 | @end 463 | 464 | @implementation NSObject (MJKeyValueDeprecated_v_2_5_16) 465 | - (instancetype)setKeyValues:(id)keyValues 466 | { 467 | return [self mj_setKeyValues:keyValues]; 468 | } 469 | 470 | - (instancetype)setKeyValues:(id)keyValues error:(NSError **)error 471 | { 472 | id value = [self mj_setKeyValues:keyValues]; 473 | if (error != NULL) { 474 | *error = [self.class mj_error]; 475 | } 476 | return value; 477 | 478 | } 479 | 480 | - (instancetype)setKeyValues:(id)keyValues context:(NSManagedObjectContext *)context 481 | { 482 | return [self mj_setKeyValues:keyValues context:context]; 483 | } 484 | 485 | - (instancetype)setKeyValues:(id)keyValues context:(NSManagedObjectContext *)context error:(NSError **)error 486 | { 487 | id value = [self mj_setKeyValues:keyValues context:context]; 488 | if (error != NULL) { 489 | *error = [self.class mj_error]; 490 | } 491 | return value; 492 | } 493 | 494 | + (void)referenceReplacedKeyWhenCreatingKeyValues:(BOOL)reference 495 | { 496 | [self mj_referenceReplacedKeyWhenCreatingKeyValues:reference]; 497 | } 498 | 499 | - (NSMutableDictionary *)keyValues 500 | { 501 | return [self mj_keyValues]; 502 | } 503 | 504 | - (NSMutableDictionary *)keyValuesWithError:(NSError **)error 505 | { 506 | id value = [self mj_keyValues]; 507 | if (error != NULL) { 508 | *error = [self.class mj_error]; 509 | } 510 | return value; 511 | } 512 | 513 | - (NSMutableDictionary *)keyValuesWithKeys:(NSArray *)keys 514 | { 515 | return [self mj_keyValuesWithKeys:keys]; 516 | } 517 | 518 | - (NSMutableDictionary *)keyValuesWithKeys:(NSArray *)keys error:(NSError **)error 519 | { 520 | id value = [self mj_keyValuesWithKeys:keys]; 521 | if (error != NULL) { 522 | *error = [self.class mj_error]; 523 | } 524 | return value; 525 | } 526 | 527 | - (NSMutableDictionary *)keyValuesWithIgnoredKeys:(NSArray *)ignoredKeys 528 | { 529 | return [self mj_keyValuesWithIgnoredKeys:ignoredKeys]; 530 | } 531 | 532 | - (NSMutableDictionary *)keyValuesWithIgnoredKeys:(NSArray *)ignoredKeys error:(NSError **)error 533 | { 534 | id value = [self mj_keyValuesWithIgnoredKeys:ignoredKeys]; 535 | if (error != NULL) { 536 | *error = [self.class mj_error]; 537 | } 538 | return value; 539 | } 540 | 541 | + (NSMutableArray *)keyValuesArrayWithObjectArray:(NSArray *)objectArray 542 | { 543 | return [self mj_keyValuesArrayWithObjectArray:objectArray]; 544 | } 545 | 546 | + (NSMutableArray *)keyValuesArrayWithObjectArray:(NSArray *)objectArray error:(NSError **)error 547 | { 548 | id value = [self mj_keyValuesArrayWithObjectArray:objectArray]; 549 | if (error != NULL) { 550 | *error = [self mj_error]; 551 | } 552 | return value; 553 | } 554 | 555 | + (NSMutableArray *)keyValuesArrayWithObjectArray:(NSArray *)objectArray keys:(NSArray *)keys 556 | { 557 | return [self mj_keyValuesArrayWithObjectArray:objectArray keys:keys]; 558 | } 559 | 560 | + (NSMutableArray *)keyValuesArrayWithObjectArray:(NSArray *)objectArray keys:(NSArray *)keys error:(NSError **)error 561 | { 562 | id value = [self mj_keyValuesArrayWithObjectArray:objectArray keys:keys]; 563 | if (error != NULL) { 564 | *error = [self mj_error]; 565 | } 566 | return value; 567 | } 568 | 569 | + (NSMutableArray *)keyValuesArrayWithObjectArray:(NSArray *)objectArray ignoredKeys:(NSArray *)ignoredKeys 570 | { 571 | return [self mj_keyValuesArrayWithObjectArray:objectArray ignoredKeys:ignoredKeys]; 572 | } 573 | 574 | + (NSMutableArray *)keyValuesArrayWithObjectArray:(NSArray *)objectArray ignoredKeys:(NSArray *)ignoredKeys error:(NSError **)error 575 | { 576 | id value = [self mj_keyValuesArrayWithObjectArray:objectArray ignoredKeys:ignoredKeys]; 577 | if (error != NULL) { 578 | *error = [self mj_error]; 579 | } 580 | return value; 581 | } 582 | 583 | + (instancetype)objectWithKeyValues:(id)keyValues 584 | { 585 | return [self mj_objectWithKeyValues:keyValues]; 586 | } 587 | 588 | + (instancetype)objectWithKeyValues:(id)keyValues error:(NSError **)error 589 | { 590 | id value = [self mj_objectWithKeyValues:keyValues]; 591 | if (error != NULL) { 592 | *error = [self mj_error]; 593 | } 594 | return value; 595 | } 596 | 597 | + (instancetype)objectWithKeyValues:(id)keyValues context:(NSManagedObjectContext *)context 598 | { 599 | return [self mj_objectWithKeyValues:keyValues context:context]; 600 | } 601 | 602 | + (instancetype)objectWithKeyValues:(id)keyValues context:(NSManagedObjectContext *)context error:(NSError **)error 603 | { 604 | id value = [self mj_objectWithKeyValues:keyValues context:context]; 605 | if (error != NULL) { 606 | *error = [self mj_error]; 607 | } 608 | return value; 609 | } 610 | 611 | + (instancetype)objectWithFilename:(NSString *)filename 612 | { 613 | return [self mj_objectWithFilename:filename]; 614 | } 615 | 616 | + (instancetype)objectWithFilename:(NSString *)filename error:(NSError **)error 617 | { 618 | id value = [self mj_objectWithFilename:filename]; 619 | if (error != NULL) { 620 | *error = [self mj_error]; 621 | } 622 | return value; 623 | } 624 | 625 | + (instancetype)objectWithFile:(NSString *)file 626 | { 627 | return [self mj_objectWithFile:file]; 628 | } 629 | 630 | + (instancetype)objectWithFile:(NSString *)file error:(NSError **)error 631 | { 632 | id value = [self mj_objectWithFile:file]; 633 | if (error != NULL) { 634 | *error = [self mj_error]; 635 | } 636 | return value; 637 | } 638 | 639 | + (NSMutableArray *)objectArrayWithKeyValuesArray:(id)keyValuesArray 640 | { 641 | return [self mj_objectArrayWithKeyValuesArray:keyValuesArray]; 642 | } 643 | 644 | + (NSMutableArray *)objectArrayWithKeyValuesArray:(id)keyValuesArray error:(NSError **)error 645 | { 646 | id value = [self mj_objectArrayWithKeyValuesArray:keyValuesArray]; 647 | if (error != NULL) { 648 | *error = [self mj_error]; 649 | } 650 | return value; 651 | } 652 | 653 | + (NSMutableArray *)objectArrayWithKeyValuesArray:(id)keyValuesArray context:(NSManagedObjectContext *)context 654 | { 655 | return [self mj_objectArrayWithKeyValuesArray:keyValuesArray context:context]; 656 | } 657 | 658 | + (NSMutableArray *)objectArrayWithKeyValuesArray:(id)keyValuesArray context:(NSManagedObjectContext *)context error:(NSError **)error 659 | { 660 | id value = [self mj_objectArrayWithKeyValuesArray:keyValuesArray context:context]; 661 | if (error != NULL) { 662 | *error = [self mj_error]; 663 | } 664 | return value; 665 | } 666 | 667 | + (NSMutableArray *)objectArrayWithFilename:(NSString *)filename 668 | { 669 | return [self mj_objectArrayWithFilename:filename]; 670 | } 671 | 672 | + (NSMutableArray *)objectArrayWithFilename:(NSString *)filename error:(NSError **)error 673 | { 674 | id value = [self mj_objectArrayWithFilename:filename]; 675 | if (error != NULL) { 676 | *error = [self mj_error]; 677 | } 678 | return value; 679 | } 680 | 681 | + (NSMutableArray *)objectArrayWithFile:(NSString *)file 682 | { 683 | return [self mj_objectArrayWithFile:file]; 684 | } 685 | 686 | + (NSMutableArray *)objectArrayWithFile:(NSString *)file error:(NSError **)error 687 | { 688 | id value = [self mj_objectArrayWithFile:file]; 689 | if (error != NULL) { 690 | *error = [self mj_error]; 691 | } 692 | return value; 693 | } 694 | 695 | - (NSData *)JSONData 696 | { 697 | return [self mj_JSONData]; 698 | } 699 | 700 | - (id)JSONObject 701 | { 702 | return [self mj_JSONObject]; 703 | } 704 | 705 | - (NSString *)JSONString 706 | { 707 | return [self mj_JSONString]; 708 | } 709 | @end -------------------------------------------------------------------------------- /TBModelTool/MJExtension/NSObject+MJProperty.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+MJProperty.h 3 | // MJExtensionExample 4 | // 5 | // Created by MJ Lee on 15/4/17. 6 | // Copyright (c) 2015年 小码哥. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "MJExtensionConst.h" 11 | 12 | @class MJProperty; 13 | 14 | /** 15 | * 遍历成员变量用的block 16 | * 17 | * @param property 成员的包装对象 18 | * @param stop YES代表停止遍历,NO代表继续遍历 19 | */ 20 | typedef void (^MJPropertiesEnumeration)(MJProperty *property, BOOL *stop); 21 | 22 | /** 将属性名换为其他key去字典中取值 */ 23 | typedef NSDictionary * (^MJReplacedKeyFromPropertyName)(); 24 | typedef id (^MJReplacedKeyFromPropertyName121)(NSString *propertyName); 25 | /** 数组中需要转换的模型类 */ 26 | typedef NSDictionary * (^MJObjectClassInArray)(); 27 | /** 用于过滤字典中的值 */ 28 | typedef id (^MJNewValueFromOldValue)(id object, id oldValue, MJProperty *property); 29 | 30 | /** 31 | * 成员属性相关的扩展 32 | */ 33 | @interface NSObject (MJProperty) 34 | #pragma mark - 遍历 35 | /** 36 | * 遍历所有的成员 37 | */ 38 | + (void)mj_enumerateProperties:(MJPropertiesEnumeration)enumeration; 39 | 40 | #pragma mark - 新值配置 41 | /** 42 | * 用于过滤字典中的值 43 | * 44 | * @param newValueFormOldValue 用于过滤字典中的值 45 | */ 46 | + (void)mj_setupNewValueFromOldValue:(MJNewValueFromOldValue)newValueFormOldValue; 47 | + (id)mj_getNewValueFromObject:(__unsafe_unretained id)object oldValue:(__unsafe_unretained id)oldValue property:(__unsafe_unretained MJProperty *)property; 48 | 49 | #pragma mark - key配置 50 | /** 51 | * 将属性名换为其他key去字典中取值 52 | * 53 | * @param replacedKeyFromPropertyName 将属性名换为其他key去字典中取值 54 | */ 55 | + (void)mj_setupReplacedKeyFromPropertyName:(MJReplacedKeyFromPropertyName)replacedKeyFromPropertyName; 56 | /** 57 | * 将属性名换为其他key去字典中取值 58 | * 59 | * @param replacedKeyFromPropertyName121 将属性名换为其他key去字典中取值 60 | */ 61 | + (void)mj_setupReplacedKeyFromPropertyName121:(MJReplacedKeyFromPropertyName121)replacedKeyFromPropertyName121; 62 | 63 | #pragma mark - array model class配置 64 | /** 65 | * 数组中需要转换的模型类 66 | * 67 | * @param objectClassInArray 数组中需要转换的模型类 68 | */ 69 | + (void)mj_setupObjectClassInArray:(MJObjectClassInArray)objectClassInArray; 70 | @end 71 | 72 | @interface NSObject (MJPropertyDeprecated_v_2_5_16) 73 | + (void)enumerateProperties:(MJPropertiesEnumeration)enumeration MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 74 | + (void)setupNewValueFromOldValue:(MJNewValueFromOldValue)newValueFormOldValue MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 75 | + (id)getNewValueFromObject:(__unsafe_unretained id)object oldValue:(__unsafe_unretained id)oldValue property:(__unsafe_unretained MJProperty *)property MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 76 | + (void)setupReplacedKeyFromPropertyName:(MJReplacedKeyFromPropertyName)replacedKeyFromPropertyName MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 77 | + (void)setupReplacedKeyFromPropertyName121:(MJReplacedKeyFromPropertyName121)replacedKeyFromPropertyName121 MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 78 | + (void)setupObjectClassInArray:(MJObjectClassInArray)objectClassInArray MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 79 | @end -------------------------------------------------------------------------------- /TBModelTool/MJExtension/NSObject+MJProperty.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+MJProperty.m 3 | // MJExtensionExample 4 | // 5 | // Created by MJ Lee on 15/4/17. 6 | // Copyright (c) 2015年 小码哥. All rights reserved. 7 | // 8 | 9 | #import "NSObject+MJProperty.h" 10 | #import "NSObject+MJKeyValue.h" 11 | #import "NSObject+MJCoding.h" 12 | #import "NSObject+MJClass.h" 13 | #import "MJProperty.h" 14 | #import "MJFoundation.h" 15 | #import 16 | 17 | #pragma clang diagnostic push 18 | #pragma clang diagnostic ignored "-Wundeclared-selector" 19 | #pragma clang diagnostic ignored "-Warc-performSelector-leaks" 20 | 21 | static const char MJReplacedKeyFromPropertyNameKey = '\0'; 22 | static const char MJReplacedKeyFromPropertyName121Key = '\0'; 23 | static const char MJNewValueFromOldValueKey = '\0'; 24 | static const char MJObjectClassInArrayKey = '\0'; 25 | 26 | static const char MJCachedPropertiesKey = '\0'; 27 | 28 | @implementation NSObject (Property) 29 | 30 | static NSMutableDictionary *replacedKeyFromPropertyNameDict_; 31 | static NSMutableDictionary *replacedKeyFromPropertyName121Dict_; 32 | static NSMutableDictionary *newValueFromOldValueDict_; 33 | static NSMutableDictionary *objectClassInArrayDict_; 34 | static NSMutableDictionary *cachedPropertiesDict_; 35 | 36 | + (void)load 37 | { 38 | replacedKeyFromPropertyNameDict_ = [NSMutableDictionary dictionary]; 39 | replacedKeyFromPropertyName121Dict_ = [NSMutableDictionary dictionary]; 40 | newValueFromOldValueDict_ = [NSMutableDictionary dictionary]; 41 | objectClassInArrayDict_ = [NSMutableDictionary dictionary]; 42 | cachedPropertiesDict_ = [NSMutableDictionary dictionary]; 43 | } 44 | 45 | + (NSMutableDictionary *)dictForKey:(const void *)key 46 | { 47 | @synchronized (self) { 48 | if (key == &MJReplacedKeyFromPropertyNameKey) return replacedKeyFromPropertyNameDict_; 49 | if (key == &MJReplacedKeyFromPropertyName121Key) return replacedKeyFromPropertyName121Dict_; 50 | if (key == &MJNewValueFromOldValueKey) return newValueFromOldValueDict_; 51 | if (key == &MJObjectClassInArrayKey) return objectClassInArrayDict_; 52 | if (key == &MJCachedPropertiesKey) return cachedPropertiesDict_; 53 | return nil; 54 | } 55 | } 56 | 57 | #pragma mark - --私有方法-- 58 | + (id)propertyKey:(NSString *)propertyName 59 | { 60 | MJExtensionAssertParamNotNil2(propertyName, nil); 61 | 62 | __block id key = nil; 63 | // 查看有没有需要替换的key 64 | if ([self respondsToSelector:@selector(mj_replacedKeyFromPropertyName121:)]) { 65 | key = [self mj_replacedKeyFromPropertyName121:propertyName]; 66 | } 67 | // 兼容旧版本 68 | if ([self respondsToSelector:@selector(replacedKeyFromPropertyName121:)]) { 69 | key = [self performSelector:@selector(replacedKeyFromPropertyName121) withObject:propertyName]; 70 | } 71 | 72 | // 调用block 73 | if (!key) { 74 | [self mj_enumerateAllClasses:^(__unsafe_unretained Class c, BOOL *stop) { 75 | MJReplacedKeyFromPropertyName121 block = objc_getAssociatedObject(c, &MJReplacedKeyFromPropertyName121Key); 76 | if (block) { 77 | key = block(propertyName); 78 | } 79 | if (key) *stop = YES; 80 | }]; 81 | } 82 | 83 | // 查看有没有需要替换的key 84 | if ((!key || [key isEqual:propertyName]) && [self respondsToSelector:@selector(mj_replacedKeyFromPropertyName)]) { 85 | key = [self mj_replacedKeyFromPropertyName][propertyName]; 86 | } 87 | // 兼容旧版本 88 | if ((!key || [key isEqual:propertyName]) && [self respondsToSelector:@selector(replacedKeyFromPropertyName)]) { 89 | key = [self performSelector:@selector(replacedKeyFromPropertyName)][propertyName]; 90 | } 91 | 92 | if (!key || [key isEqual:propertyName]) { 93 | [self mj_enumerateAllClasses:^(__unsafe_unretained Class c, BOOL *stop) { 94 | NSDictionary *dict = objc_getAssociatedObject(c, &MJReplacedKeyFromPropertyNameKey); 95 | if (dict) { 96 | key = dict[propertyName]; 97 | } 98 | if (key && ![key isEqual:propertyName]) *stop = YES; 99 | }]; 100 | } 101 | 102 | // 2.用属性名作为key 103 | if (!key) key = propertyName; 104 | 105 | return key; 106 | } 107 | 108 | + (Class)propertyObjectClassInArray:(NSString *)propertyName 109 | { 110 | __block id clazz = nil; 111 | if ([self respondsToSelector:@selector(mj_objectClassInArray)]) { 112 | clazz = [self mj_objectClassInArray][propertyName]; 113 | } 114 | // 兼容旧版本 115 | if ([self respondsToSelector:@selector(objectClassInArray)]) { 116 | clazz = [self performSelector:@selector(objectClassInArray)][propertyName]; 117 | } 118 | 119 | if (!clazz) { 120 | [self mj_enumerateAllClasses:^(__unsafe_unretained Class c, BOOL *stop) { 121 | NSDictionary *dict = objc_getAssociatedObject(c, &MJObjectClassInArrayKey); 122 | if (dict) { 123 | clazz = dict[propertyName]; 124 | } 125 | if (clazz) *stop = YES; 126 | }]; 127 | } 128 | 129 | // 如果是NSString类型 130 | if ([clazz isKindOfClass:[NSString class]]) { 131 | clazz = NSClassFromString(clazz); 132 | } 133 | return clazz; 134 | } 135 | 136 | #pragma mark - --公共方法-- 137 | + (void)mj_enumerateProperties:(MJPropertiesEnumeration)enumeration 138 | { 139 | // 获得成员变量 140 | NSArray *cachedProperties = [self properties]; 141 | 142 | // 遍历成员变量 143 | BOOL stop = NO; 144 | for (MJProperty *property in cachedProperties) { 145 | enumeration(property, &stop); 146 | if (stop) break; 147 | } 148 | } 149 | 150 | #pragma mark - 公共方法 151 | + (NSMutableArray *)properties 152 | { 153 | NSMutableArray *cachedProperties = [self dictForKey:&MJCachedPropertiesKey][NSStringFromClass(self)]; 154 | 155 | if (cachedProperties == nil) { 156 | cachedProperties = [NSMutableArray array]; 157 | 158 | [self mj_enumerateClasses:^(__unsafe_unretained Class c, BOOL *stop) { 159 | // 1.获得所有的成员变量 160 | unsigned int outCount = 0; 161 | objc_property_t *properties = class_copyPropertyList(c, &outCount); 162 | 163 | // 2.遍历每一个成员变量 164 | for (unsigned int i = 0; i 10 | #import "MJExtensionConst.h" 11 | 12 | @interface NSString (MJExtension) 13 | /** 14 | * 驼峰转下划线(loveYou -> love_you) 15 | */ 16 | - (NSString *)mj_underlineFromCamel; 17 | /** 18 | * 下划线转驼峰(love_you -> loveYou) 19 | */ 20 | - (NSString *)mj_camelFromUnderline; 21 | /** 22 | * 首字母变大写 23 | */ 24 | - (NSString *)mj_firstCharUpper; 25 | /** 26 | * 首字母变小写 27 | */ 28 | - (NSString *)mj_firstCharLower; 29 | 30 | - (BOOL)mj_isPureInt; 31 | 32 | - (NSURL *)mj_url; 33 | @end 34 | 35 | @interface NSString (MJExtensionDeprecated_v_2_5_16) 36 | - (NSString *)underlineFromCamel MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 37 | - (NSString *)camelFromUnderline MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 38 | - (NSString *)firstCharUpper MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 39 | - (NSString *)firstCharLower MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 40 | - (BOOL)isPureInt MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 41 | - (NSURL *)url MJExtensionDeprecated("请在方法名前面加上mj_前缀,使用mj_***"); 42 | @end 43 | -------------------------------------------------------------------------------- /TBModelTool/MJExtension/NSString+MJExtension.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+MJExtension.m 3 | // MJExtensionExample 4 | // 5 | // Created by MJ Lee on 15/6/7. 6 | // Copyright (c) 2015年 小码哥. All rights reserved. 7 | // 8 | 9 | #import "NSString+MJExtension.h" 10 | 11 | @implementation NSString (MJExtension) 12 | - (NSString *)mj_underlineFromCamel 13 | { 14 | if (self.length == 0) return self; 15 | NSMutableString *string = [NSMutableString string]; 16 | for (NSUInteger i = 0; i= 2) [string appendString:[cmp substringFromIndex:1]]; 40 | } else { 41 | [string appendString:cmp]; 42 | } 43 | } 44 | return string; 45 | } 46 | 47 | - (NSString *)mj_firstCharLower 48 | { 49 | if (self.length == 0) return self; 50 | NSMutableString *string = [NSMutableString string]; 51 | [string appendString:[NSString stringWithFormat:@"%c", [self characterAtIndex:0]].lowercaseString]; 52 | if (self.length >= 2) [string appendString:[self substringFromIndex:1]]; 53 | return string; 54 | } 55 | 56 | - (NSString *)mj_firstCharUpper 57 | { 58 | if (self.length == 0) return self; 59 | NSMutableString *string = [NSMutableString string]; 60 | [string appendString:[NSString stringWithFormat:@"%c", [self characterAtIndex:0]].uppercaseString]; 61 | if (self.length >= 2) [string appendString:[self substringFromIndex:1]]; 62 | return string; 63 | } 64 | 65 | - (BOOL)mj_isPureInt 66 | { 67 | NSScanner *scan = [NSScanner scannerWithString:self]; 68 | int val; 69 | return [scan scanInt:&val] && [scan isAtEnd]; 70 | } 71 | 72 | - (NSURL *)mj_url 73 | { 74 | // [self stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet characterSetWithCharactersInString:@"!$&'()*+,-./:;=?@_~%#[]"]]; 75 | #pragma clang diagnostic push 76 | #pragma clang diagnostic ignored"-Wdeprecated-declarations" 77 | return [NSURL URLWithString:(NSString *)CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef)self, (CFStringRef)@"!$&'()*+,-./:;=?@_~%#[]", NULL,kCFStringEncodingUTF8))]; 78 | #pragma clang diagnostic pop 79 | } 80 | @end 81 | 82 | @implementation NSString (MJExtensionDeprecated_v_2_5_16) 83 | - (NSString *)underlineFromCamel 84 | { 85 | return self.mj_underlineFromCamel; 86 | } 87 | 88 | - (NSString *)camelFromUnderline 89 | { 90 | return self.mj_camelFromUnderline; 91 | } 92 | 93 | - (NSString *)firstCharLower 94 | { 95 | return self.mj_firstCharLower; 96 | } 97 | 98 | - (NSString *)firstCharUpper 99 | { 100 | return self.mj_firstCharUpper; 101 | } 102 | 103 | - (BOOL)isPureInt 104 | { 105 | return self.mj_isPureInt; 106 | } 107 | 108 | - (NSURL *)url 109 | { 110 | return self.mj_url; 111 | } 112 | @end 113 | -------------------------------------------------------------------------------- /TBModelTool/Model/NSObject+YYModel.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+YYModel.h 3 | // YYKit 4 | // 5 | // Created by ibireme on 15/5/10. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | /** 17 | Provide some data-model method: 18 | 19 | * Convert json to any object, or convert any object to json. 20 | * Set object properties with a key-value dictionary (like KVC). 21 | * Implementations of `NSCoding`, `NSCopying`, `-hash` and `-isEqual:`. 22 | 23 | See `YYModel` protocol for custom methods. 24 | 25 | 26 | Sample Code: 27 | 28 | ********************** json convertor ********************* 29 | @interface YYAuthor : NSObject 30 | @property (nonatomic, strong) NSString *name; 31 | @property (nonatomic, assign) NSDate *birthday; 32 | @end 33 | @implementation YYAuthor 34 | @end 35 | 36 | @interface YYBook : NSObject 37 | @property (nonatomic, copy) NSString *name; 38 | @property (nonatomic, assign) NSUInteger pages; 39 | @property (nonatomic, strong) YYAuthor *author; 40 | @end 41 | @implementation YYBook 42 | @end 43 | 44 | int main() { 45 | // create model from json 46 | YYBook *book = [YYBook modelWithJSON:@"{\"name\": \"Harry Potter\", \"pages\": 256, \"author\": {\"name\": \"J.K.Rowling\", \"birthday\": \"1965-07-31\" }}"]; 47 | 48 | // convert model to json 49 | NSString *json = [book modelToJSONString]; 50 | // {"author":{"name":"J.K.Rowling","birthday":"1965-07-31T00:00:00+0000"},"name":"Harry Potter","pages":256} 51 | } 52 | 53 | ********************** Coding/Copying/hash/equal ********************* 54 | @interface YYShadow :NSObject 55 | @property (nonatomic, copy) NSString *name; 56 | @property (nonatomic, assign) CGSize size; 57 | @end 58 | 59 | @implementation YYShadow 60 | - (void)encodeWithCoder:(NSCoder *)aCoder { [self modelEncodeWithCoder:aCoder]; } 61 | - (id)initWithCoder:(NSCoder *)aDecoder { self = [super init]; return [self modelInitWithCoder:aDecoder]; } 62 | - (id)copyWithZone:(NSZone *)zone { return [self modelCopy]; } 63 | - (NSUInteger)hash { return [self modelHash]; } 64 | - (BOOL)isEqual:(id)object { return [self modelIsEqual:object]; } 65 | @end 66 | 67 | */ 68 | @interface NSObject (YYModel) 69 | 70 | /** 71 | Creates and returns a new instance of the receiver from a json. 72 | This method is thread-safe. 73 | 74 | @param json A json object in `NSDictionary`, `NSString` or `NSData`. 75 | 76 | @return A new instance created from the json, or nil if an error occurs. 77 | */ 78 | + (nullable instancetype)modelWithJSON:(id)json; 79 | 80 | /** 81 | Creates and returns a new instance of the receiver from a key-value dictionary. 82 | This method is thread-safe. 83 | 84 | @param dictionary A key-value dictionary mapped to the instance's properties. 85 | Any invalid key-value pair in dictionary will be ignored. 86 | 87 | @return A new instance created from the dictionary, or nil if an error occurs. 88 | 89 | @discussion The key in `dictionary` will mapped to the reciever's property name, 90 | and the value will set to the property. If the value's type does not match the 91 | property, this method will try to convert the value based on these rules: 92 | 93 | `NSString` or `NSNumber` -> c number, such as BOOL, int, long, float, NSUInteger... 94 | `NSString` -> NSDate, parsed with format "yyyy-MM-dd'T'HH:mm:ssZ", "yyyy-MM-dd HH:mm:ss" or "yyyy-MM-dd". 95 | `NSString` -> NSURL. 96 | `NSValue` -> struct or union, such as CGRect, CGSize, ... 97 | `NSString` -> SEL, Class. 98 | */ 99 | + (nullable instancetype)modelWithDictionary:(NSDictionary *)dictionary; 100 | 101 | /** 102 | Set the receiver's properties with a json object. 103 | 104 | @discussion Any invalid data in json will be ignored. 105 | 106 | @param json A json object of `NSDictionary`, `NSString` or `NSData`, mapped to the 107 | receiver's properties. 108 | 109 | @return Whether succeed. 110 | */ 111 | - (BOOL)modelSetWithJSON:(id)json; 112 | 113 | /** 114 | Set the receiver's properties with a key-value dictionary. 115 | 116 | @param dic A key-value dictionary mapped to the receiver's properties. 117 | Any invalid key-value pair in dictionary will be ignored. 118 | 119 | @discussion The key in `dictionary` will mapped to the reciever's property name, 120 | and the value will set to the property. If the value's type doesn't match the 121 | property, this method will try to convert the value based on these rules: 122 | 123 | `NSString`, `NSNumber` -> c number, such as BOOL, int, long, float, NSUInteger... 124 | `NSString` -> NSDate, parsed with format "yyyy-MM-dd'T'HH:mm:ssZ", "yyyy-MM-dd HH:mm:ss" or "yyyy-MM-dd". 125 | `NSString` -> NSURL. 126 | `NSValue` -> struct or union, such as CGRect, CGSize, ... 127 | `NSString` -> SEL, Class. 128 | 129 | @return Whether succeed. 130 | */ 131 | - (BOOL)modelSetWithDictionary:(NSDictionary *)dic; 132 | 133 | /** 134 | Generate a json object from the receiver's properties. 135 | 136 | @return A json object in `NSDictionary` or `NSArray`, or nil if an error occurs. 137 | See [NSJSONSerialization isValidJSONObject] for more information. 138 | 139 | @discussion Any of the invalid property is ignored. 140 | If the reciver is `NSArray`, `NSDictionary` or `NSSet`, it just convert 141 | the inner object to json object. 142 | */ 143 | - (nullable id)modelToJSONObject; 144 | 145 | /** 146 | Generate a json string's data from the receiver's properties. 147 | 148 | @return A json string's data, or nil if an error occurs. 149 | 150 | @discussion Any of the invalid property is ignored. 151 | If the reciver is `NSArray`, `NSDictionary` or `NSSet`, it will also convert the 152 | inner object to json string. 153 | */ 154 | - (nullable NSData *)modelToJSONData; 155 | 156 | /** 157 | Generate a json string from the receiver's properties. 158 | 159 | @return A json string, or nil if an error occurs. 160 | 161 | @discussion Any of the invalid property is ignored. 162 | If the reciver is `NSArray`, `NSDictionary` or `NSSet`, it will also convert the 163 | inner object to json string. 164 | */ 165 | - (nullable NSString *)modelToJSONString; 166 | 167 | /** 168 | Copy a instance with the receiver's properties. 169 | 170 | @return A copied instance, or nil if an error occurs. 171 | */ 172 | - (nullable id)modelCopy; 173 | 174 | /** 175 | Encode the receiver's properties to a coder. 176 | 177 | @param aCoder An archiver object. 178 | */ 179 | - (void)modelEncodeWithCoder:(NSCoder *)aCoder; 180 | 181 | /** 182 | Decode the receiver's properties from a decoder. 183 | 184 | @param aDecoder An archiver object. 185 | 186 | @return self 187 | */ 188 | - (id)modelInitWithCoder:(NSCoder *)aDecoder; 189 | 190 | /** 191 | Get a hash code with the receiver's properties. 192 | 193 | @return Hash code. 194 | */ 195 | - (NSUInteger)modelHash; 196 | 197 | /** 198 | Compares the receiver with another object for equality, based on properties. 199 | 200 | @param model Another object. 201 | 202 | @return `YES` if the reciever is equal to the object, otherwise `NO`. 203 | */ 204 | - (BOOL)modelIsEqual:(id)model; 205 | 206 | /** 207 | Description method for debugging purposes based on properties. 208 | 209 | @return A string that describes the contents of the receiver. 210 | */ 211 | - (NSString *)modelDescription; 212 | 213 | @end 214 | 215 | 216 | 217 | /** 218 | Provide some data-model method for NSArray. 219 | */ 220 | @interface NSArray (YYModel) 221 | 222 | /** 223 | Creates and returns an array from a json-array. 224 | This method is thread-safe. 225 | 226 | @param cls The instance's class in array. 227 | @param json A json array of `NSArray`, `NSString` or `NSData`. 228 | Example: [{"name","Mary"},{name:"Joe"}] 229 | 230 | @return A array, or nil if an error occurs. 231 | */ 232 | + (nullable NSArray *)modelArrayWithClass:(Class)cls json:(id)json; 233 | 234 | @end 235 | 236 | 237 | 238 | /** 239 | Provide some data-model method for NSDictionary. 240 | */ 241 | @interface NSDictionary (YYModel) 242 | 243 | /** 244 | Creates and returns a dictionary from a json. 245 | This method is thread-safe. 246 | 247 | @param cls The value instance's class in dictionary. 248 | @param json A json dictionary of `NSDictionary`, `NSString` or `NSData`. 249 | Example: {"user1":{"name","Mary"}, "user2": {name:"Joe"}} 250 | 251 | @return A dictionary, or nil if an error occurs. 252 | */ 253 | + (nullable NSDictionary *)modelDictionaryWithClass:(Class)cls json:(id)json; 254 | @end 255 | 256 | 257 | 258 | /** 259 | If the default model transform does not fit to your model class, implement one or 260 | more method in this protocol to change the default key-value transform process. 261 | There's no need to add '' to your class header. 262 | */ 263 | @protocol YYModel 264 | @optional 265 | 266 | /** 267 | Custom property mapper. 268 | 269 | @discussion If the key in JSON/Dictionary does not match to the model's property name, 270 | implements this method and returns the additional mapper. 271 | 272 | Example: 273 | 274 | json: 275 | { 276 | "n":"Harry Pottery", 277 | "p": 256, 278 | "ext" : { 279 | "desc" : "A book written by J.K.Rowling." 280 | }, 281 | "ID" : 100010 282 | } 283 | 284 | model: 285 | @interface YYBook : NSObject 286 | @property NSString *name; 287 | @property NSInteger page; 288 | @property NSString *desc; 289 | @property NSString *bookID; 290 | @end 291 | 292 | @implementation YYBook 293 | + (NSDictionary *)modelCustomPropertyMapper { 294 | return @{@"name" : @"n", 295 | @"page" : @"p", 296 | @"desc" : @"ext.desc", 297 | @"bookID": @[@"id", @"ID", @"book_id"]}; 298 | } 299 | @end 300 | 301 | @return A custom mapper for properties. 302 | */ 303 | + (nullable NSDictionary *)modelCustomPropertyMapper; 304 | 305 | /** 306 | The generic class mapper for container properties. 307 | 308 | @discussion If the property is a container object, such as NSArray/NSSet/NSDictionary, 309 | implements this method and returns a property->class mapper, tells which kind of 310 | object will be add to the array/set/dictionary. 311 | 312 | Example: 313 | @class YYShadow, YYBorder, YYAttachment; 314 | 315 | @interface YYAttributes 316 | @property NSString *name; 317 | @property NSArray *shadows; 318 | @property NSSet *borders; 319 | @property NSDictionary *attachments; 320 | @end 321 | 322 | @implementation YYAttributes 323 | + (NSDictionary *)modelContainerPropertyGenericClass { 324 | return @{@"shadows" : [YYShadow class], 325 | @"borders" : YYBorder.class, 326 | @"attachments" : @"YYAttachment" }; 327 | } 328 | @end 329 | 330 | @return A class mapper. 331 | */ 332 | + (nullable NSDictionary *)modelContainerPropertyGenericClass; 333 | 334 | /** 335 | If you need to create instances of different classes during json->object transform, 336 | use the method to choose custom class based on dictionary data. 337 | 338 | @discussion If the model implements this method, it will be called to determine resulting class 339 | during `+modelWithJSON:`, `+modelWithDictionary:`, conveting object of properties of parent objects 340 | (both singular and containers via `+modelContainerPropertyGenericClass`). 341 | 342 | Example: 343 | @class YYCircle, YYRectangle, YYLine; 344 | 345 | @implementation YYShape 346 | 347 | + (Class)modelCustomClassForDictionary:(NSDictionary*)dictionary { 348 | if (dictionary[@"radius"] != nil) { 349 | return [YYCircle class]; 350 | } else if (dictionary[@"width"] != nil) { 351 | return [YYRectangle class]; 352 | } else if (dictionary[@"y2"] != nil) { 353 | return [YYLine class]; 354 | } else { 355 | return [self class]; 356 | } 357 | } 358 | 359 | @end 360 | 361 | @param dictionary The json/kv dictionary. 362 | 363 | @return Class to create from this dictionary, `nil` to use current class. 364 | 365 | */ 366 | + (nullable Class)modelCustomClassForDictionary:(NSDictionary *)dictionary; 367 | 368 | /** 369 | All the properties in blacklist will be ignored in model transform process. 370 | Returns nil to ignore this feature. 371 | 372 | @return An array of property's name. 373 | */ 374 | + (nullable NSArray *)modelPropertyBlacklist; 375 | 376 | /** 377 | If a property is not in the whitelist, it will be ignored in model transform process. 378 | Returns nil to ignore this feature. 379 | 380 | @return An array of property's name. 381 | */ 382 | + (nullable NSArray *)modelPropertyWhitelist; 383 | 384 | /** 385 | This method's behavior is similar to `- (BOOL)modelCustomTransformFromDictionary:(NSDictionary *)dic;`, 386 | but be called before the model transform. 387 | 388 | @discussion If the model implements this method, it will be called before 389 | `+modelWithJSON:`, `+modelWithDictionary:`, `-modelSetWithJSON:` and `-modelSetWithDictionary:`. 390 | If this method returns nil, the transform process will ignore this model. 391 | 392 | @param dic The json/kv dictionary. 393 | 394 | @return Returns the modified dictionary, or nil to ignore this model. 395 | */ 396 | - (NSDictionary *)modelCustomWillTransformFromDictionary:(NSDictionary *)dic; 397 | 398 | /** 399 | If the default json-to-model transform does not fit to your model object, implement 400 | this method to do additional process. You can also use this method to validate the 401 | model's properties. 402 | 403 | @discussion If the model implements this method, it will be called at the end of 404 | `+modelWithJSON:`, `+modelWithDictionary:`, `-modelSetWithJSON:` and `-modelSetWithDictionary:`. 405 | If this method returns NO, the transform process will ignore this model. 406 | 407 | @param dic The json/kv dictionary. 408 | 409 | @return Returns YES if the model is valid, or NO to ignore this model. 410 | */ 411 | - (BOOL)modelCustomTransformFromDictionary:(NSDictionary *)dic; 412 | 413 | /** 414 | If the default model-to-json transform does not fit to your model class, implement 415 | this method to do additional process. You can also use this method to validate the 416 | json dictionary. 417 | 418 | @discussion If the model implements this method, it will be called at the end of 419 | `-modelToJSONObject` and `-modelToJSONString`. 420 | If this method returns NO, the transform process will ignore this json dictionary. 421 | 422 | @param dic The json dictionary. 423 | 424 | @return Returns YES if the model is valid, or NO to ignore this model. 425 | */ 426 | - (BOOL)modelCustomTransformToDictionary:(NSMutableDictionary *)dic; 427 | 428 | @end 429 | 430 | NS_ASSUME_NONNULL_END 431 | -------------------------------------------------------------------------------- /TBModelTool/Model/YYClassInfo.h: -------------------------------------------------------------------------------- 1 | // 2 | // YYClassInfo.h 3 | // YYKit 4 | // 5 | // Created by ibireme on 15/5/9. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import 13 | #import 14 | 15 | NS_ASSUME_NONNULL_BEGIN 16 | 17 | /** 18 | Type encoding's type. 19 | */ 20 | typedef NS_OPTIONS(NSUInteger, YYEncodingType) { 21 | YYEncodingTypeMask = 0xFF, ///< mask of type value 22 | YYEncodingTypeUnknown = 0, ///< unknown 23 | YYEncodingTypeVoid = 1, ///< void 24 | YYEncodingTypeBool = 2, ///< bool 25 | YYEncodingTypeInt8 = 3, ///< char / BOOL 26 | YYEncodingTypeUInt8 = 4, ///< unsigned char 27 | YYEncodingTypeInt16 = 5, ///< short 28 | YYEncodingTypeUInt16 = 6, ///< unsigned short 29 | YYEncodingTypeInt32 = 7, ///< int 30 | YYEncodingTypeUInt32 = 8, ///< unsigned int 31 | YYEncodingTypeInt64 = 9, ///< long long 32 | YYEncodingTypeUInt64 = 10, ///< unsigned long long 33 | YYEncodingTypeFloat = 11, ///< float 34 | YYEncodingTypeDouble = 12, ///< double 35 | YYEncodingTypeLongDouble = 13, ///< long double 36 | YYEncodingTypeObject = 14, ///< id 37 | YYEncodingTypeClass = 15, ///< Class 38 | YYEncodingTypeSEL = 16, ///< SEL 39 | YYEncodingTypeBlock = 17, ///< block 40 | YYEncodingTypePointer = 18, ///< void* 41 | YYEncodingTypeStruct = 19, ///< struct 42 | YYEncodingTypeUnion = 20, ///< union 43 | YYEncodingTypeCString = 21, ///< char* 44 | YYEncodingTypeCArray = 22, ///< char[10] (for example) 45 | 46 | YYEncodingTypeQualifierMask = 0xFF00, ///< mask of qualifier 47 | YYEncodingTypeQualifierConst = 1 << 8, ///< const 48 | YYEncodingTypeQualifierIn = 1 << 9, ///< in 49 | YYEncodingTypeQualifierInout = 1 << 10, ///< inout 50 | YYEncodingTypeQualifierOut = 1 << 11, ///< out 51 | YYEncodingTypeQualifierBycopy = 1 << 12, ///< bycopy 52 | YYEncodingTypeQualifierByref = 1 << 13, ///< byref 53 | YYEncodingTypeQualifierOneway = 1 << 14, ///< oneway 54 | 55 | YYEncodingTypePropertyMask = 0xFF0000, ///< mask of property 56 | YYEncodingTypePropertyReadonly = 1 << 16, ///< readonly 57 | YYEncodingTypePropertyCopy = 1 << 17, ///< copy 58 | YYEncodingTypePropertyRetain = 1 << 18, ///< retain 59 | YYEncodingTypePropertyNonatomic = 1 << 19, ///< nonatomic 60 | YYEncodingTypePropertyWeak = 1 << 20, ///< weak 61 | YYEncodingTypePropertyCustomGetter = 1 << 21, ///< getter= 62 | YYEncodingTypePropertyCustomSetter = 1 << 22, ///< setter= 63 | YYEncodingTypePropertyDynamic = 1 << 23, ///< @dynamic 64 | }; 65 | 66 | /** 67 | Get the type from a Type-Encoding string. 68 | 69 | @discussion See also: 70 | https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html 71 | https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html 72 | 73 | @param typeEncoding A Type-Encoding string. 74 | @return The encoding type. 75 | */ 76 | YYEncodingType YYEncodingGetType(const char *typeEncoding); 77 | 78 | 79 | /** 80 | Instance variable information. 81 | */ 82 | @interface YYClassIvarInfo : NSObject 83 | @property (nonatomic, assign, readonly) Ivar ivar; ///< ivar opaque struct 84 | @property (nonatomic, strong, readonly) NSString *name; ///< Ivar's name 85 | @property (nonatomic, assign, readonly) ptrdiff_t offset; ///< Ivar's offset 86 | @property (nonatomic, strong, readonly) NSString *typeEncoding; ///< Ivar's type encoding 87 | @property (nonatomic, assign, readonly) YYEncodingType type; ///< Ivar's type 88 | 89 | /** 90 | Creates and returns an ivar info object. 91 | 92 | @param ivar ivar opaque struct 93 | @return A new object, or nil if an error occurs. 94 | */ 95 | - (instancetype)initWithIvar:(Ivar)ivar; 96 | @end 97 | 98 | 99 | /** 100 | Method information. 101 | */ 102 | @interface YYClassMethodInfo : NSObject 103 | @property (nonatomic, assign, readonly) Method method; ///< method opaque struct 104 | @property (nonatomic, strong, readonly) NSString *name; ///< method name 105 | @property (nonatomic, assign, readonly) SEL sel; ///< method's selector 106 | @property (nonatomic, assign, readonly) IMP imp; ///< method's implementation 107 | @property (nonatomic, strong, readonly) NSString *typeEncoding; ///< method's parameter and return types 108 | @property (nonatomic, strong, readonly) NSString *returnTypeEncoding; ///< return value's type 109 | @property (nullable, nonatomic, strong, readonly) NSArray *argumentTypeEncodings; ///< array of arguments' type 110 | 111 | /** 112 | Creates and returns a method info object. 113 | 114 | @param method method opaque struct 115 | @return A new object, or nil if an error occurs. 116 | */ 117 | - (instancetype)initWithMethod:(Method)method; 118 | @end 119 | 120 | 121 | /** 122 | Property information. 123 | */ 124 | @interface YYClassPropertyInfo : NSObject 125 | @property (nonatomic, assign, readonly) objc_property_t property; ///< property's opaque struct 126 | @property (nonatomic, strong, readonly) NSString *name; ///< property's name 127 | @property (nonatomic, assign, readonly) YYEncodingType type; ///< property's type 128 | @property (nonatomic, strong, readonly) NSString *typeEncoding; ///< property's encoding value 129 | @property (nonatomic, strong, readonly) NSString *ivarName; ///< property's ivar name 130 | @property (nullable, nonatomic, assign, readonly) Class cls; ///< may be nil 131 | @property (nullable, nonatomic, strong, readonly) NSArray *protocols; ///< may nil 132 | @property (nonatomic, assign, readonly) SEL getter; ///< getter (nonnull) 133 | @property (nonatomic, assign, readonly) SEL setter; ///< setter (nonnull) 134 | 135 | /** 136 | Creates and returns a property info object. 137 | 138 | @param property property opaque struct 139 | @return A new object, or nil if an error occurs. 140 | */ 141 | - (instancetype)initWithProperty:(objc_property_t)property; 142 | @end 143 | 144 | 145 | /** 146 | Class information for a class. 147 | */ 148 | @interface YYClassInfo : NSObject 149 | @property (nonatomic, assign, readonly) Class cls; ///< class object 150 | @property (nullable, nonatomic, assign, readonly) Class superCls; ///< super class object 151 | @property (nullable, nonatomic, assign, readonly) Class metaCls; ///< class's meta class object 152 | @property (nonatomic, readonly) BOOL isMeta; ///< whether this class is meta class 153 | @property (nonatomic, strong, readonly) NSString *name; ///< class name 154 | @property (nullable, nonatomic, strong, readonly) YYClassInfo *superClassInfo; ///< super class's class info 155 | @property (nullable, nonatomic, strong, readonly) NSDictionary *ivarInfos; ///< ivars 156 | @property (nullable, nonatomic, strong, readonly) NSDictionary *methodInfos; ///< methods 157 | @property (nullable, nonatomic, strong, readonly) NSDictionary *propertyInfos; ///< properties 158 | 159 | /** 160 | If the class is changed (for example: you add a method to this class with 161 | 'class_addMethod()'), you should call this method to refresh the class info cache. 162 | 163 | After called this method, `needUpdate` will returns `YES`, and you should call 164 | 'classInfoWithClass' or 'classInfoWithClassName' to get the updated class info. 165 | */ 166 | - (void)setNeedUpdate; 167 | 168 | /** 169 | If this method returns `YES`, you should stop using this instance and call 170 | `classInfoWithClass` or `classInfoWithClassName` to get the updated class info. 171 | 172 | @return Whether this class info need update. 173 | */ 174 | - (BOOL)needUpdate; 175 | 176 | /** 177 | Get the class info of a specified Class. 178 | 179 | @discussion This method will cache the class info and super-class info 180 | at the first access to the Class. This method is thread-safe. 181 | 182 | @param cls A class. 183 | @return A class info, or nil if an error occurs. 184 | */ 185 | + (nullable instancetype)classInfoWithClass:(Class)cls; 186 | 187 | /** 188 | Get the class info of a specified Class. 189 | 190 | @discussion This method will cache the class info and super-class info 191 | at the first access to the Class. This method is thread-safe. 192 | 193 | @param className A class name. 194 | @return A class info, or nil if an error occurs. 195 | */ 196 | + (nullable instancetype)classInfoWithClassName:(NSString *)className; 197 | 198 | @end 199 | 200 | NS_ASSUME_NONNULL_END 201 | -------------------------------------------------------------------------------- /TBModelTool/Model/YYClassInfo.m: -------------------------------------------------------------------------------- 1 | // 2 | // YYClassInfo.m 3 | // YYKit 4 | // 5 | // Created by ibireme on 15/5/9. 6 | // Copyright (c) 2015 ibireme. 7 | // 8 | // This source code is licensed under the MIT-style license found in the 9 | // LICENSE file in the root directory of this source tree. 10 | // 11 | 12 | #import "YYClassInfo.h" 13 | #import 14 | 15 | YYEncodingType YYEncodingGetType(const char *typeEncoding) { 16 | char *type = (char *)typeEncoding; 17 | if (!type) return YYEncodingTypeUnknown; 18 | size_t len = strlen(type); 19 | if (len == 0) return YYEncodingTypeUnknown; 20 | 21 | YYEncodingType qualifier = 0; 22 | bool prefix = true; 23 | while (prefix) { 24 | switch (*type) { 25 | case 'r': { 26 | qualifier |= YYEncodingTypeQualifierConst; 27 | type++; 28 | } break; 29 | case 'n': { 30 | qualifier |= YYEncodingTypeQualifierIn; 31 | type++; 32 | } break; 33 | case 'N': { 34 | qualifier |= YYEncodingTypeQualifierInout; 35 | type++; 36 | } break; 37 | case 'o': { 38 | qualifier |= YYEncodingTypeQualifierOut; 39 | type++; 40 | } break; 41 | case 'O': { 42 | qualifier |= YYEncodingTypeQualifierBycopy; 43 | type++; 44 | } break; 45 | case 'R': { 46 | qualifier |= YYEncodingTypeQualifierByref; 47 | type++; 48 | } break; 49 | case 'V': { 50 | qualifier |= YYEncodingTypeQualifierOneway; 51 | type++; 52 | } break; 53 | default: { prefix = false; } break; 54 | } 55 | } 56 | 57 | len = strlen(type); 58 | if (len == 0) return YYEncodingTypeUnknown | qualifier; 59 | 60 | switch (*type) { 61 | case 'v': return YYEncodingTypeVoid | qualifier; 62 | case 'B': return YYEncodingTypeBool | qualifier; 63 | case 'c': return YYEncodingTypeInt8 | qualifier; 64 | case 'C': return YYEncodingTypeUInt8 | qualifier; 65 | case 's': return YYEncodingTypeInt16 | qualifier; 66 | case 'S': return YYEncodingTypeUInt16 | qualifier; 67 | case 'i': return YYEncodingTypeInt32 | qualifier; 68 | case 'I': return YYEncodingTypeUInt32 | qualifier; 69 | case 'l': return YYEncodingTypeInt32 | qualifier; 70 | case 'L': return YYEncodingTypeUInt32 | qualifier; 71 | case 'q': return YYEncodingTypeInt64 | qualifier; 72 | case 'Q': return YYEncodingTypeUInt64 | qualifier; 73 | case 'f': return YYEncodingTypeFloat | qualifier; 74 | case 'd': return YYEncodingTypeDouble | qualifier; 75 | case 'D': return YYEncodingTypeLongDouble | qualifier; 76 | case '#': return YYEncodingTypeClass | qualifier; 77 | case ':': return YYEncodingTypeSEL | qualifier; 78 | case '*': return YYEncodingTypeCString | qualifier; 79 | case '^': return YYEncodingTypePointer | qualifier; 80 | case '[': return YYEncodingTypeCArray | qualifier; 81 | case '(': return YYEncodingTypeUnion | qualifier; 82 | case '{': return YYEncodingTypeStruct | qualifier; 83 | case '@': { 84 | if (len == 2 && *(type + 1) == '?') 85 | return YYEncodingTypeBlock | qualifier; 86 | else 87 | return YYEncodingTypeObject | qualifier; 88 | } 89 | default: return YYEncodingTypeUnknown | qualifier; 90 | } 91 | } 92 | 93 | @implementation YYClassIvarInfo 94 | 95 | - (instancetype)initWithIvar:(Ivar)ivar { 96 | if (!ivar) return nil; 97 | self = [super init]; 98 | _ivar = ivar; 99 | const char *name = ivar_getName(ivar); 100 | if (name) { 101 | _name = [NSString stringWithUTF8String:name]; 102 | } 103 | _offset = ivar_getOffset(ivar); 104 | const char *typeEncoding = ivar_getTypeEncoding(ivar); 105 | if (typeEncoding) { 106 | _typeEncoding = [NSString stringWithUTF8String:typeEncoding]; 107 | _type = YYEncodingGetType(typeEncoding); 108 | } 109 | return self; 110 | } 111 | 112 | @end 113 | 114 | @implementation YYClassMethodInfo 115 | 116 | - (instancetype)initWithMethod:(Method)method { 117 | if (!method) return nil; 118 | self = [super init]; 119 | _method = method; 120 | _sel = method_getName(method); 121 | _imp = method_getImplementation(method); 122 | const char *name = sel_getName(_sel); 123 | if (name) { 124 | _name = [NSString stringWithUTF8String:name]; 125 | } 126 | const char *typeEncoding = method_getTypeEncoding(method); 127 | if (typeEncoding) { 128 | _typeEncoding = [NSString stringWithUTF8String:typeEncoding]; 129 | } 130 | char *returnType = method_copyReturnType(method); 131 | if (returnType) { 132 | _returnTypeEncoding = [NSString stringWithUTF8String:returnType]; 133 | free(returnType); 134 | } 135 | unsigned int argumentCount = method_getNumberOfArguments(method); 136 | if (argumentCount > 0) { 137 | NSMutableArray *argumentTypes = [NSMutableArray new]; 138 | for (unsigned int i = 0; i < argumentCount; i++) { 139 | char *argumentType = method_copyArgumentType(method, i); 140 | NSString *type = argumentType ? [NSString stringWithUTF8String:argumentType] : nil; 141 | [argumentTypes addObject:type ? type : @""]; 142 | if (argumentType) free(argumentType); 143 | } 144 | _argumentTypeEncodings = argumentTypes; 145 | } 146 | return self; 147 | } 148 | 149 | @end 150 | 151 | @implementation YYClassPropertyInfo 152 | 153 | - (instancetype)initWithProperty:(objc_property_t)property { 154 | if (!property) return nil; 155 | self = [super init]; 156 | _property = property; 157 | const char *name = property_getName(property); 158 | if (name) { 159 | _name = [NSString stringWithUTF8String:name]; 160 | } 161 | 162 | YYEncodingType type = 0; 163 | unsigned int attrCount; 164 | objc_property_attribute_t *attrs = property_copyAttributeList(property, &attrCount); 165 | for (unsigned int i = 0; i < attrCount; i++) { 166 | switch (attrs[i].name[0]) { 167 | case 'T': { // Type encoding 168 | if (attrs[i].value) { 169 | _typeEncoding = [NSString stringWithUTF8String:attrs[i].value]; 170 | type = YYEncodingGetType(attrs[i].value); 171 | 172 | if ((type & YYEncodingTypeMask) == YYEncodingTypeObject && _typeEncoding.length) { 173 | NSScanner *scanner = [NSScanner scannerWithString:_typeEncoding]; 174 | if (![scanner scanString:@"@\"" intoString:NULL]) continue; 175 | 176 | NSString *clsName = nil; 177 | if ([scanner scanUpToCharactersFromSet: [NSCharacterSet characterSetWithCharactersInString:@"\"<"] intoString:&clsName]) { 178 | if (clsName.length) _cls = objc_getClass(clsName.UTF8String); 179 | } 180 | 181 | NSMutableArray *protocols = nil; 182 | while ([scanner scanString:@"<" intoString:NULL]) { 183 | NSString* protocol = nil; 184 | if ([scanner scanUpToString:@">" intoString: &protocol]) { 185 | if (protocol.length) { 186 | if (!protocols) protocols = [NSMutableArray new]; 187 | [protocols addObject:protocol]; 188 | } 189 | } 190 | [scanner scanString:@">" intoString:NULL]; 191 | } 192 | _protocols = protocols; 193 | } 194 | } 195 | } break; 196 | case 'V': { // Instance variable 197 | if (attrs[i].value) { 198 | _ivarName = [NSString stringWithUTF8String:attrs[i].value]; 199 | } 200 | } break; 201 | case 'R': { 202 | type |= YYEncodingTypePropertyReadonly; 203 | } break; 204 | case 'C': { 205 | type |= YYEncodingTypePropertyCopy; 206 | } break; 207 | case '&': { 208 | type |= YYEncodingTypePropertyRetain; 209 | } break; 210 | case 'N': { 211 | type |= YYEncodingTypePropertyNonatomic; 212 | } break; 213 | case 'D': { 214 | type |= YYEncodingTypePropertyDynamic; 215 | } break; 216 | case 'W': { 217 | type |= YYEncodingTypePropertyWeak; 218 | } break; 219 | case 'G': { 220 | type |= YYEncodingTypePropertyCustomGetter; 221 | if (attrs[i].value) { 222 | _getter = NSSelectorFromString([NSString stringWithUTF8String:attrs[i].value]); 223 | } 224 | } break; 225 | case 'S': { 226 | type |= YYEncodingTypePropertyCustomSetter; 227 | if (attrs[i].value) { 228 | _setter = NSSelectorFromString([NSString stringWithUTF8String:attrs[i].value]); 229 | } 230 | } // break; commented for code coverage in next line 231 | default: break; 232 | } 233 | } 234 | if (attrs) { 235 | free(attrs); 236 | attrs = NULL; 237 | } 238 | 239 | _type = type; 240 | if (_name.length) { 241 | if (!_getter) { 242 | _getter = NSSelectorFromString(_name); 243 | } 244 | if (!_setter) { 245 | _setter = NSSelectorFromString([NSString stringWithFormat:@"set%@%@:", [_name substringToIndex:1].uppercaseString, [_name substringFromIndex:1]]); 246 | } 247 | } 248 | return self; 249 | } 250 | 251 | @end 252 | 253 | @implementation YYClassInfo { 254 | BOOL _needUpdate; 255 | } 256 | 257 | - (instancetype)initWithClass:(Class)cls { 258 | if (!cls) return nil; 259 | self = [super init]; 260 | _cls = cls; 261 | _superCls = class_getSuperclass(cls); 262 | _isMeta = class_isMetaClass(cls); 263 | if (!_isMeta) { 264 | _metaCls = objc_getMetaClass(class_getName(cls)); 265 | } 266 | _name = NSStringFromClass(cls); 267 | [self _update]; 268 | 269 | _superClassInfo = [self.class classInfoWithClass:_superCls]; 270 | return self; 271 | } 272 | 273 | - (void)_update { 274 | _ivarInfos = nil; 275 | _methodInfos = nil; 276 | _propertyInfos = nil; 277 | 278 | Class cls = self.cls; 279 | unsigned int methodCount = 0; 280 | Method *methods = class_copyMethodList(cls, &methodCount); 281 | if (methods) { 282 | NSMutableDictionary *methodInfos = [NSMutableDictionary new]; 283 | _methodInfos = methodInfos; 284 | for (unsigned int i = 0; i < methodCount; i++) { 285 | YYClassMethodInfo *info = [[YYClassMethodInfo alloc] initWithMethod:methods[i]]; 286 | if (info.name) methodInfos[info.name] = info; 287 | } 288 | free(methods); 289 | } 290 | unsigned int propertyCount = 0; 291 | objc_property_t *properties = class_copyPropertyList(cls, &propertyCount); 292 | if (properties) { 293 | NSMutableDictionary *propertyInfos = [NSMutableDictionary new]; 294 | _propertyInfos = propertyInfos; 295 | for (unsigned int i = 0; i < propertyCount; i++) { 296 | YYClassPropertyInfo *info = [[YYClassPropertyInfo alloc] initWithProperty:properties[i]]; 297 | if (info.name) propertyInfos[info.name] = info; 298 | } 299 | free(properties); 300 | } 301 | 302 | unsigned int ivarCount = 0; 303 | Ivar *ivars = class_copyIvarList(cls, &ivarCount); 304 | if (ivars) { 305 | NSMutableDictionary *ivarInfos = [NSMutableDictionary new]; 306 | _ivarInfos = ivarInfos; 307 | for (unsigned int i = 0; i < ivarCount; i++) { 308 | YYClassIvarInfo *info = [[YYClassIvarInfo alloc] initWithIvar:ivars[i]]; 309 | if (info.name) ivarInfos[info.name] = info; 310 | } 311 | free(ivars); 312 | } 313 | 314 | if (!_ivarInfos) _ivarInfos = @{}; 315 | if (!_methodInfos) _methodInfos = @{}; 316 | if (!_propertyInfos) _propertyInfos = @{}; 317 | 318 | _needUpdate = NO; 319 | } 320 | 321 | - (void)setNeedUpdate { 322 | _needUpdate = YES; 323 | } 324 | 325 | - (BOOL)needUpdate { 326 | return _needUpdate; 327 | } 328 | 329 | + (instancetype)classInfoWithClass:(Class)cls { 330 | if (!cls) return nil; 331 | static CFMutableDictionaryRef classCache; 332 | static CFMutableDictionaryRef metaCache; 333 | static dispatch_once_t onceToken; 334 | static dispatch_semaphore_t lock; 335 | dispatch_once(&onceToken, ^{ 336 | classCache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 337 | metaCache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 338 | lock = dispatch_semaphore_create(1); 339 | }); 340 | dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER); 341 | YYClassInfo *info = CFDictionaryGetValue(class_isMetaClass(cls) ? metaCache : classCache, (__bridge const void *)(cls)); 342 | if (info && info->_needUpdate) { 343 | [info _update]; 344 | } 345 | dispatch_semaphore_signal(lock); 346 | if (!info) { 347 | info = [[YYClassInfo alloc] initWithClass:cls]; 348 | if (info) { 349 | dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER); 350 | CFDictionarySetValue(info.isMeta ? metaCache : classCache, (__bridge const void *)(cls), (__bridge const void *)(info)); 351 | dispatch_semaphore_signal(lock); 352 | } 353 | } 354 | return info; 355 | } 356 | 357 | + (instancetype)classInfoWithClassName:(NSString *)className { 358 | Class cls = NSClassFromString(className); 359 | return [self classInfoWithClass:cls]; 360 | } 361 | 362 | @end 363 | -------------------------------------------------------------------------------- /TBModelTool/TBModelTool/TBModelTool.h: -------------------------------------------------------------------------------- 1 | // 2 | // TBModelTool.h 3 | // Property 4 | // 5 | // Created by tangbin on 2017/2/6. 6 | // Copyright © 2017年 tangbin. All rights reserved. 7 | // 8 | 9 | 10 | #import 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | /** 15 | 指定文件生成的位置 16 | */ 17 | #define MODEL_FILE_PATH @"/Users/ducktobey/Documents/GithubDemo/TBModelTool/TBModelTool/ModelToolFiles" 18 | 19 | @interface TBModelTool : NSObject 20 | 21 | 22 | /** 23 | 在每个属性头部生成注释代码,永久保存 24 | */ 25 | + (void)addRemarkCode; 26 | 27 | 28 | /** 29 | 移除属性头部注释代码,永久保存 30 | */ 31 | + (void)removeRemarkCode; 32 | 33 | /** 34 | 通过代码生成模型对应模型 适用 MJExtension 框架解析的模型 35 | 36 | @param resource 需要生成的 json 源数据 37 | @param fileName 最外层模型的名称,⚠️如果json 数据中包含嵌套模型,默认是父模型名+属性名+ Moldel 38 | @param replacedKeyFromPropertyName 将属性名换为其他,会永久保存,伴随 APP 生命周期 ---> 如将 id 替换为 ID 则字典为@{@"id":@"ID"} 39 | */ 40 | + (void)MJ_createModelClassFileWithResource:(_Nonnull id)resource andModelClassFileName:(NSString *_Nonnull)fileName andReplacedKeyFromPropertyName:(NSDictionary * _Nullable )replacedKeyFromPropertyName; 41 | 42 | /** 43 | 通过代码生成模型对应模型 适用通过 KVC 解析的模型 44 | */ 45 | + (void)KVC_createModelClassFileWithResource:(_Nonnull id)resource andModelClassFileName:(NSString *_Nonnull)fileName andReplacedKeyFromPropertyName:(NSDictionary *_Nullable)replacedKeyFromPropertyName; 46 | 47 | /** 48 | 通过代码生成模型对应模型 适用通过 YYModel 框架解析的模型 49 | */ 50 | + (void)YYModel_createModelClassFileWithResource:(_Nonnull id)resource andModelClassFileName:(NSString *_Nonnull)fileName andReplacedKeyFromPropertyName:(NSDictionary *_Nullable)replacedKeyFromPropertyName; 51 | 52 | 53 | /** 54 | 获取在框架中已经存在的属性替换为其他 55 | */ 56 | + (NSDictionary *_Nullable)getReplacedKeyFromPropertyName; 57 | @end 58 | 59 | NS_ASSUME_NONNULL_END 60 | -------------------------------------------------------------------------------- /TBModelTool/TBModelTool/TBModelTool.m: -------------------------------------------------------------------------------- 1 | // 2 | // TBModelTool.m 3 | // Property 4 | // 5 | // Created by tangbin on 2017/2/6. 6 | // Copyright © 2017年 tangbin. All rights reserved. 7 | // 8 | 9 | #import "TBModelTool.h" 10 | typedef NS_ENUM(NSInteger, ModelToolType) { 11 | ModelToolTypeKVC, //KVC 12 | ModelToolTypeMJKit, //MJExtension 13 | ModelToolTypeYYModel //YYModel 14 | }; 15 | 16 | @interface TBModelTool (){ 17 | NSMutableDictionary *_replacedKey; 18 | ModelToolType _toolType; 19 | BOOL _addRemarkCode; 20 | } 21 | @end 22 | @implementation TBModelTool 23 | static TBModelTool *sInstance; 24 | + (instancetype)allocWithZone:(struct _NSZone *)zone{ 25 | static dispatch_once_t onceToken; 26 | dispatch_once(&onceToken, ^{ 27 | if (sInstance == nil) { 28 | sInstance = [super allocWithZone:zone]; 29 | } 30 | }); 31 | return sInstance; 32 | } 33 | + (void)initial { 34 | [self sharedInstance]; 35 | } 36 | - (instancetype)init { 37 | if (self = [super init]) { 38 | } 39 | return self; 40 | } 41 | + (TBModelTool *)sharedInstance { 42 | if (sInstance == nil) { 43 | sInstance = [[TBModelTool alloc] init]; 44 | sInstance->_replacedKey = [NSMutableDictionary dictionary]; 45 | } 46 | return sInstance; 47 | } 48 | 49 | //用于MJExtension 框架 解析生成 model 50 | + (void)MJ_createModelClassFileWithResource:(id)resource andModelClassFileName:(NSString *)fileName andReplacedKeyFromPropertyName:(NSDictionary *)replacedKeyFromPropertyName{ 51 | [self sharedInstance]; 52 | sInstance->_toolType = ModelToolTypeMJKit; 53 | if ([resource isKindOfClass:[NSArray class]]) { 54 | for (NSDictionary *dic in resource) { 55 | [self createModelClassFileWithResource:dic andModelClassFileName:fileName andReplacedKeyFromPropertyName:replacedKeyFromPropertyName]; 56 | } 57 | } else{ 58 | [self createModelClassFileWithResource:resource andModelClassFileName:fileName andReplacedKeyFromPropertyName:replacedKeyFromPropertyName]; 59 | } 60 | return; 61 | } 62 | 63 | //用于KVC解析生成 model 64 | + (void)KVC_createModelClassFileWithResource:(id)resource andModelClassFileName:(NSString *)fileName andReplacedKeyFromPropertyName:(NSDictionary *)replacedKeyFromPropertyName{ 65 | [self sharedInstance]; 66 | sInstance->_toolType = ModelToolTypeKVC; 67 | 68 | if ([resource isKindOfClass:[NSArray class]]) { 69 | for (NSDictionary *dic in resource) { 70 | [self createModelClassFileWithResource:dic andModelClassFileName:fileName andReplacedKeyFromPropertyName:replacedKeyFromPropertyName]; 71 | } 72 | } else{ 73 | [self createModelClassFileWithResource:resource andModelClassFileName:fileName andReplacedKeyFromPropertyName:replacedKeyFromPropertyName]; 74 | } 75 | return; 76 | } 77 | 78 | //用于 YYKit 框架解析生成 model 79 | + (void)YYModel_createModelClassFileWithResource:(id)resource andModelClassFileName:(NSString *)fileName andReplacedKeyFromPropertyName:(NSDictionary *)replacedKeyFromPropertyName { 80 | [self sharedInstance]; 81 | sInstance->_toolType = ModelToolTypeYYModel; 82 | if ([resource isKindOfClass:[NSArray class]]) { 83 | for (NSDictionary *dic in resource) { 84 | [self createModelClassFileWithResource:dic andModelClassFileName:fileName andReplacedKeyFromPropertyName:replacedKeyFromPropertyName]; 85 | } 86 | } else{ 87 | [self createModelClassFileWithResource:resource andModelClassFileName:fileName andReplacedKeyFromPropertyName:replacedKeyFromPropertyName]; 88 | } 89 | return; 90 | } 91 | 92 | 93 | //生成属性代码 => 根据字典中所有key 94 | + (void)createModelClassFileWithResource:(id)resource andModelClassFileName:(NSString *)fileName andReplacedKeyFromPropertyName:(NSDictionary *)replacedKeyFromPropertyName{ 95 | 96 | NSAssert(fileName != nil && resource != nil, @"源数据或者文件名不能为空!"); 97 | NSAssert([resource isKindOfClass:[NSDictionary class]], @"json格式不正确"); 98 | [self modelToolSetupReplacedKeyFromPropertyName:replacedKeyFromPropertyName]; 99 | NSString *hFileName = [NSString stringWithFormat:@"%@%@.h",[MODEL_FILE_PATH hasSuffix:@"/"] ? MODEL_FILE_PATH : [NSString stringWithFormat:@"%@/",MODEL_FILE_PATH],fileName]; 100 | NSString *hFileContent = [NSString stringWithContentsOfFile:hFileName encoding:NSUTF8StringEncoding error:nil] ; 101 | if ([hFileContent hasSuffix:@"@end"]) { 102 | hFileContent = [hFileContent stringByReplacingCharactersInRange:NSMakeRange(hFileContent.length - 4, 4) withString:@""]; 103 | } 104 | 105 | 106 | 107 | NSMutableString *codes = [NSMutableString string]; 108 | NSMutableString *importString = [NSMutableString string]; 109 | NSMutableString *AnalysisCodeString = [NSMutableString string]; 110 | NSMutableArray *propertyToModelDicStringArr = [NSMutableArray array]; 111 | 112 | NSMutableArray *oldPropertyArr = [NSMutableArray array]; 113 | if (hFileContent) { 114 | NSArray *hFileContentArr = [hFileContent componentsSeparatedByString:@"\n"]; 115 | for (NSString *string in hFileContentArr) { 116 | if ([string containsString:@"#import"]) { 117 | if (![importString containsString:string]) { 118 | [importString appendString:[NSString stringWithFormat:@"%@\n",string]]; 119 | } 120 | } else if([string containsString:@"@property"]) { 121 | [oldPropertyArr addObject:string]; 122 | } 123 | } 124 | } 125 | 126 | if (sInstance->_toolType == ModelToolTypeMJKit) { 127 | 128 | } else if (sInstance->_toolType == ModelToolTypeKVC) { 129 | AnalysisCodeString = [NSMutableString stringWithString:@"- (void)setValue:(id)value forUndefinedKey:(NSString *)key {}\n- (void)setValue:(id)value forKey:(NSString *)key {\n if ([key isEqualToString:@\"id\"]) {\n[super setValue:value forKey:@\"Id\"];\n} \n "]; 130 | } else if (sInstance -> _toolType == ModelToolTypeYYModel) { 131 | AnalysisCodeString = [NSMutableString stringWithString: 132 | @"+ (NSDictionary *)modelContainerPropertyGenericClass {\ 133 | \n return @{\n\ 134 | "]; 135 | } 136 | if (oldPropertyArr.count) { 137 | codes = [[oldPropertyArr componentsJoinedByString:@"\n"] mutableCopy]; 138 | } 139 | //遍历字典 140 | [resource enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) { 141 | NSString *code; 142 | NSString *theKey = sInstance->_replacedKey[key]; 143 | if (theKey) { 144 | key = theKey; 145 | } 146 | if ([obj isKindOfClass:[NSString class]]) { 147 | NSString *currentPropertyString = [NSString stringWithFormat:@"@property (nonatomic, strong) NSString *%@;", key]; 148 | if (![codes containsString:currentPropertyString]) { 149 | code = currentPropertyString; 150 | } 151 | } else if ([obj isKindOfClass:NSClassFromString(@"__NSCFBoolean")]) { 152 | NSString *currentPropertyString = [NSString stringWithFormat:@"@property (nonatomic, assign) BOOL %@;", key]; 153 | if (![codes containsString:currentPropertyString]) { 154 | code = currentPropertyString; 155 | } 156 | } else if ([obj isKindOfClass:[NSNumber class]]) { 157 | NSString *currentPropertyString = [NSString stringWithFormat:@"@property (nonatomic, assign) NSInteger %@;", key]; 158 | if (![codes containsString:currentPropertyString]) { 159 | code = currentPropertyString; 160 | } 161 | } else if ([obj isKindOfClass:[NSArray class]]) { 162 | NSString *currentPropertyString = [NSString stringWithFormat:@"@property (nonatomic, strong) NSArray *%@;", key]; 163 | NSString *currentModelName = [NSString stringWithFormat:@"%@%@Model",[fileName stringByReplacingOccurrencesOfString:@"Model" withString:@""],[key capitalizedString]]; 164 | 165 | if (![codes containsString:currentPropertyString]) { 166 | code = currentPropertyString; 167 | [importString appendString:[NSString stringWithFormat:@"#import \"%@.h\" \n",currentModelName]]; 168 | } 169 | for (id currentObj in obj) { 170 | if ([currentObj isKindOfClass:[NSDictionary class]]) { 171 | [self createModelClassFileWithResource:currentObj andModelClassFileName:currentModelName andReplacedKeyFromPropertyName:replacedKeyFromPropertyName]; 172 | } 173 | } 174 | if (sInstance->_toolType == ModelToolTypeMJKit) { 175 | [propertyToModelDicStringArr addObject:[NSString stringWithFormat:@"@\"%@\":@\"%@\"",key,currentModelName]]; 176 | } else if (sInstance->_toolType == ModelToolTypeKVC) { 177 | NSString *arrayPropertyAnalysisCodeString = [NSString stringWithFormat:@" else if ([key isEqualToString:@\"%@\"]) {\n NSMutableArray *objArr = [NSMutableArray array];\n for (NSInteger i = 0; i < [value count]; i++) { \n %@ *%@ = [[%@ alloc] init]; \n [%@ setValuesForKeysWithDictionary:value[i]]; \n [objArr addObject:%@]; \n } \n _%@ = objArr; \n }",key,currentModelName,key,currentModelName,key,key,key]; 178 | [AnalysisCodeString appendString:arrayPropertyAnalysisCodeString]; 179 | } else if (sInstance->_toolType == ModelToolTypeYYModel) { 180 | NSString *arrayPropertyAnalysisCodeString = [NSString stringWithFormat:@"@\"%@\":%@.class,",key,currentModelName]; 181 | [AnalysisCodeString appendString:arrayPropertyAnalysisCodeString]; 182 | } 183 | 184 | } else if ([obj isKindOfClass:[NSDictionary class]]) { 185 | NSString *currentModelName = [NSString stringWithFormat:@"%@%@Model",[fileName stringByReplacingOccurrencesOfString:@"Model" withString:@""],[key capitalizedString]]; 186 | 187 | NSString *currentPropertyString = [NSString stringWithFormat:@"@property (nonatomic, strong) %@ *%@;",currentModelName, key]; 188 | if (![codes containsString:currentPropertyString]) { 189 | code = currentPropertyString; 190 | [importString appendString:[NSString stringWithFormat:@"#import \"%@.h\" \n",currentModelName]]; 191 | 192 | } 193 | [self createModelClassFileWithResource:obj andModelClassFileName:currentModelName andReplacedKeyFromPropertyName:replacedKeyFromPropertyName]; 194 | 195 | if (sInstance->_toolType == ModelToolTypeMJKit) { 196 | } else if (sInstance->_toolType == ModelToolTypeKVC) { 197 | NSString *dictionaryPropertyAnalysisCodeString = [NSString stringWithFormat:@"else if ([key isEqualToString:@\"%@\"]) { \n %@ *%@ = [[%@ alloc] init]; \n [%@ setValuesForKeysWithDictionary:value]; \n_%@ = %@; \n}",key,currentModelName,key,currentModelName,key,key,key]; 198 | [AnalysisCodeString appendString:dictionaryPropertyAnalysisCodeString]; 199 | } else if (sInstance->_toolType == ModelToolTypeYYModel) { 200 | NSString *arrayPropertyAnalysisCodeString = [NSString stringWithFormat:@"@\"%@\":%@.class,",key,currentModelName]; 201 | [AnalysisCodeString appendString:arrayPropertyAnalysisCodeString]; 202 | } 203 | } 204 | if (code) { 205 | [codes appendFormat:@"\n%@\n", code]; 206 | } 207 | }]; 208 | NSMutableArray *allPropertyArr = [[codes componentsSeparatedByString:@";"] mutableCopy]; 209 | if ([allPropertyArr.lastObject length] == 0) { 210 | //[allPropertyArr removeLastObject]; 211 | } 212 | NSMutableArray *tempPropertyArr = [NSMutableArray array]; 213 | for (NSString *property in allPropertyArr) { 214 | 215 | NSMutableString *replaceString = [NSMutableString string]; 216 | NSString *remarkCode = @"/**\n <\#Description\#>\n*/\n"; 217 | 218 | [replaceString appendString:[[property stringByReplacingOccurrencesOfString:@"\n" withString:@""] stringByReplacingOccurrencesOfString:remarkCode withString:@""]]; 219 | // if (replaceString.length == 0) { 220 | // continue; 221 | // } 222 | if (sInstance->_addRemarkCode) { 223 | [replaceString insertString:remarkCode atIndex:0]; 224 | if (replaceString.length == remarkCode.length) { 225 | replaceString = [@"" mutableCopy]; 226 | } 227 | } 228 | [tempPropertyArr addObject:replaceString]; 229 | } 230 | codes = [[tempPropertyArr componentsJoinedByString:@";\n\n"] mutableCopy]; 231 | if (sInstance->_toolType == ModelToolTypeMJKit) { 232 | if (propertyToModelDicStringArr.count > 0) { 233 | [AnalysisCodeString appendString:[NSString stringWithFormat:@"+ (NSDictionary *)mj_objectClassInArray { \nreturn @{%@};\n}",[propertyToModelDicStringArr componentsJoinedByString:@","]]]; 234 | } 235 | } else if (sInstance->_toolType == ModelToolTypeKVC) { 236 | [AnalysisCodeString appendString: @"\n else \n{\n[super setValue:value forKey:key];\n}\n}\n"]; 237 | } else if (sInstance->_toolType == ModelToolTypeYYModel) { 238 | [AnalysisCodeString appendString: @"\n };\n }\n"]; 239 | if (sInstance->_replacedKey) { 240 | NSDictionary *dic = sInstance->_replacedKey; 241 | NSMutableString *appendString = [NSMutableString string]; 242 | for (int i = 0; i < dic.allKeys.count; i++) { 243 | [appendString appendFormat:@"@\"%@\":@\"%@\",",dic.allValues[i],dic.allKeys[i]]; 244 | } 245 | [AnalysisCodeString appendString:[NSString stringWithFormat:@"+ (NSDictionary *)modelCustomPropertyMapper {\ 246 | \nreturn @{%@\ 247 | \n};\n \ 248 | }",appendString]]; 249 | } 250 | } 251 | 252 | [self create_hFileWithImportString:importString andFileName:fileName andPropertyCodes:codes]; 253 | [self create_mFileWithMethodString:AnalysisCodeString andFileName:fileName]; 254 | } 255 | 256 | #pragma mark - privite Method 257 | + (NSDictionary *)getReplacedKeyFromPropertyName { 258 | return sInstance->_replacedKey; 259 | } 260 | 261 | + (void)modelToolSetupReplacedKeyFromPropertyName:(NSDictionary *)replacedKeyFromPropertyName{ 262 | NSMutableDictionary *replacedKey = sInstance->_replacedKey; 263 | [replacedKey addEntriesFromDictionary:replacedKeyFromPropertyName]; 264 | sInstance->_replacedKey = replacedKey; 265 | } 266 | + (void)create_hFileWithImportString:(NSString *)importString andFileName:(NSString *)fileName andPropertyCodes:(NSString *)propertyCodes{ 267 | NSString *headerString = [NSString stringWithFormat:@"%@%@@interface %@ : NSObject \n",[importString containsString:@"#import \n"]?@"":@"#import \n",importString,fileName]; 268 | if ([propertyCodes containsString:headerString]) { 269 | headerString = @""; 270 | } 271 | NSString *hCodes = [NSString stringWithFormat:@"%@%@ \n@end",headerString,propertyCodes]; 272 | [hCodes writeToFile:[NSString stringWithFormat:@"%@%@.h",[MODEL_FILE_PATH hasSuffix:@"/"] ? MODEL_FILE_PATH : [NSString stringWithFormat:@"%@/",MODEL_FILE_PATH],fileName] atomically:YES encoding:NSUTF8StringEncoding error:nil]; 273 | } 274 | 275 | + (void)create_mFileWithMethodString:(NSString *)MethodString andFileName:(NSString *)fileName { 276 | NSString *mCodes = [NSString stringWithFormat:@"#import \"%@.h\"\n@implementation %@\n\n%@\n@end",fileName,fileName,MethodString]; 277 | [mCodes writeToFile:[NSString stringWithFormat:@"%@%@.m",[MODEL_FILE_PATH hasSuffix:@"/"] ? MODEL_FILE_PATH : [NSString stringWithFormat:@"%@/",MODEL_FILE_PATH],fileName] atomically:YES encoding:NSUTF8StringEncoding error:nil]; 278 | } 279 | 280 | + (BOOL)fileIsExistWithFileName:(NSString *)fileName { 281 | NSFileManager *manager = [NSFileManager defaultManager]; 282 | NSString *hFileName = [NSString stringWithFormat:@"%@%@.h",[MODEL_FILE_PATH hasSuffix:@"/"] ? MODEL_FILE_PATH : [NSString stringWithFormat:@"%@/",MODEL_FILE_PATH],fileName]; 283 | if ([manager fileExistsAtPath:hFileName]) { 284 | return YES; 285 | } 286 | NSString *mFileName = [NSString stringWithFormat:@"%@%@.m",[MODEL_FILE_PATH hasSuffix:@"/"] ? MODEL_FILE_PATH : [NSString stringWithFormat:@"%@/",MODEL_FILE_PATH],fileName]; 287 | if ([manager fileExistsAtPath:mFileName]) { 288 | return YES; 289 | } 290 | return NO; 291 | } 292 | 293 | + (void)addRemarkCode { 294 | [self sharedInstance]; 295 | sInstance->_addRemarkCode = 1; 296 | } 297 | + (void)removeRemarkCode { 298 | [self sharedInstance]; 299 | sInstance->_addRemarkCode = 0; 300 | } 301 | 302 | @end 303 | -------------------------------------------------------------------------------- /TBModelTool/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // TBModelTool 4 | // 5 | // Created by tangbin on 2017/2/16. 6 | // Copyright © 2017年 tangbin. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /TBModelTool/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // Property 4 | // 5 | // Created by tangbin on 2017/2/6. 6 | // Copyright © 2017年 tangbin. All rights reserved. 7 | // 8 | 9 | #define open 0 10 | 11 | #import "ViewController.h" 12 | #import "TBModelTool.h" 13 | #import "MJExtension.h" 14 | #import "NSObject+YYModel.h" 15 | 16 | //#import "TBKVCModel.h" 17 | //#import "TBMJKitModel.h" 18 | //#import "TBYYModel.h" 19 | 20 | @interface ViewController () 21 | @property (nonatomic,strong) UIScrollView *scrollView; 22 | 23 | @end 24 | 25 | @implementation ViewController 26 | + (void)load { 27 | [NSObject mj_setupReplacedKeyFromPropertyName:^NSDictionary *{ 28 | return @{ 29 | @"Id" : @"id" 30 | }; 31 | }]; 32 | } 33 | 34 | - (void)viewDidLoad { 35 | [super viewDidLoad]; 36 | NSString * jsonPath = [[NSBundle mainBundle]pathForResource:@"json" ofType:@"json"]; 37 | NSData * jsonData = [[NSData alloc]initWithContentsOfFile:jsonPath]; 38 | NSMutableDictionary *jsonDic = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingAllowFragments error:nil]; 39 | 40 | [TBModelTool KVC_createModelClassFileWithResource:jsonDic andModelClassFileName:@"TBKVCModel" andReplacedKeyFromPropertyName:@{@"id":@"Id"}]; 41 | 42 | [TBModelTool MJ_createModelClassFileWithResource:jsonDic andModelClassFileName:@"TBMJKitModel" andReplacedKeyFromPropertyName:nil]; 43 | 44 | [TBModelTool YYModel_createModelClassFileWithResource:jsonDic andModelClassFileName:@"TBYYModel" andReplacedKeyFromPropertyName:@{@"amount":@"amountamountamountamount"}]; 45 | #if open 46 | [self testKVCWithDic:jsonDic]; 47 | [self testMJKitWithDic:jsonDic]; 48 | [self testYYKitWithDic:jsonDic]; 49 | #endif 50 | 51 | } 52 | 53 | #if open 54 | 55 | - (void)testKVCWithDic:(NSDictionary *)dic { 56 | TBKVCModel *kvcModel = [[TBKVCModel alloc]init]; 57 | [kvcModel setValuesForKeysWithDictionary:dic]; 58 | NSLog(@"%@",kvcModel); 59 | } 60 | 61 | - (void)testMJKitWithDic:(NSDictionary *)dic { 62 | TBMJKitModel *mjModel = [TBMJKitModel mj_objectWithKeyValues:dic]; 63 | NSLog(@"%@",mjModel); 64 | } 65 | 66 | - (void)testYYKitWithDic:(NSDictionary *)dic { 67 | TBYYModel *YYModel = [TBYYModel modelWithDictionary:dic]; 68 | NSLog(@"%@",YYModel); 69 | } 70 | 71 | #endif 72 | 73 | @end 74 | -------------------------------------------------------------------------------- /TBModelTool/json.json: -------------------------------------------------------------------------------- 1 | { 2 | "NumberTest" : 999, 3 | "amount" : "50", 4 | "boolTest" : true, 5 | "commonAttachmentList" : [ 6 | { 7 | "_Arr" : [ 8 | { 9 | "_Key1" : "_Value1", 10 | "_Key2" : "_Value2", 11 | "_Key3" : "_Value3", 12 | "id" : "123" 13 | } 14 | ], 15 | "a" : "aa" 16 | }, 17 | { 18 | "_Arr" : [ 19 | { 20 | "_Key1" : "_Value1", 21 | "_Key2" : "_Value2", 22 | "_Key3" : "_Value3", 23 | "iiid" : "1111" 24 | } 25 | ], 26 | "a" : "aa" 27 | }, 28 | { 29 | "_Arr" : [ 30 | { 31 | "_Key1" : "_Value1", 32 | "_Key2" : "_Value2", 33 | "_Key3" : "_Value3" 34 | } 35 | ], 36 | "a" : "aa" 37 | } 38 | ], 39 | "costCategory" : "财务费用", 40 | "costType" : "3003", 41 | "costTypeName" : "财务部杂费用", 42 | "dictTest" : { 43 | "key1" : "value1", 44 | "key2" : "value2" 45 | }, 46 | "diffAmount" : "50", 47 | "empId" : "CW0012", 48 | "empName" : "农岚", 49 | "id" : "BX1701200015", 50 | "orderDate" : "2017-01-20", 51 | "orderMonth" : "1", 52 | "orderType" : "10", 53 | "orderTypeName" : "费用申请单", 54 | "orderYear" : "2017", 55 | "orgId" : "C6", 56 | "orgName" : "财务部", 57 | "relBudgetName" : "2017年1月财务类月度费用预算表", 58 | "relOrderNo" : "YS1701190002", 59 | "relOrderType" : "40", 60 | "relOrderTypeName" : "市场费用预算", 61 | "remainingBudget" : "2900", 62 | "remark" : "财务部杂费 注:2017.1.6财务打印机4012碳粉50元,现金付梁莉", 63 | "statusId" : "10", 64 | "statusName" : "制单", 65 | "usedBudget" : "0", 66 | "verAmount" : "0", 67 | "verificatingAmount" : "0" 68 | } -------------------------------------------------------------------------------- /TBModelTool/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // TBModelTool 4 | // 5 | // Created by tangbin on 2017/2/16. 6 | // Copyright © 2017年 tangbin. 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 | -------------------------------------------------------------------------------- /TBModelToolTests/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 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /TBModelToolTests/TBModelToolTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // TBModelToolTests.m 3 | // TBModelToolTests 4 | // 5 | // Created by tangbin on 2017/2/16. 6 | // Copyright © 2017年 tangbin. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface TBModelToolTests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation TBModelToolTests 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 | -------------------------------------------------------------------------------- /TBModelToolUITests/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 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /TBModelToolUITests/TBModelToolUITests.m: -------------------------------------------------------------------------------- 1 | // 2 | // TBModelToolUITests.m 3 | // TBModelToolUITests 4 | // 5 | // Created by tangbin on 2017/2/16. 6 | // Copyright © 2017年 tangbin. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface TBModelToolUITests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation TBModelToolUITests 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 | --------------------------------------------------------------------------------