├── .gitignore ├── CategoryPropertyDynamicSupport.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ ├── NathanLi.xcuserdatad │ │ └── UserInterfaceState.xcuserstate │ │ └── nathan.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ ├── NathanLi.xcuserdatad │ ├── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ │ ├── CategoryPropertyDynamicSupport.xcscheme │ │ └── xcschememanagement.plist │ └── nathan.xcuserdatad │ └── xcschemes │ ├── CategoryPropertyDynamicSupport.xcscheme │ └── xcschememanagement.plist ├── CategoryPropertyDynamicSupport ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── Info.plist ├── ViewController.h ├── ViewController.m └── main.m ├── NSObject+nl_dynamicPropertySupport ├── NLDynamicPropertyPrefix.h ├── NLDynamicPropertyPrefix.m ├── NLPropertyDescriptor.h ├── NLPropertyDescriptor.m ├── NSObject+nl_dynamicPropertyCustomeStruct.h ├── NSObject+nl_dynamicPropertyCustomeStruct.m ├── NSObject+nl_dynamicPropertyStore.h ├── NSObject+nl_dynamicPropertyStore.m ├── NSObject+nl_dynamicPropertySupport.h └── NSObject+nl_dynamicPropertySupport.m └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | *.xcuserstate 3 | 4 | *.xcuserstate 5 | -------------------------------------------------------------------------------- /CategoryPropertyDynamicSupport.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 5A4C70691C21B47A00DDBFE5 /* NLPropertyDescriptor.m in Sources */ = {isa = PBXBuildFile; fileRef = 5A4C70621C21B47A00DDBFE5 /* NLPropertyDescriptor.m */; }; 11 | 5A4C706A1C21B47A00DDBFE5 /* NSObject+nl_dynamicPropertyCustomeStruct.m in Sources */ = {isa = PBXBuildFile; fileRef = 5A4C70641C21B47A00DDBFE5 /* NSObject+nl_dynamicPropertyCustomeStruct.m */; }; 12 | 5A4C706B1C21B47A00DDBFE5 /* NSObject+nl_dynamicPropertyStore.m in Sources */ = {isa = PBXBuildFile; fileRef = 5A4C70661C21B47A00DDBFE5 /* NSObject+nl_dynamicPropertyStore.m */; }; 13 | 5A4C706C1C21B47A00DDBFE5 /* NSObject+nl_dynamicPropertySupport.m in Sources */ = {isa = PBXBuildFile; fileRef = 5A4C70681C21B47A00DDBFE5 /* NSObject+nl_dynamicPropertySupport.m */; }; 14 | 5A71A8431DEC62A50036B64B /* NLDynamicPropertyPrefix.m in Sources */ = {isa = PBXBuildFile; fileRef = 5A71A8421DEC62A50036B64B /* NLDynamicPropertyPrefix.m */; }; 15 | D8B811A81C04913900AD7F84 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = D8B811A71C04913900AD7F84 /* main.m */; }; 16 | D8B811AB1C04913900AD7F84 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = D8B811AA1C04913900AD7F84 /* AppDelegate.m */; }; 17 | D8B811AE1C04913900AD7F84 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = D8B811AD1C04913900AD7F84 /* ViewController.m */; }; 18 | D8B811B11C04913900AD7F84 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D8B811AF1C04913900AD7F84 /* Main.storyboard */; }; 19 | D8B811B31C04913900AD7F84 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D8B811B21C04913900AD7F84 /* Assets.xcassets */; }; 20 | D8B811B61C04913900AD7F84 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D8B811B41C04913900AD7F84 /* LaunchScreen.storyboard */; }; 21 | D8B811CB1C04929700AD7F84 /* MapKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8B811CA1C04929700AD7F84 /* MapKit.framework */; }; 22 | D8B811CD1C0492A100AD7F84 /* CoreMedia.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8B811CC1C0492A100AD7F84 /* CoreMedia.framework */; }; 23 | /* End PBXBuildFile section */ 24 | 25 | /* Begin PBXFileReference section */ 26 | 5A4C70611C21B47A00DDBFE5 /* NLPropertyDescriptor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NLPropertyDescriptor.h; sourceTree = ""; }; 27 | 5A4C70621C21B47A00DDBFE5 /* NLPropertyDescriptor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NLPropertyDescriptor.m; sourceTree = ""; }; 28 | 5A4C70631C21B47A00DDBFE5 /* NSObject+nl_dynamicPropertyCustomeStruct.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+nl_dynamicPropertyCustomeStruct.h"; sourceTree = ""; }; 29 | 5A4C70641C21B47A00DDBFE5 /* NSObject+nl_dynamicPropertyCustomeStruct.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+nl_dynamicPropertyCustomeStruct.m"; sourceTree = ""; }; 30 | 5A4C70651C21B47A00DDBFE5 /* NSObject+nl_dynamicPropertyStore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+nl_dynamicPropertyStore.h"; sourceTree = ""; }; 31 | 5A4C70661C21B47A00DDBFE5 /* NSObject+nl_dynamicPropertyStore.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+nl_dynamicPropertyStore.m"; sourceTree = ""; }; 32 | 5A4C70671C21B47A00DDBFE5 /* NSObject+nl_dynamicPropertySupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+nl_dynamicPropertySupport.h"; sourceTree = ""; }; 33 | 5A4C70681C21B47A00DDBFE5 /* NSObject+nl_dynamicPropertySupport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+nl_dynamicPropertySupport.m"; sourceTree = ""; }; 34 | 5A71A8411DEC62A50036B64B /* NLDynamicPropertyPrefix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NLDynamicPropertyPrefix.h; sourceTree = ""; }; 35 | 5A71A8421DEC62A50036B64B /* NLDynamicPropertyPrefix.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NLDynamicPropertyPrefix.m; sourceTree = ""; }; 36 | D8B811A31C04913900AD7F84 /* CategoryPropertyDynamicSupport.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CategoryPropertyDynamicSupport.app; sourceTree = BUILT_PRODUCTS_DIR; }; 37 | D8B811A71C04913900AD7F84 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 38 | D8B811A91C04913900AD7F84 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 39 | D8B811AA1C04913900AD7F84 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 40 | D8B811AC1C04913900AD7F84 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 41 | D8B811AD1C04913900AD7F84 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 42 | D8B811B01C04913900AD7F84 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 43 | D8B811B21C04913900AD7F84 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 44 | D8B811B51C04913900AD7F84 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 45 | D8B811B71C04913900AD7F84 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 46 | D8B811CA1C04929700AD7F84 /* MapKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MapKit.framework; path = System/Library/Frameworks/MapKit.framework; sourceTree = SDKROOT; }; 47 | D8B811CC1C0492A100AD7F84 /* CoreMedia.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreMedia.framework; path = System/Library/Frameworks/CoreMedia.framework; sourceTree = SDKROOT; }; 48 | /* End PBXFileReference section */ 49 | 50 | /* Begin PBXFrameworksBuildPhase section */ 51 | D8B811A01C04913900AD7F84 /* Frameworks */ = { 52 | isa = PBXFrameworksBuildPhase; 53 | buildActionMask = 2147483647; 54 | files = ( 55 | D8B811CD1C0492A100AD7F84 /* CoreMedia.framework in Frameworks */, 56 | D8B811CB1C04929700AD7F84 /* MapKit.framework in Frameworks */, 57 | ); 58 | runOnlyForDeploymentPostprocessing = 0; 59 | }; 60 | /* End PBXFrameworksBuildPhase section */ 61 | 62 | /* Begin PBXGroup section */ 63 | 5A4C70601C21B47A00DDBFE5 /* NSObject+nl_dynamicPropertySupport */ = { 64 | isa = PBXGroup; 65 | children = ( 66 | 5A71A8411DEC62A50036B64B /* NLDynamicPropertyPrefix.h */, 67 | 5A71A8421DEC62A50036B64B /* NLDynamicPropertyPrefix.m */, 68 | 5A4C70671C21B47A00DDBFE5 /* NSObject+nl_dynamicPropertySupport.h */, 69 | 5A4C70681C21B47A00DDBFE5 /* NSObject+nl_dynamicPropertySupport.m */, 70 | 5A4C70611C21B47A00DDBFE5 /* NLPropertyDescriptor.h */, 71 | 5A4C70621C21B47A00DDBFE5 /* NLPropertyDescriptor.m */, 72 | 5A4C70631C21B47A00DDBFE5 /* NSObject+nl_dynamicPropertyCustomeStruct.h */, 73 | 5A4C70641C21B47A00DDBFE5 /* NSObject+nl_dynamicPropertyCustomeStruct.m */, 74 | 5A4C70651C21B47A00DDBFE5 /* NSObject+nl_dynamicPropertyStore.h */, 75 | 5A4C70661C21B47A00DDBFE5 /* NSObject+nl_dynamicPropertyStore.m */, 76 | ); 77 | path = "NSObject+nl_dynamicPropertySupport"; 78 | sourceTree = ""; 79 | }; 80 | D8B8119A1C04913900AD7F84 = { 81 | isa = PBXGroup; 82 | children = ( 83 | D8B811CC1C0492A100AD7F84 /* CoreMedia.framework */, 84 | D8B811CA1C04929700AD7F84 /* MapKit.framework */, 85 | 5A4C70601C21B47A00DDBFE5 /* NSObject+nl_dynamicPropertySupport */, 86 | D8B811A51C04913900AD7F84 /* CategoryPropertyDynamicSupport */, 87 | D8B811A41C04913900AD7F84 /* Products */, 88 | ); 89 | sourceTree = ""; 90 | }; 91 | D8B811A41C04913900AD7F84 /* Products */ = { 92 | isa = PBXGroup; 93 | children = ( 94 | D8B811A31C04913900AD7F84 /* CategoryPropertyDynamicSupport.app */, 95 | ); 96 | name = Products; 97 | sourceTree = ""; 98 | }; 99 | D8B811A51C04913900AD7F84 /* CategoryPropertyDynamicSupport */ = { 100 | isa = PBXGroup; 101 | children = ( 102 | D8B811A91C04913900AD7F84 /* AppDelegate.h */, 103 | D8B811AA1C04913900AD7F84 /* AppDelegate.m */, 104 | D8B811AC1C04913900AD7F84 /* ViewController.h */, 105 | D8B811AD1C04913900AD7F84 /* ViewController.m */, 106 | D8B811AF1C04913900AD7F84 /* Main.storyboard */, 107 | D8B811B21C04913900AD7F84 /* Assets.xcassets */, 108 | D8B811B41C04913900AD7F84 /* LaunchScreen.storyboard */, 109 | D8B811B71C04913900AD7F84 /* Info.plist */, 110 | D8B811A61C04913900AD7F84 /* Supporting Files */, 111 | ); 112 | path = CategoryPropertyDynamicSupport; 113 | sourceTree = ""; 114 | }; 115 | D8B811A61C04913900AD7F84 /* Supporting Files */ = { 116 | isa = PBXGroup; 117 | children = ( 118 | D8B811A71C04913900AD7F84 /* main.m */, 119 | ); 120 | name = "Supporting Files"; 121 | sourceTree = ""; 122 | }; 123 | /* End PBXGroup section */ 124 | 125 | /* Begin PBXNativeTarget section */ 126 | D8B811A21C04913900AD7F84 /* CategoryPropertyDynamicSupport */ = { 127 | isa = PBXNativeTarget; 128 | buildConfigurationList = D8B811BA1C04913900AD7F84 /* Build configuration list for PBXNativeTarget "CategoryPropertyDynamicSupport" */; 129 | buildPhases = ( 130 | D8B8119F1C04913900AD7F84 /* Sources */, 131 | D8B811A01C04913900AD7F84 /* Frameworks */, 132 | D8B811A11C04913900AD7F84 /* Resources */, 133 | ); 134 | buildRules = ( 135 | ); 136 | dependencies = ( 137 | ); 138 | name = CategoryPropertyDynamicSupport; 139 | productName = CategoryPropertyDynamicSupport; 140 | productReference = D8B811A31C04913900AD7F84 /* CategoryPropertyDynamicSupport.app */; 141 | productType = "com.apple.product-type.application"; 142 | }; 143 | /* End PBXNativeTarget section */ 144 | 145 | /* Begin PBXProject section */ 146 | D8B8119B1C04913900AD7F84 /* Project object */ = { 147 | isa = PBXProject; 148 | attributes = { 149 | LastUpgradeCheck = 0810; 150 | ORGANIZATIONNAME = "听榆大叔"; 151 | TargetAttributes = { 152 | D8B811A21C04913900AD7F84 = { 153 | CreatedOnToolsVersion = 7.1; 154 | }; 155 | }; 156 | }; 157 | buildConfigurationList = D8B8119E1C04913900AD7F84 /* Build configuration list for PBXProject "CategoryPropertyDynamicSupport" */; 158 | compatibilityVersion = "Xcode 3.2"; 159 | developmentRegion = English; 160 | hasScannedForEncodings = 0; 161 | knownRegions = ( 162 | en, 163 | Base, 164 | ); 165 | mainGroup = D8B8119A1C04913900AD7F84; 166 | productRefGroup = D8B811A41C04913900AD7F84 /* Products */; 167 | projectDirPath = ""; 168 | projectRoot = ""; 169 | targets = ( 170 | D8B811A21C04913900AD7F84 /* CategoryPropertyDynamicSupport */, 171 | ); 172 | }; 173 | /* End PBXProject section */ 174 | 175 | /* Begin PBXResourcesBuildPhase section */ 176 | D8B811A11C04913900AD7F84 /* Resources */ = { 177 | isa = PBXResourcesBuildPhase; 178 | buildActionMask = 2147483647; 179 | files = ( 180 | D8B811B61C04913900AD7F84 /* LaunchScreen.storyboard in Resources */, 181 | D8B811B31C04913900AD7F84 /* Assets.xcassets in Resources */, 182 | D8B811B11C04913900AD7F84 /* Main.storyboard in Resources */, 183 | ); 184 | runOnlyForDeploymentPostprocessing = 0; 185 | }; 186 | /* End PBXResourcesBuildPhase section */ 187 | 188 | /* Begin PBXSourcesBuildPhase section */ 189 | D8B8119F1C04913900AD7F84 /* Sources */ = { 190 | isa = PBXSourcesBuildPhase; 191 | buildActionMask = 2147483647; 192 | files = ( 193 | 5A4C706C1C21B47A00DDBFE5 /* NSObject+nl_dynamicPropertySupport.m in Sources */, 194 | 5A4C706B1C21B47A00DDBFE5 /* NSObject+nl_dynamicPropertyStore.m in Sources */, 195 | 5A4C706A1C21B47A00DDBFE5 /* NSObject+nl_dynamicPropertyCustomeStruct.m in Sources */, 196 | D8B811AE1C04913900AD7F84 /* ViewController.m in Sources */, 197 | D8B811AB1C04913900AD7F84 /* AppDelegate.m in Sources */, 198 | D8B811A81C04913900AD7F84 /* main.m in Sources */, 199 | 5A71A8431DEC62A50036B64B /* NLDynamicPropertyPrefix.m in Sources */, 200 | 5A4C70691C21B47A00DDBFE5 /* NLPropertyDescriptor.m in Sources */, 201 | ); 202 | runOnlyForDeploymentPostprocessing = 0; 203 | }; 204 | /* End PBXSourcesBuildPhase section */ 205 | 206 | /* Begin PBXVariantGroup section */ 207 | D8B811AF1C04913900AD7F84 /* Main.storyboard */ = { 208 | isa = PBXVariantGroup; 209 | children = ( 210 | D8B811B01C04913900AD7F84 /* Base */, 211 | ); 212 | name = Main.storyboard; 213 | sourceTree = ""; 214 | }; 215 | D8B811B41C04913900AD7F84 /* LaunchScreen.storyboard */ = { 216 | isa = PBXVariantGroup; 217 | children = ( 218 | D8B811B51C04913900AD7F84 /* Base */, 219 | ); 220 | name = LaunchScreen.storyboard; 221 | sourceTree = ""; 222 | }; 223 | /* End PBXVariantGroup section */ 224 | 225 | /* Begin XCBuildConfiguration section */ 226 | D8B811B81C04913900AD7F84 /* Debug */ = { 227 | isa = XCBuildConfiguration; 228 | buildSettings = { 229 | ALWAYS_SEARCH_USER_PATHS = NO; 230 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 231 | CLANG_CXX_LIBRARY = "libc++"; 232 | CLANG_ENABLE_MODULES = YES; 233 | CLANG_ENABLE_OBJC_ARC = YES; 234 | CLANG_WARN_BOOL_CONVERSION = YES; 235 | CLANG_WARN_CONSTANT_CONVERSION = YES; 236 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 237 | CLANG_WARN_EMPTY_BODY = YES; 238 | CLANG_WARN_ENUM_CONVERSION = YES; 239 | CLANG_WARN_INFINITE_RECURSION = YES; 240 | CLANG_WARN_INT_CONVERSION = YES; 241 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 242 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 243 | CLANG_WARN_UNREACHABLE_CODE = YES; 244 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 245 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 246 | COPY_PHASE_STRIP = NO; 247 | DEBUG_INFORMATION_FORMAT = dwarf; 248 | ENABLE_STRICT_OBJC_MSGSEND = YES; 249 | ENABLE_TESTABILITY = YES; 250 | GCC_C_LANGUAGE_STANDARD = gnu99; 251 | GCC_DYNAMIC_NO_PIC = NO; 252 | GCC_NO_COMMON_BLOCKS = YES; 253 | GCC_OPTIMIZATION_LEVEL = 0; 254 | GCC_PREPROCESSOR_DEFINITIONS = ( 255 | "DEBUG=1", 256 | "$(inherited)", 257 | ); 258 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 259 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 260 | GCC_WARN_UNDECLARED_SELECTOR = YES; 261 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 262 | GCC_WARN_UNUSED_FUNCTION = YES; 263 | GCC_WARN_UNUSED_VARIABLE = YES; 264 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 265 | MTL_ENABLE_DEBUG_INFO = YES; 266 | ONLY_ACTIVE_ARCH = YES; 267 | SDKROOT = iphoneos; 268 | }; 269 | name = Debug; 270 | }; 271 | D8B811B91C04913900AD7F84 /* Release */ = { 272 | isa = XCBuildConfiguration; 273 | buildSettings = { 274 | ALWAYS_SEARCH_USER_PATHS = NO; 275 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 276 | CLANG_CXX_LIBRARY = "libc++"; 277 | CLANG_ENABLE_MODULES = YES; 278 | CLANG_ENABLE_OBJC_ARC = YES; 279 | CLANG_WARN_BOOL_CONVERSION = YES; 280 | CLANG_WARN_CONSTANT_CONVERSION = YES; 281 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 282 | CLANG_WARN_EMPTY_BODY = YES; 283 | CLANG_WARN_ENUM_CONVERSION = YES; 284 | CLANG_WARN_INFINITE_RECURSION = YES; 285 | CLANG_WARN_INT_CONVERSION = YES; 286 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 287 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 288 | CLANG_WARN_UNREACHABLE_CODE = YES; 289 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 290 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 291 | COPY_PHASE_STRIP = NO; 292 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 293 | ENABLE_NS_ASSERTIONS = NO; 294 | ENABLE_STRICT_OBJC_MSGSEND = YES; 295 | GCC_C_LANGUAGE_STANDARD = gnu99; 296 | GCC_NO_COMMON_BLOCKS = YES; 297 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 298 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 299 | GCC_WARN_UNDECLARED_SELECTOR = YES; 300 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 301 | GCC_WARN_UNUSED_FUNCTION = YES; 302 | GCC_WARN_UNUSED_VARIABLE = YES; 303 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 304 | MTL_ENABLE_DEBUG_INFO = NO; 305 | SDKROOT = iphoneos; 306 | VALIDATE_PRODUCT = YES; 307 | }; 308 | name = Release; 309 | }; 310 | D8B811BB1C04913900AD7F84 /* Debug */ = { 311 | isa = XCBuildConfiguration; 312 | buildSettings = { 313 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 314 | INFOPLIST_FILE = CategoryPropertyDynamicSupport/Info.plist; 315 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 316 | PRODUCT_BUNDLE_IDENTIFIER = NL.CategoryPropertyDynamicSupport; 317 | PRODUCT_NAME = "$(TARGET_NAME)"; 318 | }; 319 | name = Debug; 320 | }; 321 | D8B811BC1C04913900AD7F84 /* Release */ = { 322 | isa = XCBuildConfiguration; 323 | buildSettings = { 324 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 325 | INFOPLIST_FILE = CategoryPropertyDynamicSupport/Info.plist; 326 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 327 | PRODUCT_BUNDLE_IDENTIFIER = NL.CategoryPropertyDynamicSupport; 328 | PRODUCT_NAME = "$(TARGET_NAME)"; 329 | }; 330 | name = Release; 331 | }; 332 | /* End XCBuildConfiguration section */ 333 | 334 | /* Begin XCConfigurationList section */ 335 | D8B8119E1C04913900AD7F84 /* Build configuration list for PBXProject "CategoryPropertyDynamicSupport" */ = { 336 | isa = XCConfigurationList; 337 | buildConfigurations = ( 338 | D8B811B81C04913900AD7F84 /* Debug */, 339 | D8B811B91C04913900AD7F84 /* Release */, 340 | ); 341 | defaultConfigurationIsVisible = 0; 342 | defaultConfigurationName = Release; 343 | }; 344 | D8B811BA1C04913900AD7F84 /* Build configuration list for PBXNativeTarget "CategoryPropertyDynamicSupport" */ = { 345 | isa = XCConfigurationList; 346 | buildConfigurations = ( 347 | D8B811BB1C04913900AD7F84 /* Debug */, 348 | D8B811BC1C04913900AD7F84 /* Release */, 349 | ); 350 | defaultConfigurationIsVisible = 0; 351 | defaultConfigurationName = Release; 352 | }; 353 | /* End XCConfigurationList section */ 354 | }; 355 | rootObject = D8B8119B1C04913900AD7F84 /* Project object */; 356 | } 357 | -------------------------------------------------------------------------------- /CategoryPropertyDynamicSupport.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /CategoryPropertyDynamicSupport.xcodeproj/project.xcworkspace/xcuserdata/NathanLi.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NathanLi/iOSCategoryPropertyDynamicSupport/47e047cc25048602895374191dc5d22de11a6c28/CategoryPropertyDynamicSupport.xcodeproj/project.xcworkspace/xcuserdata/NathanLi.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /CategoryPropertyDynamicSupport.xcodeproj/project.xcworkspace/xcuserdata/nathan.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NathanLi/iOSCategoryPropertyDynamicSupport/47e047cc25048602895374191dc5d22de11a6c28/CategoryPropertyDynamicSupport.xcodeproj/project.xcworkspace/xcuserdata/nathan.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /CategoryPropertyDynamicSupport.xcodeproj/xcuserdata/NathanLi.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /CategoryPropertyDynamicSupport.xcodeproj/xcuserdata/NathanLi.xcuserdatad/xcschemes/CategoryPropertyDynamicSupport.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /CategoryPropertyDynamicSupport.xcodeproj/xcuserdata/NathanLi.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | CategoryPropertyDynamicSupport.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | D8B811A21C04913900AD7F84 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /CategoryPropertyDynamicSupport.xcodeproj/xcuserdata/nathan.xcuserdatad/xcschemes/CategoryPropertyDynamicSupport.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /CategoryPropertyDynamicSupport.xcodeproj/xcuserdata/nathan.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | CategoryPropertyDynamicSupport.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | D8B811A21C04913900AD7F84 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /CategoryPropertyDynamicSupport/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // CategoryPropertyDynamicSupport 4 | // 5 | // Created by nathan@hoomic.com on 15/11/24. 6 | // Copyright © 2015年 听榆大叔. 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 | -------------------------------------------------------------------------------- /CategoryPropertyDynamicSupport/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // CategoryPropertyDynamicSupport 4 | // 5 | // Created by nathan@hoomic.com on 15/11/24. 6 | // Copyright © 2015年 听榆大叔. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | - (void)applicationWillResignActive:(UIApplication *)application { 24 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 25 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 26 | } 27 | 28 | - (void)applicationDidEnterBackground:(UIApplication *)application { 29 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 30 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 31 | } 32 | 33 | - (void)applicationWillEnterForeground:(UIApplication *)application { 34 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 35 | } 36 | 37 | - (void)applicationDidBecomeActive:(UIApplication *)application { 38 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 39 | } 40 | 41 | - (void)applicationWillTerminate:(UIApplication *)application { 42 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /CategoryPropertyDynamicSupport/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 | } -------------------------------------------------------------------------------- /CategoryPropertyDynamicSupport/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 | -------------------------------------------------------------------------------- /CategoryPropertyDynamicSupport/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /CategoryPropertyDynamicSupport/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /CategoryPropertyDynamicSupport/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // CategoryPropertyDynamicSupport 4 | // 5 | // Created by nathan@hoomic.com on 15/11/24. 6 | // Copyright © 2015年 听榆大叔. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /CategoryPropertyDynamicSupport/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // NLActivityDemo 4 | // 5 | // Created by 听榆大叔 on 15/10/20. 6 | // Copyright © 2015年 NL. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | @import AVFoundation; 11 | @import MapKit; 12 | @import SceneKit; 13 | @import CoreMedia; 14 | #import 15 | #import 16 | #import "NLDynamicPropertyPrefix.h" 17 | 18 | DynamicPropertySetPrefix("demo_"); 19 | 20 | 21 | 22 | @interface NLUIViewController : ViewController 23 | 24 | @end 25 | 26 | @interface ViewController (demo_ex) 27 | 28 | @property (nonatomic, assign) int demo_int; 29 | @property (nonatomic, strong) id demo_object; 30 | @property (nonatomic, assign) BOOL demo_bool; 31 | @property (nonatomic, assign) char demo_char; 32 | @property (nonatomic, assign) float demo_float; 33 | @property (nonatomic, assign) double demo_double; 34 | @property (nonatomic, assign) long demo_long; 35 | @property (nonatomic, assign) uint demo_uint; 36 | 37 | @property (nonatomic, assign) CGRect demo_rect; 38 | @property (nonatomic, assign) CGPoint demo_point; 39 | @property (nonatomic, assign) CGSize demo_size; 40 | @property (nonatomic, assign) CGVector demo_vector; 41 | @property (nonatomic, assign) CGAffineTransform demo_affineTransform; 42 | @property (nonatomic, assign) UIEdgeInsets demo_edgeInsets; 43 | @property (nonatomic, assign) UIOffset demo_offset; 44 | @property (nonatomic, assign) CATransform3D demo_transform3D; 45 | 46 | 47 | @property (nonatomic, assign) CMTime demo_cmtime; 48 | @property (nonatomic, assign) CMTimeRange demo_cmtimeRange; 49 | @property (nonatomic, assign) CMTimeMapping demo_cmtimeMapping; 50 | 51 | @property (nonatomic, assign) CLLocationCoordinate2D demo_coordinate; 52 | @property (nonatomic, assign) MKCoordinateSpan demo_coordinateSpan; 53 | 54 | @property (nonatomic, assign) SCNVector3 demo_scnvector3; 55 | @property (nonatomic, assign) SCNVector4 demo_scnvector4; 56 | @property (nonatomic, assign) SCNMatrix4 demo_matrix4; 57 | 58 | @property (nonatomic, copy) void (^demo_copyBlock)(void); 59 | @property (nonatomic, weak) id demo_weakObject; 60 | 61 | @property (nonatomic, strong) id demo_strongObject; 62 | 63 | @end 64 | 65 | @interface ViewController () 66 | 67 | @property (nonatomic, strong) id obj; 68 | 69 | @property (nonatomic, strong) NLUIViewController *subVC; 70 | @end 71 | 72 | @implementation ViewController 73 | 74 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { 75 | id value = [object valueForKeyPath:keyPath]; 76 | fprintf(stdout, "observe %s = %s\n", [keyPath cStringUsingEncoding:NSUTF8StringEncoding], [[value description] cStringUsingEncoding:NSUTF8StringEncoding]); 77 | } 78 | 79 | 80 | - (void)viewDidLoad { 81 | [super viewDidLoad]; 82 | 83 | [self addObserver:self forKeyPath:@"demo_int" options:NSKeyValueObservingOptionNew context:nil]; 84 | [self addObserver:self forKeyPath:@"demo_object" options:NSKeyValueObservingOptionNew context:nil]; 85 | [self addObserver:self forKeyPath:@"demo_bool" options:NSKeyValueObservingOptionNew context:nil]; 86 | [self addObserver:self forKeyPath:@"demo_char" options:NSKeyValueObservingOptionNew context:nil]; 87 | [self addObserver:self forKeyPath:@"demo_float" options:NSKeyValueObservingOptionNew context:nil]; 88 | [self addObserver:self forKeyPath:@"demo_double" options:NSKeyValueObservingOptionNew context:nil]; 89 | [self addObserver:self forKeyPath:@"demo_long" options:NSKeyValueObservingOptionNew context:nil]; 90 | [self addObserver:self forKeyPath:@"demo_uint" options:NSKeyValueObservingOptionNew context:nil]; 91 | [self addObserver:self forKeyPath:@"demo_point" options:NSKeyValueObservingOptionNew context:nil]; 92 | [self addObserver:self forKeyPath:@"demo_rect" options:NSKeyValueObservingOptionNew context:nil]; 93 | [self addObserver:self forKeyPath:@"demo_size" options:NSKeyValueObservingOptionNew context:nil]; 94 | [self addObserver:self forKeyPath:@"demo_vector" options:NSKeyValueObservingOptionNew context:nil]; 95 | [self addObserver:self forKeyPath:@"demo_offset" options:NSKeyValueObservingOptionNew context:nil]; 96 | [self addObserver:self forKeyPath:@"demo_edgeInsets" options:NSKeyValueObservingOptionNew context:nil]; 97 | [self addObserver:self forKeyPath:@"demo_affineTransform" options:NSKeyValueObservingOptionNew context:nil]; 98 | [self addObserver:self forKeyPath:@"demo_transform3D" options:NSKeyValueObservingOptionNew context:nil]; 99 | /** 100 | * @brief 注意,当观察动态生成的 weak 属性时,不会接收到 weak 自动设置为 nil 的通知(在 weak 指向的对象被 101 | * 销毁时,weak 属性会自动设置为 nil)。 102 | */ 103 | [self addObserver:self forKeyPath:@"demo_weakObject" options:NSKeyValueObservingOptionNew context:nil]; 104 | [self addObserver:self forKeyPath:@"demo_copyBlock" options:NSKeyValueObservingOptionNew context:nil]; 105 | [self addObserver:self forKeyPath:@"demo_coordinate" options:NSKeyValueObservingOptionNew context:nil]; 106 | [self addObserver:self forKeyPath:@"demo_coordinateSpan" options:NSKeyValueObservingOptionNew context:nil]; 107 | [self addObserver:self forKeyPath:@"demo_cmtime" options:NSKeyValueObservingOptionNew context:nil]; 108 | [self addObserver:self forKeyPath:@"demo_cmtimeRange" options:NSKeyValueObservingOptionNew context:nil]; 109 | [self addObserver:self forKeyPath:@"demo_scnvector3" options:NSKeyValueObservingOptionNew context:nil]; 110 | [self addObserver:self forKeyPath:@"demo_scnvector4" options:NSKeyValueObservingOptionNew context:nil]; 111 | [self addObserver:self forKeyPath:@"demo_matrix4" options:NSKeyValueObservingOptionNew context:nil]; 112 | [self addObserver:self forKeyPath:@"demo_strongObject" options:NSKeyValueObservingOptionNew context:nil]; 113 | 114 | 115 | self.subVC = [NLUIViewController new]; 116 | self.subVC.demo_int = 30; 117 | self.subVC.demo_object = [UIView new]; 118 | [self setValue:@40 forKeyPath:@"subVC.demo_int"]; 119 | 120 | self.demo_int = 20; 121 | self.demo_object = [UIView new]; 122 | [self setValue:@34 forKey:@"demo_int"]; 123 | [self setValue:@"new demo_object string" forKey:@"demo_object"]; 124 | 125 | self.demo_bool = YES; 126 | self.demo_char = 'c'; 127 | self.demo_float = 32.2; 128 | self.demo_double = 51351654411243.3; 129 | self.demo_long = 31124L; 130 | self.demo_uint = 24; 131 | 132 | self.demo_rect = CGRectMake(10, 10, 100, 100); 133 | [self setValue:[NSValue valueWithCGRect:CGRectMake(0, 0, 200, 200)] forKey:@"demo_rect"]; 134 | 135 | self.demo_point = CGPointMake(10, 10); 136 | self.demo_size = CGSizeMake(100, 100); 137 | self.demo_vector = CGVectorMake(20.0, 20.0); 138 | self.demo_offset = UIOffsetMake(10.0, 20.0); 139 | self.demo_edgeInsets = UIEdgeInsetsMake(10, 20, 30, 40); 140 | self.demo_affineTransform = CGAffineTransformMake(1.0, 2.0, 3.0, 4.0, 5.0, 6.0); 141 | self.demo_transform3D = CATransform3DMakeRotation(90, 1, 1, 1); 142 | 143 | UIButton *tempButton = [UIButton new]; 144 | self.obj = tempButton; 145 | self.demo_weakObject = self.obj; 146 | self.demo_strongObject = [NSObject new]; 147 | self.demo_copyBlock = ^{ 148 | fprintf(stdout, "demo_object: just printf call copy block\n"); 149 | }; 150 | 151 | self.demo_coordinate = CLLocationCoordinate2DMake(21.016f, 135.05135f); 152 | self.demo_coordinateSpan = MKCoordinateSpanMake(32.51f, 145.6156f); 153 | // KVC 154 | [self setValue:[NSValue valueWithMKCoordinate:CLLocationCoordinate2DMake(32.051f, 154.053f)] forKey:@"demo_coordinate"]; 155 | 156 | self.demo_cmtime = CMTimeMake(1000, 24); 157 | self.demo_cmtimeRange = CMTimeRangeMake(kCMTimeZero, CMTimeMakeWithEpoch(2400, 24, 0)); 158 | 159 | CMTimeRange timeRangeTarget = CMTimeRangeMake(kCMTimeZero, CMTimeMakeWithEpoch(3600, 12, 0)); 160 | CMTimeMapping timeMapping = {self.demo_cmtimeRange, timeRangeTarget}; 161 | self.demo_cmtimeMapping = timeMapping; 162 | 163 | self.demo_scnvector3 = SCNVector3Make(1.f, 2.f, 3.f); 164 | self.demo_scnvector4 = SCNVector4Make(4.f, 6.f, 8.f, 10.f); 165 | self.demo_matrix4 = SCNMatrix4MakeScale(1.0, 1.0, 1.0); 166 | } 167 | 168 | - (void)viewWillAppear:(BOOL)animated { 169 | [super viewWillAppear:animated]; 170 | 171 | self.obj = nil; 172 | } 173 | 174 | - (void)viewDidAppear:(BOOL)animated { 175 | [super viewDidAppear:animated]; 176 | 177 | fprintf(stdout, "\nsub class demo_int = %d\n", self.subVC.demo_int); 178 | fprintf(stdout, "sub class demo_object = %s\n\n", [[self.subVC.demo_object description] cStringUsingEncoding:NSUTF8StringEncoding]); 179 | 180 | fprintf(stdout, "demo_int = %d\n", self.demo_int); 181 | fprintf(stdout, "demo_object = %s\n", [[self.demo_object description] cStringUsingEncoding:NSUTF8StringEncoding]); 182 | fprintf(stdout, "demo_bool = %d\n", self.demo_bool); 183 | fprintf(stdout, "demo_char = %c\n", self.demo_char); 184 | fprintf(stdout, "demo_float = %f\n", self.demo_float); 185 | fprintf(stdout, "demo_double = %f\n", self.demo_double); 186 | fprintf(stdout, "demo_long = %ld\n", self.demo_long); 187 | fprintf(stdout, "demo_uint = %d\n", self.demo_uint); 188 | 189 | fprintf(stdout, "demo_rect = %s\n", [NSStringFromCGRect(self.demo_rect) cStringUsingEncoding:NSUTF8StringEncoding]); 190 | fprintf(stdout, "demo_point = %s\n", [NSStringFromCGPoint(self.demo_point) cStringUsingEncoding:NSUTF8StringEncoding]); 191 | fprintf(stdout, "demo_size = %s\n", [NSStringFromCGSize(self.demo_size) cStringUsingEncoding:NSUTF8StringEncoding]); 192 | fprintf(stdout, "demo_vector = %s\n", [NSStringFromCGVector(self.demo_vector) cStringUsingEncoding:NSUTF8StringEncoding]); 193 | fprintf(stdout, "demo_offset = %s\n", [NSStringFromUIOffset(self.demo_offset) cStringUsingEncoding:NSUTF8StringEncoding]); 194 | fprintf(stdout, "demo_edgeInsets = %s\n", [NSStringFromUIEdgeInsets(self.demo_edgeInsets) cStringUsingEncoding:NSUTF8StringEncoding]); 195 | fprintf(stdout, "demo_affineTransform = %s\n", [NSStringFromCGAffineTransform(self.demo_affineTransform) cStringUsingEncoding:NSUTF8StringEncoding]); 196 | fprintf(stdout, "demo_transform3D is equal CATransform3DMakeRotation(90, 1, 1, 1) = %s\n", CATransform3DEqualToTransform(self.demo_transform3D, CATransform3DMakeRotation(90, 1, 1, 1)) ? "YES" : "NO"); 197 | 198 | fprintf(stdout, "demo_weakObject = %s\n", [[self.demo_weakObject description] cStringUsingEncoding:NSUTF8StringEncoding]); 199 | 200 | 201 | self.demo_copyBlock(); 202 | 203 | fprintf(stdout, "demo_coordinate = {%f, %f}\n", self.demo_coordinate.latitude, self.demo_coordinate.longitude); 204 | fprintf(stdout, "demo_coordinateSpan = {%f, %f}\n", self.demo_coordinateSpan.latitudeDelta, self.demo_coordinateSpan.longitudeDelta); 205 | 206 | fprintf(stdout, "demo_cmtime = "); 207 | CMTimeShow(self.demo_cmtime); 208 | 209 | fprintf(stdout, "demo_cmtimeRange = "); 210 | CMTimeRangeShow(self.demo_cmtimeRange); 211 | 212 | CFStringRef sourceStringRef = CMTimeRangeCopyDescription(CFAllocatorGetDefault(), self.demo_cmtimeMapping.source); 213 | CFStringRef targetStringRef = CMTimeRangeCopyDescription(CFAllocatorGetDefault(), self.demo_cmtimeMapping.target); 214 | fprintf(stdout, "demo_cmtimeMapping = {%s,%s}\n", [((__bridge NSString *)sourceStringRef) cStringUsingEncoding:NSUTF8StringEncoding], [((__bridge NSString *)targetStringRef) cStringUsingEncoding:NSUTF8StringEncoding]); 215 | 216 | fprintf(stdout, "demo_scnvector3 = {%f, %f, %f}\n", self.demo_scnvector3.x, self.demo_scnvector3.y, self.demo_scnvector3.z); 217 | fprintf(stdout, "demo_scnvector4 = {%f, %f, %f, %f}\n", self.demo_scnvector4.x, self.demo_scnvector4.y, self.demo_scnvector4.z, self.demo_scnvector4.w); 218 | fprintf(stdout, "demo_matrix4 is equal SCNMatrix4MakeScale(1.0, 1.0, 1.0) = %s\n", SCNMatrix4EqualToMatrix4(self.demo_matrix4, SCNMatrix4MakeScale(1.0, 1.0, 1.0)) ? "YES" : "NO"); 219 | 220 | fprintf(stdout, "demo_strongObject = {%s}\n", [[self.demo_strongObject description] cStringUsingEncoding:NSUTF8StringEncoding]); 221 | 222 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 223 | self.demo_strongObject = nil; 224 | self.demo_weakObject = nil; 225 | }); 226 | } 227 | 228 | @end 229 | 230 | 231 | 232 | @implementation ViewController (demo_ex) 233 | 234 | @dynamic demo_uint; 235 | @dynamic demo_long; 236 | @dynamic demo_double; 237 | @dynamic demo_float; 238 | @dynamic demo_char; 239 | @dynamic demo_bool; 240 | @dynamic demo_object; 241 | @dynamic demo_int; 242 | 243 | @dynamic demo_rect; 244 | @dynamic demo_point; 245 | @dynamic demo_size; 246 | @dynamic demo_affineTransform; 247 | @dynamic demo_vector; 248 | @dynamic demo_offset; 249 | @dynamic demo_transform3D; 250 | @dynamic demo_edgeInsets; 251 | 252 | @dynamic demo_cmtime; 253 | @dynamic demo_cmtimeMapping; 254 | @dynamic demo_cmtimeRange; 255 | 256 | @dynamic demo_coordinate; 257 | @dynamic demo_coordinateSpan; 258 | 259 | @dynamic demo_copyBlock; 260 | @dynamic demo_weakObject; 261 | @dynamic demo_strongObject; 262 | 263 | @dynamic demo_matrix4; 264 | @dynamic demo_scnvector4; 265 | @dynamic demo_scnvector3; 266 | 267 | @end 268 | 269 | 270 | 271 | @implementation NLUIViewController 272 | 273 | @end 274 | -------------------------------------------------------------------------------- /CategoryPropertyDynamicSupport/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // CategoryPropertyDynamicSupport 4 | // 5 | // Created by nathan@hoomic.com on 15/11/24. 6 | // Copyright © 2015年 听榆大叔. 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 | -------------------------------------------------------------------------------- /NSObject+nl_dynamicPropertySupport/NLDynamicPropertyPrefix.h: -------------------------------------------------------------------------------- 1 | // 2 | // NLDynamicPropertyPrefix.h 3 | // CategoryPropertyDynamicSupport 4 | // 5 | // Created by NathanLi on 28/11/2016. 6 | // Copyright © 2016 听榆大叔. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | /** 12 | * 可以通过定义宏:DynamicPropertySetPrefix 来定义动态属性的前辍名 13 | * 前辍名长度不能为 0,你应该定义成字母加下划线的形式,如下 14 | * 15 | * DynamicPropertySetPrefix("demo_") 16 | * 17 | * 注意,要在 m 文件中调用 18 | */ 19 | #define DynamicPropertySetPrefix(prefix)\ 20 | @interface NSObject (nl__dynamicPropertySetPre__) @end\ 21 | @implementation NSObject (nl__dynamicPropertySetPre__)\ 22 | - (const char * const)__nl_dynamicPropertyPrefix__ {\ 23 | return prefix;\ 24 | }\ 25 | @end 26 | 27 | /** 28 | * @brief 动态属性前辍名 29 | * 30 | * @return 前辍字符串 31 | */ 32 | extern const char * _Nullable nl_dynamicPropertyPrefix(); 33 | 34 | 35 | -------------------------------------------------------------------------------- /NSObject+nl_dynamicPropertySupport/NLDynamicPropertyPrefix.m: -------------------------------------------------------------------------------- 1 | // 2 | // NLDynamicPropertyPrefix.m 3 | // CategoryPropertyDynamicSupport 4 | // 5 | // Created by NathanLi on 28/11/2016. 6 | // Copyright © 2016 听榆大叔. All rights reserved. 7 | // 8 | 9 | #import "NLDynamicPropertyPrefix.h" 10 | #import 11 | 12 | #ifndef DynamicPropertyPrefixDefault 13 | #define DynamicPropertyPrefixDefault "nl_" 14 | #endif 15 | 16 | typedef char *(*NLDynamicPropertyPrefix)(id); 17 | 18 | const char *nl_dynamicPropertyPrefix() { 19 | static char *prefix = DynamicPropertyPrefixDefault; 20 | static BOOL didLoadSetPrefix = NO; 21 | 22 | if (!didLoadSetPrefix) { 23 | if ([NSObject instancesRespondToSelector:sel_registerName("__nl_dynamicPropertyPrefix__")]) { 24 | Method method = class_getInstanceMethod([NSObject class], sel_registerName("__nl_dynamicPropertyPrefix__")); 25 | IMP imp = method_getImplementation(method); 26 | prefix = ((NLDynamicPropertyPrefix)imp)(nil); 27 | } 28 | 29 | didLoadSetPrefix = YES; 30 | } 31 | 32 | 33 | #ifdef DEBUG 34 | static BOOL isVaild = NO; 35 | if (!isVaild) { 36 | assert(strlen(prefix) > 0); 37 | 38 | isVaild = YES; 39 | } 40 | #endif 41 | 42 | return prefix; 43 | } 44 | -------------------------------------------------------------------------------- /NSObject+nl_dynamicPropertySupport/NLPropertyDescriptor.h: -------------------------------------------------------------------------------- 1 | // 2 | // NLPropertyDescriptor.h 3 | // CategoryPropertyDynamicSupport 4 | // 5 | // Created by 听榆大叔 on 15/11/19. 6 | // Copyright © 2015年 NL. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import 12 | 13 | /** 14 | * 15 | c A char 16 | i An int 17 | s A short 18 | l A long 19 | l is treated as a 32-bit quantity on 64-bit programs. 20 | q A long long 21 | C An unsigned char 22 | I An unsigned int 23 | S An unsigned short 24 | L An unsigned long 25 | Q An unsigned long long 26 | f A float 27 | d A double 28 | B A C++ bool or a C99 _Bool 29 | v A void 30 | * A character string (char *) 31 | @ An object (whether statically typed or typed id) 32 | # A class object (Class) 33 | : A method selector (SEL) 34 | [array type] An array 35 | {name=type...} A structure 36 | (name=type...) A union 37 | bnum A bit field of num bits 38 | ^type A pointer to type 39 | ? An unknown type (among other things, this code is used for function pointers) 40 | 41 | */ 42 | 43 | typedef NS_ENUM(NSUInteger, NLPropertyPolicy) { 44 | NLPropertyPolicyAssign, 45 | NLPropertyPolicyStrong, 46 | NLPropertyPolicyCopy, 47 | NLPropertyPolicyWeak, 48 | }; 49 | 50 | @interface NLPropertyDescriptor : NSObject 51 | 52 | /** 53 | * @brief 属性名 54 | */ 55 | @property (nonatomic, copy, readonly) NSString *name; 56 | 57 | /** 58 | * @brief getter 方法名 59 | */ 60 | @property (nonatomic, copy, readonly) NSString *getterName; 61 | 62 | /** 63 | * @brief setter 方法名 64 | */ 65 | @property (nonatomic, copy, readonly) NSString *setterName; 66 | 67 | /** 68 | * @brief 变量名 69 | */ 70 | @property (nonatomic, copy, readonly) NSString *variableName; 71 | 72 | /** 73 | * @brief 属性类型编码 74 | */ 75 | @property (nonatomic, copy, readonly) NSString *typeEncoding; 76 | 77 | /** 78 | * @brief 属性类型 79 | */ 80 | @property (nonatomic, assign, readonly) NLPropertyPolicy propertyPolicy; 81 | 82 | /** 83 | * @brief 初始化 84 | */ 85 | - (instancetype)initWithObjcProperty:(objc_property_t)objcProperty; 86 | 87 | - (BOOL)isObjectType; 88 | - (BOOL)isCharType; 89 | - (BOOL)isIntType; 90 | - (BOOL)isShortType; 91 | - (BOOL)isLongType; 92 | - (BOOL)isLongLongType; 93 | - (BOOL)isUCharType; 94 | - (BOOL)isUIntType; 95 | - (BOOL)isUShortType; 96 | - (BOOL)isULongType; 97 | - (BOOL)isULongLongType; 98 | - (BOOL)isFloatType; 99 | - (BOOL)isDoubleType; 100 | - (BOOL)isBoolType; 101 | 102 | - (BOOL)isRectType; 103 | - (BOOL)isPointType; 104 | - (BOOL)isSizeType; 105 | - (BOOL)isVectorType; 106 | - (BOOL)isOffsetType; 107 | - (BOOL)isEdgeInsetsType; 108 | - (BOOL)isAffineTransormType; 109 | - (BOOL)isTransorm3DType; 110 | @end 111 | -------------------------------------------------------------------------------- /NSObject+nl_dynamicPropertySupport/NLPropertyDescriptor.m: -------------------------------------------------------------------------------- 1 | // 2 | // NLPropertyDescriptor.m 3 | // CategoryPropertyDynamicSupport 4 | // 5 | // Created by 听榆大叔 on 15/11/19. 6 | // Copyright © 2015年 NL. All rights reserved. 7 | // 8 | 9 | #import "NLPropertyDescriptor.h" 10 | 11 | @implementation NLPropertyDescriptor 12 | 13 | - (instancetype)initWithObjcProperty:(objc_property_t)objcProperty { 14 | if (self = [super init]) { 15 | _propertyPolicy = NLPropertyPolicyAssign; 16 | 17 | const char *cPropertyName = property_getName(objcProperty); 18 | _name = [[NSString stringWithCString:cPropertyName encoding:NSUTF8StringEncoding] copy]; 19 | _getterName = [_name copy]; 20 | _variableName = [@"_" stringByAppendingString:_name]; 21 | 22 | ({ 23 | // default setter name. 24 | NSString *firstChar = [[_name substringToIndex:1] uppercaseString]; 25 | NSString *subjectName = [_name substringFromIndex:1] ?: @""; 26 | subjectName = [subjectName stringByAppendingString:@":"]; 27 | _setterName = [[NSString stringWithFormat:@"set%@%@", firstChar, subjectName] copy]; 28 | }); 29 | 30 | 31 | const char *cPropertyAttributes = property_getAttributes(objcProperty); 32 | NSString *sPropertyAttributes = [NSString stringWithCString:cPropertyAttributes encoding:NSUTF8StringEncoding]; 33 | NSArray *attributes = [sPropertyAttributes componentsSeparatedByString:@","]; 34 | [attributes enumerateObjectsUsingBlock:^(NSString *_Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 35 | if (idx == 0) { 36 | // 第一个一定是类型编码 37 | _typeEncoding = [obj copy]; 38 | } 39 | 40 | if ([obj hasPrefix:@"G"]) { 41 | // getter 方法名 42 | NSString *getterName = [obj substringFromIndex:1]; 43 | _getterName = [getterName copy]; 44 | } else if ([obj hasPrefix:@"S"]) { 45 | // setter 方法名 46 | NSString *setterName = [obj substringFromIndex:1]; 47 | _setterName = [setterName copy]; 48 | } else if ([obj hasPrefix:@"V"]) { 49 | // 变量名 50 | NSString *variableName = [obj substringFromIndex:1]; 51 | _variableName = [variableName copy]; 52 | } else if ([obj isEqualToString:@"&"]) { 53 | _propertyPolicy = NLPropertyPolicyStrong; 54 | } else if ([obj isEqualToString:@"C"]) { 55 | _propertyPolicy = NLPropertyPolicyCopy; 56 | } else if ([obj isEqualToString:@"W"]) { 57 | _propertyPolicy = NLPropertyPolicyWeak; 58 | } else if ([obj isEqualToString:@"R"]) { 59 | // readonly 60 | _setterName = nil; 61 | } 62 | 63 | }]; 64 | } 65 | return self; 66 | } 67 | 68 | - (BOOL)isObjectType { 69 | return [self.typeEncoding hasPrefix:@"T@"]; 70 | } 71 | 72 | - (BOOL)isCharType { 73 | return [self.typeEncoding isEqualToString:@"Tc"]; 74 | } 75 | 76 | - (BOOL)isIntType { 77 | return [self.typeEncoding isEqualToString:@"Ti"]; 78 | } 79 | 80 | - (BOOL)isShortType { 81 | return [self.typeEncoding isEqualToString:@"Ts"]; 82 | } 83 | 84 | - (BOOL)isLongType { 85 | return [self.typeEncoding isEqualToString:@"Tl"]; 86 | } 87 | 88 | - (BOOL)isLongLongType { 89 | return [self.typeEncoding isEqualToString:@"Tq"]; 90 | } 91 | 92 | - (BOOL)isUCharType { 93 | return [self.typeEncoding isEqualToString:@"TC"]; 94 | } 95 | 96 | - (BOOL)isUIntType { 97 | return [self.typeEncoding isEqualToString:@"TI"]; 98 | } 99 | 100 | - (BOOL)isUShortType { 101 | return [self.typeEncoding isEqualToString:@"TS"]; 102 | } 103 | 104 | - (BOOL)isULongType { 105 | return [self.typeEncoding isEqualToString:@"TL"]; 106 | } 107 | 108 | - (BOOL)isULongLongType { 109 | return [self.typeEncoding isEqualToString:@"TQ"]; 110 | } 111 | 112 | - (BOOL)isFloatType { 113 | return [self.typeEncoding isEqualToString:@"Tf"]; 114 | } 115 | 116 | - (BOOL)isDoubleType { 117 | return [self.typeEncoding isEqualToString:@"Td"]; 118 | } 119 | 120 | - (BOOL)isBoolType { 121 | return [self.typeEncoding isEqualToString:@"TB"]; 122 | } 123 | 124 | - (BOOL)isRectType { 125 | return [self _isStructTypeWithEncode:@encode(CGRect)]; 126 | } 127 | 128 | - (BOOL)isPointType { 129 | return [self _isStructTypeWithEncode:@encode(CGPoint)]; 130 | } 131 | 132 | - (BOOL)isSizeType { 133 | return [self _isStructTypeWithEncode:@encode(CGSize)]; 134 | } 135 | 136 | - (BOOL)isVectorType { 137 | return [self _isStructTypeWithEncode:@encode(CGVector)]; 138 | } 139 | 140 | - (BOOL)isOffsetType { 141 | return [self _isStructTypeWithEncode:@encode(UIOffset)]; 142 | } 143 | 144 | - (BOOL)isEdgeInsetsType { 145 | return [self _isStructTypeWithEncode:@encode(UIEdgeInsets)]; 146 | } 147 | 148 | - (BOOL)isAffineTransormType { 149 | return [self _isStructTypeWithEncode:@encode(CGAffineTransform)]; 150 | } 151 | 152 | - (BOOL)isTransorm3DType { 153 | return [self _isStructTypeWithEncode:@encode(CATransform3D)]; 154 | } 155 | 156 | - (BOOL)_isStructTypeWithEncode:(const char *)structEncode { 157 | NSParameterAssert(structEncode != NULL); 158 | NSString *encodeString = [NSString stringWithCString:structEncode encoding:NSUTF8StringEncoding]; 159 | return [self.typeEncoding isEqualToString:[@"T" stringByAppendingString:encodeString]]; 160 | } 161 | 162 | @end 163 | -------------------------------------------------------------------------------- /NSObject+nl_dynamicPropertySupport/NSObject+nl_dynamicPropertyCustomeStruct.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+nl_dynamicPropertyPrivate.h 3 | // CategoryPropertyDynamicSupport 4 | // 5 | // Created by 听榆大叔 on 15/11/24. 6 | // Copyright © 2015年 NL. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | /** 12 | * @brief CMTime 和 CLLocationCoordinate2D 等 struct 需要导入相关的库后有 13 | * 这里是为了不用导入这些库也能原生支持这些 struct。 14 | * 15 | * 这里支持: 16 | * CLLocationCoordinate2D 17 | * MKCoordinateSpan 18 | * 19 | * CMTime 20 | * CMTimeRange 21 | * CMTimeMapping 22 | * 23 | * SCNVector3 24 | * SCNVector4 25 | * SCNMatrix4 26 | * 27 | * 与上述结构体的字段完全一致的结构也同样支持 28 | * 29 | */ 30 | @interface NSObject (nl_dynamicPropertyCustomeStruct) 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /NSObject+nl_dynamicPropertySupport/NSObject+nl_dynamicPropertyCustomeStruct.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+nl_dynamicPropertyPrivate.m 3 | // CategoryPropertyDynamicSupport 4 | // 5 | // Created by 听榆大叔 on 15/11/24. 6 | // Copyright © 2015年 NL. All rights reserved. 7 | // 8 | 9 | #import "NSObject+nl_dynamicPropertyCustomeStruct.h" 10 | #import "NSObject+nl_dynamicPropertyStore.h" 11 | #import "NLPropertyDescriptor.h" 12 | #import "NSObject+nl_dynamicPropertySupport.h" 13 | #import 14 | #import "NLDynamicPropertyPrefix.h" 15 | 16 | typedef int64_t NLCMTimeValue; 17 | typedef int32_t NLCMTimeScale; 18 | typedef CF_OPTIONS( uint32_t, NLCMTimeFlags ) { 19 | kCMTimeFlags_Valid = 1UL<<0, 20 | kCMTimeFlags_HasBeenRounded = 1UL<<1, 21 | kCMTimeFlags_PositiveInfinity = 1UL<<2, 22 | kCMTimeFlags_NegativeInfinity = 1UL<<3, 23 | kCMTimeFlags_Indefinite = 1UL<<4, 24 | kCMTimeFlags_ImpliedValueFlagsMask = kCMTimeFlags_PositiveInfinity | kCMTimeFlags_NegativeInfinity | kCMTimeFlags_Indefinite 25 | }; 26 | typedef int64_t NLCMTimeEpoch; 27 | 28 | typedef struct 29 | { 30 | NLCMTimeValue value; 31 | NLCMTimeScale timescale; 32 | NLCMTimeFlags flags; 33 | NLCMTimeEpoch epoch; 34 | } NLCMTime; // simulate for CMTime 35 | 36 | 37 | typedef struct 38 | { 39 | NLCMTime start; 40 | NLCMTime duration; 41 | } NLCMTimeRange; // simulate for CMTimeRange 42 | 43 | typedef struct 44 | { 45 | NLCMTimeRange source; 46 | NLCMTimeRange target; 47 | } NLCMTimeMapping; // simulate for CMTimeMapping 48 | 49 | typedef double NLCLLocationDegrees; 50 | typedef struct { 51 | NLCLLocationDegrees latitude; 52 | NLCLLocationDegrees longitude; 53 | } NLCLLocationCoordinate2D; // simulate for CLLocationCoordinate2D 54 | 55 | typedef struct { 56 | NLCLLocationDegrees latitudeDelta; 57 | NLCLLocationDegrees longitudeDelta; 58 | } NLMKCoordinateSpan; // simulate for MKCoordinateSpan 59 | 60 | typedef struct { 61 | float x, y, z; 62 | } NLSCNVector3; // simulate for SCNVector3 63 | 64 | typedef struct { 65 | float x, y, z, w; 66 | } NLSCNVector4; // simuate for SCNVector4 67 | 68 | typedef struct { 69 | float m11, m12, m13, m14; 70 | float m21, m22, m23, m24; 71 | float m31, m32, m33, m34; 72 | float m41, m42, m43, m44; 73 | } NLSCNMatrix4; // simuate for SCNMatrix4 74 | 75 | @interface NSValue (nl_NSValueMapKitAndAVFoundationAndSceneKitNLDynamicPropertySupport) 76 | 77 | + (NSValue *)valueWithNLCLLocationCoordinate2D:(NLCLLocationCoordinate2D)NLCLLocationCoordinate2D; 78 | + (NSValue *)valueWithNLMKCoordinateSpan:(NLMKCoordinateSpan)span; 79 | 80 | @property (readonly) NLCLLocationCoordinate2D NLCLLocationCoordinate2DValue; 81 | @property (readonly) NLMKCoordinateSpan NLMKCoordinateSpanValue; 82 | 83 | + (NSValue *)valueWithNLCMTime:(NLCMTime)time NS_AVAILABLE(10_7, 4_0); 84 | @property (readonly) NLCMTime NLCMTimeValue NS_AVAILABLE(10_7, 4_0); 85 | 86 | + (NSValue *)valueWithNLCMTimeRange:(NLCMTimeRange)timeRange NS_AVAILABLE(10_7, 4_0); 87 | @property (readonly) NLCMTimeRange NLCMTimeRangeValue NS_AVAILABLE(10_7, 4_0); 88 | 89 | + (NSValue *)valueWithNLCMTimeMapping:(NLCMTimeMapping)timeMapping NS_AVAILABLE(10_7, 4_0); 90 | @property (readonly) NLCMTimeMapping NLCMTimeMappingValue NS_AVAILABLE(10_7, 4_0); 91 | 92 | /** 93 | * @brief SceneKitAdditions 94 | */ 95 | + (NSValue *)valueWithNLSCNVector3:(NLSCNVector3)v; 96 | + (NSValue *)valueWithNLSCNVector4:(NLSCNVector4)v; 97 | + (NSValue *)valueWithNLSCNMatrix4:(NLSCNMatrix4)v; 98 | 99 | @property(nonatomic, readonly) NLSCNVector3 NLSCNVector3Value; 100 | @property(nonatomic, readonly) NLSCNVector4 NLSCNVector4Value; 101 | @property(nonatomic, readonly) NLSCNMatrix4 NLSCNMatrix4Value; 102 | 103 | @end 104 | 105 | @implementation NSValue (nl_NSValueMapKitAndAVFoundationNLDynamicPropertySupport) 106 | 107 | + (NSValue *)valueWithNLCLLocationCoordinate2D:(NLCLLocationCoordinate2D)coordinate { 108 | return [self valueWithBytes:&coordinate objCType:@encode(NLCLLocationCoordinate2D)]; 109 | } 110 | 111 | + (NSValue *)valueWithNLMKCoordinateSpan:(NLMKCoordinateSpan)span { 112 | return [self valueWithBytes:&span objCType:@encode(NLMKCoordinateSpan)]; 113 | } 114 | 115 | - (NLCLLocationCoordinate2D)NLCLLocationCoordinate2DValue { 116 | NLCLLocationCoordinate2D value; 117 | [self getValue:&value]; 118 | return value; 119 | } 120 | 121 | - (NLMKCoordinateSpan)NLMKCoordinateSpanValue { 122 | NLMKCoordinateSpan value; 123 | [self getValue:&value]; 124 | return value; 125 | } 126 | 127 | + (NSValue *)valueWithNLCMTime:(NLCMTime)time { 128 | return [self valueWithBytes:&time objCType:@encode(NLCMTime)]; 129 | } 130 | 131 | - (NLCMTime)NLCMTimeValue { 132 | NLCMTime value; 133 | [self getValue:&value]; 134 | return value; 135 | } 136 | 137 | + (NSValue *)valueWithNLCMTimeRange:(NLCMTimeRange)timeRange NS_AVAILABLE(10_7, 4_0) { 138 | return [self valueWithBytes:&timeRange objCType:@encode(NLCMTimeRange)]; 139 | } 140 | 141 | - (NLCMTimeRange)NLCMTimeRangeValue { 142 | NLCMTimeRange value; 143 | [self getValue:&value]; 144 | return value; 145 | } 146 | 147 | + (NSValue *)valueWithNLCMTimeMapping:(NLCMTimeMapping)timeMapping NS_AVAILABLE(10_7, 4_0) { 148 | return [self valueWithBytes:&timeMapping objCType:@encode(NLCMTimeMapping)]; 149 | } 150 | 151 | - (NLCMTimeMapping)NLCMTimeMappingValue { 152 | NLCMTimeMapping value; 153 | [self getValue:&value]; 154 | return value; 155 | } 156 | 157 | + (NSValue *)valueWithNLSCNVector3:(NLSCNVector3)v { 158 | return [self valueWithBytes:&v objCType:@encode(NLSCNVector3)]; 159 | } 160 | + (NSValue *)valueWithNLSCNVector4:(NLSCNVector4)v { 161 | return [self valueWithBytes:&v objCType:@encode(NLSCNVector4)]; 162 | } 163 | + (NSValue *)valueWithNLSCNMatrix4:(NLSCNMatrix4)v { 164 | return [self valueWithBytes:&v objCType:@encode(NLSCNMatrix4)]; 165 | } 166 | 167 | - (NLSCNVector3)NLSCNVector3Value { 168 | NLSCNVector3 value; 169 | [self getValue:&value]; 170 | return value; 171 | } 172 | - (NLSCNVector4)NLSCNVector4Value { 173 | NLSCNVector4 value; 174 | [self getValue:&value]; 175 | return value; 176 | } 177 | - (NLSCNMatrix4)NLSCNMatrix4Value { 178 | NLSCNMatrix4 value; 179 | [self getValue:&value]; 180 | return value; 181 | } 182 | 183 | @end 184 | 185 | 186 | #define NLDynamicIMPNameGetterCustomeStructType(typeName) __NL__##typeName##__custome_dynamicGetterIMP 187 | #define NLDynamicIMPNameSetterCustomeStructType(typeName) __NL__##typeName##__custome_dynamicSetterIMP 188 | 189 | #define NLDefineDynamicIMPGetterCustomeStructType(typeName) \ 190 | typeName NLDynamicIMPNameGetterCustomeStructType(typeName)(id self, SEL _cmd) {\ 191 | NSString *propertyName = [[self class] nl_dynamicPropertyNameWithSelctor:_cmd];\ 192 | return [[[self nl_dynamicPropertyDictionary] objectForKey:propertyName] typeName##Value];\ 193 | } 194 | 195 | #define NLDefineDynamicIMPSetterCustomeStructType(typeName) \ 196 | void NLDynamicIMPNameSetterCustomeStructType(typeName)(id self, SEL _cmd, typeName arg) {\ 197 | NSString *propertyName = [[self class] nl_dynamicPropertyNameWithSelctor:_cmd];\ 198 | [self willChangeValueForKey:propertyName];\ 199 | [[self nl_dynamicPropertyDictionary] setObject:[NSValue valueWith##typeName:arg] forKey:propertyName];\ 200 | [self didChangeValueForKey:propertyName];\ 201 | } 202 | 203 | #define NLDefineDynamicIMPCustomeStructType(typeName) \ 204 | NLDefineDynamicIMPGetterCustomeStructType(typeName);\ 205 | NLDefineDynamicIMPSetterCustomeStructType(typeName); 206 | 207 | NLDefineDynamicIMPCustomeStructType(NLCLLocationCoordinate2D); 208 | NLDefineDynamicIMPCustomeStructType(NLMKCoordinateSpan); 209 | NLDefineDynamicIMPCustomeStructType(NLCMTime); 210 | NLDefineDynamicIMPCustomeStructType(NLCMTimeRange); 211 | NLDefineDynamicIMPCustomeStructType(NLCMTimeMapping); 212 | NLDefineDynamicIMPCustomeStructType(NLSCNVector3); 213 | NLDefineDynamicIMPCustomeStructType(NLSCNVector4); 214 | NLDefineDynamicIMPCustomeStructType(NLSCNMatrix4); 215 | 216 | @interface NLPropertyDescriptor (nl_customeStruct) 217 | 218 | - (BOOL)nl_isNLCLLocationCoordinate2D; 219 | - (BOOL)nl_isNLMKCoordinateSpan; 220 | - (BOOL)nl_isNLCMTime; 221 | - (BOOL)nl_isNLCMTimeRange; 222 | - (BOOL)nl_isNLCMTimeMapping; 223 | - (BOOL)nl_isNLSCNVector3; 224 | - (BOOL)nl_isNLSCNVector4; 225 | - (BOOL)nl_isNLSCNMatrix4; 226 | 227 | - (NSString *)nl_anonymityPropertyEncode; 228 | 229 | @end 230 | 231 | @implementation NLPropertyDescriptor (nl_customeStruct) 232 | 233 | - (NSString *)nl_anonymityPropertyEncode { 234 | NSString *propertyEncode = objc_getAssociatedObject(self, _cmd); 235 | 236 | if (propertyEncode == nil) { 237 | propertyEncode = [self.typeEncoding stringByReplacingOccurrencesOfString:@"\\{\\w+=" withString:@"{?=" options:NSRegularExpressionSearch range:NSMakeRange(0, [self.typeEncoding length])]; 238 | if ([propertyEncode hasPrefix:@"T"]) { 239 | propertyEncode = [propertyEncode substringFromIndex:1]; 240 | } 241 | objc_setAssociatedObject(self, _cmd, propertyEncode, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 242 | } 243 | return propertyEncode; 244 | } 245 | 246 | - (BOOL)nl_isNLCLLocationCoordinate2D { 247 | return [[self nl_anonymityPropertyEncode] isEqualToString:[NSString stringWithCString:@encode(NLCLLocationCoordinate2D) encoding:NSUTF8StringEncoding]]; 248 | } 249 | 250 | - (BOOL)nl_isNLMKCoordinateSpan { 251 | return [[self nl_anonymityPropertyEncode] isEqualToString:[NSString stringWithCString:@encode(NLMKCoordinateSpan) encoding:NSUTF8StringEncoding]]; 252 | } 253 | 254 | - (BOOL)nl_isNLCMTime { 255 | return [[self nl_anonymityPropertyEncode] isEqualToString:[NSString stringWithCString:@encode(NLCMTime) encoding:NSUTF8StringEncoding]]; 256 | } 257 | 258 | - (BOOL)nl_isNLCMTimeRange { 259 | return [[self nl_anonymityPropertyEncode] isEqualToString:[NSString stringWithCString:@encode(NLCMTimeRange) encoding:NSUTF8StringEncoding]]; 260 | } 261 | 262 | - (BOOL)nl_isNLCMTimeMapping { 263 | return [[self nl_anonymityPropertyEncode] isEqualToString:[NSString stringWithCString:@encode(NLCMTimeMapping) encoding:NSUTF8StringEncoding]]; 264 | } 265 | 266 | - (BOOL)nl_isNLSCNVector3 { 267 | return [[self nl_anonymityPropertyEncode] isEqualToString:[NSString stringWithCString:@encode(NLSCNVector3) encoding:NSUTF8StringEncoding]]; 268 | } 269 | 270 | - (BOOL)nl_isNLSCNVector4 { 271 | return [[self nl_anonymityPropertyEncode] isEqualToString:[NSString stringWithCString:@encode(NLSCNVector4) encoding:NSUTF8StringEncoding]]; 272 | } 273 | 274 | - (BOOL)nl_isNLSCNMatrix4 { 275 | return [[self nl_anonymityPropertyEncode] isEqualToString:[NSString stringWithCString:@encode(NLSCNMatrix4) encoding:NSUTF8StringEncoding]]; 276 | } 277 | 278 | @end 279 | 280 | @implementation NSObject (nl_dynamicPropertyCustomeStruct) 281 | 282 | + (void)load { 283 | Method nl_missMethod = class_getClassMethod(self, @selector(nl_missMethodWithPropertyDescriptor:selector:)); 284 | Method nl_customeStruct_missMethod = class_getClassMethod(self, @selector(nl_customeStruct_missMethodWithPropertyDescriptor:selector:)); 285 | if (nl_missMethod && nl_customeStruct_missMethod) { 286 | method_exchangeImplementations(nl_missMethod, nl_customeStruct_missMethod); 287 | } 288 | 289 | Method setValueMethod = class_getInstanceMethod(self, @selector(setValue:forKey:)); 290 | Method customeSetValueMethod = class_getInstanceMethod(self, @selector(custome_nl_setValue:forKey:)); 291 | if (setValueMethod && customeSetValueMethod) { 292 | method_exchangeImplementations(setValueMethod, customeSetValueMethod); 293 | } 294 | } 295 | 296 | #pragma mark - dynamic add getter setter 297 | + (BOOL)nl_customeStruct_missMethodWithPropertyDescriptor:(NLPropertyDescriptor *)descriptor selector:(SEL)sel { 298 | NSString *selectorName = NSStringFromSelector(sel); 299 | if ([descriptor.getterName isEqualToString:selectorName]) { 300 | return [self nl_customeStruct_getterMethodWithPropertyDescriptor:descriptor]; 301 | } 302 | 303 | if ([descriptor.setterName isEqualToString:selectorName]) { 304 | return [self nl_customeStruct_setterMethodWithPropertyDescriptor:descriptor]; 305 | } 306 | 307 | return NO; 308 | } 309 | 310 | + (BOOL)nl_customeStruct_setterMethodWithPropertyDescriptor:(NLPropertyDescriptor *)descriptor { 311 | IMP imp = NULL; 312 | 313 | if ([descriptor nl_isNLCLLocationCoordinate2D]) { 314 | imp = (IMP)NLDynamicIMPNameSetterCustomeStructType(NLCLLocationCoordinate2D); 315 | } 316 | 317 | if (imp == NULL && [descriptor nl_isNLMKCoordinateSpan]) { 318 | imp = (IMP)NLDynamicIMPNameSetterCustomeStructType(NLMKCoordinateSpan); 319 | } 320 | 321 | if (imp == NULL && [descriptor nl_isNLCMTime]) { 322 | imp = (IMP)NLDynamicIMPNameSetterCustomeStructType(NLCMTime); 323 | } 324 | 325 | if (imp == NULL && [descriptor nl_isNLCMTimeRange]) { 326 | imp = (IMP)NLDynamicIMPNameSetterCustomeStructType(NLCMTimeRange); 327 | } 328 | 329 | if (imp == NULL && [descriptor nl_isNLCMTimeMapping]) { 330 | imp = (IMP)NLDynamicIMPNameSetterCustomeStructType(NLCMTimeMapping); 331 | } 332 | 333 | if (imp == NULL && [descriptor nl_isNLSCNVector3]) { 334 | imp = (IMP)NLDynamicIMPNameSetterCustomeStructType(NLSCNVector3); 335 | } 336 | 337 | if (imp == NULL && [descriptor nl_isNLSCNVector4]) { 338 | imp = (IMP)NLDynamicIMPNameSetterCustomeStructType(NLSCNVector4); 339 | } 340 | 341 | if (imp == NULL && [descriptor nl_isNLSCNMatrix4]) { 342 | imp = (IMP)NLDynamicIMPNameSetterCustomeStructType(NLSCNMatrix4); 343 | } 344 | 345 | if (imp) { 346 | class_addMethod(self, NSSelectorFromString(descriptor.setterName), imp, "v@:"); 347 | return YES; 348 | } 349 | 350 | return NO; 351 | } 352 | 353 | + (BOOL)nl_customeStruct_getterMethodWithPropertyDescriptor:(NLPropertyDescriptor *)descriptor { 354 | IMP imp = NULL; 355 | if ([descriptor nl_isNLCLLocationCoordinate2D]) { 356 | imp = (IMP)NLDynamicIMPNameGetterCustomeStructType(NLCLLocationCoordinate2D); 357 | } 358 | 359 | if (imp == NULL && [descriptor nl_isNLMKCoordinateSpan]) { 360 | imp = (IMP)NLDynamicIMPNameGetterCustomeStructType(NLMKCoordinateSpan); 361 | } 362 | 363 | if (imp == NULL && [descriptor nl_isNLCMTime]) { 364 | imp = (IMP)NLDynamicIMPNameGetterCustomeStructType(NLCMTime); 365 | } 366 | 367 | if (imp == NULL && [descriptor nl_isNLCMTimeRange]) { 368 | imp = (IMP)NLDynamicIMPNameGetterCustomeStructType(NLCMTimeRange); 369 | } 370 | 371 | if (imp == NULL && [descriptor nl_isNLCMTimeMapping]) { 372 | imp = (IMP)NLDynamicIMPNameGetterCustomeStructType(NLCMTimeMapping); 373 | } 374 | 375 | if (imp == NULL && [descriptor nl_isNLSCNVector3]) { 376 | imp = (IMP)NLDynamicIMPNameGetterCustomeStructType(NLSCNVector3); 377 | } 378 | 379 | if (imp == NULL && [descriptor nl_isNLSCNVector4]) { 380 | imp = (IMP)NLDynamicIMPNameGetterCustomeStructType(NLSCNVector4); 381 | } 382 | 383 | if (imp == NULL && [descriptor nl_isNLSCNMatrix4]) { 384 | imp = (IMP)NLDynamicIMPNameGetterCustomeStructType(NLSCNMatrix4); 385 | } 386 | 387 | if (imp) { 388 | const char *cFunctionTypes = [[[descriptor nl_anonymityPropertyEncode] stringByAppendingString:@"@:"] cStringUsingEncoding:NSUTF8StringEncoding]; 389 | class_addMethod(self, NSSelectorFromString(descriptor.getterName), imp, cFunctionTypes); 390 | return YES; 391 | } 392 | 393 | return NO; 394 | } 395 | 396 | #pragma mark - KVC 397 | - (void)custome_nl_setValue:(id)value forKey:(NSString *)key { 398 | // 名字得以 staticPropertyNamePrefix 为前辍 399 | static NSString *staticPropertyNamePrefix = nil; 400 | 401 | static dispatch_once_t onceToken; 402 | dispatch_once(&onceToken, ^{ 403 | const char *prefix = nl_dynamicPropertyPrefix(); 404 | if (prefix != NULL) { 405 | staticPropertyNamePrefix = [NSString stringWithCString:prefix encoding:NSUTF8StringEncoding]; 406 | } 407 | }); 408 | 409 | if (staticPropertyNamePrefix == NULL || ![key hasPrefix:staticPropertyNamePrefix]) { 410 | [self custome_nl_setValue:value forKey:key]; 411 | return; 412 | } 413 | 414 | 415 | NSArray *propertyDescriptors = [self.class nl_dynamicPropertyDescriptors]; 416 | NLPropertyDescriptor *keyPropertyDescriptor = nil; 417 | SEL setterSelector = nil; 418 | 419 | for (NLPropertyDescriptor *propertyDescriptor in propertyDescriptors) { 420 | if ([propertyDescriptor.name isEqualToString:key]) { 421 | if ([self respondsToSelector:NSSelectorFromString(propertyDescriptor.setterName)]) { 422 | keyPropertyDescriptor = propertyDescriptor; 423 | setterSelector = NSSelectorFromString(propertyDescriptor.setterName); 424 | } 425 | break; 426 | } 427 | } 428 | 429 | // 如果不是对象,则一定是基础类型或结构体 430 | // 而这两者所对象的类一定是 NSValue 431 | if (![value isKindOfClass:[NSValue class]]) { 432 | [self custome_nl_setValue:value forKey:key]; 433 | return; 434 | } 435 | 436 | if ([keyPropertyDescriptor nl_isNLCLLocationCoordinate2D]) { 437 | NLDynamicIMPNameSetterCustomeStructType(NLCLLocationCoordinate2D)(self, setterSelector, [value NLCLLocationCoordinate2DValue]); 438 | return; 439 | } 440 | 441 | if ([keyPropertyDescriptor nl_isNLMKCoordinateSpan]) { 442 | NLDynamicIMPNameSetterCustomeStructType(NLMKCoordinateSpan)(self, setterSelector, [value NLMKCoordinateSpanValue]); 443 | return; 444 | } 445 | 446 | if ([keyPropertyDescriptor nl_isNLCMTime]) { 447 | NLDynamicIMPNameSetterCustomeStructType(NLCMTime)(self, setterSelector, [value NLCMTimeValue]); 448 | return; 449 | } 450 | 451 | if ([keyPropertyDescriptor nl_isNLCMTimeRange]) { 452 | NLDynamicIMPNameSetterCustomeStructType(NLCMTimeRange)(self, setterSelector, [value NLCMTimeRangeValue]); 453 | return; 454 | } 455 | 456 | if ([keyPropertyDescriptor nl_isNLCMTimeMapping]) { 457 | NLDynamicIMPNameSetterCustomeStructType(NLCMTimeMapping)(self, setterSelector, [value NLCMTimeMappingValue]); 458 | return; 459 | } 460 | 461 | if ([keyPropertyDescriptor nl_isNLSCNVector3]) { 462 | NLDynamicIMPNameSetterCustomeStructType(NLSCNVector3)(self, setterSelector, [value NLSCNVector3Value]); 463 | return; 464 | } 465 | 466 | if ([keyPropertyDescriptor nl_isNLSCNVector4]) { 467 | NLDynamicIMPNameSetterCustomeStructType(NLSCNVector4)(self, setterSelector, [value NLSCNVector4Value]); 468 | return; 469 | } 470 | 471 | if ([keyPropertyDescriptor nl_isNLSCNMatrix4]) { 472 | NLDynamicIMPNameSetterCustomeStructType(NLSCNMatrix4)(self, setterSelector, [value NLSCNMatrix4Value]); 473 | return; 474 | } 475 | 476 | [self custome_nl_setValue:value forKey:key]; 477 | } 478 | 479 | @end 480 | -------------------------------------------------------------------------------- /NSObject+nl_dynamicPropertySupport/NSObject+nl_dynamicPropertyStore.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+nl_dynamicProperty.h 3 | // CategoryPropertyDynamicSupport 4 | // 5 | // Created by 听榆大叔 on 15/11/20. 6 | // Copyright © 2015年 NL. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import "NLPropertyDescriptor.h" 12 | 13 | @interface NSObject (nl_dynamicPropertyStore) 14 | 15 | /** 16 | * @brief 用来存储自动生成的 `getter`、`setter` 操作的数据 17 | */ 18 | @property (nonatomic, strong, readonly) NSMutableDictionary * _Nullable nl_dynamicPropertyDictionary; 19 | 20 | /** 21 | * @brief 用来存储自动生成的 `getter`、`setter` 操作的 __weak 类型的数据 22 | */ 23 | @property (nonatomic, strong, readonly) NSMapTable * _Nonnull nl_dynamicPropertyWeakDictionary; 24 | 25 | /** 26 | * @brief 判断属性是否应该自动生成方法 27 | * 28 | * @param objProperty 需要判断的属性 29 | * 30 | * @return 如果是,则返回 YES;否则返回 NO; 31 | */ 32 | + (BOOL)nl_validDynamicProperty:(_Nonnull objc_property_t)objProperty; 33 | 34 | /** 35 | * 获取该选择子对应的属性名 36 | */ 37 | + (NSString * _Nullable)nl_dynamicPropertyNameWithSelctor:(_Nonnull SEL)selector; 38 | 39 | /** 40 | * 获取该选择子对应的属性描述器 41 | */ 42 | + (NLPropertyDescriptor * _Nullable)nl_descriptorWithSelector:(_Nonnull SEL)selector; 43 | 44 | /** 45 | * @brief 所有需要动态增加 getter、setter 方法的属性描述器 46 | * 47 | * @return NSArray 48 | */ 49 | + (NSArray * _Nullable)nl_dynamicPropertyDescriptors; 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /NSObject+nl_dynamicPropertySupport/NSObject+nl_dynamicPropertyStore.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+nl_dynamicProperty.m 3 | // CategoryPropertyDynamicSupport 4 | // 5 | // Created by 听榆大叔 on 15/11/20. 6 | // Copyright © 2015年 NL. All rights reserved. 7 | // 8 | 9 | #ifdef DEBUG 10 | #define NLDLogError(xx, ...) fprintf(stderr, ##__VA_ARGS__) 11 | #else 12 | #define NLDLogError(xx, ...) ((void)0) 13 | #endif 14 | 15 | #import "NSObject+nl_dynamicPropertyStore.h" 16 | #import "NSObject+nl_dynamicPropertySupport.h" 17 | #import "NLDynamicPropertyPrefix.h" 18 | 19 | @implementation NSObject (nl_dynamicPropertyStore) 20 | 21 | + (BOOL)nl_validDynamicProperty:(objc_property_t)objProperty { 22 | const char *propertyAttributes = property_getAttributes(objProperty); 23 | 24 | // 必须是 @dynamic 25 | static char *const staticDynamicAttribute = ",D,"; 26 | if (strstr(propertyAttributes, staticDynamicAttribute) == NULL) { 27 | return NO; 28 | } 29 | 30 | // 名字得以 staticPropertyNamePrefix 为前辍 31 | static const char *staticPropertyNamePrefix = NULL; 32 | 33 | static dispatch_once_t onceToken; 34 | dispatch_once(&onceToken, ^{ 35 | staticPropertyNamePrefix = nl_dynamicPropertyPrefix(); 36 | }); 37 | 38 | const char *propertyName = property_getName(objProperty); 39 | if (staticPropertyNamePrefix != NULL && strstr(propertyName, staticPropertyNamePrefix) != propertyName) { 40 | return NO; 41 | } 42 | 43 | // 不支持普通指针类型 44 | NSString *propertyAttributesString = [NSString stringWithCString:propertyAttributes encoding:NSUTF8StringEncoding]; 45 | NSString *propertyEncoding = [propertyAttributesString substringToIndex:[propertyAttributesString rangeOfString:@","].location]; 46 | if ([propertyEncoding hasPrefix:@"T*"]) { 47 | NLDLogError("%s", "nl dynamic property dot not support C character string\n"); 48 | return NO; 49 | } 50 | 51 | if ([propertyEncoding hasPrefix:@"T^"]) { 52 | NLDLogError("%s\n", "nl dynamic property dot not support point type"); 53 | return NO; 54 | } 55 | 56 | return YES; 57 | } 58 | 59 | + (NSArray *)nl_dynamicPropertyDescriptors { 60 | NSMutableArray *descriptors = objc_getAssociatedObject(self, _cmd); 61 | 62 | if (nil == descriptors) { 63 | unsigned int outCount, index; 64 | descriptors = [NSMutableArray arrayWithCapacity:outCount]; 65 | objc_setAssociatedObject(self, _cmd, descriptors, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 66 | 67 | // 获取到本类所有的属性结构体,并转换为属性描述器 68 | objc_property_t *properties = class_copyPropertyList([self class], &outCount); 69 | for (index = 0; index < outCount; ++index) { 70 | objc_property_t property = properties[index]; 71 | if ([self nl_validDynamicProperty:property]) { 72 | NLPropertyDescriptor *descriptor = [[NLPropertyDescriptor alloc] initWithObjcProperty:property]; 73 | [descriptors addObject:descriptor]; 74 | } 75 | } 76 | 77 | free(properties); 78 | 79 | if (self != [NSObject class]) { 80 | // 加上父类的属性结构体 81 | [descriptors addObjectsFromArray:[class_getSuperclass(self) nl_dynamicPropertyDescriptors]]; 82 | } 83 | } 84 | 85 | return descriptors; 86 | } 87 | 88 | + (NLPropertyDescriptor *)nl_descriptorWithSelector:(SEL)selector { 89 | for (NLPropertyDescriptor *descriptor in [self nl_dynamicPropertyDescriptors]) { 90 | NSString *selectorName = NSStringFromSelector(selector); 91 | if ([descriptor.getterName isEqualToString:selectorName] || [descriptor.setterName isEqualToString:selectorName]) { 92 | return descriptor; 93 | } 94 | } 95 | return nil; 96 | } 97 | 98 | + (NSString *)nl_dynamicPropertyNameWithSelctor:(SEL)selector { 99 | return [[self nl_descriptorWithSelector:selector] name]; 100 | } 101 | 102 | 103 | - (NSMutableDictionary *)nl_dynamicPropertyDictionary { 104 | NSMutableDictionary *dynamicProperties = objc_getAssociatedObject(self, _cmd); 105 | if (!dynamicProperties) { 106 | dynamicProperties = [NSMutableDictionary dictionaryWithCapacity:2]; 107 | objc_setAssociatedObject(self, _cmd, dynamicProperties, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 108 | } 109 | return dynamicProperties; 110 | } 111 | 112 | - (NSMapTable *)nl_dynamicPropertyWeakDictionary { 113 | NSMapTable *weakDynamicProperties = objc_getAssociatedObject(self, _cmd); 114 | if (!weakDynamicProperties) { 115 | weakDynamicProperties = [[NSMapTable alloc] initWithKeyOptions:NSPointerFunctionsStrongMemory valueOptions:NSMapTableWeakMemory capacity:2]; 116 | objc_setAssociatedObject(self, _cmd, weakDynamicProperties, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 117 | } 118 | return weakDynamicProperties; 119 | } 120 | 121 | @end 122 | -------------------------------------------------------------------------------- /NSObject+nl_dynamicPropertySupport/NSObject+nl_dynamicPropertySupport.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+nl_dynamicPropertySupport.h 3 | // CategoryPropertyDynamicSupport 4 | // 5 | // Created by 听榆大叔 on 15/11/23. 6 | // Copyright © 2015年 NL. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class NLPropertyDescriptor; 12 | 13 | 14 | /* 15 | iOS Class Category property dynamic support 16 | 17 | 在运行时自动给分类的动态属性添加相应的 `getter`、`setter` 方法
18 | 在分类中添加一些自定义的属性的场景还是蛮多的。一般的方法是自定义其 `getter`、`setter` 方法,在这些方法 19 | 里面再用关联对象等手段来实现。虽然不难,却也麻烦且重复。 20 | 本类的目的在于解放这些重复的劳动。 21 | 22 |  使用: 23 |   1、导入本类及相关类(NSObject+nl_dynamicPropertySupport目录下) 24 |   2、在分类中定义属性时,默认要以 `nl_` 开头 25 |   3、在分类的实现体中,给属性加上 @dynamic 26 | 27 |  优点: 28 |   1、所有的对象、基本数据类型 29 |   2、支持系统已有的 [NSValue valueWith...] 方法的结构体(详见 NSValue )。如:CGRect 30 |   3、支持 KVC 31 |   4、支持 KVO 32 |   5、支持 strong、copy、weak 33 | 34 |  不足: 35 |   1、不支持自定义的结构体。 36 |    但可以通过 `+ nl_missMethodWithPropertyDescriptor:selector:` 来实现。实现方法可见:(`nl_dynamicPropertyCustomeStruct`分类) 37 |   2、注意:KVO 支持不完美。 38 |    当观察动态生成的 weak 属性时,不会接收到 weak 自动设置为 nil 的通知(在 weak 指向的对象被销毁时,weak 属性会自动设置为 nil)。 39 | 40 | ## 工作原理: 41 |   1、使用 `Associated Objects` 42 |   2、OC runtime 43 |   3、Type Encoding 44 |   详见:http://nathanli.cn/2015/12/14/objective-c-%E5%85%83%E7%BC%96%E7%A8%8B%E5%AE%9E%E8%B7%B5-%E5%88%86%E7%B1%BB%E5%8A%A8%E6%80%81%E5%B1%9E%E6%80%A7/ 45 |    46 | ## 代码使用: 47 | 头文件 .h: 48 | ```Objective-C 49 | @interface YourClass (YourCategory) 50 | 51 | @property NSString *nl_yourProperty; 52 | 53 | @end 54 | ``` 55 | 56 | 实现文件: 57 | ```Objective-C 58 | @implementation YourClass (YourCategory) 59 | 60 | @dynamic nl_yourProperty; 61 | 62 | @end 63 | ``` 64 | 65 | ## 自定义属性前辍 66 | 67 | 68 | 可以通过定义宏:DynamicPropertySetPrefix 来定义动态属性的前辍名 69 | 前辍名长度不能为 0,你应该定义成字母加下划线的形式,如下 70 | 71 | 注意,要在 m 文件中调用 72 | 73 | ``` 74 | DynamicPropertySetPrefix("demo_") 75 | ``` 76 | 77 |  如果你有好的 idea,请随时提 issue 或 request。 78 | */ 79 | @interface NSObject (nl_dynamicPropertySupport) 80 | 81 | /** 82 | * @brief 当本分类无法动态加载属性的方法时,会调用。这给你一个自己动态加载属性的机会 83 | * 默认实现是直接返回NO 84 | * 85 | * @param descriptor 无法加载的属性描述器 86 | * @param sel 无法加载的方法 87 | * 88 | * @return 如果你能在这个方法里加载所需要的方法,在 class_addMethod... 加载了所需方法后返回 YES; 否则返回NO; 89 | */ 90 | + (BOOL)nl_missMethodWithPropertyDescriptor:(NLPropertyDescriptor * _Nonnull)descriptor selector:(SEL _Nonnull)sel; 91 | 92 | @end 93 | 94 | 95 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /NSObject+nl_dynamicPropertySupport/NSObject+nl_dynamicPropertySupport.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+nl_dynamicPropertySupport.m 3 | // CategoryPropertyDynamicSupport 4 | // 5 | // Created by 听榆大叔 on 15/11/23. 6 | // Copyright © 2015年 NL. All rights reserved. 7 | // 8 | 9 | #import "NSObject+nl_dynamicPropertySupport.h" 10 | #import 11 | #import "NSObject+nl_dynamicPropertyStore.h" 12 | #import "NLDynamicPropertyPrefix.h" 13 | 14 | /** 15 | * 基本数据类型的 getter 方法名 16 | */ 17 | #define NLDynamicIMPNameGetterBaseDataType(typeName) __NL__##typeName##_dynamicGetterIMP 18 | 19 | /** 20 | * 基本数据类型的 setter 方法名 21 | */ 22 | #define NLDynamicIMPNameSetterBaseDataType(typeName) __NL__##typeName##_dynamicSetterIMP 23 | 24 | #define NLDefineDynamicIMPGetterBaseDataType(typeName, BaseType) \ 25 | BaseType NLDynamicIMPNameGetterBaseDataType(typeName)(id self, SEL _cmd) {\ 26 | \ 27 | NSString *propertyName = [[self class] nl_dynamicPropertyNameWithSelctor:_cmd]; \ 28 | return [[[self nl_dynamicPropertyDictionary] objectForKey:propertyName] typeName##Value];\ 29 | } 30 | 31 | #define NLDefineDynamicIMPSetterBaseDataType(typeName, BaseType) \ 32 | void NLDynamicIMPNameSetterBaseDataType(typeName)(id self, SEL _cmd, BaseType arg) {\ 33 | NSString *propertyName = [[self class] nl_dynamicPropertyNameWithSelctor:_cmd];\ 34 | [self willChangeValueForKey:propertyName];\ 35 | [[self nl_dynamicPropertyDictionary] setObject:@(arg) forKey:propertyName];\ 36 | [self didChangeValueForKey:propertyName];\ 37 | }\ 38 | 39 | #define NLDefineDynamicIMPBaseDataType(name, BaseType) \ 40 | NLDefineDynamicIMPGetterBaseDataType(name, BaseType); \ 41 | NLDefineDynamicIMPSetterBaseDataType(name, BaseType); 42 | 43 | NLDefineDynamicIMPBaseDataType(char, char); 44 | NLDefineDynamicIMPBaseDataType(int, int); 45 | NLDefineDynamicIMPBaseDataType(short, short); 46 | NLDefineDynamicIMPBaseDataType(long, long); 47 | NLDefineDynamicIMPBaseDataType(longLong, long long); 48 | NLDefineDynamicIMPBaseDataType(unsignedChar, unsigned char); 49 | NLDefineDynamicIMPBaseDataType(unsignedInt, unsigned int); 50 | NLDefineDynamicIMPBaseDataType(unsignedShort, unsigned short); 51 | NLDefineDynamicIMPBaseDataType(unsignedLong, unsigned long); 52 | NLDefineDynamicIMPBaseDataType(unsignedLongLong, unsigned long long); 53 | NLDefineDynamicIMPBaseDataType(float, float); 54 | NLDefineDynamicIMPBaseDataType(double, double); 55 | NLDefineDynamicIMPSetterBaseDataType(bool, bool); 56 | 57 | 58 | #define NLDynamicIMPNameGetterStructType(typeName) __NL__struct_##typeName##_dynamicGetterIMP 59 | #define NLDynamicIMPNameSetterStructType(typeName) __NL__struct_##typeName##_dynamicSetterIMP 60 | 61 | #define NLDefineDynamicIMPGetterStructType(typeName) \ 62 | typeName NLDynamicIMPNameGetterStructType(typeName)(id self, SEL _cmd) {\ 63 | NSString *propertyName = [[self class] nl_dynamicPropertyNameWithSelctor:_cmd];\ 64 | return [[[self nl_dynamicPropertyDictionary] objectForKey:propertyName] typeName##Value];\ 65 | } 66 | 67 | #define NLDefineDynamicIMPSetterStructType(typeName) \ 68 | void NLDynamicIMPNameSetterStructType(typeName)(id self, SEL _cmd, typeName arg) {\ 69 | NSString *propertyName = [[self class] nl_dynamicPropertyNameWithSelctor:_cmd];\ 70 | [self willChangeValueForKey:propertyName];\ 71 | [[self nl_dynamicPropertyDictionary] setObject:[NSValue valueWith##typeName:arg] forKey:propertyName];\ 72 | [self didChangeValueForKey:propertyName];\ 73 | } 74 | 75 | #define NLDefineDynamicIMPStructType(typeName) \ 76 | NLDefineDynamicIMPGetterStructType(typeName);\ 77 | NLDefineDynamicIMPSetterStructType(typeName); 78 | 79 | NLDefineDynamicIMPStructType(CGRect); 80 | NLDefineDynamicIMPStructType(CGPoint); 81 | NLDefineDynamicIMPStructType(CGSize); 82 | NLDefineDynamicIMPStructType(CGVector); 83 | NLDefineDynamicIMPStructType(UIOffset); 84 | NLDefineDynamicIMPStructType(UIEdgeInsets); 85 | NLDefineDynamicIMPStructType(CGAffineTransform); 86 | NLDefineDynamicIMPStructType(CATransform3D); 87 | 88 | /** 89 | * 因为 bool 本身就是一个宏定义,所以其无法使用 NLDefineDynamicIMPGetterBaseDataType 宏 90 | */ 91 | bool __NL__bool_dynamicGetterIMP(id self, SEL _cmd) { 92 | NSString *propertyName = [[self class] nl_dynamicPropertyNameWithSelctor:_cmd]; 93 | return [[[self nl_dynamicPropertyDictionary] objectForKey:propertyName] boolValue]; 94 | } 95 | 96 | void __NL__object_dynamicSetterIMP(id self, SEL _cmd, id arg) { 97 | NSString *propertyName = [[self class] nl_dynamicPropertyNameWithSelctor:_cmd]; 98 | 99 | [self willChangeValueForKey:propertyName]; 100 | if (arg) { 101 | [[self nl_dynamicPropertyDictionary] setObject:arg forKey:propertyName]; 102 | } else { 103 | [[self nl_dynamicPropertyDictionary] removeObjectForKey:propertyName]; 104 | } 105 | [self didChangeValueForKey:propertyName]; 106 | } 107 | 108 | void __NL__object_dynamicSetterCopyIMP(id self, SEL _cmd, id arg) { 109 | NSString *propertyName = [[self class] nl_dynamicPropertyNameWithSelctor:_cmd]; 110 | [self willChangeValueForKey:propertyName]; 111 | [[self nl_dynamicPropertyDictionary] setObject:[arg copy] forKey:propertyName]; 112 | [self didChangeValueForKey:propertyName]; 113 | } 114 | 115 | void __NL__object_dynamicSetterWeakIMP(id self, SEL _cmd, id arg) { 116 | NSString *propertyName = [[self class] nl_dynamicPropertyNameWithSelctor:_cmd]; 117 | [self willChangeValueForKey:propertyName]; 118 | [[self nl_dynamicPropertyWeakDictionary] setObject:arg forKey:propertyName]; 119 | [self didChangeValueForKey:propertyName]; 120 | } 121 | 122 | id __NL__object_dynamicGetterIMP(id self, SEL _cmd) { 123 | NSString *propertyName = [[self class] nl_dynamicPropertyNameWithSelctor:_cmd]; 124 | return [[self nl_dynamicPropertyDictionary] objectForKey:propertyName]; 125 | } 126 | 127 | id __NL__object_dynamicGetterWeakIMP(id self, SEL _cmd) { 128 | NSString *propertyName = [[self class] nl_dynamicPropertyNameWithSelctor:_cmd]; 129 | return [[self nl_dynamicPropertyWeakDictionary] objectForKey:propertyName]; 130 | } 131 | 132 | @interface NSObject (nl_dynamicSupport) 133 | 134 | + (BOOL)nl_addMethodWithDescriptor:(NLPropertyDescriptor *)desciptor selector:(SEL)sel; 135 | 136 | @end 137 | 138 | @implementation NSObject (nl_dynamicSupport) 139 | 140 | + (void)load { 141 | Method resolveInstanceMethod = class_getClassMethod(self, @selector(resolveInstanceMethod:)); 142 | Method nl_resolveInstanceMethod = class_getClassMethod(self, @selector(nl_resolveInstanceMethod:)); 143 | if (resolveInstanceMethod && nl_resolveInstanceMethod) { 144 | method_exchangeImplementations(resolveInstanceMethod, nl_resolveInstanceMethod); 145 | } 146 | 147 | Method setValueForKeyMethod = class_getInstanceMethod(self, @selector(setValue:forKey:)); 148 | Method nl_setValueForKeyMethod = class_getInstanceMethod(self, @selector(nl_setValue:forKey:)); 149 | if (setValueForKeyMethod && nl_setValueForKeyMethod) { 150 | method_exchangeImplementations(setValueForKeyMethod, nl_setValueForKeyMethod); 151 | } 152 | } 153 | 154 | #pragma mark - swizzle +resolveInstanceMethod and - setValue:forUndefinedKey: 155 | + (BOOL)nl_resolveInstanceMethod:(SEL)sel { 156 | NSArray *propertyDescriptors = [self nl_dynamicPropertyDescriptors]; 157 | for (NLPropertyDescriptor *propertyDescriptor in propertyDescriptors) { 158 | BOOL didAddMethod = [self nl_addMethodWithDescriptor:propertyDescriptor selector:sel]; 159 | if (didAddMethod) { 160 | return YES; 161 | } 162 | } 163 | 164 | return [self nl_resolveInstanceMethod:sel]; 165 | } 166 | 167 | /** 168 | * @brief support for KVO 169 | * 由于动态属性的 setter 方法是在 runtime 时增加的,系统的 KVO 寻 key 路径无法得知, 170 | * 所以在系统要调用 -setValue:forKey: 时,手动调用咱们的 setter 方法 171 | */ 172 | - (void)nl_setValue:(id)value forKey:(NSString *)key { 173 | // 名字得以 staticPropertyNamePrefix 为前辍 174 | static NSString *staticPropertyNamePrefix = nil; 175 | 176 | static dispatch_once_t onceToken; 177 | dispatch_once(&onceToken, ^{ 178 | const char *prefix = nl_dynamicPropertyPrefix(); 179 | if (prefix != NULL) { 180 | staticPropertyNamePrefix = [NSString stringWithCString:prefix encoding:NSUTF8StringEncoding]; 181 | } 182 | }); 183 | 184 | if (staticPropertyNamePrefix == NULL || ![key hasPrefix:staticPropertyNamePrefix]) { 185 | [self nl_setValue:value forKey:key]; 186 | return; 187 | } 188 | 189 | NSArray *propertyDescriptors = [self.class nl_dynamicPropertyDescriptors]; 190 | NLPropertyDescriptor *keyPropertyDescriptor = nil; 191 | SEL setterSelector = nil; 192 | 193 | for (NLPropertyDescriptor *propertyDescriptor in propertyDescriptors) { 194 | if ([propertyDescriptor.name isEqualToString:key]) { 195 | if ([self respondsToSelector:NSSelectorFromString(propertyDescriptor.setterName)]) { 196 | keyPropertyDescriptor = propertyDescriptor; 197 | setterSelector = NSSelectorFromString(propertyDescriptor.setterName); 198 | } 199 | break; 200 | } 201 | } 202 | 203 | if (keyPropertyDescriptor == nil || setterSelector == nil) { 204 | [self nl_setValue:value forKey:key]; 205 | return; 206 | } 207 | 208 | if ([keyPropertyDescriptor isObjectType]){ 209 | _Pragma("clang diagnostic push") 210 | _Pragma("clang diagnostic ignored \"-Warc-performSelector-leaks\"") 211 | [self performSelector:setterSelector withObject:value]; 212 | _Pragma("clang diagnostic pop") 213 | return; 214 | } 215 | 216 | // 如果不是对象,则一定是基础类型或结构体 217 | // 而这两者所对象的类一定是 NSValue 218 | if (![value isKindOfClass:[NSValue class]]) { 219 | [self nl_setValue:value forKey:key]; 220 | return; 221 | } 222 | 223 | if ([keyPropertyDescriptor isCharType] 224 | || [keyPropertyDescriptor isIntType] 225 | || [keyPropertyDescriptor isShortType] 226 | || [keyPropertyDescriptor isLongType] 227 | || [keyPropertyDescriptor isLongLongType] 228 | || [keyPropertyDescriptor isBoolType] 229 | || [keyPropertyDescriptor isUCharType] 230 | || [keyPropertyDescriptor isUIntType] 231 | || [keyPropertyDescriptor isUShortType] 232 | || [keyPropertyDescriptor isULongType] 233 | || [keyPropertyDescriptor isULongLongType]) { 234 | NLDynamicIMPNameSetterBaseDataType(unsignedLongLong)(self, setterSelector, [value unsignedLongLongValue]); 235 | return; 236 | } 237 | 238 | if ([keyPropertyDescriptor isFloatType]) { 239 | NLDynamicIMPNameSetterBaseDataType(float)(self, setterSelector, [value floatValue]); 240 | return; 241 | } 242 | 243 | if ([keyPropertyDescriptor isDoubleType]) { 244 | NLDynamicIMPNameSetterBaseDataType(double)(self, setterSelector, [value doubleValue]); 245 | return; 246 | } 247 | 248 | if ([keyPropertyDescriptor isRectType]) { 249 | NLDynamicIMPNameSetterStructType(CGRect)(self, setterSelector, [value CGRectValue]); 250 | return; 251 | } 252 | 253 | if ([keyPropertyDescriptor isPointType]) { 254 | NLDynamicIMPNameSetterStructType(CGPoint)(self, setterSelector, [value CGPointValue]); 255 | return; 256 | } 257 | 258 | if ([keyPropertyDescriptor isSizeType]) { 259 | NLDynamicIMPNameSetterStructType(CGSize)(self, setterSelector, [value CGSizeValue]); 260 | return; 261 | } 262 | 263 | if ([keyPropertyDescriptor isVectorType]) { 264 | NLDynamicIMPNameSetterStructType(CGVector)(self, setterSelector, [value CGVectorValue]); 265 | return; 266 | } 267 | 268 | if ([keyPropertyDescriptor isOffsetType]) { 269 | NLDynamicIMPNameSetterStructType(UIOffset)(self, setterSelector, [value UIOffsetValue]); 270 | return; 271 | } 272 | 273 | if ([keyPropertyDescriptor isEdgeInsetsType]) { 274 | NLDynamicIMPNameSetterStructType(UIEdgeInsets)(self, setterSelector, [value UIEdgeInsetsValue]); 275 | return; 276 | } 277 | 278 | if ([keyPropertyDescriptor isAffineTransormType]) { 279 | NLDynamicIMPNameSetterStructType(CGAffineTransform)(self, setterSelector, [value CGAffineTransformValue]); 280 | return; 281 | } 282 | 283 | if ([keyPropertyDescriptor isTransorm3DType]) { 284 | NLDynamicIMPNameSetterStructType(CATransform3D)(self, setterSelector, [value CATransform3DValue]); 285 | return; 286 | } 287 | 288 | [self nl_setValue:value forKey:key]; 289 | } 290 | 291 | #pragma mark - dynamic add method 292 | + (BOOL)nl_addMethodWithDescriptor:(NLPropertyDescriptor *)desciptor selector:(SEL)sel { 293 | NSString *selName = NSStringFromSelector(sel); 294 | if ([desciptor.setterName isEqualToString:selName]) { 295 | BOOL addedSetter = [self nl_addSetterMethodWithDescriptor:desciptor]; 296 | if (!addedSetter) { 297 | return [self nl_missMethodWithPropertyDescriptor:desciptor selector:sel]; 298 | } 299 | 300 | return YES; 301 | } 302 | 303 | if ([desciptor.getterName isEqualToString:selName]) { 304 | BOOL addedGetter = [self nl_addGetterMethodWithDescriptor:desciptor]; 305 | if (!addedGetter) { 306 | return [self nl_missMethodWithPropertyDescriptor:desciptor selector:sel]; 307 | } 308 | return YES; 309 | } 310 | 311 | return NO; 312 | } 313 | 314 | + (BOOL)nl_addSetterMethodWithDescriptor:(NLPropertyDescriptor *)desciptor { 315 | if (desciptor.setterName == nil) { 316 | return NO; 317 | } 318 | 319 | IMP setterIMP = NULL; 320 | 321 | if ([desciptor isCharType] 322 | || [desciptor isIntType] 323 | || [desciptor isShortType] 324 | || [desciptor isLongType] 325 | || [desciptor isLongLongType] 326 | || [desciptor isBoolType] 327 | || [desciptor isUCharType] 328 | || [desciptor isUIntType] 329 | || [desciptor isUShortType] 330 | || [desciptor isULongType] 331 | || [desciptor isULongLongType]) { 332 | setterIMP = (IMP)(NLDynamicIMPNameSetterBaseDataType(unsignedLongLong)); 333 | } 334 | 335 | if ([desciptor isFloatType]) { 336 | setterIMP = (IMP)NLDynamicIMPNameSetterBaseDataType(float); 337 | } 338 | 339 | if ([desciptor isDoubleType]) { 340 | setterIMP = (IMP)NLDynamicIMPNameSetterBaseDataType(double); 341 | } 342 | 343 | if ([desciptor isObjectType]) { 344 | if (desciptor.propertyPolicy == NLPropertyPolicyAssign 345 | || desciptor.propertyPolicy == NLPropertyPolicyWeak) { 346 | setterIMP = (IMP)__NL__object_dynamicSetterWeakIMP; 347 | } else if (desciptor.propertyPolicy == NLPropertyPolicyCopy) { 348 | setterIMP = (IMP)__NL__object_dynamicSetterCopyIMP; 349 | } else { 350 | setterIMP = (IMP)__NL__object_dynamicSetterIMP; 351 | } 352 | } 353 | 354 | if (setterIMP == NULL && [desciptor isRectType]) { 355 | setterIMP = (IMP)NLDynamicIMPNameSetterStructType(CGRect); 356 | } 357 | 358 | if (setterIMP == NULL && [desciptor isPointType]) { 359 | setterIMP = (IMP)NLDynamicIMPNameSetterStructType(CGPoint); 360 | } 361 | 362 | if (setterIMP == NULL && [desciptor isSizeType]) { 363 | setterIMP = (IMP)NLDynamicIMPNameSetterStructType(CGSize); 364 | } 365 | 366 | if (setterIMP == NULL && [desciptor isVectorType]) { 367 | setterIMP = (IMP)NLDynamicIMPNameSetterStructType(CGVector); 368 | } 369 | 370 | if (setterIMP == NULL && [desciptor isOffsetType]) { 371 | setterIMP = (IMP)NLDynamicIMPNameSetterStructType(UIOffset); 372 | } 373 | 374 | if (setterIMP == NULL && [desciptor isEdgeInsetsType]) { 375 | setterIMP = (IMP)NLDynamicIMPNameSetterStructType(UIEdgeInsets); 376 | } 377 | 378 | if (setterIMP == NULL && [desciptor isAffineTransormType]) { 379 | setterIMP = (IMP)NLDynamicIMPNameSetterStructType(CGAffineTransform); 380 | } 381 | 382 | if (setterIMP == NULL && [desciptor isTransorm3DType]) { 383 | setterIMP = (IMP)NLDynamicIMPNameSetterStructType(CATransform3D); 384 | } 385 | 386 | if (setterIMP != NULL) { 387 | class_addMethod(self, NSSelectorFromString(desciptor.setterName), setterIMP, "v@:"); 388 | return YES; 389 | } 390 | 391 | return NO; 392 | } 393 | 394 | + (BOOL)nl_addGetterMethodWithDescriptor:(NLPropertyDescriptor *)desciptor { 395 | if (desciptor.getterName == nil) { 396 | return NO; 397 | } 398 | 399 | SEL selector = NSSelectorFromString(desciptor.getterName); 400 | IMP getterIMP = NULL; 401 | if ([desciptor isCharType] 402 | || [desciptor isIntType] 403 | || [desciptor isShortType] 404 | || [desciptor isLongType] 405 | || [desciptor isLongLongType] 406 | || [desciptor isBoolType] 407 | || [desciptor isUCharType] 408 | || [desciptor isUIntType] 409 | || [desciptor isUShortType] 410 | || [desciptor isULongType] 411 | || [desciptor isULongLongType]) { 412 | getterIMP = (IMP)(NLDynamicIMPNameGetterBaseDataType(unsignedLongLong)); 413 | } 414 | 415 | if (getterIMP != NULL) { 416 | class_addMethod(self, selector, getterIMP, "Q@:"); 417 | return YES; 418 | } 419 | 420 | if ([desciptor isFloatType]) { 421 | class_addMethod(self, selector, (IMP)NLDynamicIMPNameGetterBaseDataType(float), "f@:"); 422 | return YES; 423 | } 424 | 425 | if ([desciptor isDoubleType]) { 426 | class_addMethod(self, selector, (IMP)NLDynamicIMPNameGetterBaseDataType(double), "d@:"); 427 | return YES; 428 | } 429 | 430 | NSString *typeEncoding = [desciptor typeEncoding]; 431 | if ([typeEncoding hasPrefix:@"T"]) { 432 | typeEncoding = [typeEncoding substringFromIndex:1]; 433 | } 434 | 435 | const char *cFuncationTypes = [[typeEncoding stringByAppendingString:@"@:"] cStringUsingEncoding:NSUTF8StringEncoding]; 436 | 437 | if ([desciptor isObjectType]) { 438 | if (desciptor.propertyPolicy == NLPropertyPolicyWeak) { 439 | class_addMethod(self, selector, (IMP)__NL__object_dynamicGetterWeakIMP, cFuncationTypes); 440 | } else { 441 | class_addMethod(self, selector, (IMP)__NL__object_dynamicGetterIMP, cFuncationTypes); 442 | } 443 | return YES; 444 | } 445 | 446 | if ([desciptor isRectType]) { 447 | class_addMethod(self, selector, (IMP)NLDynamicIMPNameGetterStructType(CGRect), cFuncationTypes); 448 | return YES; 449 | } 450 | 451 | if ([desciptor isPointType]) { 452 | class_addMethod(self, selector, (IMP)NLDynamicIMPNameGetterStructType(CGPoint), cFuncationTypes); 453 | return YES; 454 | } 455 | 456 | if ([desciptor isSizeType]) { 457 | class_addMethod(self, selector, (IMP)NLDynamicIMPNameGetterStructType(CGSize), cFuncationTypes); 458 | return YES; 459 | } 460 | 461 | if ([desciptor isVectorType]) { 462 | class_addMethod(self, selector, (IMP)NLDynamicIMPNameGetterStructType(CGVector), cFuncationTypes); 463 | return YES; 464 | } 465 | 466 | if ([desciptor isOffsetType]) { 467 | class_addMethod(self, selector, (IMP)NLDynamicIMPNameGetterStructType(UIOffset), cFuncationTypes); 468 | return YES; 469 | } 470 | 471 | if ([desciptor isEdgeInsetsType]) { 472 | class_addMethod(self, selector, (IMP)NLDynamicIMPNameGetterStructType(UIEdgeInsets), cFuncationTypes); 473 | return YES; 474 | } 475 | 476 | if ([desciptor isAffineTransormType]) { 477 | class_addMethod(self, selector, (IMP)NLDynamicIMPNameGetterStructType(CGAffineTransform), cFuncationTypes); 478 | return YES; 479 | } 480 | 481 | if ([desciptor isTransorm3DType]) { 482 | class_addMethod(self, selector, (IMP)NLDynamicIMPNameGetterStructType(CATransform3D), cFuncationTypes); 483 | return YES; 484 | } 485 | 486 | return NO; 487 | } 488 | 489 | @end 490 | 491 | @implementation NSObject (nl_dynamicPropertySupport) 492 | 493 | + (BOOL)nl_missMethodWithPropertyDescriptor:(NLPropertyDescriptor *)descriptor selector:(SEL)sel { 494 | return NO; 495 | } 496 | 497 | @end 498 | 499 | 500 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # iOSCategoryPropertyDynamicSupport 2 | iOS Class Category property dynamic support 3 | 4 | 在运行时自动给分类的动态属性添加相应的 `getter`、`setter` 方法
5 | 在分类中添加一些自定义的属性的场景还是蛮多的。一般的方法是自定义其 `getter`、`setter` 方法,在这些方法 6 | 里面再用关联对象等手段来实现。虽然不难,却也麻烦且重复。 7 | 本类的目的在于解放这些重复的劳动。 8 | 9 |  使用: 10 |   1、导入本类及相关类(NSObject+nl_dynamicPropertySupport目录下) 11 |   2、在分类中定义属性时,默认要以 `nl_` 开头 12 |   3、在分类的实现体中,给属性加上 @dynamic 13 | 14 |  优点: 15 |   1、所有的对象、基本数据类型 16 |   2、支持系统已有的 [NSValue valueWith...] 方法的结构体(详见 NSValue )。如:CGRect 17 |   3、支持 KVC 18 |   4、支持 KVO 19 |   5、支持 strong、copy、weak 20 | 21 |  不足: 22 |   1、不支持自定义的结构体。 23 |    但可以通过 `+ nl_missMethodWithPropertyDescriptor:selector:` 来实现。实现方法可见:(`nl_dynamicPropertyCustomeStruct`分类) 24 |   2、注意:KVO 支持不完美。 25 |    当观察动态生成的 weak 属性时,不会接收到 weak 自动设置为 nil 的通知(在 weak 指向的对象被销毁时,weak 属性会自动设置为 nil)。 26 | 27 | ##工作原理: 28 |   1、使用 `Associated Objects` 29 |   2、OC runtime 30 |   3、Type Encoding 31 |   详见:http://nathanli.cn/2015/12/14/objective-c-%E5%85%83%E7%BC%96%E7%A8%8B%E5%AE%9E%E8%B7%B5-%E5%88%86%E7%B1%BB%E5%8A%A8%E6%80%81%E5%B1%9E%E6%80%A7/ 32 |    33 | ##代码使用: 34 | 头文件 .h: 35 | ```Objective-C 36 | @interface YourClass (YourCategory) 37 | 38 | @property NSString *nl_yourProperty; 39 | 40 | @end 41 | ``` 42 | 43 | 实现文件: 44 | ```Objective-C 45 | @implementation YourClass (YourCategory) 46 | 47 | @dynamic nl_yourProperty; 48 | 49 | @end 50 | ``` 51 | 52 | ##自定义属性前辍 53 | 1、`#import "NLDynamicPropertyPrefix.h"`
54 | 2、 设置前辍 `DynamicPropertySetPrefix("demo_")`
55 | 注意,要在 m 文件中调用;前辍名长度不能为 0,你应该定义成字母加下划线的形式;不能重复调用。 56 | 57 | 58 | 59 |  如果你有好的 idea 或 疑问,请随时提 issue 或 request。 60 | 61 | ##License 62 | Released under the MIT-license 63 | --------------------------------------------------------------------------------