├── ObjCRuntimeDemo.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcshareddata │ │ └── ObjCRuntimeDemo.xcscmblueprint │ └── xcuserdata │ │ └── lizelu.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ └── lizelu.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ ├── ObjCRuntimeDemo.xcscheme │ └── xcschememanagement.plist ├── ObjCRuntimeDemo ├── Runtime │ ├── RuntimeKit.h │ └── RuntimeKit.m ├── TestClass │ ├── TestClass+AssociatedObject.h │ ├── TestClass+AssociatedObject.m │ ├── TestClass+Category.h │ ├── TestClass+Category.m │ ├── TestClass+SwapMethod.h │ ├── TestClass+SwapMethod.m │ ├── TestClass.h │ └── TestClass.m └── main.m └── README.md /ObjCRuntimeDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | F482C91E1E1CEB66009021C7 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = F482C91D1E1CEB66009021C7 /* main.m */; }; 11 | F4ED27961E1CF1CE006126CA /* TestClass+Category.m in Sources */ = {isa = PBXBuildFile; fileRef = F4ED27921E1CF1CE006126CA /* TestClass+Category.m */; }; 12 | F4ED27971E1CF1CE006126CA /* TestClass.m in Sources */ = {isa = PBXBuildFile; fileRef = F4ED27941E1CF1CE006126CA /* TestClass.m */; }; 13 | F4ED279A1E1CF54F006126CA /* RuntimeKit.m in Sources */ = {isa = PBXBuildFile; fileRef = F4ED27991E1CF54F006126CA /* RuntimeKit.m */; }; 14 | F4ED27A21E1E2CA4006126CA /* TestClass+SwapMethod.m in Sources */ = {isa = PBXBuildFile; fileRef = F4ED27A11E1E2CA4006126CA /* TestClass+SwapMethod.m */; }; 15 | F4ED27A51E1E2D09006126CA /* TestClass+AssociatedObject.m in Sources */ = {isa = PBXBuildFile; fileRef = F4ED27A41E1E2D09006126CA /* TestClass+AssociatedObject.m */; }; 16 | /* End PBXBuildFile section */ 17 | 18 | /* Begin PBXCopyFilesBuildPhase section */ 19 | F482C9181E1CEB65009021C7 /* CopyFiles */ = { 20 | isa = PBXCopyFilesBuildPhase; 21 | buildActionMask = 2147483647; 22 | dstPath = /usr/share/man/man1/; 23 | dstSubfolderSpec = 0; 24 | files = ( 25 | ); 26 | runOnlyForDeploymentPostprocessing = 1; 27 | }; 28 | /* End PBXCopyFilesBuildPhase section */ 29 | 30 | /* Begin PBXFileReference section */ 31 | F482C91A1E1CEB66009021C7 /* ObjCRuntimeDemo */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = ObjCRuntimeDemo; sourceTree = BUILT_PRODUCTS_DIR; }; 32 | F482C91D1E1CEB66009021C7 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 33 | F4ED27911E1CF1CE006126CA /* TestClass+Category.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "TestClass+Category.h"; sourceTree = ""; }; 34 | F4ED27921E1CF1CE006126CA /* TestClass+Category.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "TestClass+Category.m"; sourceTree = ""; }; 35 | F4ED27931E1CF1CE006126CA /* TestClass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TestClass.h; sourceTree = ""; }; 36 | F4ED27941E1CF1CE006126CA /* TestClass.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TestClass.m; sourceTree = ""; }; 37 | F4ED27981E1CF54F006126CA /* RuntimeKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RuntimeKit.h; sourceTree = ""; }; 38 | F4ED27991E1CF54F006126CA /* RuntimeKit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RuntimeKit.m; sourceTree = ""; }; 39 | F4ED27A01E1E2CA4006126CA /* TestClass+SwapMethod.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "TestClass+SwapMethod.h"; sourceTree = ""; }; 40 | F4ED27A11E1E2CA4006126CA /* TestClass+SwapMethod.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "TestClass+SwapMethod.m"; sourceTree = ""; }; 41 | F4ED27A31E1E2D09006126CA /* TestClass+AssociatedObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "TestClass+AssociatedObject.h"; sourceTree = ""; }; 42 | F4ED27A41E1E2D09006126CA /* TestClass+AssociatedObject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "TestClass+AssociatedObject.m"; sourceTree = ""; }; 43 | /* End PBXFileReference section */ 44 | 45 | /* Begin PBXFrameworksBuildPhase section */ 46 | F482C9171E1CEB65009021C7 /* Frameworks */ = { 47 | isa = PBXFrameworksBuildPhase; 48 | buildActionMask = 2147483647; 49 | files = ( 50 | ); 51 | runOnlyForDeploymentPostprocessing = 0; 52 | }; 53 | /* End PBXFrameworksBuildPhase section */ 54 | 55 | /* Begin PBXGroup section */ 56 | F482C9111E1CEB65009021C7 = { 57 | isa = PBXGroup; 58 | children = ( 59 | F482C91C1E1CEB66009021C7 /* ObjCRuntimeDemo */, 60 | F482C91B1E1CEB66009021C7 /* Products */, 61 | ); 62 | sourceTree = ""; 63 | }; 64 | F482C91B1E1CEB66009021C7 /* Products */ = { 65 | isa = PBXGroup; 66 | children = ( 67 | F482C91A1E1CEB66009021C7 /* ObjCRuntimeDemo */, 68 | ); 69 | name = Products; 70 | sourceTree = ""; 71 | }; 72 | F482C91C1E1CEB66009021C7 /* ObjCRuntimeDemo */ = { 73 | isa = PBXGroup; 74 | children = ( 75 | F4ED278F1E1CF1CE006126CA /* Runtime */, 76 | F4ED27901E1CF1CE006126CA /* TestClass */, 77 | F482C91D1E1CEB66009021C7 /* main.m */, 78 | ); 79 | path = ObjCRuntimeDemo; 80 | sourceTree = ""; 81 | }; 82 | F4ED278F1E1CF1CE006126CA /* Runtime */ = { 83 | isa = PBXGroup; 84 | children = ( 85 | F4ED27981E1CF54F006126CA /* RuntimeKit.h */, 86 | F4ED27991E1CF54F006126CA /* RuntimeKit.m */, 87 | ); 88 | path = Runtime; 89 | sourceTree = ""; 90 | }; 91 | F4ED27901E1CF1CE006126CA /* TestClass */ = { 92 | isa = PBXGroup; 93 | children = ( 94 | F4ED27931E1CF1CE006126CA /* TestClass.h */, 95 | F4ED27941E1CF1CE006126CA /* TestClass.m */, 96 | F4ED27911E1CF1CE006126CA /* TestClass+Category.h */, 97 | F4ED27921E1CF1CE006126CA /* TestClass+Category.m */, 98 | F4ED27A01E1E2CA4006126CA /* TestClass+SwapMethod.h */, 99 | F4ED27A11E1E2CA4006126CA /* TestClass+SwapMethod.m */, 100 | F4ED27A31E1E2D09006126CA /* TestClass+AssociatedObject.h */, 101 | F4ED27A41E1E2D09006126CA /* TestClass+AssociatedObject.m */, 102 | ); 103 | path = TestClass; 104 | sourceTree = ""; 105 | }; 106 | /* End PBXGroup section */ 107 | 108 | /* Begin PBXNativeTarget section */ 109 | F482C9191E1CEB65009021C7 /* ObjCRuntimeDemo */ = { 110 | isa = PBXNativeTarget; 111 | buildConfigurationList = F482C9211E1CEB66009021C7 /* Build configuration list for PBXNativeTarget "ObjCRuntimeDemo" */; 112 | buildPhases = ( 113 | F482C9161E1CEB65009021C7 /* Sources */, 114 | F482C9171E1CEB65009021C7 /* Frameworks */, 115 | F482C9181E1CEB65009021C7 /* CopyFiles */, 116 | ); 117 | buildRules = ( 118 | ); 119 | dependencies = ( 120 | ); 121 | name = ObjCRuntimeDemo; 122 | productName = ObjCRuntimeDemo; 123 | productReference = F482C91A1E1CEB66009021C7 /* ObjCRuntimeDemo */; 124 | productType = "com.apple.product-type.tool"; 125 | }; 126 | /* End PBXNativeTarget section */ 127 | 128 | /* Begin PBXProject section */ 129 | F482C9121E1CEB65009021C7 /* Project object */ = { 130 | isa = PBXProject; 131 | attributes = { 132 | LastUpgradeCheck = 0820; 133 | ORGANIZATIONNAME = ZeluLi; 134 | TargetAttributes = { 135 | F482C9191E1CEB65009021C7 = { 136 | CreatedOnToolsVersion = 8.2.1; 137 | DevelopmentTeam = AWN3A347S4; 138 | ProvisioningStyle = Automatic; 139 | }; 140 | }; 141 | }; 142 | buildConfigurationList = F482C9151E1CEB65009021C7 /* Build configuration list for PBXProject "ObjCRuntimeDemo" */; 143 | compatibilityVersion = "Xcode 3.2"; 144 | developmentRegion = English; 145 | hasScannedForEncodings = 0; 146 | knownRegions = ( 147 | en, 148 | ); 149 | mainGroup = F482C9111E1CEB65009021C7; 150 | productRefGroup = F482C91B1E1CEB66009021C7 /* Products */; 151 | projectDirPath = ""; 152 | projectRoot = ""; 153 | targets = ( 154 | F482C9191E1CEB65009021C7 /* ObjCRuntimeDemo */, 155 | ); 156 | }; 157 | /* End PBXProject section */ 158 | 159 | /* Begin PBXSourcesBuildPhase section */ 160 | F482C9161E1CEB65009021C7 /* Sources */ = { 161 | isa = PBXSourcesBuildPhase; 162 | buildActionMask = 2147483647; 163 | files = ( 164 | F4ED27971E1CF1CE006126CA /* TestClass.m in Sources */, 165 | F4ED27A51E1E2D09006126CA /* TestClass+AssociatedObject.m in Sources */, 166 | F4ED27961E1CF1CE006126CA /* TestClass+Category.m in Sources */, 167 | F482C91E1E1CEB66009021C7 /* main.m in Sources */, 168 | F4ED279A1E1CF54F006126CA /* RuntimeKit.m in Sources */, 169 | F4ED27A21E1E2CA4006126CA /* TestClass+SwapMethod.m in Sources */, 170 | ); 171 | runOnlyForDeploymentPostprocessing = 0; 172 | }; 173 | /* End PBXSourcesBuildPhase section */ 174 | 175 | /* Begin XCBuildConfiguration section */ 176 | F482C91F1E1CEB66009021C7 /* Debug */ = { 177 | isa = XCBuildConfiguration; 178 | buildSettings = { 179 | ALWAYS_SEARCH_USER_PATHS = NO; 180 | CLANG_ANALYZER_NONNULL = YES; 181 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 182 | CLANG_CXX_LIBRARY = "libc++"; 183 | CLANG_ENABLE_MODULES = YES; 184 | CLANG_ENABLE_OBJC_ARC = YES; 185 | CLANG_WARN_BOOL_CONVERSION = YES; 186 | CLANG_WARN_CONSTANT_CONVERSION = YES; 187 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 188 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 189 | CLANG_WARN_EMPTY_BODY = YES; 190 | CLANG_WARN_ENUM_CONVERSION = YES; 191 | CLANG_WARN_INFINITE_RECURSION = YES; 192 | CLANG_WARN_INT_CONVERSION = YES; 193 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 194 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 195 | CLANG_WARN_UNREACHABLE_CODE = YES; 196 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 197 | CODE_SIGN_IDENTITY = "-"; 198 | COPY_PHASE_STRIP = NO; 199 | DEBUG_INFORMATION_FORMAT = dwarf; 200 | ENABLE_STRICT_OBJC_MSGSEND = YES; 201 | ENABLE_TESTABILITY = YES; 202 | GCC_C_LANGUAGE_STANDARD = gnu99; 203 | GCC_DYNAMIC_NO_PIC = NO; 204 | GCC_NO_COMMON_BLOCKS = YES; 205 | GCC_OPTIMIZATION_LEVEL = 0; 206 | GCC_PREPROCESSOR_DEFINITIONS = ( 207 | "DEBUG=1", 208 | "$(inherited)", 209 | ); 210 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 211 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 212 | GCC_WARN_UNDECLARED_SELECTOR = YES; 213 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 214 | GCC_WARN_UNUSED_FUNCTION = YES; 215 | GCC_WARN_UNUSED_VARIABLE = YES; 216 | MACOSX_DEPLOYMENT_TARGET = 10.12; 217 | MTL_ENABLE_DEBUG_INFO = YES; 218 | ONLY_ACTIVE_ARCH = YES; 219 | SDKROOT = macosx; 220 | }; 221 | name = Debug; 222 | }; 223 | F482C9201E1CEB66009021C7 /* Release */ = { 224 | isa = XCBuildConfiguration; 225 | buildSettings = { 226 | ALWAYS_SEARCH_USER_PATHS = NO; 227 | CLANG_ANALYZER_NONNULL = YES; 228 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 229 | CLANG_CXX_LIBRARY = "libc++"; 230 | CLANG_ENABLE_MODULES = YES; 231 | CLANG_ENABLE_OBJC_ARC = YES; 232 | CLANG_WARN_BOOL_CONVERSION = YES; 233 | CLANG_WARN_CONSTANT_CONVERSION = YES; 234 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 235 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 236 | CLANG_WARN_EMPTY_BODY = YES; 237 | CLANG_WARN_ENUM_CONVERSION = YES; 238 | CLANG_WARN_INFINITE_RECURSION = YES; 239 | CLANG_WARN_INT_CONVERSION = YES; 240 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 241 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 242 | CLANG_WARN_UNREACHABLE_CODE = YES; 243 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 244 | CODE_SIGN_IDENTITY = "-"; 245 | COPY_PHASE_STRIP = NO; 246 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 247 | ENABLE_NS_ASSERTIONS = NO; 248 | ENABLE_STRICT_OBJC_MSGSEND = YES; 249 | GCC_C_LANGUAGE_STANDARD = gnu99; 250 | GCC_NO_COMMON_BLOCKS = YES; 251 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 252 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 253 | GCC_WARN_UNDECLARED_SELECTOR = YES; 254 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 255 | GCC_WARN_UNUSED_FUNCTION = YES; 256 | GCC_WARN_UNUSED_VARIABLE = YES; 257 | MACOSX_DEPLOYMENT_TARGET = 10.12; 258 | MTL_ENABLE_DEBUG_INFO = NO; 259 | SDKROOT = macosx; 260 | }; 261 | name = Release; 262 | }; 263 | F482C9221E1CEB66009021C7 /* Debug */ = { 264 | isa = XCBuildConfiguration; 265 | buildSettings = { 266 | DEVELOPMENT_TEAM = AWN3A347S4; 267 | PRODUCT_NAME = "$(TARGET_NAME)"; 268 | }; 269 | name = Debug; 270 | }; 271 | F482C9231E1CEB66009021C7 /* Release */ = { 272 | isa = XCBuildConfiguration; 273 | buildSettings = { 274 | DEVELOPMENT_TEAM = AWN3A347S4; 275 | PRODUCT_NAME = "$(TARGET_NAME)"; 276 | }; 277 | name = Release; 278 | }; 279 | /* End XCBuildConfiguration section */ 280 | 281 | /* Begin XCConfigurationList section */ 282 | F482C9151E1CEB65009021C7 /* Build configuration list for PBXProject "ObjCRuntimeDemo" */ = { 283 | isa = XCConfigurationList; 284 | buildConfigurations = ( 285 | F482C91F1E1CEB66009021C7 /* Debug */, 286 | F482C9201E1CEB66009021C7 /* Release */, 287 | ); 288 | defaultConfigurationIsVisible = 0; 289 | defaultConfigurationName = Release; 290 | }; 291 | F482C9211E1CEB66009021C7 /* Build configuration list for PBXNativeTarget "ObjCRuntimeDemo" */ = { 292 | isa = XCConfigurationList; 293 | buildConfigurations = ( 294 | F482C9221E1CEB66009021C7 /* Debug */, 295 | F482C9231E1CEB66009021C7 /* Release */, 296 | ); 297 | defaultConfigurationIsVisible = 0; 298 | defaultConfigurationName = Release; 299 | }; 300 | /* End XCConfigurationList section */ 301 | }; 302 | rootObject = F482C9121E1CEB65009021C7 /* Project object */; 303 | } 304 | -------------------------------------------------------------------------------- /ObjCRuntimeDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ObjCRuntimeDemo.xcodeproj/project.xcworkspace/xcshareddata/ObjCRuntimeDemo.xcscmblueprint: -------------------------------------------------------------------------------- 1 | { 2 | "DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey" : "5D47AB54832BAE701C958946CDAAC540BA9F6253", 3 | "DVTSourceControlWorkspaceBlueprintWorkingCopyRepositoryLocationsKey" : { 4 | 5 | }, 6 | "DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey" : { 7 | "5D47AB54832BAE701C958946CDAAC540BA9F6253" : 9223372036854775807, 8 | "59B0DD3A0DC0B86D2A06DD4B68CBD1C997876614" : 9223372036854775807 9 | }, 10 | "DVTSourceControlWorkspaceBlueprintIdentifierKey" : "BDEA3AA8-9476-4623-81B4-BD060E146595", 11 | "DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : { 12 | "5D47AB54832BAE701C958946CDAAC540BA9F6253" : "ObjCRuntimeDemo\/", 13 | "59B0DD3A0DC0B86D2A06DD4B68CBD1C997876614" : "" 14 | }, 15 | "DVTSourceControlWorkspaceBlueprintNameKey" : "ObjCRuntimeDemo", 16 | "DVTSourceControlWorkspaceBlueprintVersion" : 204, 17 | "DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey" : "ObjCRuntimeDemo.xcodeproj", 18 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey" : [ 19 | { 20 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/lizelu\/CollectionViewControllerDemo.git", 21 | "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", 22 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "59B0DD3A0DC0B86D2A06DD4B68CBD1C997876614" 23 | }, 24 | { 25 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/lizelu\/ObjCRuntimeDemo.git", 26 | "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", 27 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "5D47AB54832BAE701C958946CDAAC540BA9F6253" 28 | } 29 | ] 30 | } -------------------------------------------------------------------------------- /ObjCRuntimeDemo.xcodeproj/project.xcworkspace/xcuserdata/lizelu.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lizelu/ObjCRuntimeDemo/724e213b496f88c30bb0dbd93b84c40f1777f9ca/ObjCRuntimeDemo.xcodeproj/project.xcworkspace/xcuserdata/lizelu.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /ObjCRuntimeDemo.xcodeproj/xcuserdata/lizelu.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 8 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /ObjCRuntimeDemo.xcodeproj/xcuserdata/lizelu.xcuserdatad/xcschemes/ObjCRuntimeDemo.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 | -------------------------------------------------------------------------------- /ObjCRuntimeDemo.xcodeproj/xcuserdata/lizelu.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | ObjCRuntimeDemo.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | F482C9191E1CEB65009021C7 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /ObjCRuntimeDemo/Runtime/RuntimeKit.h: -------------------------------------------------------------------------------- 1 | // 2 | // RuntimeKit.h 3 | // ObjCRuntimeDemo 4 | // 5 | // Created by Mr.LuDashi on 2017/1/4. 6 | // Copyright © 2017年 ZeluLi. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import 12 | 13 | @interface RuntimeKit : NSObject 14 | /** 15 | 获取类名 16 | 17 | @param class 相应类 18 | @return NSString:类名 19 | */ 20 | + (NSString *)fetchClassName:(Class)class; 21 | 22 | /** 23 | 获取成员变量 24 | 25 | @param class 成员变量所在的类 26 | @return 返回成员变量字符串数组 27 | */ 28 | + (NSArray *)fetchIvarList:(Class)class; 29 | 30 | /** 31 | 获取类的属性列表, 包括私有和公有属性,即定义在延展中的属性 32 | 33 | @param class Class 34 | @return 属性列表数组 35 | */ 36 | + (NSArray *)fetchPropertyList:(Class)class; 37 | 38 | /** 39 | 获取对象方法列表:getter, setter, 对象方法等。但不能获取类方法 40 | 41 | @param class 方法所在的类 42 | @return 该类的方法列表 43 | */ 44 | + (NSArray *)fetchMethodList:(Class)class; 45 | 46 | /** 47 | 获取协议列表 48 | 49 | @param class 实现协议的类 50 | @return 返回该类实现的协议列表 51 | */ 52 | + (NSArray *)fetchProtocolList:(Class)class; 53 | 54 | 55 | /** 56 | 添加新的方法 57 | 58 | @param class 被添加方法的类 59 | @param methodSel SEL 60 | @param methodSelImpl 提供IMP的SEL 61 | */ 62 | + (void)addMethod: (Class)class method:(SEL)methodSel method:(SEL)methodSelImpl; 63 | 64 | /** 65 | 方法交换 66 | 67 | @param class 交换方法所在的类 68 | @param method1 方法1 69 | @param method2 方法2 70 | */ 71 | + (void)methodSwap:(Class)class firstMethod:(SEL)method1 secondMethod:(SEL)method2; 72 | @end 73 | -------------------------------------------------------------------------------- /ObjCRuntimeDemo/Runtime/RuntimeKit.m: -------------------------------------------------------------------------------- 1 | // 2 | // RuntimeKit.m 3 | // ObjCRuntimeDemo 4 | // 5 | // Created by Mr.LuDashi on 2017/1/4. 6 | // Copyright © 2017年 ZeluLi. All rights reserved. 7 | // 8 | 9 | #import "RuntimeKit.h" 10 | 11 | @implementation RuntimeKit 12 | 13 | /** 14 | 获取类名 15 | 16 | @param class 相应类 17 | @return NSString:类名 18 | */ 19 | + (NSString *)fetchClassName:(Class)class { 20 | const char *className = class_getName(class); 21 | return [NSString stringWithUTF8String:className]; 22 | } 23 | 24 | /** 25 | 获取成员变量 26 | 27 | @param class Class 28 | @return NSArray 29 | */ 30 | + (NSArray *)fetchIvarList:(Class)class { 31 | unsigned int count = 0; 32 | Ivar *ivarList = class_copyIvarList(class, &count); 33 | 34 | NSMutableArray *mutableList = [NSMutableArray arrayWithCapacity:count]; 35 | for (unsigned int i = 0; i < count; i++ ) { 36 | NSMutableDictionary *dic = [NSMutableDictionary dictionaryWithCapacity:2]; 37 | const char *ivarName = ivar_getName(ivarList[i]); 38 | const char *ivarType = ivar_getTypeEncoding(ivarList[i]); 39 | dic[@"type"] = [NSString stringWithUTF8String: ivarType]; 40 | dic[@"ivarName"] = [NSString stringWithUTF8String: ivarName]; 41 | 42 | [mutableList addObject:dic]; 43 | } 44 | free(ivarList); 45 | return [NSArray arrayWithArray:mutableList]; 46 | } 47 | 48 | /** 49 | 获取类的属性列表, 包括私有和公有属性,以及定义在延展中的属性 50 | 51 | @param class Class 52 | @return 属性列表数组 53 | */ 54 | + (NSArray *)fetchPropertyList:(Class)class { 55 | unsigned int count = 0; 56 | objc_property_t *propertyList = class_copyPropertyList(class, &count); 57 | 58 | NSMutableArray *mutableList = [NSMutableArray arrayWithCapacity:count]; 59 | for (unsigned int i = 0; i < count; i++ ) { 60 | const char *propertyName = property_getName(propertyList[i]); 61 | [mutableList addObject:[NSString stringWithUTF8String: propertyName]]; 62 | } 63 | free(propertyList); 64 | return [NSArray arrayWithArray:mutableList]; 65 | } 66 | 67 | 68 | /** 69 | 获取类的实例方法列表:getter, setter, 对象方法等。但不能获取类方法 70 | 71 | @param class <#class description#> 72 | @return <#return value description#> 73 | */ 74 | + (NSArray *)fetchMethodList:(Class)class { 75 | unsigned int count = 0; 76 | Method *methodList = class_copyMethodList(class, &count); 77 | 78 | NSMutableArray *mutableList = [NSMutableArray arrayWithCapacity:count]; 79 | for (unsigned int i = 0; i < count; i++ ) { 80 | Method method = methodList[i]; 81 | SEL methodName = method_getName(method); 82 | [mutableList addObject:NSStringFromSelector(methodName)]; 83 | } 84 | free(methodList); 85 | return [NSArray arrayWithArray:mutableList]; 86 | } 87 | 88 | /** 89 | 获取协议列表 90 | 91 | @param class <#class description#> 92 | @return <#return value description#> 93 | */ 94 | + (NSArray *)fetchProtocolList:(Class)class { 95 | unsigned int count = 0; 96 | __unsafe_unretained Protocol **protocolList = class_copyProtocolList(class, &count); 97 | 98 | NSMutableArray *mutableList = [NSMutableArray arrayWithCapacity:count]; 99 | for (unsigned int i = 0; i < count; i++ ) { 100 | Protocol *protocol = protocolList[i]; 101 | const char *protocolName = protocol_getName(protocol); 102 | [mutableList addObject:[NSString stringWithUTF8String: protocolName]]; 103 | } 104 | 105 | return [NSArray arrayWithArray:mutableList]; 106 | return nil; 107 | } 108 | 109 | 110 | /** 111 | 往类上添加新的方法与其实现 112 | 113 | @param class 相应的类 114 | @param methodSel 方法的名 115 | @param methodSelImpl 对应方法实现的方法名 116 | */ 117 | + (void)addMethod:(Class)class method:(SEL)methodSel method:(SEL)methodSelImpl { 118 | Method method = class_getInstanceMethod(class, methodSelImpl); 119 | IMP methodIMP = method_getImplementation(method); 120 | const char *types = method_getTypeEncoding(method); 121 | class_addMethod(class, methodSel, methodIMP, types); 122 | } 123 | 124 | /** 125 | 方法交换 126 | 127 | @param class 交换方法所在的类 128 | @param method1 方法1 129 | @param method2 方法2 130 | */ 131 | + (void)methodSwap:(Class)class firstMethod:(SEL)method1 secondMethod:(SEL)method2 { 132 | Method firstMethod = class_getInstanceMethod(class, method1); 133 | Method secondMethod = class_getInstanceMethod(class, method2); 134 | method_exchangeImplementations(firstMethod, secondMethod); 135 | } 136 | @end 137 | -------------------------------------------------------------------------------- /ObjCRuntimeDemo/TestClass/TestClass+AssociatedObject.h: -------------------------------------------------------------------------------- 1 | // 2 | // TestClass+AssociatedObject.h 3 | // ObjCRuntimeDemo 4 | // 5 | // Created by Mr.LuDashi on 2017/1/5. 6 | // Copyright © 2017年 ZeluLi. All rights reserved. 7 | // 8 | 9 | #import "TestClass.h" 10 | #import "RuntimeKit.h" 11 | @interface TestClass (AssociatedObject) 12 | @property (nonatomic, strong) NSString *dynamicAddProperty; 13 | @end 14 | 15 | -------------------------------------------------------------------------------- /ObjCRuntimeDemo/TestClass/TestClass+AssociatedObject.m: -------------------------------------------------------------------------------- 1 | // 2 | // TestClass+AssociatedObject.m 3 | // ObjCRuntimeDemo 4 | // 5 | // Created by Mr.LuDashi on 2017/1/5. 6 | // Copyright © 2017年 ZeluLi. All rights reserved. 7 | // 8 | 9 | #import "TestClass+AssociatedObject.h" 10 | #pragma mark - 动态属性关联 11 | static char kDynamicAddProperty; 12 | @implementation TestClass (AssociatedObject) 13 | /** 14 | getter方法 15 | 16 | @return 返回关联属性的值 17 | */ 18 | - (NSString *)dynamicAddProperty { 19 | return objc_getAssociatedObject(self, &kDynamicAddProperty); 20 | } 21 | 22 | 23 | /** 24 | setter方法 25 | 26 | @param dynamicAddProperty 设置关联属性的值 27 | */ 28 | - (void)setDynamicAddProperty:(NSString *)dynamicAddProperty { 29 | objc_setAssociatedObject(self, &kDynamicAddProperty, dynamicAddProperty, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 30 | } 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /ObjCRuntimeDemo/TestClass/TestClass+Category.h: -------------------------------------------------------------------------------- 1 | // 2 | // TestClass+Category.h 3 | // ObjCRuntimeDemo 4 | // 5 | // Created by Mr.LuDashi on 2017/1/4. 6 | // Copyright © 2017年 ZeluLi. All rights reserved. 7 | // 8 | 9 | #import "TestClass.h" 10 | 11 | @interface TestClass (Category) 12 | - (void)categoryMethod; 13 | @end 14 | -------------------------------------------------------------------------------- /ObjCRuntimeDemo/TestClass/TestClass+Category.m: -------------------------------------------------------------------------------- 1 | // 2 | // TestClass+Category.m 3 | // ObjCRuntimeDemo 4 | // 5 | // Created by Mr.LuDashi on 2017/1/4. 6 | // Copyright © 2017年 ZeluLi. All rights reserved. 7 | // 8 | 9 | #import "TestClass+Category.h" 10 | #import "RuntimeKit.h" 11 | 12 | @implementation TestClass (Category) 13 | - (void)categoryMethod { 14 | NSLog(@"categoryMethod"); 15 | } 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /ObjCRuntimeDemo/TestClass/TestClass+SwapMethod.h: -------------------------------------------------------------------------------- 1 | // 2 | // TestClass+SwapMethod.h 3 | // ObjCRuntimeDemo 4 | // 5 | // Created by Mr.LuDashi on 2017/1/5. 6 | // Copyright © 2017年 ZeluLi. All rights reserved. 7 | // 8 | 9 | #import "TestClass.h" 10 | 11 | @interface TestClass (SwapMethod) 12 | - (void)swapMethod; 13 | - (void)method2; 14 | @end 15 | -------------------------------------------------------------------------------- /ObjCRuntimeDemo/TestClass/TestClass+SwapMethod.m: -------------------------------------------------------------------------------- 1 | // 2 | // TestClass+SwapMethod.m 3 | // ObjCRuntimeDemo 4 | // 5 | // Created by Mr.LuDashi on 2017/1/5. 6 | // Copyright © 2017年 ZeluLi. All rights reserved. 7 | // 8 | 9 | #import "TestClass+SwapMethod.h" 10 | #import "RuntimeKit.h" 11 | @implementation TestClass (SwapMethod) 12 | 13 | - (void)swapMethod { 14 | [RuntimeKit methodSwap:[self class] 15 | firstMethod:@selector(method1) 16 | secondMethod:@selector(method2)]; 17 | } 18 | 19 | - (void)method2 { 20 | //下方调用的实际上是method1的实现 21 | [self method2]; 22 | NSLog(@"可以在Method1的基础上添加各种东西了"); 23 | } 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /ObjCRuntimeDemo/TestClass/TestClass.h: -------------------------------------------------------------------------------- 1 | // 2 | // TestClass.h 3 | // ObjCRuntimeDemo 4 | // 5 | // Created by Mr.LuDashi on 2017/1/4. 6 | // Copyright © 2017年 ZeluLi. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface TestClass : NSObject 12 | @property (nonatomic, strong) NSArray *publicProperty1; 13 | @property (nonatomic, strong) NSString *publicProperty2; 14 | 15 | + (void)classMethod: (NSString *)value; 16 | - (void)publicTestMethod1: (NSString *)value1 Second: (NSString *)value2; 17 | - (void)publicTestMethod2; 18 | 19 | - (void)method1; 20 | @end 21 | -------------------------------------------------------------------------------- /ObjCRuntimeDemo/TestClass/TestClass.m: -------------------------------------------------------------------------------- 1 | // 2 | // TestClass.m 3 | // ObjCRuntimeDemo 4 | // 5 | // Created by Mr.LuDashi on 2017/1/4. 6 | // Copyright © 2017年 ZeluLi. All rights reserved. 7 | // 8 | 9 | #import "TestClass.h" 10 | #import "RuntimeKit.h" 11 | 12 | @interface SecondClass : NSObject 13 | - (void)noThisMethod:(NSString *)value; 14 | @end 15 | 16 | @implementation SecondClass 17 | - (void)noThisMethod:(NSString *)value { 18 | NSLog(@"SecondClass中的方法实现%@", value); 19 | } 20 | @end 21 | 22 | 23 | @interface TestClass(){ 24 | NSInteger _var1; 25 | int _var2; 26 | BOOL _var3; 27 | double _var4; 28 | float _var5; 29 | } 30 | @property (nonatomic, strong) NSMutableArray *privateProperty1; 31 | @property (nonatomic, strong) NSNumber *privateProperty2; 32 | @property (nonatomic, strong) NSDictionary *privateProperty3; 33 | @end 34 | 35 | @implementation TestClass 36 | 37 | + (void)classMethod: (NSString *)value { 38 | NSLog(@"publicTestMethod1"); 39 | } 40 | 41 | - (void)publicTestMethod1: (NSString *)value1 Second: (NSString *)value2 { 42 | NSLog(@"publicTestMethod1"); 43 | } 44 | 45 | - (void)publicTestMethod2 { 46 | NSLog(@"publicTestMethod2"); 47 | } 48 | 49 | - (void)privateTestMethod1 { 50 | NSLog(@"privateTestMethod1"); 51 | } 52 | 53 | - (void)privateTestMethod2 { 54 | NSLog(@"privateTestMethod2"); 55 | } 56 | 57 | #pragma mark - 方法交换时使用 58 | - (void)method1 { 59 | NSLog(@"我是Method1的实现"); 60 | } 61 | 62 | //运行时方法拦截 63 | - (void)dynamicAddMethod: (NSString *) value { 64 | NSLog(@"OC替换的方法:%@", value); 65 | } 66 | 67 | /** 68 | 没有找到SEL的IML实现时会执行下方的方法 69 | 70 | @param sel 当前对象调用并且找不到IML的SEL 71 | @return 找到其他的执行方法,并返回yes 72 | */ 73 | + (BOOL)resolveInstanceMethod:(SEL)sel { 74 | return NO; //当返回NO时,会接着执行forwordingTargetForSelector:方法, 75 | [RuntimeKit addMethod:[self class] method:sel method:@selector(dynamicAddMethod:)]; 76 | return YES; 77 | } 78 | 79 | 80 | /** 81 | 将当前对象不存在的SEL传给其他存在该SEL的对象 82 | 83 | @param aSelector 当前类中不存在的SEL 84 | @return 存在该SEL的对象 85 | */ 86 | - (id)forwardingTargetForSelector:(SEL)aSelector { 87 | return self; 88 | return [SecondClass new]; //让SecondClass中相应的SEL去执行该方法 89 | } 90 | 91 | - (NSMethodSignature *)methodSignatureForSelector:(SEL)selector { 92 | //查找父类的方法签名 93 | NSMethodSignature *signature = [super methodSignatureForSelector:selector]; 94 | if(signature == nil) { 95 | signature = [NSMethodSignature signatureWithObjCTypes:"@@:"]; 96 | 97 | } 98 | return signature; 99 | } 100 | 101 | - (void)forwardInvocation:(NSInvocation *)invocation { 102 | SecondClass * forwardClass = [SecondClass new]; 103 | SEL sel = invocation.selector; 104 | if ([forwardClass respondsToSelector:sel]) { 105 | [invocation invokeWithTarget:forwardClass]; 106 | } else { 107 | [self doesNotRecognizeSelector:sel]; 108 | } 109 | } 110 | 111 | @end 112 | -------------------------------------------------------------------------------- /ObjCRuntimeDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // ObjCRuntimeDemo 4 | // 5 | // Created by Mr.LuDashi on 2017/1/4. 6 | // Copyright © 2017年 ZeluLi. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "TestClass.h" 11 | #import "TestClass+Category.h" 12 | #import "TestClass+SwapMethod.h" 13 | 14 | #import "TestClass+AssociatedObject.h" 15 | 16 | #import "RuntimeKit.h" 17 | int main(int argc, const char * argv[]) { 18 | @autoreleasepool { 19 | 20 | NSString *className = [RuntimeKit fetchClassName:[TestClass class]]; 21 | NSLog(@"测试类的类名为:%@\n", className); 22 | 23 | NSArray *ivarList = [RuntimeKit fetchIvarList:[TestClass class]]; 24 | NSLog(@"\n获取TestClass的成员变量列表:%@", ivarList); 25 | 26 | NSArray *propertyList = [RuntimeKit fetchPropertyList:[TestClass class]]; 27 | NSLog(@"\n获取TestClass的属性列表:%@", propertyList); 28 | 29 | NSArray *methodList = [RuntimeKit fetchMethodList:[TestClass class]]; 30 | NSLog(@"\n获取TestClass的方法列表:%@", methodList); 31 | 32 | NSArray *protocolList = [RuntimeKit fetchProtocolList:[TestClass class]]; 33 | NSLog(@"\n获取TestClass的协议列表:%@", protocolList); 34 | TestClass *instance = [TestClass new]; 35 | [instance publicTestMethod2]; 36 | [instance performSelector:@selector(noThisMethod:) withObject:@"实例方法参数"]; 37 | 38 | instance.dynamicAddProperty = @"我是动态添加的属性"; 39 | NSLog(@"%@", instance.dynamicAddProperty); 40 | 41 | 42 | [instance swapMethod]; 43 | [instance method1]; 44 | 45 | } 46 | return 0; 47 | } 48 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # iOS开发之Runtime常用示例总结 2 | 经常有小伙伴私下在Q上问一些关于Runtime的东西,问我有没有Runtime的相关博客,之前还真没正儿八经的总结过。之前只是在解析第三方框架源码时,聊过一些用法,也就是这些第三方框架中用到的Runtime。比如属性关联,动态获取属性等等。本篇博客就针对Runtime这个主题来总结一些其常用的一些方法,当然“空谈误国”,今天博客中所聊的Runtime依然要依托于本篇博客所涉及的Demo。 3 | 4 | 本篇博客所聊的Runtime的内容大概有:动态获取类名、动态获取类的成员变量、动态获取类的属性列表、动态获取类的方法列表、动态获取类所遵循的协议列表、动态添加新的方法、类的实例方法实现的交换、动态属性关联、消息发送与消息转发机制等。当然,本篇博客总结的是运行时常用的功能,并不是所有Runtime的内容。 5 | 6 | ### 一、构建Runtime测试用例 7 | 本篇博客的内容是依托于实例的,所以我们在本篇博客中先构建我们的测试类,Runtime将会对该类进行相关的操作。下方就是本篇博客所涉及Demo的目录,上面的RuntimeKit类是讲Runtime常用的功能进行了简单的封装,而下方的TestClass以及相关的类目就是我们Runtime要操作的对象了。下方会对TestClass以及类目中的内容进行详细介绍。 8 | ![](http://images2015.cnblogs.com/blog/545446/201701/545446-20170118101510578-1257087631.png) 9 | 10 | 下方这几个截图就是我们的测试类TestClass的主要部分,因为TestClass是专门用来测试的类,所以其涉及的内容要尽量的全面。TestClass遵循了NSCoding, NSCopying这两个协议,并且为其添加了公有属性、私有属性、私有成员变量、 公有实例方法、私有实例方法、类方法等。这些添加的内容,都将是我们Runtime的操作对象。下方那几个TestClass的类目稍后在使用Runtime时再进行介绍。 11 | ![](http://images2015.cnblogs.com/blog/545446/201701/545446-20170118103313015-1479742483.png) 12 | ![](http://images2015.cnblogs.com/blog/545446/201701/545446-20170118103417765-232417568.png) 13 | ![](http://images2015.cnblogs.com/blog/545446/201701/545446-20170118103544421-84919050.png) 14 | 15 | 16 | ### 二、RuntimeKit的封装 17 | 18 | 接下来我们就来看看RuntimeKit中的内容,其中对Runtime常用的方法进行了简单的封装。主要是动态的获取类的一些属性和方法的,以及动态方法添加和方法交换的。本部分的干货还是不少的。 19 | 20 | #### 1、获取类名 21 | 动态的获取类名是比较简单的,使用class_getName(Class)就可以在运行时来获取类的名称。class_getName()函数返回的是一个char类型的指针,也就是C语言的字符串类型,所以我们要将其转换成NSString类型,然后再返回出去。下方的+fetchClassName:方法就是我们封装的获取类名的方法,如下所示: 22 | ![](http://images2015.cnblogs.com/blog/545446/201701/545446-20170118112743265-118863449.png) 23 | 24 | #### 2、获取成员变量 25 | 下方这个+fetchIvarList:这个方法就是我们封装的获取类的成员变量的方法。当然我们在获取成员变量时,可以用ivar_getTypeEncoding()来获取相应成员变量的类型。使用ivar_getName()来获取相应成员变量的名称。下方就是对获取成员变量的功能的封装。返回的是一个数组,数组的元素是一个字典,而字典中存储的就是相应成员变量的名称和类型。 26 | ![](http://images2015.cnblogs.com/blog/545446/201701/545446-20170118113158109-999455528.png) 27 | 28 | 下方就是调用上述方法获取的TestClass类的成员变量。当然在运行时就没有什么私有和公有之分了,只要是成员变量就可以获取到。在OC中的给类添加成员属性其实就是添加了一个成员变量和getter以及setter方法。所以获取的成员列表中肯定带有成员属性,不过成员属性的名称前方添加了下划线来与成员属性进行区分。我们也可以获取成员变量的类型,下方的_var1是NSInteger类型,动态获取到的是q字母,其实是NSInteger的符号。而i就表示int类型,c表示Bool类型,d表示double类型,f则就表示float类型。当然这些基本类型都是由一个字母代替的,如果是引用类型的话,则直接就是一个字符串了,比如NSArray类型就是"@NSArray"。 29 | ![](http://images2015.cnblogs.com/blog/545446/201701/545446-20170118113754109-548155613.png) 30 | 31 | #### 3.获取成员属性 32 | 33 | 上面获取的是类的成员变量,那么下方这个+fetchPropertyList:获取的就是成员属性。当然此刻获取的只包括成员属性,也就是那些有setter或者getter方法的成员变量。下方主要是使用了class_copyPropertyList(Class,&count)来获取的属性列表,然后通过for循环通过property_getName()来获取每个属性的名字。当然使用property_getName()获取到的名字依然是C语言的char类型的指针,所以我们还需要将其转换成NSString类型,然后放到数组中一并返回。如下所示: 34 | ![](http://images2015.cnblogs.com/blog/545446/201701/545446-20170118144442359-273804131.png) 35 | 36 | 下方这个截图就是调用上述方法获取的TestClass的所有的属性,当然dynamicAddProperty是我们使用Runtime动态给TestClass添加的,所以也是可以获取到的。当然我们获取到的属性的名称为了与其对应的成员变量进行区分,成员属性的名字前边是没有下划线的。 37 | ![](http://images2015.cnblogs.com/blog/545446/201701/545446-20170118145048296-3098824.png) 38 | 39 | #### 4、获取类的实例方法 40 | 41 | 接下来我们就来封装一下获取类的实例方法列表的功能,下方这个+fetchMethodList:就是我们封装的获取类的实例方法列表的函数。在下方函数中,通过class_copyMethodList()方法获取类的实例方法列表,然后通过for循环使用method_getName()来获取每个方法的名称,然后将方法的名称转换成NSString类型,存储到数组中一并返回。具体代码如下所示: 42 | ![](http://images2015.cnblogs.com/blog/545446/201701/545446-20170118145607781-1348896976.png) 43 | 44 | 下方这个截图就是上述方法在TestClass上运行的结果,其中打印了TestClass类的所有实例方法,当然其中也必须得包含成员属性的getter和setter方法。当然TestClass类目中的方法也是必须能获取到的。结果如下所示: 45 | ![](http://images2015.cnblogs.com/blog/545446/201701/545446-20170118145934453-2070090169.png) 46 | 47 | #### 5、获取协议列表 48 | 49 | 下方是获取我们类所遵循协议列表的方法,主要使用了class_copyProtocolList()来获取列表,然后通过for循序使用protocol_getName()来获取协议的名称,最后将其转换成NSString类型放入数组中返回即可。 50 | ![](http://images2015.cnblogs.com/blog/545446/201701/545446-20170118150811609-1928867834.png) 51 | 52 | 下方就是我们获取到的TestClass类所遵循的协议列表: 53 | ![](http://images2015.cnblogs.com/blog/545446/201701/545446-20170118151103687-542725031.png) 54 | 55 | #### 6、动态添加方法实现 56 | 57 | 下方就是动态的往相应类上添加方法以及实现。下方的+addMethod方法有三个参数,第一个参数是要添加方法的类,第二个参数是方法的SEL,第三个参数则是提供方法实现的SEL。稍后在消息发送和消息转发时会用到下方的方法。下方主要是使用class_getInstanceMethod()和method_getImplementation()这两个方法相结合获取相应SEL的方法实现。下方的IMP其实就是Implementation的方法缩写,获取到相应的方法实现后,然后再调用class_addMethod()方法将IMP与SEL进行绑定即可。具体做法如下所示。 58 | ![](http://images2015.cnblogs.com/blog/545446/201701/545446-20170118151243703-2097705863.png) 59 | 60 | #### 7、方法实现交换 61 | 62 | 下方就是讲类的两个方法的实现进行交换。如果将MethodA与MethodB的方法实现进行交换的话,调用MethodA时就会执行MethodB的内容,反之亦然。 63 | ![](http://images2015.cnblogs.com/blog/545446/201701/545446-20170118152731953-1045070707.png) 64 | 65 | 下方这段代码就是对上述方法的测试。下方是TestClass的一个类目,在该类目中将类目中的方法与TestClass中的方法进行了替换。也就是将method1与method2进行了替换,替换后在method2中调用的method2其实就是调用的method1。在第三方库中,经常会使用该特性,已达到AOP编程的目的。 66 | ![](http://images2015.cnblogs.com/blog/545446/201701/545446-20170118153114359-1221499852.png) 67 | 68 | 69 | ### 三、属性关联 70 | 71 | 属性关联说白了就是在类目中动态的为我们的类添加相应的属性,如果看过之前发布的对Masonry框架源码解析的博客的话,对下方的属性关联并不陌生。在Masonry框架中就利用Runtime的属性关联在UIView的类目中给UIView添加了一个约束数组,用来记录添加在当前View上的所有约束。下方就是在TestClass的类目中通过objc_getAssociatedObject()和objc_setAssociatedObject()两个方法为TestClass类添加了一个dynamicAddProperty属性。上面我们获取到的属性列表中就含有该动态添加的成员属性。 72 | 73 | 下方就是属性关联的具体代码,如下所示。 74 | ![](http://images2015.cnblogs.com/blog/545446/201701/545446-20170118155437390-1222095219.png) 75 | 76 | 77 | 78 | ### 四、消息处理与消息转发 79 | 80 | 在Runtime中不得不提的就是OC的消息处理和消息转发机制。当然网上也有不少相关资料,本篇博客为了完整性,还是要聊一下消息处理与消息转发的。当你调用一个类的方法时,先在本类中的方法缓存列表中进行查询,如果在缓存列表中找到了该方法的实现,就执行,如果找不到就在本类中的方列表中进行查找。在本类方列表中查找到相应的方法实现后就进行调用,如果没找到,就去父类中进行查找。如果在父类中的方法列表中找到了相应方法的实现,那么就执行,否则就执行下方的几步。 81 | 82 | 当调用一个方法在缓存列表,本类中的方法列表以及父类的方法列表找不到相应的实现时,到程序崩溃阶段中间还会有几步让你来挽救。接下来就来看看这几步该怎么走。 83 | 84 | #### 1.消息处理(Resolve Method) 85 | 86 | 当在相应的类以及父类中找不到类方法实现时会执行+resolveInstanceMethod:这个类方法。该方法如果在类中不被重写的话,默认返回NO。如果返回NO就表明不做任何处理,走下一步。如果返回YES的话,就说明在该方法中对这个找不到实现的方法进行了处理。在该方法中,我们可以为找不到实现的SEL动态的添加一个方法实现,添加完毕后,就会执行我们添加的方法实现。这样,当一个类调用不存在的方法时,就不会崩溃了。具体做法如下所示: 87 | ![](http://images2015.cnblogs.com/blog/545446/201701/545446-20170118162200968-1043136530.png) 88 | 89 | #### 2、消息快速转发 90 | 91 | 如果不对上述消息进行处理的话,也就是+resolveInstanceMethod:返回NO时,会走下一步消息转发,即-forwardingTargetForSelector:。该方法会返回一个类的对象,这个类的对象有SEL对应的实现,当调用这个找不到的方法时,就会被转发到SecondClass中去进行处理。这也就是所谓的消息转发。当该方法返回self或者nil, 说明不对相应的方法进行转发,那么就该走下一步了。 92 | ![](http://images2015.cnblogs.com/blog/545446/201701/545446-20170118163329359-1237143561.png) 93 | 94 | #### 3.消息常规转发 95 | 96 | 如果不将消息转发给其他类的对象,那么就只能自己进行处理了。如果上述方法返回self的话,会执行-methodSignatureForSelector:方法来获取方法的参数以及返回数据类型,也就是说该方法获取的是方法的签名并返回。如果上述方法返回nil的话,那么消息转发就结束,程序崩溃,报出找不到相应的方法实现的崩溃信息。 97 | 98 | 在+resolveInstanceMethod:返回NO时就会执行下方的方法,下方也是讲该方法转发给SecondClass,如下所示: 99 | ![](http://images2015.cnblogs.com/blog/545446/201701/545446-20170118164935531-2092077162.png) 100 | --------------------------------------------------------------------------------