├── .gitignore ├── HotFixDemo ├── HotFixDemo.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ ├── xcshareddata │ │ └── xcschemes │ │ │ └── HotFixDemo.xcscheme │ └── xcuserdata │ │ └── zzyong.xcuserdatad │ │ ├── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist │ │ └── xcschemes │ │ └── xcschememanagement.plist ├── HotFixDemo.xcworkspace │ ├── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ └── xcuserdata │ │ └── zzyong.xcuserdatad │ │ └── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist ├── HotFixDemo │ ├── App │ │ ├── AppDelegate.h │ │ ├── AppDelegate.m │ │ ├── SceneDelegate.h │ │ ├── SceneDelegate.m │ │ └── main.m │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ └── Contents.json │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── Class │ │ ├── MyClassA.h │ │ ├── MyClassA.m │ │ ├── MyClassB.h │ │ ├── MyClassB.m │ │ ├── MyClassC.h │ │ ├── MyClassC.m │ │ ├── OCDynamic.h │ │ └── OCDynamic.m │ ├── Info.plist │ ├── VCCell.h │ ├── VCCell.m │ ├── VCModel.h │ ├── VCModel.m │ ├── ViewController+DataSource.h │ ├── ViewController+DataSource.m │ ├── ViewController.h │ └── ViewController.m ├── HotFixDemoTests │ ├── HotFixDemoTests.m │ └── Info.plist ├── HotFixDemoUITests │ ├── HotFixDemoUITests.m │ └── Info.plist ├── Podfile ├── Podfile.lock └── Pods │ ├── Aspects │ ├── Aspects.h │ ├── Aspects.m │ ├── LICENSE │ └── README.md │ ├── Headers │ ├── Private │ │ └── Aspects │ │ │ └── Aspects.h │ └── Public │ │ └── Aspects │ │ └── Aspects.h │ ├── Manifest.lock │ ├── Pods.xcodeproj │ ├── project.pbxproj │ └── xcuserdata │ │ └── zzyong.xcuserdatad │ │ └── xcschemes │ │ ├── Aspects.xcscheme │ │ ├── Pods-HotFixDemo-HotFixDemoUITests.xcscheme │ │ ├── Pods-HotFixDemo.xcscheme │ │ ├── Pods-HotFixDemoTests.xcscheme │ │ └── xcschememanagement.plist │ └── Target Support Files │ ├── Aspects │ ├── Aspects-dummy.m │ ├── Aspects-prefix.pch │ ├── Aspects.debug.xcconfig │ ├── Aspects.release.xcconfig │ └── Aspects.xcconfig │ ├── Pods-HotFixDemo-HotFixDemoUITests │ ├── Pods-HotFixDemo-HotFixDemoUITests-acknowledgements.markdown │ ├── Pods-HotFixDemo-HotFixDemoUITests-acknowledgements.plist │ ├── Pods-HotFixDemo-HotFixDemoUITests-dummy.m │ ├── Pods-HotFixDemo-HotFixDemoUITests.debug.xcconfig │ └── Pods-HotFixDemo-HotFixDemoUITests.release.xcconfig │ ├── Pods-HotFixDemo │ ├── Pods-HotFixDemo-acknowledgements.markdown │ ├── Pods-HotFixDemo-acknowledgements.plist │ ├── Pods-HotFixDemo-dummy.m │ ├── Pods-HotFixDemo.debug.xcconfig │ └── Pods-HotFixDemo.release.xcconfig │ └── Pods-HotFixDemoTests │ ├── Pods-HotFixDemoTests-acknowledgements.markdown │ ├── Pods-HotFixDemoTests-acknowledgements.plist │ ├── Pods-HotFixDemoTests-dummy.m │ ├── Pods-HotFixDemoTests.debug.xcconfig │ └── Pods-HotFixDemoTests.release.xcconfig ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | *.xcuserstate 3 | -------------------------------------------------------------------------------- /HotFixDemo/HotFixDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 6C9CACDD0D6BA49839E6C5A3 /* libPods-HotFixDemoTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3FCC07275F07BBC7ADBC2E77 /* libPods-HotFixDemoTests.a */; }; 11 | 739EBE3B670046012F8116C8 /* libPods-HotFixDemo.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B7DF08297ABB588ABD5D2440 /* libPods-HotFixDemo.a */; }; 12 | 814D5D5D2491E320006A0DA1 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 814D5D5C2491E320006A0DA1 /* AppDelegate.m */; }; 13 | 814D5D602491E320006A0DA1 /* SceneDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 814D5D5F2491E320006A0DA1 /* SceneDelegate.m */; }; 14 | 814D5D632491E320006A0DA1 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 814D5D622491E320006A0DA1 /* ViewController.m */; }; 15 | 814D5D662491E320006A0DA1 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 814D5D642491E320006A0DA1 /* Main.storyboard */; }; 16 | 814D5D682491E321006A0DA1 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 814D5D672491E321006A0DA1 /* Assets.xcassets */; }; 17 | 814D5D6B2491E321006A0DA1 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 814D5D692491E321006A0DA1 /* LaunchScreen.storyboard */; }; 18 | 814D5D6E2491E321006A0DA1 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 814D5D6D2491E321006A0DA1 /* main.m */; }; 19 | 814D5D782491E321006A0DA1 /* HotFixDemoTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 814D5D772491E321006A0DA1 /* HotFixDemoTests.m */; }; 20 | 814D5D832491E321006A0DA1 /* HotFixDemoUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = 814D5D822491E321006A0DA1 /* HotFixDemoUITests.m */; }; 21 | 816019A724979E420014AC2D /* MyClassA.m in Sources */ = {isa = PBXBuildFile; fileRef = 816019A624979E420014AC2D /* MyClassA.m */; }; 22 | 816019AA2498F16C0014AC2D /* MyClassB.m in Sources */ = {isa = PBXBuildFile; fileRef = 816019A92498F16C0014AC2D /* MyClassB.m */; }; 23 | 816019AD2499BED20014AC2D /* OCDynamic.m in Sources */ = {isa = PBXBuildFile; fileRef = 816019AC2499BED20014AC2D /* OCDynamic.m */; }; 24 | 816019B02499D3DD0014AC2D /* MyClassC.m in Sources */ = {isa = PBXBuildFile; fileRef = 816019AF2499D3DD0014AC2D /* MyClassC.m */; }; 25 | 8171550C26665F6200D26E6B /* VCCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 8171550926665F6200D26E6B /* VCCell.m */; }; 26 | 8171550D26665F6200D26E6B /* VCModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 8171550B26665F6200D26E6B /* VCModel.m */; }; 27 | 8171551326665F6800D26E6B /* ViewController+DataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = 8171551126665F6800D26E6B /* ViewController+DataSource.m */; }; 28 | D04F2599717BF025564ED16F /* libPods-HotFixDemo-HotFixDemoUITests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A92D959134503905C69A1509 /* libPods-HotFixDemo-HotFixDemoUITests.a */; }; 29 | /* End PBXBuildFile section */ 30 | 31 | /* Begin PBXContainerItemProxy section */ 32 | 814D5D742491E321006A0DA1 /* PBXContainerItemProxy */ = { 33 | isa = PBXContainerItemProxy; 34 | containerPortal = 814D5D502491E320006A0DA1 /* Project object */; 35 | proxyType = 1; 36 | remoteGlobalIDString = 814D5D572491E320006A0DA1; 37 | remoteInfo = HotFixDemo; 38 | }; 39 | 814D5D7F2491E321006A0DA1 /* PBXContainerItemProxy */ = { 40 | isa = PBXContainerItemProxy; 41 | containerPortal = 814D5D502491E320006A0DA1 /* Project object */; 42 | proxyType = 1; 43 | remoteGlobalIDString = 814D5D572491E320006A0DA1; 44 | remoteInfo = HotFixDemo; 45 | }; 46 | /* End PBXContainerItemProxy section */ 47 | 48 | /* Begin PBXFileReference section */ 49 | 25514866ACE6ED6B818AE4B9 /* Pods-HotFixDemoTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-HotFixDemoTests.debug.xcconfig"; path = "Target Support Files/Pods-HotFixDemoTests/Pods-HotFixDemoTests.debug.xcconfig"; sourceTree = ""; }; 50 | 31DF8DB01CB7C67AAE6E38E1 /* Pods-HotFixDemo.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-HotFixDemo.release.xcconfig"; path = "Target Support Files/Pods-HotFixDemo/Pods-HotFixDemo.release.xcconfig"; sourceTree = ""; }; 51 | 3FCC07275F07BBC7ADBC2E77 /* libPods-HotFixDemoTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-HotFixDemoTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 52 | 56E701A87A75E873A7A555B7 /* Pods-HotFixDemo.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-HotFixDemo.debug.xcconfig"; path = "Target Support Files/Pods-HotFixDemo/Pods-HotFixDemo.debug.xcconfig"; sourceTree = ""; }; 53 | 814D5D582491E320006A0DA1 /* HotFixDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = HotFixDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 54 | 814D5D5B2491E320006A0DA1 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 55 | 814D5D5C2491E320006A0DA1 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 56 | 814D5D5E2491E320006A0DA1 /* SceneDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SceneDelegate.h; sourceTree = ""; }; 57 | 814D5D5F2491E320006A0DA1 /* SceneDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SceneDelegate.m; sourceTree = ""; }; 58 | 814D5D612491E320006A0DA1 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 59 | 814D5D622491E320006A0DA1 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 60 | 814D5D652491E320006A0DA1 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 61 | 814D5D672491E321006A0DA1 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 62 | 814D5D6A2491E321006A0DA1 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 63 | 814D5D6C2491E321006A0DA1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 64 | 814D5D6D2491E321006A0DA1 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 65 | 814D5D732491E321006A0DA1 /* HotFixDemoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = HotFixDemoTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 66 | 814D5D772491E321006A0DA1 /* HotFixDemoTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HotFixDemoTests.m; sourceTree = ""; }; 67 | 814D5D792491E321006A0DA1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 68 | 814D5D7E2491E321006A0DA1 /* HotFixDemoUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = HotFixDemoUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 69 | 814D5D822491E321006A0DA1 /* HotFixDemoUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HotFixDemoUITests.m; sourceTree = ""; }; 70 | 814D5D842491E321006A0DA1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 71 | 816019A524979E420014AC2D /* MyClassA.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MyClassA.h; sourceTree = ""; }; 72 | 816019A624979E420014AC2D /* MyClassA.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MyClassA.m; sourceTree = ""; }; 73 | 816019A82498F16C0014AC2D /* MyClassB.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MyClassB.h; sourceTree = ""; }; 74 | 816019A92498F16C0014AC2D /* MyClassB.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MyClassB.m; sourceTree = ""; }; 75 | 816019AB2499BED20014AC2D /* OCDynamic.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OCDynamic.h; sourceTree = ""; }; 76 | 816019AC2499BED20014AC2D /* OCDynamic.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OCDynamic.m; sourceTree = ""; }; 77 | 816019AE2499D3DD0014AC2D /* MyClassC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MyClassC.h; sourceTree = ""; }; 78 | 816019AF2499D3DD0014AC2D /* MyClassC.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MyClassC.m; sourceTree = ""; }; 79 | 8171550826665F6200D26E6B /* VCModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VCModel.h; sourceTree = ""; }; 80 | 8171550926665F6200D26E6B /* VCCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VCCell.m; sourceTree = ""; }; 81 | 8171550A26665F6200D26E6B /* VCCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VCCell.h; sourceTree = ""; }; 82 | 8171550B26665F6200D26E6B /* VCModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VCModel.m; sourceTree = ""; }; 83 | 8171551126665F6800D26E6B /* ViewController+DataSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "ViewController+DataSource.m"; sourceTree = ""; }; 84 | 8171551226665F6800D26E6B /* ViewController+DataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ViewController+DataSource.h"; sourceTree = ""; }; 85 | A92D959134503905C69A1509 /* libPods-HotFixDemo-HotFixDemoUITests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-HotFixDemo-HotFixDemoUITests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 86 | B741A181A9680FADC7B98CC8 /* Pods-HotFixDemo-HotFixDemoUITests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-HotFixDemo-HotFixDemoUITests.debug.xcconfig"; path = "Target Support Files/Pods-HotFixDemo-HotFixDemoUITests/Pods-HotFixDemo-HotFixDemoUITests.debug.xcconfig"; sourceTree = ""; }; 87 | B7DF08297ABB588ABD5D2440 /* libPods-HotFixDemo.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-HotFixDemo.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 88 | CBAC2E4952F2BCF9E58B6F4B /* Pods-HotFixDemoTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-HotFixDemoTests.release.xcconfig"; path = "Target Support Files/Pods-HotFixDemoTests/Pods-HotFixDemoTests.release.xcconfig"; sourceTree = ""; }; 89 | EEB7BC2A436401170265BC94 /* Pods-HotFixDemo-HotFixDemoUITests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-HotFixDemo-HotFixDemoUITests.release.xcconfig"; path = "Target Support Files/Pods-HotFixDemo-HotFixDemoUITests/Pods-HotFixDemo-HotFixDemoUITests.release.xcconfig"; sourceTree = ""; }; 90 | /* End PBXFileReference section */ 91 | 92 | /* Begin PBXFrameworksBuildPhase section */ 93 | 814D5D552491E320006A0DA1 /* Frameworks */ = { 94 | isa = PBXFrameworksBuildPhase; 95 | buildActionMask = 2147483647; 96 | files = ( 97 | 739EBE3B670046012F8116C8 /* libPods-HotFixDemo.a in Frameworks */, 98 | ); 99 | runOnlyForDeploymentPostprocessing = 0; 100 | }; 101 | 814D5D702491E321006A0DA1 /* Frameworks */ = { 102 | isa = PBXFrameworksBuildPhase; 103 | buildActionMask = 2147483647; 104 | files = ( 105 | 6C9CACDD0D6BA49839E6C5A3 /* libPods-HotFixDemoTests.a in Frameworks */, 106 | ); 107 | runOnlyForDeploymentPostprocessing = 0; 108 | }; 109 | 814D5D7B2491E321006A0DA1 /* Frameworks */ = { 110 | isa = PBXFrameworksBuildPhase; 111 | buildActionMask = 2147483647; 112 | files = ( 113 | D04F2599717BF025564ED16F /* libPods-HotFixDemo-HotFixDemoUITests.a in Frameworks */, 114 | ); 115 | runOnlyForDeploymentPostprocessing = 0; 116 | }; 117 | /* End PBXFrameworksBuildPhase section */ 118 | 119 | /* Begin PBXGroup section */ 120 | 40DF1ED89FEF20CF6B801E8B /* Frameworks */ = { 121 | isa = PBXGroup; 122 | children = ( 123 | B7DF08297ABB588ABD5D2440 /* libPods-HotFixDemo.a */, 124 | A92D959134503905C69A1509 /* libPods-HotFixDemo-HotFixDemoUITests.a */, 125 | 3FCC07275F07BBC7ADBC2E77 /* libPods-HotFixDemoTests.a */, 126 | ); 127 | name = Frameworks; 128 | sourceTree = ""; 129 | }; 130 | 77E7CF720AFE5C9D70B8C37A /* Pods */ = { 131 | isa = PBXGroup; 132 | children = ( 133 | 56E701A87A75E873A7A555B7 /* Pods-HotFixDemo.debug.xcconfig */, 134 | 31DF8DB01CB7C67AAE6E38E1 /* Pods-HotFixDemo.release.xcconfig */, 135 | B741A181A9680FADC7B98CC8 /* Pods-HotFixDemo-HotFixDemoUITests.debug.xcconfig */, 136 | EEB7BC2A436401170265BC94 /* Pods-HotFixDemo-HotFixDemoUITests.release.xcconfig */, 137 | 25514866ACE6ED6B818AE4B9 /* Pods-HotFixDemoTests.debug.xcconfig */, 138 | CBAC2E4952F2BCF9E58B6F4B /* Pods-HotFixDemoTests.release.xcconfig */, 139 | ); 140 | path = Pods; 141 | sourceTree = ""; 142 | }; 143 | 814D5D4F2491E320006A0DA1 = { 144 | isa = PBXGroup; 145 | children = ( 146 | 814D5D5A2491E320006A0DA1 /* HotFixDemo */, 147 | 814D5D762491E321006A0DA1 /* HotFixDemoTests */, 148 | 814D5D812491E321006A0DA1 /* HotFixDemoUITests */, 149 | 814D5D592491E320006A0DA1 /* Products */, 150 | 77E7CF720AFE5C9D70B8C37A /* Pods */, 151 | 40DF1ED89FEF20CF6B801E8B /* Frameworks */, 152 | ); 153 | sourceTree = ""; 154 | }; 155 | 814D5D592491E320006A0DA1 /* Products */ = { 156 | isa = PBXGroup; 157 | children = ( 158 | 814D5D582491E320006A0DA1 /* HotFixDemo.app */, 159 | 814D5D732491E321006A0DA1 /* HotFixDemoTests.xctest */, 160 | 814D5D7E2491E321006A0DA1 /* HotFixDemoUITests.xctest */, 161 | ); 162 | name = Products; 163 | sourceTree = ""; 164 | }; 165 | 814D5D5A2491E320006A0DA1 /* HotFixDemo */ = { 166 | isa = PBXGroup; 167 | children = ( 168 | 814D5D912491E381006A0DA1 /* App */, 169 | 816019A424979E310014AC2D /* Class */, 170 | 8171550A26665F6200D26E6B /* VCCell.h */, 171 | 8171550926665F6200D26E6B /* VCCell.m */, 172 | 8171550826665F6200D26E6B /* VCModel.h */, 173 | 8171550B26665F6200D26E6B /* VCModel.m */, 174 | 814D5D612491E320006A0DA1 /* ViewController.h */, 175 | 814D5D622491E320006A0DA1 /* ViewController.m */, 176 | 8171551226665F6800D26E6B /* ViewController+DataSource.h */, 177 | 8171551126665F6800D26E6B /* ViewController+DataSource.m */, 178 | 814D5D642491E320006A0DA1 /* Main.storyboard */, 179 | 814D5D672491E321006A0DA1 /* Assets.xcassets */, 180 | 814D5D692491E321006A0DA1 /* LaunchScreen.storyboard */, 181 | 814D5D6C2491E321006A0DA1 /* Info.plist */, 182 | ); 183 | path = HotFixDemo; 184 | sourceTree = ""; 185 | }; 186 | 814D5D762491E321006A0DA1 /* HotFixDemoTests */ = { 187 | isa = PBXGroup; 188 | children = ( 189 | 814D5D772491E321006A0DA1 /* HotFixDemoTests.m */, 190 | 814D5D792491E321006A0DA1 /* Info.plist */, 191 | ); 192 | path = HotFixDemoTests; 193 | sourceTree = ""; 194 | }; 195 | 814D5D812491E321006A0DA1 /* HotFixDemoUITests */ = { 196 | isa = PBXGroup; 197 | children = ( 198 | 814D5D822491E321006A0DA1 /* HotFixDemoUITests.m */, 199 | 814D5D842491E321006A0DA1 /* Info.plist */, 200 | ); 201 | path = HotFixDemoUITests; 202 | sourceTree = ""; 203 | }; 204 | 814D5D912491E381006A0DA1 /* App */ = { 205 | isa = PBXGroup; 206 | children = ( 207 | 814D5D6D2491E321006A0DA1 /* main.m */, 208 | 814D5D5B2491E320006A0DA1 /* AppDelegate.h */, 209 | 814D5D5C2491E320006A0DA1 /* AppDelegate.m */, 210 | 814D5D5E2491E320006A0DA1 /* SceneDelegate.h */, 211 | 814D5D5F2491E320006A0DA1 /* SceneDelegate.m */, 212 | ); 213 | path = App; 214 | sourceTree = ""; 215 | }; 216 | 816019A424979E310014AC2D /* Class */ = { 217 | isa = PBXGroup; 218 | children = ( 219 | 816019AB2499BED20014AC2D /* OCDynamic.h */, 220 | 816019AC2499BED20014AC2D /* OCDynamic.m */, 221 | 816019A524979E420014AC2D /* MyClassA.h */, 222 | 816019A624979E420014AC2D /* MyClassA.m */, 223 | 816019A82498F16C0014AC2D /* MyClassB.h */, 224 | 816019A92498F16C0014AC2D /* MyClassB.m */, 225 | 816019AE2499D3DD0014AC2D /* MyClassC.h */, 226 | 816019AF2499D3DD0014AC2D /* MyClassC.m */, 227 | ); 228 | path = Class; 229 | sourceTree = ""; 230 | }; 231 | /* End PBXGroup section */ 232 | 233 | /* Begin PBXNativeTarget section */ 234 | 814D5D572491E320006A0DA1 /* HotFixDemo */ = { 235 | isa = PBXNativeTarget; 236 | buildConfigurationList = 814D5D872491E321006A0DA1 /* Build configuration list for PBXNativeTarget "HotFixDemo" */; 237 | buildPhases = ( 238 | 8B810CC56483C6A98B7AAE36 /* [CP] Check Pods Manifest.lock */, 239 | 814D5D542491E320006A0DA1 /* Sources */, 240 | 814D5D552491E320006A0DA1 /* Frameworks */, 241 | 814D5D562491E320006A0DA1 /* Resources */, 242 | ); 243 | buildRules = ( 244 | ); 245 | dependencies = ( 246 | ); 247 | name = HotFixDemo; 248 | productName = HotFixDemo; 249 | productReference = 814D5D582491E320006A0DA1 /* HotFixDemo.app */; 250 | productType = "com.apple.product-type.application"; 251 | }; 252 | 814D5D722491E321006A0DA1 /* HotFixDemoTests */ = { 253 | isa = PBXNativeTarget; 254 | buildConfigurationList = 814D5D8A2491E321006A0DA1 /* Build configuration list for PBXNativeTarget "HotFixDemoTests" */; 255 | buildPhases = ( 256 | FC786F11E3F6DBB315C62301 /* [CP] Check Pods Manifest.lock */, 257 | 814D5D6F2491E321006A0DA1 /* Sources */, 258 | 814D5D702491E321006A0DA1 /* Frameworks */, 259 | 814D5D712491E321006A0DA1 /* Resources */, 260 | ); 261 | buildRules = ( 262 | ); 263 | dependencies = ( 264 | 814D5D752491E321006A0DA1 /* PBXTargetDependency */, 265 | ); 266 | name = HotFixDemoTests; 267 | productName = HotFixDemoTests; 268 | productReference = 814D5D732491E321006A0DA1 /* HotFixDemoTests.xctest */; 269 | productType = "com.apple.product-type.bundle.unit-test"; 270 | }; 271 | 814D5D7D2491E321006A0DA1 /* HotFixDemoUITests */ = { 272 | isa = PBXNativeTarget; 273 | buildConfigurationList = 814D5D8D2491E321006A0DA1 /* Build configuration list for PBXNativeTarget "HotFixDemoUITests" */; 274 | buildPhases = ( 275 | E0DC341811D381D06172E7EA /* [CP] Check Pods Manifest.lock */, 276 | 814D5D7A2491E321006A0DA1 /* Sources */, 277 | 814D5D7B2491E321006A0DA1 /* Frameworks */, 278 | 814D5D7C2491E321006A0DA1 /* Resources */, 279 | ); 280 | buildRules = ( 281 | ); 282 | dependencies = ( 283 | 814D5D802491E321006A0DA1 /* PBXTargetDependency */, 284 | ); 285 | name = HotFixDemoUITests; 286 | productName = HotFixDemoUITests; 287 | productReference = 814D5D7E2491E321006A0DA1 /* HotFixDemoUITests.xctest */; 288 | productType = "com.apple.product-type.bundle.ui-testing"; 289 | }; 290 | /* End PBXNativeTarget section */ 291 | 292 | /* Begin PBXProject section */ 293 | 814D5D502491E320006A0DA1 /* Project object */ = { 294 | isa = PBXProject; 295 | attributes = { 296 | LastUpgradeCheck = 1140; 297 | ORGANIZATIONNAME = zzyong; 298 | TargetAttributes = { 299 | 814D5D572491E320006A0DA1 = { 300 | CreatedOnToolsVersion = 11.4.1; 301 | }; 302 | 814D5D722491E321006A0DA1 = { 303 | CreatedOnToolsVersion = 11.4.1; 304 | TestTargetID = 814D5D572491E320006A0DA1; 305 | }; 306 | 814D5D7D2491E321006A0DA1 = { 307 | CreatedOnToolsVersion = 11.4.1; 308 | TestTargetID = 814D5D572491E320006A0DA1; 309 | }; 310 | }; 311 | }; 312 | buildConfigurationList = 814D5D532491E320006A0DA1 /* Build configuration list for PBXProject "HotFixDemo" */; 313 | compatibilityVersion = "Xcode 9.3"; 314 | developmentRegion = en; 315 | hasScannedForEncodings = 0; 316 | knownRegions = ( 317 | en, 318 | Base, 319 | ); 320 | mainGroup = 814D5D4F2491E320006A0DA1; 321 | productRefGroup = 814D5D592491E320006A0DA1 /* Products */; 322 | projectDirPath = ""; 323 | projectRoot = ""; 324 | targets = ( 325 | 814D5D572491E320006A0DA1 /* HotFixDemo */, 326 | 814D5D722491E321006A0DA1 /* HotFixDemoTests */, 327 | 814D5D7D2491E321006A0DA1 /* HotFixDemoUITests */, 328 | ); 329 | }; 330 | /* End PBXProject section */ 331 | 332 | /* Begin PBXResourcesBuildPhase section */ 333 | 814D5D562491E320006A0DA1 /* Resources */ = { 334 | isa = PBXResourcesBuildPhase; 335 | buildActionMask = 2147483647; 336 | files = ( 337 | 814D5D6B2491E321006A0DA1 /* LaunchScreen.storyboard in Resources */, 338 | 814D5D682491E321006A0DA1 /* Assets.xcassets in Resources */, 339 | 814D5D662491E320006A0DA1 /* Main.storyboard in Resources */, 340 | ); 341 | runOnlyForDeploymentPostprocessing = 0; 342 | }; 343 | 814D5D712491E321006A0DA1 /* Resources */ = { 344 | isa = PBXResourcesBuildPhase; 345 | buildActionMask = 2147483647; 346 | files = ( 347 | ); 348 | runOnlyForDeploymentPostprocessing = 0; 349 | }; 350 | 814D5D7C2491E321006A0DA1 /* Resources */ = { 351 | isa = PBXResourcesBuildPhase; 352 | buildActionMask = 2147483647; 353 | files = ( 354 | ); 355 | runOnlyForDeploymentPostprocessing = 0; 356 | }; 357 | /* End PBXResourcesBuildPhase section */ 358 | 359 | /* Begin PBXShellScriptBuildPhase section */ 360 | 8B810CC56483C6A98B7AAE36 /* [CP] Check Pods Manifest.lock */ = { 361 | isa = PBXShellScriptBuildPhase; 362 | buildActionMask = 2147483647; 363 | files = ( 364 | ); 365 | inputFileListPaths = ( 366 | ); 367 | inputPaths = ( 368 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 369 | "${PODS_ROOT}/Manifest.lock", 370 | ); 371 | name = "[CP] Check Pods Manifest.lock"; 372 | outputFileListPaths = ( 373 | ); 374 | outputPaths = ( 375 | "$(DERIVED_FILE_DIR)/Pods-HotFixDemo-checkManifestLockResult.txt", 376 | ); 377 | runOnlyForDeploymentPostprocessing = 0; 378 | shellPath = /bin/sh; 379 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 380 | showEnvVarsInLog = 0; 381 | }; 382 | E0DC341811D381D06172E7EA /* [CP] Check Pods Manifest.lock */ = { 383 | isa = PBXShellScriptBuildPhase; 384 | buildActionMask = 2147483647; 385 | files = ( 386 | ); 387 | inputFileListPaths = ( 388 | ); 389 | inputPaths = ( 390 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 391 | "${PODS_ROOT}/Manifest.lock", 392 | ); 393 | name = "[CP] Check Pods Manifest.lock"; 394 | outputFileListPaths = ( 395 | ); 396 | outputPaths = ( 397 | "$(DERIVED_FILE_DIR)/Pods-HotFixDemo-HotFixDemoUITests-checkManifestLockResult.txt", 398 | ); 399 | runOnlyForDeploymentPostprocessing = 0; 400 | shellPath = /bin/sh; 401 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 402 | showEnvVarsInLog = 0; 403 | }; 404 | FC786F11E3F6DBB315C62301 /* [CP] Check Pods Manifest.lock */ = { 405 | isa = PBXShellScriptBuildPhase; 406 | buildActionMask = 2147483647; 407 | files = ( 408 | ); 409 | inputFileListPaths = ( 410 | ); 411 | inputPaths = ( 412 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 413 | "${PODS_ROOT}/Manifest.lock", 414 | ); 415 | name = "[CP] Check Pods Manifest.lock"; 416 | outputFileListPaths = ( 417 | ); 418 | outputPaths = ( 419 | "$(DERIVED_FILE_DIR)/Pods-HotFixDemoTests-checkManifestLockResult.txt", 420 | ); 421 | runOnlyForDeploymentPostprocessing = 0; 422 | shellPath = /bin/sh; 423 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 424 | showEnvVarsInLog = 0; 425 | }; 426 | /* End PBXShellScriptBuildPhase section */ 427 | 428 | /* Begin PBXSourcesBuildPhase section */ 429 | 814D5D542491E320006A0DA1 /* Sources */ = { 430 | isa = PBXSourcesBuildPhase; 431 | buildActionMask = 2147483647; 432 | files = ( 433 | 814D5D632491E320006A0DA1 /* ViewController.m in Sources */, 434 | 814D5D5D2491E320006A0DA1 /* AppDelegate.m in Sources */, 435 | 814D5D6E2491E321006A0DA1 /* main.m in Sources */, 436 | 816019AA2498F16C0014AC2D /* MyClassB.m in Sources */, 437 | 8171550D26665F6200D26E6B /* VCModel.m in Sources */, 438 | 816019B02499D3DD0014AC2D /* MyClassC.m in Sources */, 439 | 8171550C26665F6200D26E6B /* VCCell.m in Sources */, 440 | 816019AD2499BED20014AC2D /* OCDynamic.m in Sources */, 441 | 8171551326665F6800D26E6B /* ViewController+DataSource.m in Sources */, 442 | 814D5D602491E320006A0DA1 /* SceneDelegate.m in Sources */, 443 | 816019A724979E420014AC2D /* MyClassA.m in Sources */, 444 | ); 445 | runOnlyForDeploymentPostprocessing = 0; 446 | }; 447 | 814D5D6F2491E321006A0DA1 /* Sources */ = { 448 | isa = PBXSourcesBuildPhase; 449 | buildActionMask = 2147483647; 450 | files = ( 451 | 814D5D782491E321006A0DA1 /* HotFixDemoTests.m in Sources */, 452 | ); 453 | runOnlyForDeploymentPostprocessing = 0; 454 | }; 455 | 814D5D7A2491E321006A0DA1 /* Sources */ = { 456 | isa = PBXSourcesBuildPhase; 457 | buildActionMask = 2147483647; 458 | files = ( 459 | 814D5D832491E321006A0DA1 /* HotFixDemoUITests.m in Sources */, 460 | ); 461 | runOnlyForDeploymentPostprocessing = 0; 462 | }; 463 | /* End PBXSourcesBuildPhase section */ 464 | 465 | /* Begin PBXTargetDependency section */ 466 | 814D5D752491E321006A0DA1 /* PBXTargetDependency */ = { 467 | isa = PBXTargetDependency; 468 | target = 814D5D572491E320006A0DA1 /* HotFixDemo */; 469 | targetProxy = 814D5D742491E321006A0DA1 /* PBXContainerItemProxy */; 470 | }; 471 | 814D5D802491E321006A0DA1 /* PBXTargetDependency */ = { 472 | isa = PBXTargetDependency; 473 | target = 814D5D572491E320006A0DA1 /* HotFixDemo */; 474 | targetProxy = 814D5D7F2491E321006A0DA1 /* PBXContainerItemProxy */; 475 | }; 476 | /* End PBXTargetDependency section */ 477 | 478 | /* Begin PBXVariantGroup section */ 479 | 814D5D642491E320006A0DA1 /* Main.storyboard */ = { 480 | isa = PBXVariantGroup; 481 | children = ( 482 | 814D5D652491E320006A0DA1 /* Base */, 483 | ); 484 | name = Main.storyboard; 485 | sourceTree = ""; 486 | }; 487 | 814D5D692491E321006A0DA1 /* LaunchScreen.storyboard */ = { 488 | isa = PBXVariantGroup; 489 | children = ( 490 | 814D5D6A2491E321006A0DA1 /* Base */, 491 | ); 492 | name = LaunchScreen.storyboard; 493 | sourceTree = ""; 494 | }; 495 | /* End PBXVariantGroup section */ 496 | 497 | /* Begin XCBuildConfiguration section */ 498 | 814D5D852491E321006A0DA1 /* Debug */ = { 499 | isa = XCBuildConfiguration; 500 | buildSettings = { 501 | ALWAYS_SEARCH_USER_PATHS = NO; 502 | CLANG_ANALYZER_NONNULL = YES; 503 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 504 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 505 | CLANG_CXX_LIBRARY = "libc++"; 506 | CLANG_ENABLE_MODULES = YES; 507 | CLANG_ENABLE_OBJC_ARC = YES; 508 | CLANG_ENABLE_OBJC_WEAK = YES; 509 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 510 | CLANG_WARN_BOOL_CONVERSION = YES; 511 | CLANG_WARN_COMMA = YES; 512 | CLANG_WARN_CONSTANT_CONVERSION = YES; 513 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 514 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 515 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 516 | CLANG_WARN_EMPTY_BODY = YES; 517 | CLANG_WARN_ENUM_CONVERSION = YES; 518 | CLANG_WARN_INFINITE_RECURSION = YES; 519 | CLANG_WARN_INT_CONVERSION = YES; 520 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 521 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 522 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 523 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 524 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 525 | CLANG_WARN_STRICT_PROTOTYPES = YES; 526 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 527 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 528 | CLANG_WARN_UNREACHABLE_CODE = YES; 529 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 530 | COPY_PHASE_STRIP = NO; 531 | DEBUG_INFORMATION_FORMAT = dwarf; 532 | ENABLE_STRICT_OBJC_MSGSEND = YES; 533 | ENABLE_TESTABILITY = YES; 534 | GCC_C_LANGUAGE_STANDARD = gnu11; 535 | GCC_DYNAMIC_NO_PIC = NO; 536 | GCC_NO_COMMON_BLOCKS = YES; 537 | GCC_OPTIMIZATION_LEVEL = 0; 538 | GCC_PREPROCESSOR_DEFINITIONS = ( 539 | "DEBUG=1", 540 | "$(inherited)", 541 | ); 542 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 543 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 544 | GCC_WARN_UNDECLARED_SELECTOR = YES; 545 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 546 | GCC_WARN_UNUSED_FUNCTION = YES; 547 | GCC_WARN_UNUSED_VARIABLE = YES; 548 | IPHONEOS_DEPLOYMENT_TARGET = 13.0; 549 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 550 | MTL_FAST_MATH = YES; 551 | ONLY_ACTIVE_ARCH = YES; 552 | SDKROOT = iphoneos; 553 | }; 554 | name = Debug; 555 | }; 556 | 814D5D862491E321006A0DA1 /* Release */ = { 557 | isa = XCBuildConfiguration; 558 | buildSettings = { 559 | ALWAYS_SEARCH_USER_PATHS = NO; 560 | CLANG_ANALYZER_NONNULL = YES; 561 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 562 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 563 | CLANG_CXX_LIBRARY = "libc++"; 564 | CLANG_ENABLE_MODULES = YES; 565 | CLANG_ENABLE_OBJC_ARC = YES; 566 | CLANG_ENABLE_OBJC_WEAK = YES; 567 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 568 | CLANG_WARN_BOOL_CONVERSION = YES; 569 | CLANG_WARN_COMMA = YES; 570 | CLANG_WARN_CONSTANT_CONVERSION = YES; 571 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 572 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 573 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 574 | CLANG_WARN_EMPTY_BODY = YES; 575 | CLANG_WARN_ENUM_CONVERSION = YES; 576 | CLANG_WARN_INFINITE_RECURSION = YES; 577 | CLANG_WARN_INT_CONVERSION = YES; 578 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 579 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 580 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 581 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 582 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 583 | CLANG_WARN_STRICT_PROTOTYPES = YES; 584 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 585 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 586 | CLANG_WARN_UNREACHABLE_CODE = YES; 587 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 588 | COPY_PHASE_STRIP = NO; 589 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 590 | ENABLE_NS_ASSERTIONS = NO; 591 | ENABLE_STRICT_OBJC_MSGSEND = YES; 592 | GCC_C_LANGUAGE_STANDARD = gnu11; 593 | GCC_NO_COMMON_BLOCKS = YES; 594 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 595 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 596 | GCC_WARN_UNDECLARED_SELECTOR = YES; 597 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 598 | GCC_WARN_UNUSED_FUNCTION = YES; 599 | GCC_WARN_UNUSED_VARIABLE = YES; 600 | IPHONEOS_DEPLOYMENT_TARGET = 13.0; 601 | MTL_ENABLE_DEBUG_INFO = NO; 602 | MTL_FAST_MATH = YES; 603 | SDKROOT = iphoneos; 604 | VALIDATE_PRODUCT = YES; 605 | }; 606 | name = Release; 607 | }; 608 | 814D5D882491E321006A0DA1 /* Debug */ = { 609 | isa = XCBuildConfiguration; 610 | baseConfigurationReference = 56E701A87A75E873A7A555B7 /* Pods-HotFixDemo.debug.xcconfig */; 611 | buildSettings = { 612 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 613 | CODE_SIGN_STYLE = Automatic; 614 | INFOPLIST_FILE = HotFixDemo/Info.plist; 615 | IPHONEOS_DEPLOYMENT_TARGET = 13.0; 616 | LD_RUNPATH_SEARCH_PATHS = ( 617 | "$(inherited)", 618 | "@executable_path/Frameworks", 619 | ); 620 | PRODUCT_BUNDLE_IDENTIFIER = zzyong.HotFixDemo; 621 | PRODUCT_NAME = "$(TARGET_NAME)"; 622 | TARGETED_DEVICE_FAMILY = "1,2"; 623 | }; 624 | name = Debug; 625 | }; 626 | 814D5D892491E321006A0DA1 /* Release */ = { 627 | isa = XCBuildConfiguration; 628 | baseConfigurationReference = 31DF8DB01CB7C67AAE6E38E1 /* Pods-HotFixDemo.release.xcconfig */; 629 | buildSettings = { 630 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 631 | CODE_SIGN_STYLE = Automatic; 632 | INFOPLIST_FILE = HotFixDemo/Info.plist; 633 | IPHONEOS_DEPLOYMENT_TARGET = 13.0; 634 | LD_RUNPATH_SEARCH_PATHS = ( 635 | "$(inherited)", 636 | "@executable_path/Frameworks", 637 | ); 638 | PRODUCT_BUNDLE_IDENTIFIER = zzyong.HotFixDemo; 639 | PRODUCT_NAME = "$(TARGET_NAME)"; 640 | TARGETED_DEVICE_FAMILY = "1,2"; 641 | }; 642 | name = Release; 643 | }; 644 | 814D5D8B2491E321006A0DA1 /* Debug */ = { 645 | isa = XCBuildConfiguration; 646 | baseConfigurationReference = 25514866ACE6ED6B818AE4B9 /* Pods-HotFixDemoTests.debug.xcconfig */; 647 | buildSettings = { 648 | BUNDLE_LOADER = "$(TEST_HOST)"; 649 | CODE_SIGN_STYLE = Automatic; 650 | INFOPLIST_FILE = HotFixDemoTests/Info.plist; 651 | IPHONEOS_DEPLOYMENT_TARGET = 13.4; 652 | LD_RUNPATH_SEARCH_PATHS = ( 653 | "$(inherited)", 654 | "@executable_path/Frameworks", 655 | "@loader_path/Frameworks", 656 | ); 657 | PRODUCT_BUNDLE_IDENTIFIER = zzyong.HotFixDemoTests; 658 | PRODUCT_NAME = "$(TARGET_NAME)"; 659 | TARGETED_DEVICE_FAMILY = "1,2"; 660 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/HotFixDemo.app/HotFixDemo"; 661 | }; 662 | name = Debug; 663 | }; 664 | 814D5D8C2491E321006A0DA1 /* Release */ = { 665 | isa = XCBuildConfiguration; 666 | baseConfigurationReference = CBAC2E4952F2BCF9E58B6F4B /* Pods-HotFixDemoTests.release.xcconfig */; 667 | buildSettings = { 668 | BUNDLE_LOADER = "$(TEST_HOST)"; 669 | CODE_SIGN_STYLE = Automatic; 670 | INFOPLIST_FILE = HotFixDemoTests/Info.plist; 671 | IPHONEOS_DEPLOYMENT_TARGET = 13.4; 672 | LD_RUNPATH_SEARCH_PATHS = ( 673 | "$(inherited)", 674 | "@executable_path/Frameworks", 675 | "@loader_path/Frameworks", 676 | ); 677 | PRODUCT_BUNDLE_IDENTIFIER = zzyong.HotFixDemoTests; 678 | PRODUCT_NAME = "$(TARGET_NAME)"; 679 | TARGETED_DEVICE_FAMILY = "1,2"; 680 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/HotFixDemo.app/HotFixDemo"; 681 | }; 682 | name = Release; 683 | }; 684 | 814D5D8E2491E321006A0DA1 /* Debug */ = { 685 | isa = XCBuildConfiguration; 686 | baseConfigurationReference = B741A181A9680FADC7B98CC8 /* Pods-HotFixDemo-HotFixDemoUITests.debug.xcconfig */; 687 | buildSettings = { 688 | CODE_SIGN_STYLE = Automatic; 689 | INFOPLIST_FILE = HotFixDemoUITests/Info.plist; 690 | LD_RUNPATH_SEARCH_PATHS = ( 691 | "$(inherited)", 692 | "@executable_path/Frameworks", 693 | "@loader_path/Frameworks", 694 | ); 695 | PRODUCT_BUNDLE_IDENTIFIER = zzyong.HotFixDemoUITests; 696 | PRODUCT_NAME = "$(TARGET_NAME)"; 697 | TARGETED_DEVICE_FAMILY = "1,2"; 698 | TEST_TARGET_NAME = HotFixDemo; 699 | }; 700 | name = Debug; 701 | }; 702 | 814D5D8F2491E321006A0DA1 /* Release */ = { 703 | isa = XCBuildConfiguration; 704 | baseConfigurationReference = EEB7BC2A436401170265BC94 /* Pods-HotFixDemo-HotFixDemoUITests.release.xcconfig */; 705 | buildSettings = { 706 | CODE_SIGN_STYLE = Automatic; 707 | INFOPLIST_FILE = HotFixDemoUITests/Info.plist; 708 | LD_RUNPATH_SEARCH_PATHS = ( 709 | "$(inherited)", 710 | "@executable_path/Frameworks", 711 | "@loader_path/Frameworks", 712 | ); 713 | PRODUCT_BUNDLE_IDENTIFIER = zzyong.HotFixDemoUITests; 714 | PRODUCT_NAME = "$(TARGET_NAME)"; 715 | TARGETED_DEVICE_FAMILY = "1,2"; 716 | TEST_TARGET_NAME = HotFixDemo; 717 | }; 718 | name = Release; 719 | }; 720 | /* End XCBuildConfiguration section */ 721 | 722 | /* Begin XCConfigurationList section */ 723 | 814D5D532491E320006A0DA1 /* Build configuration list for PBXProject "HotFixDemo" */ = { 724 | isa = XCConfigurationList; 725 | buildConfigurations = ( 726 | 814D5D852491E321006A0DA1 /* Debug */, 727 | 814D5D862491E321006A0DA1 /* Release */, 728 | ); 729 | defaultConfigurationIsVisible = 0; 730 | defaultConfigurationName = Release; 731 | }; 732 | 814D5D872491E321006A0DA1 /* Build configuration list for PBXNativeTarget "HotFixDemo" */ = { 733 | isa = XCConfigurationList; 734 | buildConfigurations = ( 735 | 814D5D882491E321006A0DA1 /* Debug */, 736 | 814D5D892491E321006A0DA1 /* Release */, 737 | ); 738 | defaultConfigurationIsVisible = 0; 739 | defaultConfigurationName = Release; 740 | }; 741 | 814D5D8A2491E321006A0DA1 /* Build configuration list for PBXNativeTarget "HotFixDemoTests" */ = { 742 | isa = XCConfigurationList; 743 | buildConfigurations = ( 744 | 814D5D8B2491E321006A0DA1 /* Debug */, 745 | 814D5D8C2491E321006A0DA1 /* Release */, 746 | ); 747 | defaultConfigurationIsVisible = 0; 748 | defaultConfigurationName = Release; 749 | }; 750 | 814D5D8D2491E321006A0DA1 /* Build configuration list for PBXNativeTarget "HotFixDemoUITests" */ = { 751 | isa = XCConfigurationList; 752 | buildConfigurations = ( 753 | 814D5D8E2491E321006A0DA1 /* Debug */, 754 | 814D5D8F2491E321006A0DA1 /* Release */, 755 | ); 756 | defaultConfigurationIsVisible = 0; 757 | defaultConfigurationName = Release; 758 | }; 759 | /* End XCConfigurationList section */ 760 | }; 761 | rootObject = 814D5D502491E320006A0DA1 /* Project object */; 762 | } 763 | -------------------------------------------------------------------------------- /HotFixDemo/HotFixDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /HotFixDemo/HotFixDemo.xcodeproj/xcshareddata/xcschemes/HotFixDemo.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 43 | 49 | 50 | 51 | 52 | 53 | 64 | 66 | 72 | 73 | 74 | 75 | 79 | 80 | 81 | 82 | 88 | 90 | 96 | 97 | 98 | 99 | 101 | 102 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /HotFixDemo/HotFixDemo.xcodeproj/xcuserdata/zzyong.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 9 | 16 | 17 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /HotFixDemo/HotFixDemo.xcodeproj/xcuserdata/zzyong.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | HotFixDemo.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 4 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 814D5D572491E320006A0DA1 16 | 17 | primary 18 | 19 | 20 | 814D5D722491E321006A0DA1 21 | 22 | primary 23 | 24 | 25 | 814D5D7D2491E321006A0DA1 26 | 27 | primary 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /HotFixDemo/HotFixDemo.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /HotFixDemo/HotFixDemo.xcworkspace/xcuserdata/zzyong.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /HotFixDemo/HotFixDemo/App/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // HotFixDemo 4 | // 5 | // Created by zzyong on 2020/6/11. 6 | // Copyright © 2020 zzyong. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /HotFixDemo/HotFixDemo/App/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // HotFixDemo 4 | // 5 | // Created by zzyong on 2020/6/11. 6 | // Copyright © 2020 zzyong. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 18 | return YES; 19 | } 20 | 21 | #pragma mark - UISceneSession lifecycle 22 | 23 | - (UISceneConfiguration *)application:(UIApplication *)application configurationForConnectingSceneSession:(UISceneSession *)connectingSceneSession options:(UISceneConnectionOptions *)options { 24 | // Called when a new scene session is being created. 25 | // Use this method to select a configuration to create the new scene with. 26 | return [[UISceneConfiguration alloc] initWithName:@"Default Configuration" sessionRole:connectingSceneSession.role]; 27 | } 28 | 29 | 30 | - (void)application:(UIApplication *)application didDiscardSceneSessions:(NSSet *)sceneSessions { 31 | // Called when the user discards a scene session. 32 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. 33 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return. 34 | } 35 | 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /HotFixDemo/HotFixDemo/App/SceneDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.h 3 | // HotFixDemo 4 | // 5 | // Created by zzyong on 2020/6/11. 6 | // Copyright © 2020 zzyong. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SceneDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow * window; 14 | 15 | @end 16 | 17 | -------------------------------------------------------------------------------- /HotFixDemo/HotFixDemo/App/SceneDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.m 3 | // HotFixDemo 4 | // 5 | // Created by zzyong on 2020/6/11. 6 | // Copyright © 2020 zzyong. All rights reserved. 7 | // 8 | 9 | #import "SceneDelegate.h" 10 | 11 | @interface SceneDelegate () 12 | 13 | @end 14 | 15 | @implementation SceneDelegate 16 | 17 | 18 | - (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions { 19 | // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. 20 | // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. 21 | // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). 22 | } 23 | 24 | 25 | - (void)sceneDidDisconnect:(UIScene *)scene { 26 | // Called as the scene is being released by the system. 27 | // This occurs shortly after the scene enters the background, or when its session is discarded. 28 | // Release any resources associated with this scene that can be re-created the next time the scene connects. 29 | // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead). 30 | } 31 | 32 | 33 | - (void)sceneDidBecomeActive:(UIScene *)scene { 34 | // Called when the scene has moved from an inactive state to an active state. 35 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. 36 | } 37 | 38 | 39 | - (void)sceneWillResignActive:(UIScene *)scene { 40 | // Called when the scene will move from an active state to an inactive state. 41 | // This may occur due to temporary interruptions (ex. an incoming phone call). 42 | } 43 | 44 | 45 | - (void)sceneWillEnterForeground:(UIScene *)scene { 46 | // Called as the scene transitions from the background to the foreground. 47 | // Use this method to undo the changes made on entering the background. 48 | } 49 | 50 | 51 | - (void)sceneDidEnterBackground:(UIScene *)scene { 52 | // Called as the scene transitions from the foreground to the background. 53 | // Use this method to save data, release shared resources, and store enough scene-specific state information 54 | // to restore the scene back to its current state. 55 | } 56 | 57 | 58 | @end 59 | -------------------------------------------------------------------------------- /HotFixDemo/HotFixDemo/App/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // HotFixDemo 4 | // 5 | // Created by zzyong on 2020/6/11. 6 | // Copyright © 2020 zzyong. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | NSString * appDelegateClassName; 14 | @autoreleasepool { 15 | // Setup code that might create autoreleased objects goes here. 16 | appDelegateClassName = NSStringFromClass([AppDelegate class]); 17 | } 18 | return UIApplicationMain(argc, argv, nil, appDelegateClassName); 19 | } 20 | -------------------------------------------------------------------------------- /HotFixDemo/HotFixDemo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "scale" : "1x", 46 | "size" : "20x20" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "scale" : "2x", 51 | "size" : "20x20" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "scale" : "1x", 56 | "size" : "29x29" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "29x29" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "scale" : "1x", 66 | "size" : "40x40" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "scale" : "2x", 71 | "size" : "40x40" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "scale" : "1x", 76 | "size" : "76x76" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "scale" : "2x", 81 | "size" : "76x76" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "scale" : "2x", 86 | "size" : "83.5x83.5" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "scale" : "1x", 91 | "size" : "1024x1024" 92 | } 93 | ], 94 | "info" : { 95 | "author" : "xcode", 96 | "version" : 1 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /HotFixDemo/HotFixDemo/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /HotFixDemo/HotFixDemo/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 | -------------------------------------------------------------------------------- /HotFixDemo/HotFixDemo/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 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /HotFixDemo/HotFixDemo/Class/MyClassA.h: -------------------------------------------------------------------------------- 1 | // 2 | // MyClassA.h 3 | // HotFixDemo 4 | // 5 | // Created by zzyong on 2020/6/15. 6 | // Copyright © 2020 zzyong. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface MyClassA : NSObject 14 | 15 | @end 16 | 17 | NS_ASSUME_NONNULL_END 18 | -------------------------------------------------------------------------------- /HotFixDemo/HotFixDemo/Class/MyClassA.m: -------------------------------------------------------------------------------- 1 | // 2 | // MyClassA.m 3 | // HotFixDemo 4 | // 5 | // Created by zzyong on 2020/6/15. 6 | // Copyright © 2020 zzyong. All rights reserved. 7 | // 8 | 9 | #import "MyClassA.h" 10 | 11 | @implementation MyClassA 12 | 13 | - (void)commonMethod 14 | { 15 | NSLog(@"%s", __func__); 16 | } 17 | 18 | + (BOOL)resolveClassMethod:(SEL)sel 19 | { 20 | BOOL isResolve = [super resolveClassMethod:sel]; 21 | NSLog(@"%s %d", __func__, isResolve); 22 | return isResolve; 23 | } 24 | 25 | + (BOOL)resolveInstanceMethod:(SEL)sel 26 | { 27 | BOOL isResolve = [super resolveInstanceMethod:sel]; 28 | NSLog(@"%s %d", __func__, isResolve); 29 | return isResolve; 30 | } 31 | 32 | - (id)forwardingTargetForSelector:(SEL)aSelector 33 | { 34 | id target = [super forwardingTargetForSelector:aSelector]; 35 | NSLog(@"%s %@", __func__, target); 36 | return target; 37 | } 38 | 39 | - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector 40 | { 41 | NSMethodSignature *signature = [super methodSignatureForSelector:aSelector]; 42 | NSLog(@"%s %@", __func__, signature); 43 | 44 | if (signature == nil) { 45 | signature = [self methodSignatureForSelector:@selector(commonMethod)]; 46 | } 47 | 48 | return signature; 49 | } 50 | 51 | - (void)doesNotRecognizeSelector:(SEL)aSelector 52 | { 53 | NSLog(@"%s %@", __func__, NSStringFromSelector(aSelector)); 54 | } 55 | 56 | - (void)forwardInvocation:(NSInvocation *)anInvocation 57 | { 58 | NSLog(@"%s %@", __func__, anInvocation); 59 | anInvocation.selector = @selector(commonMethod); 60 | [anInvocation invoke]; 61 | } 62 | 63 | @end 64 | -------------------------------------------------------------------------------- /HotFixDemo/HotFixDemo/Class/MyClassB.h: -------------------------------------------------------------------------------- 1 | // 2 | // MyClassB.h 3 | // HotFixDemo 4 | // 5 | // Created by zzyong on 2020/6/16. 6 | // Copyright © 2020 zzyong. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface MyClassB : NSObject 14 | 15 | 16 | 17 | @end 18 | 19 | NS_ASSUME_NONNULL_END 20 | -------------------------------------------------------------------------------- /HotFixDemo/HotFixDemo/Class/MyClassB.m: -------------------------------------------------------------------------------- 1 | // 2 | // MyClassB.m 3 | // HotFixDemo 4 | // 5 | // Created by zzyong on 2020/6/16. 6 | // Copyright © 2020 zzyong. All rights reserved. 7 | // 8 | 9 | #import "MyClassB.h" 10 | 11 | @implementation MyClassB 12 | 13 | 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /HotFixDemo/HotFixDemo/Class/MyClassC.h: -------------------------------------------------------------------------------- 1 | // 2 | // MyClassC.h 3 | // HotFixDemo 4 | // 5 | // Created by zzyong on 2020/6/17. 6 | // Copyright © 2020 zzyong. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface MyClassC : NSObject 14 | 15 | - (void)sayHelloTo:(NSString *)name; 16 | 17 | - (NSString *)className; 18 | 19 | - (void)myMethod; 20 | 21 | - (void)dynamicCallMethod; 22 | 23 | @end 24 | 25 | NS_ASSUME_NONNULL_END 26 | -------------------------------------------------------------------------------- /HotFixDemo/HotFixDemo/Class/MyClassC.m: -------------------------------------------------------------------------------- 1 | // 2 | // MyClassC.m 3 | // HotFixDemo 4 | // 5 | // Created by zzyong on 2020/6/17. 6 | // Copyright © 2020 zzyong. All rights reserved. 7 | // 8 | 9 | #import "MyClassC.h" 10 | 11 | @implementation MyClassC 12 | 13 | - (void)sayHelloTo:(NSString *)name 14 | { 15 | NSLog(@"%s: %@", __func__, name); 16 | } 17 | 18 | - (NSString *)className 19 | { 20 | return @"MyClassC"; 21 | } 22 | 23 | - (void)myMethod 24 | { 25 | NSLog(@"%s", __func__); 26 | } 27 | 28 | - (void)dynamicCallMethod { 29 | NSLog(@"%s Dynamic call", __func__); 30 | } 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /HotFixDemo/HotFixDemo/Class/OCDynamic.h: -------------------------------------------------------------------------------- 1 | // 2 | // OCDynamic.h 3 | // HotFixDemo 4 | // 5 | // Created by zzyong on 2020/6/17. 6 | // Copyright © 2020 zzyong. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface NSObject (OCDynamic) 14 | 15 | + (void)dy_hookSelector:(SEL)selector withBlock:(void(^)(id self, NSInvocation *originalInvocation))block; 16 | 17 | @end 18 | 19 | NS_ASSUME_NONNULL_END 20 | -------------------------------------------------------------------------------- /HotFixDemo/HotFixDemo/Class/OCDynamic.m: -------------------------------------------------------------------------------- 1 | // 2 | // OCDynamic.m 3 | // HotFixDemo 4 | // 5 | // Created by zzyong on 2020/6/17. 6 | // Copyright © 2020 zzyong. All rights reserved. 7 | // 8 | 9 | #import "OCDynamic.h" 10 | #import 11 | #import 12 | 13 | typedef void(^OCDynamicBlock)(id self, NSInvocation *originalInvocation); 14 | 15 | @implementation NSObject (OCDynamic) 16 | 17 | + (void)dy_hookSelector:(SEL)selector withBlock:(void(^)(id self, NSInvocation *originalInvocation))block 18 | { 19 | // 保存回调 block 20 | [dynamicBlockMap() setObject:block forKey:NSStringFromSelector(selector)]; 21 | 22 | // 1.获取目标方法的 IMP 23 | Method targetMethod = class_getInstanceMethod(self, selector); 24 | IMP targetMethodIMP = method_getImplementation(targetMethod); 25 | 26 | // 2.新增一个目标方法的别名方法 27 | NSString *aliasSelString = [NSString stringWithFormat:@"oc_dynamic_%@", NSStringFromSelector(selector)]; 28 | const char *typeEncoding = method_getTypeEncoding(targetMethod); 29 | BOOL isSuccessed = class_addMethod(self, NSSelectorFromString(aliasSelString), targetMethodIMP, typeEncoding); 30 | NSLog(@"%@ add method successfully: %d", aliasSelString, isSuccessed); 31 | 32 | // 3.将目标方法实现替换成 _objc_msgForward 33 | class_replaceMethod(self, selector, (IMP)_objc_msgForward, typeEncoding); 34 | 35 | // 4.将目标类的 forwardInvocation 替换为自定义 dy_forwardInvocation_center 36 | class_replaceMethod(self, @selector(forwardInvocation:), (IMP)dy_forwardInvocation_center, "v@:@"); 37 | } 38 | 39 | static NSMutableDictionary* dynamicBlockMap(void) 40 | { 41 | static NSMutableDictionary *_dynamicBlockMap; 42 | static dispatch_once_t onceToken; 43 | dispatch_once(&onceToken, ^{ 44 | _dynamicBlockMap = [NSMutableDictionary dictionary]; 45 | }); 46 | 47 | return _dynamicBlockMap; 48 | } 49 | 50 | static void dy_forwardInvocation_center(id self, SEL _cmd, NSInvocation *anInvocation) 51 | { 52 | // 获取回调 block 53 | OCDynamicBlock targetBlock = [dynamicBlockMap() objectForKey:NSStringFromSelector(anInvocation.selector)]; 54 | 55 | // 将 anInvocation 的 sel 设置为别名 sel 56 | NSString *aliasSelString = [NSString stringWithFormat:@"oc_dynamic_%@", NSStringFromSelector(anInvocation.selector)]; 57 | anInvocation.selector = NSSelectorFromString(aliasSelString); 58 | 59 | // 调用回调 block 60 | targetBlock(self, anInvocation); 61 | } 62 | 63 | @end 64 | -------------------------------------------------------------------------------- /HotFixDemo/HotFixDemo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UIApplicationSceneManifest 24 | 25 | UIApplicationSupportsMultipleScenes 26 | 27 | UISceneConfigurations 28 | 29 | UIWindowSceneSessionRoleApplication 30 | 31 | 32 | UISceneConfigurationName 33 | Default Configuration 34 | UISceneDelegateClassName 35 | SceneDelegate 36 | UISceneStoryboardFile 37 | Main 38 | 39 | 40 | 41 | 42 | UILaunchStoryboardName 43 | LaunchScreen 44 | UIMainStoryboardFile 45 | Main 46 | UIRequiredDeviceCapabilities 47 | 48 | armv7 49 | 50 | UIStatusBarHidden 51 | 52 | UISupportedInterfaceOrientations 53 | 54 | UIInterfaceOrientationPortrait 55 | UIInterfaceOrientationLandscapeLeft 56 | UIInterfaceOrientationLandscapeRight 57 | 58 | UISupportedInterfaceOrientations~ipad 59 | 60 | UIInterfaceOrientationPortrait 61 | UIInterfaceOrientationPortraitUpsideDown 62 | UIInterfaceOrientationLandscapeLeft 63 | UIInterfaceOrientationLandscapeRight 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /HotFixDemo/HotFixDemo/VCCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // VCCell.h 3 | // CommonTest 4 | // 5 | // Created by zzyong on 2021/3/22. 6 | // 7 | 8 | #import 9 | 10 | NS_ASSUME_NONNULL_BEGIN 11 | 12 | @class VCModel; 13 | 14 | @interface VCCell : UITableViewCell 15 | 16 | @property (nonatomic, strong) VCModel *model; 17 | 18 | @end 19 | 20 | NS_ASSUME_NONNULL_END 21 | -------------------------------------------------------------------------------- /HotFixDemo/HotFixDemo/VCCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // VCCell.m 3 | // HotFixDemo 4 | // 5 | // Created by zzyong on 2021/3/22. 6 | // 7 | 8 | #import "VCCell.h" 9 | #import "VCModel.h" 10 | 11 | @implementation VCCell 12 | 13 | - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { 14 | if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) { 15 | self.textLabel.adjustsFontSizeToFitWidth = YES; 16 | } 17 | return self; 18 | } 19 | 20 | - (void)setModel:(VCModel *)model { 21 | _model = model; 22 | 23 | self.textLabel.text = model.title; 24 | } 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /HotFixDemo/HotFixDemo/VCModel.h: -------------------------------------------------------------------------------- 1 | // 2 | // VCModel.h 3 | // CommonTest 4 | // 5 | // Created by zzyong on 2021/3/22. 6 | // 7 | 8 | #import 9 | 10 | NS_ASSUME_NONNULL_BEGIN 11 | 12 | typedef void (^ClickAction)(void); 13 | 14 | @interface VCModel : NSObject 15 | 16 | @property (nonatomic, strong) NSString *title; 17 | @property (nonatomic, strong) ClickAction action; 18 | 19 | @end 20 | 21 | NS_ASSUME_NONNULL_END 22 | -------------------------------------------------------------------------------- /HotFixDemo/HotFixDemo/VCModel.m: -------------------------------------------------------------------------------- 1 | // 2 | // VCModel.m 3 | // HotFixDemo 4 | // 5 | // Created by zzyong on 2021/3/22. 6 | // 7 | 8 | #import "VCModel.h" 9 | 10 | @implementation VCModel 11 | 12 | @end 13 | -------------------------------------------------------------------------------- /HotFixDemo/HotFixDemo/ViewController+DataSource.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController+DataSource.h 3 | // CommonTest 4 | // 5 | // Created by zzyong on 2021/3/22. 6 | // 7 | 8 | #import "ViewController.h" 9 | 10 | NS_ASSUME_NONNULL_BEGIN 11 | 12 | @class VCModel; 13 | 14 | @interface ViewController () 15 | 16 | @property (nonatomic, strong) NSArray *testList; 17 | 18 | @end 19 | 20 | @interface ViewController (DataSource) 21 | 22 | - (void)setupTestList; 23 | 24 | @end 25 | 26 | 27 | NS_ASSUME_NONNULL_END 28 | -------------------------------------------------------------------------------- /HotFixDemo/HotFixDemo/ViewController+DataSource.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController+DataSource.m 3 | // HotFixDemo 4 | // 5 | // Created by zzyong on 2021/3/22. 6 | // 7 | 8 | 9 | #import 10 | #import "VCModel.h" 11 | #import "MyClassA.h" 12 | #import "MyClassB.h" 13 | #import "MyClassC.h" 14 | #import 15 | #import 16 | #import "ViewController+DataSource.h" 17 | 18 | @implementation ViewController (DataSource) 19 | 20 | - (void)setupTestList { 21 | 22 | __weak typeof(self) weakSelf = self; 23 | NSMutableArray *list = [NSMutableArray array]; 24 | 25 | VCModel *model1 = [VCModel new]; 26 | model1.title = @"动态创建类:MyObject"; 27 | model1.action = ^{ 28 | if (objc_getClass("MyObject") != NULL) { 29 | NSLog(@"MyObject 已存在"); 30 | return; 31 | } 32 | Class myCls = objc_allocateClassPair([NSObject class], "MyObject", 0); 33 | objc_registerClassPair(myCls); 34 | 35 | // 增加 sayHello 方法 36 | class_addMethod(myCls, @selector(sayHello), (IMP)sayHello, "v@:"); 37 | 38 | NSLog(@"MyObject 动态创建成功"); 39 | }; 40 | [list addObject:model1]; 41 | 42 | VCModel *model2 = [VCModel new]; 43 | model2.title = @"调用 MyObject sayHello 方法 "; 44 | model2.action = ^{ 45 | Class MyObject = NSClassFromString(@"MyObject"); 46 | if (MyObject == NULL) { 47 | NSLog(@"MyObject 类不存在"); 48 | return; 49 | } 50 | NSObject *myObj = [[MyObject alloc] init]; 51 | [myObj performSelector:@selector(sayHello)]; 52 | }; 53 | [list addObject:model2]; 54 | 55 | VCModel *model3 = [VCModel new]; 56 | model3.title = @"myMethodC 实现替换成 myMethodD"; 57 | model3.action = ^{ 58 | static dispatch_once_t onceToken; 59 | dispatch_once(&onceToken, ^{ 60 | Method methodD = class_getInstanceMethod(weakSelf.class, @selector(myMethodD)); 61 | IMP impD = method_getImplementation(methodD); 62 | class_replaceMethod(weakSelf.class, @selector(myMethodC), impD, method_getTypeEncoding(methodD)); 63 | NSLog(@"myMethodC 已替换为 myMethodD"); 64 | }); 65 | }; 66 | [list addObject:model3]; 67 | 68 | VCModel *model4 = [VCModel new]; 69 | model4.title = @"调用 myMethodC"; 70 | model4.action = ^{ 71 | // print: myMethodA 72 | [weakSelf myMethodC]; 73 | }; 74 | [list addObject:model4]; 75 | 76 | VCModel *model5 = [VCModel new]; 77 | model5.title = @"myMethodA 和 myMethodB 方法交换 "; 78 | model5.action = ^{ 79 | static dispatch_once_t onceToken; 80 | dispatch_once(&onceToken, ^{ 81 | Method methodA = class_getInstanceMethod(weakSelf.class, @selector(myMethodA)); 82 | Method methodB = class_getInstanceMethod(weakSelf.class, @selector(myMethodB)); 83 | method_exchangeImplementations(methodA, methodB); 84 | NSLog(@"myMethodA myMethodB 方法已交换"); 85 | }); 86 | }; 87 | [list addObject:model5]; 88 | 89 | VCModel *model6 = [VCModel new]; 90 | model6.title = @"调用 myMethodA"; 91 | model6.action = ^{ 92 | // print: myMethodB 93 | [weakSelf myMethodA]; 94 | }; 95 | [list addObject:model6]; 96 | 97 | VCModel *model7 = [VCModel new]; 98 | model7.title = @"调用 myMethodB"; 99 | model7.action = ^{ 100 | // print: myMethodA 101 | [weakSelf myMethodB]; 102 | }; 103 | [list addObject:model7]; 104 | 105 | VCModel *model8 = [VCModel new]; 106 | model8.title = @"forwardInvocation 测试"; 107 | model8.action = ^{ 108 | [[MyClassA new] performSelector:@selector(sayHello)]; 109 | }; 110 | [list addObject:model8]; 111 | 112 | VCModel *model9 = [VCModel new]; 113 | model9.title = @"[Fix] myEmptyMethod 替换为空实现"; 114 | model9.action = ^{ 115 | static dispatch_once_t onceToken; 116 | dispatch_once(&onceToken, ^{ 117 | [weakSelf dy_hookMethodWithHookMap:@{ 118 | @"cls": @"ViewController", 119 | @"sel": @"myEmptyMethod", 120 | @"isReplcedEmpty": @(YES) 121 | }]; 122 | }); 123 | }; 124 | [list addObject:model9]; 125 | 126 | VCModel *model10 = [VCModel new]; 127 | model10.title = @"调用 myEmptyMethod"; 128 | model10.action = ^{ 129 | [weakSelf myEmptyMethod]; 130 | }; 131 | [list addObject:model10]; 132 | 133 | VCModel *model11 = [VCModel new]; 134 | model11.title = @"[Fix] sayHelloTo: 方法参数修改"; 135 | model11.action = ^{ 136 | static dispatch_once_t onceToken; 137 | dispatch_once(&onceToken, ^{ 138 | [weakSelf dy_hookMethodWithHookMap:@{ 139 | @"cls": @"MyClassC", 140 | @"sel": @"sayHelloTo:", 141 | @"parameters": @[@"Lili"] 142 | }]; 143 | }); 144 | }; 145 | [list addObject:model11]; 146 | 147 | VCModel *model12 = [VCModel new]; 148 | model12.title = @"调用 sayHello: 方法"; 149 | model12.action = ^{ 150 | // 测试 MyClassC 151 | [[MyClassC new] sayHelloTo:@"jack"]; 152 | }; 153 | [list addObject:model12]; 154 | 155 | VCModel *model13 = [VCModel new]; 156 | model13.title = @"[Fix] className 方法返回值修改"; 157 | model13.action = ^{ 158 | static dispatch_once_t onceToken; 159 | dispatch_once(&onceToken, ^{ 160 | [weakSelf dy_hookMethodWithHookMap:@{ 161 | @"cls": @"MyClassC", 162 | @"sel": @"className", 163 | @"returnValue": @"Return value had change" 164 | }]; 165 | }); 166 | }; 167 | [list addObject:model13]; 168 | 169 | VCModel *model14 = [VCModel new]; 170 | model14.title = @"调用 className 方法"; 171 | model14.action = ^{ 172 | // 测试 MyClassC 173 | NSLog(@"%@", [[MyClassC new] className]); 174 | }; 175 | [list addObject:model14]; 176 | 177 | VCModel *model15 = [VCModel new]; 178 | model15.title = @"[Fix] myMethod 调用前调用 dynamicCallMethod"; 179 | model15.action = ^{ 180 | static dispatch_once_t onceToken; 181 | dispatch_once(&onceToken, ^{ 182 | [weakSelf dy_hookMethodWithHookMap:@{ 183 | @"cls": @"MyClassC", 184 | @"sel": @"myMethod", 185 | @"customMethods": @[@"self.dynamicCallMethod"] 186 | }]; 187 | }); 188 | }; 189 | [list addObject:model15]; 190 | 191 | VCModel *model16 = [VCModel new]; 192 | model16.title = @"调用 myMethod 方法"; 193 | model16.action = ^{ 194 | // 测试 MyClassC 195 | [[MyClassC new] myMethod]; 196 | }; 197 | [list addObject:model16]; 198 | 199 | VCModel *model17 = [VCModel new]; 200 | model17.title = @"NSInvocation 修改方法参数 EXC_BAD_ACCESS"; 201 | model17.action = ^{ 202 | [NSClassFromString(@"__NSArrayM") aspect_hookSelector:@selector(insertObject:atIndex:) withOptions:AspectPositionInstead usingBlock:^(id info){ 203 | 204 | id value = nil; 205 | [info.originalInvocation getArgument:&value atIndex:2]; 206 | if (value) { 207 | [info.originalInvocation invoke]; 208 | } 209 | } error:NULL]; 210 | }; 211 | [list addObject:model17]; 212 | 213 | VCModel *model18 = [VCModel new]; 214 | model18.title = @"NSInvocation 内存泄漏"; 215 | model18.action = ^{ 216 | { 217 | NSMethodSignature *signature = [NSObject methodSignatureForSelector:@selector(new)]; 218 | NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature]; 219 | invocation.target = MyClassA.class; 220 | invocation.selector = @selector(new); 221 | [invocation invoke]; 222 | } 223 | }; 224 | [list addObject:model18]; 225 | 226 | VCModel *model19 = [VCModel new]; 227 | model19.title = @"performSelector 内存泄漏"; 228 | model19.action = ^{ 229 | { 230 | [MyClassB performSelector:@selector(new)]; 231 | } 232 | }; 233 | [list addObject:model19]; 234 | 235 | self.testList = list; 236 | } 237 | 238 | @end 239 | -------------------------------------------------------------------------------- /HotFixDemo/HotFixDemo/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // HotFixDemo 4 | // 5 | // Created by zzyong on 2020/6/11. 6 | // Copyright © 2020 zzyong. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | void sayHello(id self, SEL _cmd); 14 | 15 | - (void)myMethodA; 16 | 17 | - (void)myMethodB; 18 | 19 | - (void)myMethodC; 20 | 21 | - (void)myMethodD; 22 | 23 | - (void)myEmptyMethod; 24 | 25 | - (void)dy_hookMethodWithHookMap:(NSDictionary *)hookMap; 26 | 27 | @end 28 | 29 | -------------------------------------------------------------------------------- /HotFixDemo/HotFixDemo/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // HotFixDemo 4 | // 5 | // Created by zzyong on 2020/6/11. 6 | // Copyright © 2020 zzyong. All rights reserved. 7 | // 8 | 9 | #import "VCCell.h" 10 | #import "VCModel.h" 11 | #import "OCDynamic.h" 12 | #import "ViewController.h" 13 | #import "ViewController+DataSource.h" 14 | 15 | @interface ViewController () 16 | 17 | @property (nonatomic, strong) UITableView *tableView; 18 | 19 | @end 20 | 21 | @implementation ViewController 22 | 23 | - (void)viewDidLoad { 24 | [super viewDidLoad]; 25 | [self setupTestList]; 26 | [self.view addSubview:self.tableView]; 27 | } 28 | 29 | - (BOOL)prefersStatusBarHidden { 30 | return YES; 31 | } 32 | 33 | - (void)viewDidLayoutSubviews { 34 | [super viewDidLayoutSubviews]; 35 | self.tableView.frame = self.view.bounds; 36 | } 37 | 38 | - (UITableView *)tableView { 39 | if (!_tableView) { 40 | _tableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain]; 41 | _tableView.backgroundColor = [UIColor whiteColor]; 42 | _tableView.separatorStyle = UITableViewCellSeparatorStyleSingleLine; 43 | _tableView.dataSource = self; 44 | _tableView.delegate = self; 45 | _tableView.rowHeight = 50; 46 | _tableView.estimatedSectionFooterHeight = 0; 47 | _tableView.estimatedSectionHeaderHeight = 0; 48 | _tableView.estimatedRowHeight = 0; 49 | [_tableView registerClass:[VCCell class] forCellReuseIdentifier:@"VCCell"]; 50 | } 51 | return _tableView; 52 | } 53 | 54 | #pragma mark - UITableViewDataSource 55 | 56 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 57 | return self.testList.count; 58 | } 59 | 60 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 61 | VCModel *testModel = [self.testList objectAtIndex:indexPath.row]; 62 | VCCell *cell = [tableView dequeueReusableCellWithIdentifier:@"VCCell" forIndexPath:indexPath]; 63 | cell.model = testModel; 64 | return cell; 65 | } 66 | 67 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { 68 | [tableView deselectRowAtIndexPath:indexPath animated:NO]; 69 | VCModel *testModel = [self.testList objectAtIndex:indexPath.row]; 70 | testModel.action(); 71 | } 72 | 73 | #pragma mark - Public 74 | 75 | - (void)myEmptyMethod { 76 | NSLog(@"%s", __func__); 77 | } 78 | 79 | void sayHello(id self, SEL _cmd) { 80 | NSLog(@"%@ %s", self, __func__); 81 | } 82 | 83 | - (void)myMethodA { 84 | NSLog(@"myMethodA"); 85 | } 86 | 87 | - (void)myMethodB { 88 | NSLog(@"myMethodB"); 89 | } 90 | 91 | - (void)myMethodC { 92 | NSLog(@"myMethodC"); 93 | } 94 | 95 | - (void)myMethodD { 96 | NSLog(@"myMethodD"); 97 | } 98 | 99 | - (void)dy_hookMethodWithHookMap:(NSDictionary *)hookMap { 100 | Class cls = NSClassFromString([hookMap objectForKey:@"cls"]); 101 | SEL sel = NSSelectorFromString([hookMap objectForKey:@"sel"]); 102 | NSArray *parameters = [hookMap objectForKey:@"parameters"]; 103 | NSArray *customMethods = [hookMap objectForKey:@"customMethods"]; 104 | 105 | [cls dy_hookSelector:sel withBlock:^(id _Nonnull self, NSInvocation * _Nonnull originalInvocation) { 106 | 107 | if ([hookMap[@"isReplcedEmpty"] boolValue]) { 108 | NSLog(@"[%@ %@] replace into empty IMP", cls, NSStringFromSelector(sel)); 109 | return; 110 | } 111 | 112 | [customMethods enumerateObjectsUsingBlock:^(NSString * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 113 | NSArray *targets = [obj componentsSeparatedByString:@"."]; 114 | 115 | id target = nil; 116 | if ([targets.firstObject isEqualToString:@"self"]) { 117 | target = self; 118 | } 119 | 120 | SEL sel = NSSelectorFromString(targets.lastObject); 121 | NSMethodSignature *targetSig = [[target class] instanceMethodSignatureForSelector:sel]; 122 | 123 | NSInvocation *customInvocation = [NSInvocation invocationWithMethodSignature:targetSig]; 124 | customInvocation.target = target; 125 | customInvocation.selector = sel; 126 | [customInvocation invoke]; 127 | 128 | target = nil; 129 | }]; 130 | 131 | [parameters enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 132 | [originalInvocation setArgument:&obj atIndex:idx + 2]; 133 | }]; 134 | 135 | [originalInvocation invoke]; 136 | 137 | id returnValue = [hookMap objectForKey:@"returnValue"]; 138 | if (returnValue) { 139 | [originalInvocation setReturnValue:&returnValue]; 140 | } 141 | }]; 142 | } 143 | 144 | @end 145 | -------------------------------------------------------------------------------- /HotFixDemo/HotFixDemoTests/HotFixDemoTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // HotFixDemoTests.m 3 | // HotFixDemoTests 4 | // 5 | // Created by zzyong on 2020/6/11. 6 | // Copyright © 2020 zzyong. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface HotFixDemoTests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation HotFixDemoTests 16 | 17 | - (void)setUp { 18 | // Put setup code here. This method is called before the invocation of each test method in the class. 19 | } 20 | 21 | - (void)tearDown { 22 | // Put teardown code here. This method is called after the invocation of each test method in the class. 23 | } 24 | 25 | - (void)testExample { 26 | // This is an example of a functional test case. 27 | // Use XCTAssert and related functions to verify your tests produce the correct results. 28 | } 29 | 30 | - (void)testPerformanceExample { 31 | // This is an example of a performance test case. 32 | [self measureBlock:^{ 33 | // Put the code you want to measure the time of here. 34 | }]; 35 | } 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /HotFixDemo/HotFixDemoTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /HotFixDemo/HotFixDemoUITests/HotFixDemoUITests.m: -------------------------------------------------------------------------------- 1 | // 2 | // HotFixDemoUITests.m 3 | // HotFixDemoUITests 4 | // 5 | // Created by zzyong on 2020/6/11. 6 | // Copyright © 2020 zzyong. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface HotFixDemoUITests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation HotFixDemoUITests 16 | 17 | - (void)setUp { 18 | // Put setup code here. This method is called before the invocation of each test method in the class. 19 | 20 | // In UI tests it is usually best to stop immediately when a failure occurs. 21 | self.continueAfterFailure = NO; 22 | 23 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 24 | } 25 | 26 | - (void)tearDown { 27 | // Put teardown code here. This method is called after the invocation of each test method in the class. 28 | } 29 | 30 | - (void)testExample { 31 | // UI tests must launch the application that they test. 32 | XCUIApplication *app = [[XCUIApplication alloc] init]; 33 | [app launch]; 34 | 35 | // Use recording to get started writing UI tests. 36 | // Use XCTAssert and related functions to verify your tests produce the correct results. 37 | } 38 | 39 | - (void)testLaunchPerformance { 40 | if (@available(macOS 10.15, iOS 13.0, tvOS 13.0, *)) { 41 | // This measures how long it takes to launch your application. 42 | [self measureWithMetrics:@[XCTOSSignpostMetric.applicationLaunchMetric] block:^{ 43 | [[[XCUIApplication alloc] init] launch]; 44 | }]; 45 | } 46 | } 47 | 48 | @end 49 | -------------------------------------------------------------------------------- /HotFixDemo/HotFixDemoUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /HotFixDemo/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment the next line to define a global platform for your project 2 | platform :ios, '13.0' 3 | 4 | target 'HotFixDemo' do 5 | # Comment the next line if you don't want to use dynamic frameworks 6 | # use_frameworks! 7 | 8 | # Pods for HotFixDemo 9 | 10 | pod 'Aspects' 11 | 12 | target 'HotFixDemoTests' do 13 | inherit! :search_paths 14 | # Pods for testing 15 | end 16 | 17 | target 'HotFixDemoUITests' do 18 | # Pods for testing 19 | end 20 | 21 | end 22 | -------------------------------------------------------------------------------- /HotFixDemo/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Aspects (1.4.1) 3 | 4 | DEPENDENCIES: 5 | - Aspects 6 | 7 | SPEC REPOS: 8 | trunk: 9 | - Aspects 10 | 11 | SPEC CHECKSUMS: 12 | Aspects: 7595ba96a6727a58ebcbfc954497fc5d2fdde546 13 | 14 | PODFILE CHECKSUM: e68acbd8e27b2f73599d3c320c5e515e0ee07dd2 15 | 16 | COCOAPODS: 1.9.3 17 | -------------------------------------------------------------------------------- /HotFixDemo/Pods/Aspects/Aspects.h: -------------------------------------------------------------------------------- 1 | // 2 | // Aspects.h 3 | // Aspects - A delightful, simple library for aspect oriented programming. 4 | // 5 | // Copyright (c) 2014 Peter Steinberger. Licensed under the MIT license. 6 | // 7 | 8 | #import 9 | 10 | typedef NS_OPTIONS(NSUInteger, AspectOptions) { 11 | AspectPositionAfter = 0, /// Called after the original implementation (default) 12 | AspectPositionInstead = 1, /// Will replace the original implementation. 13 | AspectPositionBefore = 2, /// Called before the original implementation. 14 | 15 | AspectOptionAutomaticRemoval = 1 << 3 /// Will remove the hook after the first execution. 16 | }; 17 | 18 | /// Opaque Aspect Token that allows to deregister the hook. 19 | @protocol AspectToken 20 | 21 | /// Deregisters an aspect. 22 | /// @return YES if deregistration is successful, otherwise NO. 23 | - (BOOL)remove; 24 | 25 | @end 26 | 27 | /// The AspectInfo protocol is the first parameter of our block syntax. 28 | @protocol AspectInfo 29 | 30 | /// The instance that is currently hooked. 31 | - (id)instance; 32 | 33 | /// The original invocation of the hooked method. 34 | - (NSInvocation *)originalInvocation; 35 | 36 | /// All method arguments, boxed. This is lazily evaluated. 37 | - (NSArray *)arguments; 38 | 39 | @end 40 | 41 | /** 42 | Aspects uses Objective-C message forwarding to hook into messages. This will create some overhead. Don't add aspects to methods that are called a lot. Aspects is meant for view/controller code that is not called a 1000 times per second. 43 | 44 | Adding aspects returns an opaque token which can be used to deregister again. All calls are thread safe. 45 | */ 46 | @interface NSObject (Aspects) 47 | 48 | /// Adds a block of code before/instead/after the current `selector` for a specific class. 49 | /// 50 | /// @param block Aspects replicates the type signature of the method being hooked. 51 | /// The first parameter will be `id`, followed by all parameters of the method. 52 | /// These parameters are optional and will be filled to match the block signature. 53 | /// You can even use an empty block, or one that simple gets `id`. 54 | /// 55 | /// @note Hooking static methods is not supported. 56 | /// @return A token which allows to later deregister the aspect. 57 | + (id)aspect_hookSelector:(SEL)selector 58 | withOptions:(AspectOptions)options 59 | usingBlock:(id)block 60 | error:(NSError **)error; 61 | 62 | /// Adds a block of code before/instead/after the current `selector` for a specific instance. 63 | - (id)aspect_hookSelector:(SEL)selector 64 | withOptions:(AspectOptions)options 65 | usingBlock:(id)block 66 | error:(NSError **)error; 67 | 68 | @end 69 | 70 | 71 | typedef NS_ENUM(NSUInteger, AspectErrorCode) { 72 | AspectErrorSelectorBlacklisted, /// Selectors like release, retain, autorelease are blacklisted. 73 | AspectErrorDoesNotRespondToSelector, /// Selector could not be found. 74 | AspectErrorSelectorDeallocPosition, /// When hooking dealloc, only AspectPositionBefore is allowed. 75 | AspectErrorSelectorAlreadyHookedInClassHierarchy, /// Statically hooking the same method in subclasses is not allowed. 76 | AspectErrorFailedToAllocateClassPair, /// The runtime failed creating a class pair. 77 | AspectErrorMissingBlockSignature, /// The block misses compile time signature info and can't be called. 78 | AspectErrorIncompatibleBlockSignature, /// The block signature does not match the method or is too large. 79 | 80 | AspectErrorRemoveObjectAlreadyDeallocated = 100 /// (for removing) The object hooked is already deallocated. 81 | }; 82 | 83 | extern NSString *const AspectErrorDomain; 84 | -------------------------------------------------------------------------------- /HotFixDemo/Pods/Aspects/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Peter Steinberger, steipete@gmail.com 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /HotFixDemo/Pods/Aspects/README.md: -------------------------------------------------------------------------------- 1 | Aspects v1.4.1 [![Build Status](https://travis-ci.org/steipete/Aspects.svg?branch=master)](https://travis-ci.org/steipete/Aspects) 2 | ============== 3 | 4 | Delightful, simple library for aspect oriented programming by [@steipete](http://twitter.com/steipete). 5 | 6 | **Think of Aspects as method swizzling on steroids. It allows you to add code to existing methods per class or per instance**, whilst thinking of the insertion point e.g. before/instead/after. Aspects automatically deals with calling super and is easier to use than regular method swizzling. 7 | 8 | This is stable and used in hundreds of apps since it's part of [PSPDFKit, an iOS PDF framework that ships with apps like Dropbox or Evernote](http://pspdfkit.com), and now I finally made it open source. 9 | 10 | Aspects extends `NSObject` with the following methods: 11 | 12 | ``` objc 13 | /// Adds a block of code before/instead/after the current `selector` for a specific class. 14 | /// 15 | /// @param block Aspects replicates the type signature of the method being hooked. 16 | /// The first parameter will be `id`, followed by all parameters of the method. 17 | /// These parameters are optional and will be filled to match the block signature. 18 | /// You can even use an empty block, or one that simple gets `id`. 19 | /// 20 | /// @note Hooking static methods is not supported. 21 | /// @return A token which allows to later deregister the aspect. 22 | + (id)aspect_hookSelector:(SEL)selector 23 | withOptions:(AspectOptions)options 24 | usingBlock:(id)block 25 | error:(NSError **)error; 26 | 27 | /// Adds a block of code before/instead/after the current `selector` for a specific instance. 28 | - (id)aspect_hookSelector:(SEL)selector 29 | withOptions:(AspectOptions)options 30 | usingBlock:(id)block 31 | error:(NSError **)error; 32 | 33 | /// Deregister an aspect. 34 | /// @return YES if deregistration is successful, otherwise NO. 35 | id aspect = ...; 36 | [aspect remove]; 37 | ``` 38 | 39 | Adding aspects returns an opaque token of type `AspectToken` which can be used to deregister again. All calls are thread-safe. 40 | 41 | Aspects uses Objective-C message forwarding to hook into messages. This will create some overhead. Don't add aspects to methods that are called a lot. Aspects is meant for view/controller code that is not called 1000 times per second. 42 | 43 | Aspects calls and matches block arguments. Blocks without arguments are supported as well. The first block argument will be of type `id`. 44 | 45 | When to use Aspects 46 | ------------------- 47 | Aspect-oritented programming (AOP) is used to encapsulate "cross-cutting" concerns. These are the kind of requirements that *cut-accross* many modules in your system, and so cannot be encapsulated using normal Object Oriented programming. Some examples of these kinds of requirements: 48 | 49 | * *Whenever* a user invokes a method on the service client, security should be checked. 50 | * *Whenever* a useer interacts with the store, a genius suggestion should be presented, based on their interaction. 51 | * *All* calls should be logged. 52 | 53 | If we implemented the above requirements using regular OO there'd be some drawbacks: 54 | 55 | 56 | Good OO says a class should have a single responsibility, however adding on extra *cross-cutting* requirements means a class that is taking on other responsibilites. For example you might have a **StoreClient** that supposed to be all about making purchases from an online store. Add in some cross-cutting requirements and it might also have to take on the roles of logging, security and recommendations. This is not great: 57 | 58 | * Our StoreClient is now harder to understand and maintain. 59 | * These cross-cutting requirements are duplicated and spreading throughout our app. 60 | 61 | AOP lets us modularize these cross-cutting requirements, and then cleanly identify all of the places they should be applied. As shown in the examples above cross-cutting requirements can be eithe technical or business focused in nature. 62 | 63 | ## Here are some concrete examples: 64 | 65 | 66 | Aspects can be used to **dynamically add logging** for debug builds only: 67 | 68 | ``` objc 69 | [UIViewController aspect_hookSelector:@selector(viewWillAppear:) withOptions:AspectPositionAfter usingBlock:^(id aspectInfo, BOOL animated) { 70 | NSLog(@"View Controller %@ will appear animated: %tu", aspectInfo.instance, animated); 71 | } error:NULL]; 72 | ``` 73 | 74 | ------------------- 75 | It can be used to greatly simplify your analytics setup: 76 | https://github.com/orta/ARAnalytics 77 | 78 | ------------------- 79 | You can check if methods are really being called in your test cases: 80 | ``` objc 81 | - (void)testExample { 82 | TestClass *testClass = [TestClass new]; 83 | TestClass *testClass2 = [TestClass new]; 84 | 85 | __block BOOL testCallCalled = NO; 86 | [testClass aspect_hookSelector:@selector(testCall) withOptions:AspectPositionAfter usingBlock:^{ 87 | testCallCalled = YES; 88 | } error:NULL]; 89 | 90 | [testClass2 testCallAndExecuteBlock:^{ 91 | [testClass testCall]; 92 | } error:NULL]; 93 | XCTAssertTrue(testCallCalled, @"Calling testCallAndExecuteBlock must call testCall"); 94 | } 95 | ``` 96 | ------------------- 97 | It can be really useful for debugging. Here I was curious when exactly the tap gesture changed state: 98 | 99 | ``` objc 100 | [_singleTapGesture aspect_hookSelector:@selector(setState:) withOptions:AspectPositionAfter usingBlock:^(id aspectInfo) { 101 | NSLog(@"%@: %@", aspectInfo.instance, aspectInfo.arguments); 102 | } error:NULL]; 103 | ``` 104 | 105 | ------------------- 106 | Another convenient use case is adding handlers for classes that you don't own. I've written it for use in [PSPDFKit](http://pspdfkit.com), where we require notifications when a view controller is being dismissed modally. This includes UIKit view controllers like `MFMailComposeViewController` or `UIImagePickerController`. We could have created subclasses for each of these controllers, but this would be quite a lot of unnecessary code. Aspects gives you a simpler solution for this problem: 107 | 108 | ``` objc 109 | @implementation UIViewController (DismissActionHook) 110 | 111 | // Will add a dismiss action once the controller gets dismissed. 112 | - (void)pspdf_addWillDismissAction:(void (^)(void))action { 113 | PSPDFAssert(action != NULL); 114 | 115 | [self aspect_hookSelector:@selector(viewWillDisappear:) withOptions:AspectPositionAfter usingBlock:^(id aspectInfo) { 116 | if ([aspectInfo.instance isBeingDismissed]) { 117 | action(); 118 | } 119 | } error:NULL]; 120 | } 121 | 122 | @end 123 | ``` 124 | 125 | Debugging 126 | --------- 127 | Aspects identifies itself nicely in the stack trace, so it's easy to see if a method has been hooked: 128 | 129 | 130 | 131 | Using Aspects with non-void return types 132 | ---------------------------------------- 133 | 134 | You can use the invocation object to customize the return value: 135 | 136 | ``` objc 137 | [PSPDFDrawView aspect_hookSelector:@selector(shouldProcessTouches:withEvent:) withOptions:AspectPositionInstead usingBlock:^(id info, NSSet *touches, UIEvent *event) { 138 | // Call original implementation. 139 | BOOL processTouches; 140 | NSInvocation *invocation = info.originalInvocation; 141 | [invocation invoke]; 142 | [invocation getReturnValue:&processTouches]; 143 | 144 | if (processTouches) { 145 | processTouches = pspdf_stylusShouldProcessTouches(touches, event); 146 | [invocation setReturnValue:&processTouches]; 147 | } 148 | } error:NULL]; 149 | ``` 150 | 151 | Installation 152 | ------------ 153 | The simplest option is to use `pod "Aspects"`. 154 | 155 | You can also add the two files `Aspects.h/m`. There are no further requirements. 156 | 157 | Compatibility and Limitations 158 | ----------------------------- 159 | Aspects uses quite some runtime trickery to achieve what it does. You can mostly mix this with regular method swizzling. 160 | 161 | An important limitation is that for class-based hooking, a method can only be hooked once within the subclass hierarchy. [See #2](https://github.com/steipete/Aspects/issues/2) 162 | This does not apply for objects that are hooked. Aspects creates a dynamic subclass here and has full control. 163 | 164 | KVO works if observers are created after your calls `aspect_hookSelector:` It most likely will crash the other way around. 165 | Still looking for workarounds here - any help apprechiated. 166 | 167 | Because of ugly implementation details on the ObjC runtime, methods that return unions that also contain structs might not work correctly unless this code runs on the arm64 runtime. 168 | 169 | Credits 170 | ------- 171 | The idea to use `_objc_msgForward` and parts of the `NSInvocation` argument selection is from the excellent [ReactiveCocoa](https://github.com/ReactiveCocoa/ReactiveCocoa) from the GitHub guys. [This article](http://codeshaker.blogspot.co.at/2012/01/aop-delivered.html) explains how it works under the hood. 172 | 173 | 174 | Supported iOS & SDK Versions 175 | ----------------------------- 176 | 177 | * Aspects requires ARC. 178 | * Aspects is tested with iOS 6+ and OS X 10.7 or higher. 179 | 180 | License 181 | ------- 182 | MIT licensed, Copyright (c) 2014 Peter Steinberger, steipete@gmail.com, [@steipete](http://twitter.com/steipete) 183 | 184 | 185 | Release Notes 186 | ----------------- 187 | 188 | Version 1.4.1 189 | 190 | - Rename error codes. 191 | 192 | Version 1.4.0 193 | 194 | - Add support for block signatures that match method signatures. (thanks to @nickynick) 195 | 196 | Version 1.3.1 197 | 198 | - Add support for OS X 10.7 or higher. (thanks to @ashfurrow) 199 | 200 | Version 1.3.0 201 | 202 | - Add automatic deregistration. 203 | - Checks if the selector exists before trying to hook. 204 | - Improved dealloc hooking. (no more unsafe_unretained needed) 205 | - Better examples. 206 | - Always log errors. 207 | 208 | Version 1.2.0 209 | 210 | - Adds error parameter. 211 | - Improvements in subclassing registration tracking. 212 | 213 | Version 1.1.0 214 | 215 | - Renamed the files from NSObject+Aspects.m/h to just Aspects.m/h. 216 | - Removing now works via calling `remove` on the aspect token. 217 | - Allow hooking dealloc. 218 | - Fixes infinite loop if the same method is hooked for multiple classes. Hooking will only work for one class in the hierarchy. 219 | - Additional checks to prevent things like hooking retain/release/autorelease or forwardInvocation: 220 | - The original implementation of forwardInvocation is now correctly preserved. 221 | - Classes are properly cleaned up and restored to the original state after the last hook is deregistered. 222 | - Lots and lots of new test cases! 223 | 224 | Version 1.0.1 225 | 226 | - Minor tweaks and documentation improvements. 227 | 228 | Version 1.0.0 229 | 230 | - Initial release 231 | -------------------------------------------------------------------------------- /HotFixDemo/Pods/Headers/Private/Aspects/Aspects.h: -------------------------------------------------------------------------------- 1 | ../../../Aspects/Aspects.h -------------------------------------------------------------------------------- /HotFixDemo/Pods/Headers/Public/Aspects/Aspects.h: -------------------------------------------------------------------------------- 1 | ../../../Aspects/Aspects.h -------------------------------------------------------------------------------- /HotFixDemo/Pods/Manifest.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Aspects (1.4.1) 3 | 4 | DEPENDENCIES: 5 | - Aspects 6 | 7 | SPEC REPOS: 8 | trunk: 9 | - Aspects 10 | 11 | SPEC CHECKSUMS: 12 | Aspects: 7595ba96a6727a58ebcbfc954497fc5d2fdde546 13 | 14 | PODFILE CHECKSUM: e68acbd8e27b2f73599d3c320c5e515e0ee07dd2 15 | 16 | COCOAPODS: 1.9.3 17 | -------------------------------------------------------------------------------- /HotFixDemo/Pods/Pods.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 19D9061F99D71554210DD5E2BAFE3B5B /* Aspects.h in Headers */ = {isa = PBXBuildFile; fileRef = 325E1DE4DCB4C534D06ABCD63E2F8B15 /* Aspects.h */; settings = {ATTRIBUTES = (Project, ); }; }; 11 | 3AF17343CCFAB6AFCF0BC73F380108B3 /* Aspects.m in Sources */ = {isa = PBXBuildFile; fileRef = B30E78E83656B249A2C505F8BD77F4D6 /* Aspects.m */; }; 12 | 4A5C2E4B6B683C4878F8A6432E156851 /* Pods-HotFixDemoTests-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 46F25B3333E71C18834520164DA9FB43 /* Pods-HotFixDemoTests-dummy.m */; }; 13 | 82EF4BC8912344449E75D17951FCE6E3 /* Pods-HotFixDemo-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 4DD38739DEDA742BD330BF7C0559EDBA /* Pods-HotFixDemo-dummy.m */; }; 14 | ED3669B16BA9603B4764FE727594C2F7 /* Aspects-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 294023A17A64B93731AC48F5311B08E1 /* Aspects-dummy.m */; }; 15 | FBF1379D4442E4148C8F4DE61ABD017F /* Pods-HotFixDemo-HotFixDemoUITests-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 50D46AF3077DF609A133DA281FB5110F /* Pods-HotFixDemo-HotFixDemoUITests-dummy.m */; }; 16 | /* End PBXBuildFile section */ 17 | 18 | /* Begin PBXContainerItemProxy section */ 19 | 7038B135C61CEB05ED3233D0CC779F37 /* PBXContainerItemProxy */ = { 20 | isa = PBXContainerItemProxy; 21 | containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; 22 | proxyType = 1; 23 | remoteGlobalIDString = C6539905C4D50932B36244650B93C79B; 24 | remoteInfo = Aspects; 25 | }; 26 | 738EB15C40FA19F132A2D6CBFD40987C /* PBXContainerItemProxy */ = { 27 | isa = PBXContainerItemProxy; 28 | containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; 29 | proxyType = 1; 30 | remoteGlobalIDString = C6539905C4D50932B36244650B93C79B; 31 | remoteInfo = Aspects; 32 | }; 33 | B0431DFE9616AED99BAA76E3B370B439 /* PBXContainerItemProxy */ = { 34 | isa = PBXContainerItemProxy; 35 | containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; 36 | proxyType = 1; 37 | remoteGlobalIDString = BF0F6869DCF244A47B6D5FAD85A33BDC; 38 | remoteInfo = "Pods-HotFixDemo"; 39 | }; 40 | /* End PBXContainerItemProxy section */ 41 | 42 | /* Begin PBXFileReference section */ 43 | 02BDE05B67F58C089A056CAB56124556 /* Pods-HotFixDemo-HotFixDemoUITests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-HotFixDemo-HotFixDemoUITests.release.xcconfig"; sourceTree = ""; }; 44 | 1EC07161805A58CBAEEDCE430B820A7E /* Pods-HotFixDemo-HotFixDemoUITests-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-HotFixDemo-HotFixDemoUITests-acknowledgements.plist"; sourceTree = ""; }; 45 | 294023A17A64B93731AC48F5311B08E1 /* Aspects-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Aspects-dummy.m"; sourceTree = ""; }; 46 | 325E1DE4DCB4C534D06ABCD63E2F8B15 /* Aspects.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = Aspects.h; sourceTree = ""; }; 47 | 3457AECCABA3A1F9DFD91F741F716EF1 /* Pods-HotFixDemoTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-HotFixDemoTests.debug.xcconfig"; sourceTree = ""; }; 48 | 357207B97AAFDF3424BD6BB290DA0826 /* Pods-HotFixDemo-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-HotFixDemo-acknowledgements.markdown"; sourceTree = ""; }; 49 | 37E6FC7906CFB125646D40797DC19921 /* Pods-HotFixDemo-HotFixDemoUITests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-HotFixDemo-HotFixDemoUITests.debug.xcconfig"; sourceTree = ""; }; 50 | 450FFC2794AC5D13BDFEEC527501257C /* Pods-HotFixDemo.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-HotFixDemo.release.xcconfig"; sourceTree = ""; }; 51 | 46F25B3333E71C18834520164DA9FB43 /* Pods-HotFixDemoTests-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-HotFixDemoTests-dummy.m"; sourceTree = ""; }; 52 | 4D952ED0F761C0BA1CCED14714F20D7D /* Aspects-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Aspects-prefix.pch"; sourceTree = ""; }; 53 | 4DD38739DEDA742BD330BF7C0559EDBA /* Pods-HotFixDemo-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-HotFixDemo-dummy.m"; sourceTree = ""; }; 54 | 50D46AF3077DF609A133DA281FB5110F /* Pods-HotFixDemo-HotFixDemoUITests-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-HotFixDemo-HotFixDemoUITests-dummy.m"; sourceTree = ""; }; 55 | 5614FC705A17F97DE4B5E67782B3E449 /* Aspects.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = Aspects.release.xcconfig; sourceTree = ""; }; 56 | 618D517A4DCA886A6D4D0BEAB51D0813 /* Aspects.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = Aspects.debug.xcconfig; sourceTree = ""; }; 57 | 62C2F8DA64CF20B8C6170FDBB215B2CA /* Pods-HotFixDemoTests-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-HotFixDemoTests-acknowledgements.markdown"; sourceTree = ""; }; 58 | 6333AD07F2802050F33F50C9CAC84256 /* libPods-HotFixDemoTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; name = "libPods-HotFixDemoTests.a"; path = "libPods-HotFixDemoTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 59 | 746423DF774E2551A86B851E30DE0EAD /* Pods-HotFixDemoTests-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-HotFixDemoTests-acknowledgements.plist"; sourceTree = ""; }; 60 | 7BD56CF9ADD77A5A2AAA557D46E78BEF /* libPods-HotFixDemo-HotFixDemoUITests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; name = "libPods-HotFixDemo-HotFixDemoUITests.a"; path = "libPods-HotFixDemo-HotFixDemoUITests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 61 | 7FDA5EE8F4C44D1286EC86FD9E2076B0 /* Pods-HotFixDemo-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-HotFixDemo-acknowledgements.plist"; sourceTree = ""; }; 62 | 83E06C402124E00FAD3F61C4D119D1E8 /* Pods-HotFixDemo.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-HotFixDemo.debug.xcconfig"; sourceTree = ""; }; 63 | 9808BF74538141204E4EB5B49809FE77 /* libAspects.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; name = libAspects.a; path = libAspects.a; sourceTree = BUILT_PRODUCTS_DIR; }; 64 | 9D940727FF8FB9C785EB98E56350EF41 /* Podfile */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; indentWidth = 2; lastKnownFileType = text; name = Podfile; path = ../Podfile; sourceTree = SOURCE_ROOT; tabWidth = 2; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; 65 | B2992EE75B63DE906057BABF566059B9 /* Pods-HotFixDemo-HotFixDemoUITests-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-HotFixDemo-HotFixDemoUITests-acknowledgements.markdown"; sourceTree = ""; }; 66 | B30E78E83656B249A2C505F8BD77F4D6 /* Aspects.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = Aspects.m; sourceTree = ""; }; 67 | F0375CD7036A2498F3D3C5C086289DB4 /* Pods-HotFixDemoTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-HotFixDemoTests.release.xcconfig"; sourceTree = ""; }; 68 | F9D8B5BC41C648F9A23C83DD492E3852 /* libPods-HotFixDemo.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; name = "libPods-HotFixDemo.a"; path = "libPods-HotFixDemo.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 69 | /* End PBXFileReference section */ 70 | 71 | /* Begin PBXFrameworksBuildPhase section */ 72 | 2B84A7751F5204669A86F54CFED45D73 /* Frameworks */ = { 73 | isa = PBXFrameworksBuildPhase; 74 | buildActionMask = 2147483647; 75 | files = ( 76 | ); 77 | runOnlyForDeploymentPostprocessing = 0; 78 | }; 79 | 66252DDC19617A7480EDEE4780A481E0 /* Frameworks */ = { 80 | isa = PBXFrameworksBuildPhase; 81 | buildActionMask = 2147483647; 82 | files = ( 83 | ); 84 | runOnlyForDeploymentPostprocessing = 0; 85 | }; 86 | CBA80FE0CA212EA0F63D00E923BBCF11 /* Frameworks */ = { 87 | isa = PBXFrameworksBuildPhase; 88 | buildActionMask = 2147483647; 89 | files = ( 90 | ); 91 | runOnlyForDeploymentPostprocessing = 0; 92 | }; 93 | D45922622142C50571DC23115D852394 /* Frameworks */ = { 94 | isa = PBXFrameworksBuildPhase; 95 | buildActionMask = 2147483647; 96 | files = ( 97 | ); 98 | runOnlyForDeploymentPostprocessing = 0; 99 | }; 100 | /* End PBXFrameworksBuildPhase section */ 101 | 102 | /* Begin PBXGroup section */ 103 | 03169F243B22A6EF3EF30126E7700123 /* Support Files */ = { 104 | isa = PBXGroup; 105 | children = ( 106 | 294023A17A64B93731AC48F5311B08E1 /* Aspects-dummy.m */, 107 | 4D952ED0F761C0BA1CCED14714F20D7D /* Aspects-prefix.pch */, 108 | 618D517A4DCA886A6D4D0BEAB51D0813 /* Aspects.debug.xcconfig */, 109 | 5614FC705A17F97DE4B5E67782B3E449 /* Aspects.release.xcconfig */, 110 | ); 111 | name = "Support Files"; 112 | path = "../Target Support Files/Aspects"; 113 | sourceTree = ""; 114 | }; 115 | 18ECB66E49FFC6B8E670A422FAFFC9C1 /* Aspects */ = { 116 | isa = PBXGroup; 117 | children = ( 118 | 325E1DE4DCB4C534D06ABCD63E2F8B15 /* Aspects.h */, 119 | B30E78E83656B249A2C505F8BD77F4D6 /* Aspects.m */, 120 | 03169F243B22A6EF3EF30126E7700123 /* Support Files */, 121 | ); 122 | name = Aspects; 123 | path = Aspects; 124 | sourceTree = ""; 125 | }; 126 | 2958B4773E15F602CA9AECE03ACE3259 /* Pods-HotFixDemoTests */ = { 127 | isa = PBXGroup; 128 | children = ( 129 | 62C2F8DA64CF20B8C6170FDBB215B2CA /* Pods-HotFixDemoTests-acknowledgements.markdown */, 130 | 746423DF774E2551A86B851E30DE0EAD /* Pods-HotFixDemoTests-acknowledgements.plist */, 131 | 46F25B3333E71C18834520164DA9FB43 /* Pods-HotFixDemoTests-dummy.m */, 132 | 3457AECCABA3A1F9DFD91F741F716EF1 /* Pods-HotFixDemoTests.debug.xcconfig */, 133 | F0375CD7036A2498F3D3C5C086289DB4 /* Pods-HotFixDemoTests.release.xcconfig */, 134 | ); 135 | name = "Pods-HotFixDemoTests"; 136 | path = "Target Support Files/Pods-HotFixDemoTests"; 137 | sourceTree = ""; 138 | }; 139 | 45B35F469EE4F8F89326EC8510CB16AA /* Pods-HotFixDemo-HotFixDemoUITests */ = { 140 | isa = PBXGroup; 141 | children = ( 142 | B2992EE75B63DE906057BABF566059B9 /* Pods-HotFixDemo-HotFixDemoUITests-acknowledgements.markdown */, 143 | 1EC07161805A58CBAEEDCE430B820A7E /* Pods-HotFixDemo-HotFixDemoUITests-acknowledgements.plist */, 144 | 50D46AF3077DF609A133DA281FB5110F /* Pods-HotFixDemo-HotFixDemoUITests-dummy.m */, 145 | 37E6FC7906CFB125646D40797DC19921 /* Pods-HotFixDemo-HotFixDemoUITests.debug.xcconfig */, 146 | 02BDE05B67F58C089A056CAB56124556 /* Pods-HotFixDemo-HotFixDemoUITests.release.xcconfig */, 147 | ); 148 | name = "Pods-HotFixDemo-HotFixDemoUITests"; 149 | path = "Target Support Files/Pods-HotFixDemo-HotFixDemoUITests"; 150 | sourceTree = ""; 151 | }; 152 | 4E6947A89CCF7593579DB02486490B8C /* Pods-HotFixDemo */ = { 153 | isa = PBXGroup; 154 | children = ( 155 | 357207B97AAFDF3424BD6BB290DA0826 /* Pods-HotFixDemo-acknowledgements.markdown */, 156 | 7FDA5EE8F4C44D1286EC86FD9E2076B0 /* Pods-HotFixDemo-acknowledgements.plist */, 157 | 4DD38739DEDA742BD330BF7C0559EDBA /* Pods-HotFixDemo-dummy.m */, 158 | 83E06C402124E00FAD3F61C4D119D1E8 /* Pods-HotFixDemo.debug.xcconfig */, 159 | 450FFC2794AC5D13BDFEEC527501257C /* Pods-HotFixDemo.release.xcconfig */, 160 | ); 161 | name = "Pods-HotFixDemo"; 162 | path = "Target Support Files/Pods-HotFixDemo"; 163 | sourceTree = ""; 164 | }; 165 | 8568E91BB07346688306D0D7620127B4 /* Products */ = { 166 | isa = PBXGroup; 167 | children = ( 168 | 9808BF74538141204E4EB5B49809FE77 /* libAspects.a */, 169 | F9D8B5BC41C648F9A23C83DD492E3852 /* libPods-HotFixDemo.a */, 170 | 7BD56CF9ADD77A5A2AAA557D46E78BEF /* libPods-HotFixDemo-HotFixDemoUITests.a */, 171 | 6333AD07F2802050F33F50C9CAC84256 /* libPods-HotFixDemoTests.a */, 172 | ); 173 | name = Products; 174 | sourceTree = ""; 175 | }; 176 | AC23EE67C1A802D7B4B51C32C0E19C33 /* Targets Support Files */ = { 177 | isa = PBXGroup; 178 | children = ( 179 | 4E6947A89CCF7593579DB02486490B8C /* Pods-HotFixDemo */, 180 | 45B35F469EE4F8F89326EC8510CB16AA /* Pods-HotFixDemo-HotFixDemoUITests */, 181 | 2958B4773E15F602CA9AECE03ACE3259 /* Pods-HotFixDemoTests */, 182 | ); 183 | name = "Targets Support Files"; 184 | sourceTree = ""; 185 | }; 186 | CF1408CF629C7361332E53B88F7BD30C = { 187 | isa = PBXGroup; 188 | children = ( 189 | 9D940727FF8FB9C785EB98E56350EF41 /* Podfile */, 190 | D89477F20FB1DE18A04690586D7808C4 /* Frameworks */, 191 | D4235D072F0DC7818942872CBFF5FCF4 /* Pods */, 192 | 8568E91BB07346688306D0D7620127B4 /* Products */, 193 | AC23EE67C1A802D7B4B51C32C0E19C33 /* Targets Support Files */, 194 | ); 195 | sourceTree = ""; 196 | }; 197 | D4235D072F0DC7818942872CBFF5FCF4 /* Pods */ = { 198 | isa = PBXGroup; 199 | children = ( 200 | 18ECB66E49FFC6B8E670A422FAFFC9C1 /* Aspects */, 201 | ); 202 | name = Pods; 203 | sourceTree = ""; 204 | }; 205 | D89477F20FB1DE18A04690586D7808C4 /* Frameworks */ = { 206 | isa = PBXGroup; 207 | children = ( 208 | ); 209 | name = Frameworks; 210 | sourceTree = ""; 211 | }; 212 | /* End PBXGroup section */ 213 | 214 | /* Begin PBXHeadersBuildPhase section */ 215 | 61A78E845D88CA8DC3C5F3326FF8B140 /* Headers */ = { 216 | isa = PBXHeadersBuildPhase; 217 | buildActionMask = 2147483647; 218 | files = ( 219 | 19D9061F99D71554210DD5E2BAFE3B5B /* Aspects.h in Headers */, 220 | ); 221 | runOnlyForDeploymentPostprocessing = 0; 222 | }; 223 | 7FD76922DCFBE43CF8D80ED28D90F8E7 /* Headers */ = { 224 | isa = PBXHeadersBuildPhase; 225 | buildActionMask = 2147483647; 226 | files = ( 227 | ); 228 | runOnlyForDeploymentPostprocessing = 0; 229 | }; 230 | 876E4790E608BDF0CA7FCE0A13B94BF9 /* Headers */ = { 231 | isa = PBXHeadersBuildPhase; 232 | buildActionMask = 2147483647; 233 | files = ( 234 | ); 235 | runOnlyForDeploymentPostprocessing = 0; 236 | }; 237 | E035709B1B9B74580E7419C6323F4FEF /* Headers */ = { 238 | isa = PBXHeadersBuildPhase; 239 | buildActionMask = 2147483647; 240 | files = ( 241 | ); 242 | runOnlyForDeploymentPostprocessing = 0; 243 | }; 244 | /* End PBXHeadersBuildPhase section */ 245 | 246 | /* Begin PBXNativeTarget section */ 247 | 31D6344854BA839542841D30C255E089 /* Pods-HotFixDemoTests */ = { 248 | isa = PBXNativeTarget; 249 | buildConfigurationList = DBBE0F664C9C8B7AB68E1F3309DA503B /* Build configuration list for PBXNativeTarget "Pods-HotFixDemoTests" */; 250 | buildPhases = ( 251 | 7FD76922DCFBE43CF8D80ED28D90F8E7 /* Headers */, 252 | D99B1340B3795B194A58CE2F40E2E8FF /* Sources */, 253 | 66252DDC19617A7480EDEE4780A481E0 /* Frameworks */, 254 | ); 255 | buildRules = ( 256 | ); 257 | dependencies = ( 258 | B82243ED17088518877DB7F55C644E33 /* PBXTargetDependency */, 259 | ); 260 | name = "Pods-HotFixDemoTests"; 261 | productName = "Pods-HotFixDemoTests"; 262 | productReference = 6333AD07F2802050F33F50C9CAC84256 /* libPods-HotFixDemoTests.a */; 263 | productType = "com.apple.product-type.library.static"; 264 | }; 265 | 4B2CEE9DFCE58A924B6B3D59010CF276 /* Pods-HotFixDemo-HotFixDemoUITests */ = { 266 | isa = PBXNativeTarget; 267 | buildConfigurationList = 6702A79F0B2D71EB129E219209C62D27 /* Build configuration list for PBXNativeTarget "Pods-HotFixDemo-HotFixDemoUITests" */; 268 | buildPhases = ( 269 | 876E4790E608BDF0CA7FCE0A13B94BF9 /* Headers */, 270 | 9B56A70086A883D628FBE85B09A6DC32 /* Sources */, 271 | CBA80FE0CA212EA0F63D00E923BBCF11 /* Frameworks */, 272 | ); 273 | buildRules = ( 274 | ); 275 | dependencies = ( 276 | 0D4CC035B93F55C6CF1D0BB8BD78B519 /* PBXTargetDependency */, 277 | ); 278 | name = "Pods-HotFixDemo-HotFixDemoUITests"; 279 | productName = "Pods-HotFixDemo-HotFixDemoUITests"; 280 | productReference = 7BD56CF9ADD77A5A2AAA557D46E78BEF /* libPods-HotFixDemo-HotFixDemoUITests.a */; 281 | productType = "com.apple.product-type.library.static"; 282 | }; 283 | BF0F6869DCF244A47B6D5FAD85A33BDC /* Pods-HotFixDemo */ = { 284 | isa = PBXNativeTarget; 285 | buildConfigurationList = 87D70A4B196A24711A537333F8B85D1C /* Build configuration list for PBXNativeTarget "Pods-HotFixDemo" */; 286 | buildPhases = ( 287 | E035709B1B9B74580E7419C6323F4FEF /* Headers */, 288 | 8CCC2E657E65337E172D52A8BE1517A3 /* Sources */, 289 | 2B84A7751F5204669A86F54CFED45D73 /* Frameworks */, 290 | ); 291 | buildRules = ( 292 | ); 293 | dependencies = ( 294 | 3FC94F6D7B0189C0499E8AA621CFD2E3 /* PBXTargetDependency */, 295 | ); 296 | name = "Pods-HotFixDemo"; 297 | productName = "Pods-HotFixDemo"; 298 | productReference = F9D8B5BC41C648F9A23C83DD492E3852 /* libPods-HotFixDemo.a */; 299 | productType = "com.apple.product-type.library.static"; 300 | }; 301 | C6539905C4D50932B36244650B93C79B /* Aspects */ = { 302 | isa = PBXNativeTarget; 303 | buildConfigurationList = 926BB87AF71503C64757F8405AFA5445 /* Build configuration list for PBXNativeTarget "Aspects" */; 304 | buildPhases = ( 305 | 61A78E845D88CA8DC3C5F3326FF8B140 /* Headers */, 306 | 0C759CEFF2A195ADD12C40E0E994DFE8 /* Sources */, 307 | D45922622142C50571DC23115D852394 /* Frameworks */, 308 | ); 309 | buildRules = ( 310 | ); 311 | dependencies = ( 312 | ); 313 | name = Aspects; 314 | productName = Aspects; 315 | productReference = 9808BF74538141204E4EB5B49809FE77 /* libAspects.a */; 316 | productType = "com.apple.product-type.library.static"; 317 | }; 318 | /* End PBXNativeTarget section */ 319 | 320 | /* Begin PBXProject section */ 321 | BFDFE7DC352907FC980B868725387E98 /* Project object */ = { 322 | isa = PBXProject; 323 | attributes = { 324 | LastSwiftUpdateCheck = 1100; 325 | LastUpgradeCheck = 1100; 326 | }; 327 | buildConfigurationList = 4821239608C13582E20E6DA73FD5F1F9 /* Build configuration list for PBXProject "Pods" */; 328 | compatibilityVersion = "Xcode 9.3"; 329 | developmentRegion = en; 330 | hasScannedForEncodings = 0; 331 | knownRegions = ( 332 | en, 333 | Base, 334 | ); 335 | mainGroup = CF1408CF629C7361332E53B88F7BD30C; 336 | productRefGroup = 8568E91BB07346688306D0D7620127B4 /* Products */; 337 | projectDirPath = ""; 338 | projectRoot = ""; 339 | targets = ( 340 | C6539905C4D50932B36244650B93C79B /* Aspects */, 341 | BF0F6869DCF244A47B6D5FAD85A33BDC /* Pods-HotFixDemo */, 342 | 4B2CEE9DFCE58A924B6B3D59010CF276 /* Pods-HotFixDemo-HotFixDemoUITests */, 343 | 31D6344854BA839542841D30C255E089 /* Pods-HotFixDemoTests */, 344 | ); 345 | }; 346 | /* End PBXProject section */ 347 | 348 | /* Begin PBXSourcesBuildPhase section */ 349 | 0C759CEFF2A195ADD12C40E0E994DFE8 /* Sources */ = { 350 | isa = PBXSourcesBuildPhase; 351 | buildActionMask = 2147483647; 352 | files = ( 353 | ED3669B16BA9603B4764FE727594C2F7 /* Aspects-dummy.m in Sources */, 354 | 3AF17343CCFAB6AFCF0BC73F380108B3 /* Aspects.m in Sources */, 355 | ); 356 | runOnlyForDeploymentPostprocessing = 0; 357 | }; 358 | 8CCC2E657E65337E172D52A8BE1517A3 /* Sources */ = { 359 | isa = PBXSourcesBuildPhase; 360 | buildActionMask = 2147483647; 361 | files = ( 362 | 82EF4BC8912344449E75D17951FCE6E3 /* Pods-HotFixDemo-dummy.m in Sources */, 363 | ); 364 | runOnlyForDeploymentPostprocessing = 0; 365 | }; 366 | 9B56A70086A883D628FBE85B09A6DC32 /* Sources */ = { 367 | isa = PBXSourcesBuildPhase; 368 | buildActionMask = 2147483647; 369 | files = ( 370 | FBF1379D4442E4148C8F4DE61ABD017F /* Pods-HotFixDemo-HotFixDemoUITests-dummy.m in Sources */, 371 | ); 372 | runOnlyForDeploymentPostprocessing = 0; 373 | }; 374 | D99B1340B3795B194A58CE2F40E2E8FF /* Sources */ = { 375 | isa = PBXSourcesBuildPhase; 376 | buildActionMask = 2147483647; 377 | files = ( 378 | 4A5C2E4B6B683C4878F8A6432E156851 /* Pods-HotFixDemoTests-dummy.m in Sources */, 379 | ); 380 | runOnlyForDeploymentPostprocessing = 0; 381 | }; 382 | /* End PBXSourcesBuildPhase section */ 383 | 384 | /* Begin PBXTargetDependency section */ 385 | 0D4CC035B93F55C6CF1D0BB8BD78B519 /* PBXTargetDependency */ = { 386 | isa = PBXTargetDependency; 387 | name = Aspects; 388 | target = C6539905C4D50932B36244650B93C79B /* Aspects */; 389 | targetProxy = 738EB15C40FA19F132A2D6CBFD40987C /* PBXContainerItemProxy */; 390 | }; 391 | 3FC94F6D7B0189C0499E8AA621CFD2E3 /* PBXTargetDependency */ = { 392 | isa = PBXTargetDependency; 393 | name = Aspects; 394 | target = C6539905C4D50932B36244650B93C79B /* Aspects */; 395 | targetProxy = 7038B135C61CEB05ED3233D0CC779F37 /* PBXContainerItemProxy */; 396 | }; 397 | B82243ED17088518877DB7F55C644E33 /* PBXTargetDependency */ = { 398 | isa = PBXTargetDependency; 399 | name = "Pods-HotFixDemo"; 400 | target = BF0F6869DCF244A47B6D5FAD85A33BDC /* Pods-HotFixDemo */; 401 | targetProxy = B0431DFE9616AED99BAA76E3B370B439 /* PBXContainerItemProxy */; 402 | }; 403 | /* End PBXTargetDependency section */ 404 | 405 | /* Begin XCBuildConfiguration section */ 406 | 1422B121EAEAEA11307496903FA623C6 /* Release */ = { 407 | isa = XCBuildConfiguration; 408 | buildSettings = { 409 | ALWAYS_SEARCH_USER_PATHS = NO; 410 | CLANG_ANALYZER_NONNULL = YES; 411 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 412 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 413 | CLANG_CXX_LIBRARY = "libc++"; 414 | CLANG_ENABLE_MODULES = YES; 415 | CLANG_ENABLE_OBJC_ARC = YES; 416 | CLANG_ENABLE_OBJC_WEAK = YES; 417 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 418 | CLANG_WARN_BOOL_CONVERSION = YES; 419 | CLANG_WARN_COMMA = YES; 420 | CLANG_WARN_CONSTANT_CONVERSION = YES; 421 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 422 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 423 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 424 | CLANG_WARN_EMPTY_BODY = YES; 425 | CLANG_WARN_ENUM_CONVERSION = YES; 426 | CLANG_WARN_INFINITE_RECURSION = YES; 427 | CLANG_WARN_INT_CONVERSION = YES; 428 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 429 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 430 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 431 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 432 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 433 | CLANG_WARN_STRICT_PROTOTYPES = YES; 434 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 435 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 436 | CLANG_WARN_UNREACHABLE_CODE = YES; 437 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 438 | COPY_PHASE_STRIP = NO; 439 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 440 | ENABLE_NS_ASSERTIONS = NO; 441 | ENABLE_STRICT_OBJC_MSGSEND = YES; 442 | GCC_C_LANGUAGE_STANDARD = gnu11; 443 | GCC_NO_COMMON_BLOCKS = YES; 444 | GCC_PREPROCESSOR_DEFINITIONS = ( 445 | "POD_CONFIGURATION_RELEASE=1", 446 | "$(inherited)", 447 | ); 448 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 449 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 450 | GCC_WARN_UNDECLARED_SELECTOR = YES; 451 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 452 | GCC_WARN_UNUSED_FUNCTION = YES; 453 | GCC_WARN_UNUSED_VARIABLE = YES; 454 | IPHONEOS_DEPLOYMENT_TARGET = 13.0; 455 | MTL_ENABLE_DEBUG_INFO = NO; 456 | MTL_FAST_MATH = YES; 457 | PRODUCT_NAME = "$(TARGET_NAME)"; 458 | STRIP_INSTALLED_PRODUCT = NO; 459 | SWIFT_COMPILATION_MODE = wholemodule; 460 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 461 | SWIFT_VERSION = 5.0; 462 | SYMROOT = "${SRCROOT}/../build"; 463 | }; 464 | name = Release; 465 | }; 466 | 65B7D545271BECD55349D42BED737BA2 /* Release */ = { 467 | isa = XCBuildConfiguration; 468 | baseConfigurationReference = 450FFC2794AC5D13BDFEEC527501257C /* Pods-HotFixDemo.release.xcconfig */; 469 | buildSettings = { 470 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; 471 | CLANG_ENABLE_OBJC_WEAK = NO; 472 | CODE_SIGN_IDENTITY = "iPhone Developer"; 473 | "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; 474 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 475 | "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; 476 | IPHONEOS_DEPLOYMENT_TARGET = 13.0; 477 | MACH_O_TYPE = staticlib; 478 | OTHER_LDFLAGS = ""; 479 | OTHER_LIBTOOLFLAGS = ""; 480 | PODS_ROOT = "$(SRCROOT)"; 481 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; 482 | SDKROOT = iphoneos; 483 | SKIP_INSTALL = YES; 484 | TARGETED_DEVICE_FAMILY = "1,2"; 485 | VALIDATE_PRODUCT = YES; 486 | }; 487 | name = Release; 488 | }; 489 | 6BD8E08188997DBEEB132D083BE6F128 /* Debug */ = { 490 | isa = XCBuildConfiguration; 491 | baseConfigurationReference = 37E6FC7906CFB125646D40797DC19921 /* Pods-HotFixDemo-HotFixDemoUITests.debug.xcconfig */; 492 | buildSettings = { 493 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; 494 | CLANG_ENABLE_OBJC_WEAK = NO; 495 | CODE_SIGN_IDENTITY = "iPhone Developer"; 496 | "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; 497 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 498 | "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; 499 | IPHONEOS_DEPLOYMENT_TARGET = 13.0; 500 | MACH_O_TYPE = staticlib; 501 | OTHER_LDFLAGS = ""; 502 | OTHER_LIBTOOLFLAGS = ""; 503 | PODS_ROOT = "$(SRCROOT)"; 504 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; 505 | SDKROOT = iphoneos; 506 | SKIP_INSTALL = YES; 507 | TARGETED_DEVICE_FAMILY = "1,2"; 508 | }; 509 | name = Debug; 510 | }; 511 | 770A4D79F06229D2A8F61E46B75DB6B5 /* Debug */ = { 512 | isa = XCBuildConfiguration; 513 | baseConfigurationReference = 3457AECCABA3A1F9DFD91F741F716EF1 /* Pods-HotFixDemoTests.debug.xcconfig */; 514 | buildSettings = { 515 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; 516 | CLANG_ENABLE_OBJC_WEAK = NO; 517 | CODE_SIGN_IDENTITY = "iPhone Developer"; 518 | "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; 519 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 520 | "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; 521 | IPHONEOS_DEPLOYMENT_TARGET = 13.0; 522 | MACH_O_TYPE = staticlib; 523 | OTHER_LDFLAGS = ""; 524 | OTHER_LIBTOOLFLAGS = ""; 525 | PODS_ROOT = "$(SRCROOT)"; 526 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; 527 | SDKROOT = iphoneos; 528 | SKIP_INSTALL = YES; 529 | TARGETED_DEVICE_FAMILY = "1,2"; 530 | }; 531 | name = Debug; 532 | }; 533 | 897E512637A45609DB6C8059DA7A0CB9 /* Release */ = { 534 | isa = XCBuildConfiguration; 535 | baseConfigurationReference = 5614FC705A17F97DE4B5E67782B3E449 /* Aspects.release.xcconfig */; 536 | buildSettings = { 537 | CODE_SIGN_IDENTITY = "iPhone Developer"; 538 | "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; 539 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 540 | "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; 541 | GCC_PREFIX_HEADER = "Target Support Files/Aspects/Aspects-prefix.pch"; 542 | IPHONEOS_DEPLOYMENT_TARGET = 6.0; 543 | OTHER_LDFLAGS = ""; 544 | OTHER_LIBTOOLFLAGS = ""; 545 | PRIVATE_HEADERS_FOLDER_PATH = ""; 546 | PRODUCT_MODULE_NAME = Aspects; 547 | PRODUCT_NAME = Aspects; 548 | PUBLIC_HEADERS_FOLDER_PATH = ""; 549 | SDKROOT = iphoneos; 550 | SKIP_INSTALL = YES; 551 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; 552 | TARGETED_DEVICE_FAMILY = "1,2"; 553 | VALIDATE_PRODUCT = YES; 554 | }; 555 | name = Release; 556 | }; 557 | BFB786D4B3DEDFF7377566045F43F3C4 /* Release */ = { 558 | isa = XCBuildConfiguration; 559 | baseConfigurationReference = 02BDE05B67F58C089A056CAB56124556 /* Pods-HotFixDemo-HotFixDemoUITests.release.xcconfig */; 560 | buildSettings = { 561 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; 562 | CLANG_ENABLE_OBJC_WEAK = NO; 563 | CODE_SIGN_IDENTITY = "iPhone Developer"; 564 | "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; 565 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 566 | "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; 567 | IPHONEOS_DEPLOYMENT_TARGET = 13.0; 568 | MACH_O_TYPE = staticlib; 569 | OTHER_LDFLAGS = ""; 570 | OTHER_LIBTOOLFLAGS = ""; 571 | PODS_ROOT = "$(SRCROOT)"; 572 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; 573 | SDKROOT = iphoneos; 574 | SKIP_INSTALL = YES; 575 | TARGETED_DEVICE_FAMILY = "1,2"; 576 | VALIDATE_PRODUCT = YES; 577 | }; 578 | name = Release; 579 | }; 580 | C734072140926DA62303BC246C012AE7 /* Debug */ = { 581 | isa = XCBuildConfiguration; 582 | baseConfigurationReference = 83E06C402124E00FAD3F61C4D119D1E8 /* Pods-HotFixDemo.debug.xcconfig */; 583 | buildSettings = { 584 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; 585 | CLANG_ENABLE_OBJC_WEAK = NO; 586 | CODE_SIGN_IDENTITY = "iPhone Developer"; 587 | "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; 588 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 589 | "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; 590 | IPHONEOS_DEPLOYMENT_TARGET = 13.0; 591 | MACH_O_TYPE = staticlib; 592 | OTHER_LDFLAGS = ""; 593 | OTHER_LIBTOOLFLAGS = ""; 594 | PODS_ROOT = "$(SRCROOT)"; 595 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; 596 | SDKROOT = iphoneos; 597 | SKIP_INSTALL = YES; 598 | TARGETED_DEVICE_FAMILY = "1,2"; 599 | }; 600 | name = Debug; 601 | }; 602 | E4D5709467C43DAC3FCFBBCB5C4E73FA /* Debug */ = { 603 | isa = XCBuildConfiguration; 604 | baseConfigurationReference = 618D517A4DCA886A6D4D0BEAB51D0813 /* Aspects.debug.xcconfig */; 605 | buildSettings = { 606 | CODE_SIGN_IDENTITY = "iPhone Developer"; 607 | "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; 608 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 609 | "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; 610 | GCC_PREFIX_HEADER = "Target Support Files/Aspects/Aspects-prefix.pch"; 611 | IPHONEOS_DEPLOYMENT_TARGET = 6.0; 612 | OTHER_LDFLAGS = ""; 613 | OTHER_LIBTOOLFLAGS = ""; 614 | PRIVATE_HEADERS_FOLDER_PATH = ""; 615 | PRODUCT_MODULE_NAME = Aspects; 616 | PRODUCT_NAME = Aspects; 617 | PUBLIC_HEADERS_FOLDER_PATH = ""; 618 | SDKROOT = iphoneos; 619 | SKIP_INSTALL = YES; 620 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; 621 | TARGETED_DEVICE_FAMILY = "1,2"; 622 | }; 623 | name = Debug; 624 | }; 625 | ED7888FA6713EABBF66D26A8003AD1CA /* Debug */ = { 626 | isa = XCBuildConfiguration; 627 | buildSettings = { 628 | ALWAYS_SEARCH_USER_PATHS = NO; 629 | CLANG_ANALYZER_NONNULL = YES; 630 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 631 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 632 | CLANG_CXX_LIBRARY = "libc++"; 633 | CLANG_ENABLE_MODULES = YES; 634 | CLANG_ENABLE_OBJC_ARC = YES; 635 | CLANG_ENABLE_OBJC_WEAK = YES; 636 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 637 | CLANG_WARN_BOOL_CONVERSION = YES; 638 | CLANG_WARN_COMMA = YES; 639 | CLANG_WARN_CONSTANT_CONVERSION = YES; 640 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 641 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 642 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 643 | CLANG_WARN_EMPTY_BODY = YES; 644 | CLANG_WARN_ENUM_CONVERSION = YES; 645 | CLANG_WARN_INFINITE_RECURSION = YES; 646 | CLANG_WARN_INT_CONVERSION = YES; 647 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 648 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 649 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 650 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 651 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 652 | CLANG_WARN_STRICT_PROTOTYPES = YES; 653 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 654 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 655 | CLANG_WARN_UNREACHABLE_CODE = YES; 656 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 657 | COPY_PHASE_STRIP = NO; 658 | DEBUG_INFORMATION_FORMAT = dwarf; 659 | ENABLE_STRICT_OBJC_MSGSEND = YES; 660 | ENABLE_TESTABILITY = YES; 661 | GCC_C_LANGUAGE_STANDARD = gnu11; 662 | GCC_DYNAMIC_NO_PIC = NO; 663 | GCC_NO_COMMON_BLOCKS = YES; 664 | GCC_OPTIMIZATION_LEVEL = 0; 665 | GCC_PREPROCESSOR_DEFINITIONS = ( 666 | "POD_CONFIGURATION_DEBUG=1", 667 | "DEBUG=1", 668 | "$(inherited)", 669 | ); 670 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 671 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 672 | GCC_WARN_UNDECLARED_SELECTOR = YES; 673 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 674 | GCC_WARN_UNUSED_FUNCTION = YES; 675 | GCC_WARN_UNUSED_VARIABLE = YES; 676 | IPHONEOS_DEPLOYMENT_TARGET = 13.0; 677 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 678 | MTL_FAST_MATH = YES; 679 | ONLY_ACTIVE_ARCH = YES; 680 | PRODUCT_NAME = "$(TARGET_NAME)"; 681 | STRIP_INSTALLED_PRODUCT = NO; 682 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 683 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 684 | SWIFT_VERSION = 5.0; 685 | SYMROOT = "${SRCROOT}/../build"; 686 | }; 687 | name = Debug; 688 | }; 689 | F6205EAA01C1DCFCEC87040CF8AE60B1 /* Release */ = { 690 | isa = XCBuildConfiguration; 691 | baseConfigurationReference = F0375CD7036A2498F3D3C5C086289DB4 /* Pods-HotFixDemoTests.release.xcconfig */; 692 | buildSettings = { 693 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; 694 | CLANG_ENABLE_OBJC_WEAK = NO; 695 | CODE_SIGN_IDENTITY = "iPhone Developer"; 696 | "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; 697 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 698 | "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; 699 | IPHONEOS_DEPLOYMENT_TARGET = 13.0; 700 | MACH_O_TYPE = staticlib; 701 | OTHER_LDFLAGS = ""; 702 | OTHER_LIBTOOLFLAGS = ""; 703 | PODS_ROOT = "$(SRCROOT)"; 704 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; 705 | SDKROOT = iphoneos; 706 | SKIP_INSTALL = YES; 707 | TARGETED_DEVICE_FAMILY = "1,2"; 708 | VALIDATE_PRODUCT = YES; 709 | }; 710 | name = Release; 711 | }; 712 | /* End XCBuildConfiguration section */ 713 | 714 | /* Begin XCConfigurationList section */ 715 | 4821239608C13582E20E6DA73FD5F1F9 /* Build configuration list for PBXProject "Pods" */ = { 716 | isa = XCConfigurationList; 717 | buildConfigurations = ( 718 | ED7888FA6713EABBF66D26A8003AD1CA /* Debug */, 719 | 1422B121EAEAEA11307496903FA623C6 /* Release */, 720 | ); 721 | defaultConfigurationIsVisible = 0; 722 | defaultConfigurationName = Release; 723 | }; 724 | 6702A79F0B2D71EB129E219209C62D27 /* Build configuration list for PBXNativeTarget "Pods-HotFixDemo-HotFixDemoUITests" */ = { 725 | isa = XCConfigurationList; 726 | buildConfigurations = ( 727 | 6BD8E08188997DBEEB132D083BE6F128 /* Debug */, 728 | BFB786D4B3DEDFF7377566045F43F3C4 /* Release */, 729 | ); 730 | defaultConfigurationIsVisible = 0; 731 | defaultConfigurationName = Release; 732 | }; 733 | 87D70A4B196A24711A537333F8B85D1C /* Build configuration list for PBXNativeTarget "Pods-HotFixDemo" */ = { 734 | isa = XCConfigurationList; 735 | buildConfigurations = ( 736 | C734072140926DA62303BC246C012AE7 /* Debug */, 737 | 65B7D545271BECD55349D42BED737BA2 /* Release */, 738 | ); 739 | defaultConfigurationIsVisible = 0; 740 | defaultConfigurationName = Release; 741 | }; 742 | 926BB87AF71503C64757F8405AFA5445 /* Build configuration list for PBXNativeTarget "Aspects" */ = { 743 | isa = XCConfigurationList; 744 | buildConfigurations = ( 745 | E4D5709467C43DAC3FCFBBCB5C4E73FA /* Debug */, 746 | 897E512637A45609DB6C8059DA7A0CB9 /* Release */, 747 | ); 748 | defaultConfigurationIsVisible = 0; 749 | defaultConfigurationName = Release; 750 | }; 751 | DBBE0F664C9C8B7AB68E1F3309DA503B /* Build configuration list for PBXNativeTarget "Pods-HotFixDemoTests" */ = { 752 | isa = XCConfigurationList; 753 | buildConfigurations = ( 754 | 770A4D79F06229D2A8F61E46B75DB6B5 /* Debug */, 755 | F6205EAA01C1DCFCEC87040CF8AE60B1 /* Release */, 756 | ); 757 | defaultConfigurationIsVisible = 0; 758 | defaultConfigurationName = Release; 759 | }; 760 | /* End XCConfigurationList section */ 761 | }; 762 | rootObject = BFDFE7DC352907FC980B868725387E98 /* Project object */; 763 | } 764 | -------------------------------------------------------------------------------- /HotFixDemo/Pods/Pods.xcodeproj/xcuserdata/zzyong.xcuserdatad/xcschemes/Aspects.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 45 | 46 | 52 | 53 | 55 | 56 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /HotFixDemo/Pods/Pods.xcodeproj/xcuserdata/zzyong.xcuserdatad/xcschemes/Pods-HotFixDemo-HotFixDemoUITests.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 45 | 46 | 52 | 53 | 55 | 56 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /HotFixDemo/Pods/Pods.xcodeproj/xcuserdata/zzyong.xcuserdatad/xcschemes/Pods-HotFixDemo.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 45 | 46 | 52 | 53 | 55 | 56 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /HotFixDemo/Pods/Pods.xcodeproj/xcuserdata/zzyong.xcuserdatad/xcschemes/Pods-HotFixDemoTests.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 45 | 46 | 52 | 53 | 55 | 56 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /HotFixDemo/Pods/Pods.xcodeproj/xcuserdata/zzyong.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Aspects.xcscheme 8 | 9 | isShown 10 | 11 | 12 | Pods-HotFixDemo-HotFixDemoUITests.xcscheme 13 | 14 | isShown 15 | 16 | 17 | Pods-HotFixDemo.xcscheme 18 | 19 | isShown 20 | 21 | 22 | Pods-HotFixDemoTests.xcscheme 23 | 24 | isShown 25 | 26 | 27 | 28 | SuppressBuildableAutocreation 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /HotFixDemo/Pods/Target Support Files/Aspects/Aspects-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Aspects : NSObject 3 | @end 4 | @implementation PodsDummy_Aspects 5 | @end 6 | -------------------------------------------------------------------------------- /HotFixDemo/Pods/Target Support Files/Aspects/Aspects-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /HotFixDemo/Pods/Target Support Files/Aspects/Aspects.debug.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Aspects 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/Aspects" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/Aspects" 4 | PODS_BUILD_DIR = ${BUILD_DIR} 5 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 6 | PODS_ROOT = ${SRCROOT} 7 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/Aspects 8 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 9 | SKIP_INSTALL = YES 10 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 11 | -------------------------------------------------------------------------------- /HotFixDemo/Pods/Target Support Files/Aspects/Aspects.release.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Aspects 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/Aspects" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/Aspects" 4 | PODS_BUILD_DIR = ${BUILD_DIR} 5 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 6 | PODS_ROOT = ${SRCROOT} 7 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/Aspects 8 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 9 | SKIP_INSTALL = YES 10 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 11 | -------------------------------------------------------------------------------- /HotFixDemo/Pods/Target Support Files/Aspects/Aspects.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Aspects 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/Aspects" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/Aspects" 4 | PODS_BUILD_DIR = ${BUILD_DIR} 5 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 6 | PODS_ROOT = ${SRCROOT} 7 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/Aspects 8 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 9 | SKIP_INSTALL = YES 10 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 11 | -------------------------------------------------------------------------------- /HotFixDemo/Pods/Target Support Files/Pods-HotFixDemo-HotFixDemoUITests/Pods-HotFixDemo-HotFixDemoUITests-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | 4 | ## Aspects 5 | 6 | The MIT License (MIT) 7 | 8 | Copyright (c) 2014 Peter Steinberger, steipete@gmail.com 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | Generated by CocoaPods - https://cocoapods.org 28 | -------------------------------------------------------------------------------- /HotFixDemo/Pods/Target Support Files/Pods-HotFixDemo-HotFixDemoUITests/Pods-HotFixDemo-HotFixDemoUITests-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | The MIT License (MIT) 18 | 19 | Copyright (c) 2014 Peter Steinberger, steipete@gmail.com 20 | 21 | Permission is hereby granted, free of charge, to any person obtaining a copy 22 | of this software and associated documentation files (the "Software"), to deal 23 | in the Software without restriction, including without limitation the rights 24 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 25 | copies of the Software, and to permit persons to whom the Software is 26 | furnished to do so, subject to the following conditions: 27 | 28 | The above copyright notice and this permission notice shall be included in all 29 | copies or substantial portions of the Software. 30 | 31 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 32 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 33 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 34 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 35 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 36 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 37 | SOFTWARE. 38 | License 39 | MIT 40 | Title 41 | Aspects 42 | Type 43 | PSGroupSpecifier 44 | 45 | 46 | FooterText 47 | Generated by CocoaPods - https://cocoapods.org 48 | Title 49 | 50 | Type 51 | PSGroupSpecifier 52 | 53 | 54 | StringsTable 55 | Acknowledgements 56 | Title 57 | Acknowledgements 58 | 59 | 60 | -------------------------------------------------------------------------------- /HotFixDemo/Pods/Target Support Files/Pods-HotFixDemo-HotFixDemoUITests/Pods-HotFixDemo-HotFixDemoUITests-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_HotFixDemo_HotFixDemoUITests : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_HotFixDemo_HotFixDemoUITests 5 | @end 6 | -------------------------------------------------------------------------------- /HotFixDemo/Pods/Target Support Files/Pods-HotFixDemo-HotFixDemoUITests/Pods-HotFixDemo-HotFixDemoUITests.debug.xcconfig: -------------------------------------------------------------------------------- 1 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 2 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/Aspects" 3 | LIBRARY_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Aspects" 4 | OTHER_LDFLAGS = $(inherited) -ObjC -l"Aspects" 5 | PODS_BUILD_DIR = ${BUILD_DIR} 6 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 8 | PODS_ROOT = ${SRCROOT}/Pods 9 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 10 | -------------------------------------------------------------------------------- /HotFixDemo/Pods/Target Support Files/Pods-HotFixDemo-HotFixDemoUITests/Pods-HotFixDemo-HotFixDemoUITests.release.xcconfig: -------------------------------------------------------------------------------- 1 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 2 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/Aspects" 3 | LIBRARY_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Aspects" 4 | OTHER_LDFLAGS = $(inherited) -ObjC -l"Aspects" 5 | PODS_BUILD_DIR = ${BUILD_DIR} 6 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 8 | PODS_ROOT = ${SRCROOT}/Pods 9 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 10 | -------------------------------------------------------------------------------- /HotFixDemo/Pods/Target Support Files/Pods-HotFixDemo/Pods-HotFixDemo-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | 4 | ## Aspects 5 | 6 | The MIT License (MIT) 7 | 8 | Copyright (c) 2014 Peter Steinberger, steipete@gmail.com 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | Generated by CocoaPods - https://cocoapods.org 28 | -------------------------------------------------------------------------------- /HotFixDemo/Pods/Target Support Files/Pods-HotFixDemo/Pods-HotFixDemo-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | The MIT License (MIT) 18 | 19 | Copyright (c) 2014 Peter Steinberger, steipete@gmail.com 20 | 21 | Permission is hereby granted, free of charge, to any person obtaining a copy 22 | of this software and associated documentation files (the "Software"), to deal 23 | in the Software without restriction, including without limitation the rights 24 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 25 | copies of the Software, and to permit persons to whom the Software is 26 | furnished to do so, subject to the following conditions: 27 | 28 | The above copyright notice and this permission notice shall be included in all 29 | copies or substantial portions of the Software. 30 | 31 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 32 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 33 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 34 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 35 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 36 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 37 | SOFTWARE. 38 | License 39 | MIT 40 | Title 41 | Aspects 42 | Type 43 | PSGroupSpecifier 44 | 45 | 46 | FooterText 47 | Generated by CocoaPods - https://cocoapods.org 48 | Title 49 | 50 | Type 51 | PSGroupSpecifier 52 | 53 | 54 | StringsTable 55 | Acknowledgements 56 | Title 57 | Acknowledgements 58 | 59 | 60 | -------------------------------------------------------------------------------- /HotFixDemo/Pods/Target Support Files/Pods-HotFixDemo/Pods-HotFixDemo-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_HotFixDemo : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_HotFixDemo 5 | @end 6 | -------------------------------------------------------------------------------- /HotFixDemo/Pods/Target Support Files/Pods-HotFixDemo/Pods-HotFixDemo.debug.xcconfig: -------------------------------------------------------------------------------- 1 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 2 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/Aspects" 3 | LIBRARY_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Aspects" 4 | OTHER_LDFLAGS = $(inherited) -ObjC -l"Aspects" 5 | PODS_BUILD_DIR = ${BUILD_DIR} 6 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 8 | PODS_ROOT = ${SRCROOT}/Pods 9 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 10 | -------------------------------------------------------------------------------- /HotFixDemo/Pods/Target Support Files/Pods-HotFixDemo/Pods-HotFixDemo.release.xcconfig: -------------------------------------------------------------------------------- 1 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 2 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/Aspects" 3 | LIBRARY_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Aspects" 4 | OTHER_LDFLAGS = $(inherited) -ObjC -l"Aspects" 5 | PODS_BUILD_DIR = ${BUILD_DIR} 6 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 8 | PODS_ROOT = ${SRCROOT}/Pods 9 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 10 | -------------------------------------------------------------------------------- /HotFixDemo/Pods/Target Support Files/Pods-HotFixDemoTests/Pods-HotFixDemoTests-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | Generated by CocoaPods - https://cocoapods.org 4 | -------------------------------------------------------------------------------- /HotFixDemo/Pods/Target Support Files/Pods-HotFixDemoTests/Pods-HotFixDemoTests-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | Generated by CocoaPods - https://cocoapods.org 18 | Title 19 | 20 | Type 21 | PSGroupSpecifier 22 | 23 | 24 | StringsTable 25 | Acknowledgements 26 | Title 27 | Acknowledgements 28 | 29 | 30 | -------------------------------------------------------------------------------- /HotFixDemo/Pods/Target Support Files/Pods-HotFixDemoTests/Pods-HotFixDemoTests-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_HotFixDemoTests : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_HotFixDemoTests 5 | @end 6 | -------------------------------------------------------------------------------- /HotFixDemo/Pods/Target Support Files/Pods-HotFixDemoTests/Pods-HotFixDemoTests.debug.xcconfig: -------------------------------------------------------------------------------- 1 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 2 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/Aspects" 3 | PODS_BUILD_DIR = ${BUILD_DIR} 4 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 5 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 6 | PODS_ROOT = ${SRCROOT}/Pods 7 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 8 | -------------------------------------------------------------------------------- /HotFixDemo/Pods/Target Support Files/Pods-HotFixDemoTests/Pods-HotFixDemoTests.release.xcconfig: -------------------------------------------------------------------------------- 1 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 2 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/Aspects" 3 | PODS_BUILD_DIR = ${BUILD_DIR} 4 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 5 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 6 | PODS_ROOT = ${SRCROOT}/Pods 7 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 zzyong 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![visitors](https://visitor-badge.laobi.icu/badge?page_id=zhiyongzou.dynamic.oc) 2 | ![Objective-C](https://img.shields.io/badge/language-Objective--C-orange.svg) 3 | 4 | # 深入理解 iOS 热修复原理 5 | 6 | 7 | ## 背景 8 | 顾名思义热修复就是使 App 具备线上修复 bug 的能力,但是遗憾的是苹果出于安全的考虑禁用了热修复。虽然 App 审核加快了,但是依然无法很好的控制线上 bug 的影响范围。由于 JSPatch 存在审核风险,所以我们需要另辟蹊径,自研一套适合自己的热修复框架。 9 | 10 | ## 目标 11 | 大部分线上 bug 并不需要完全替换原方法实现才能修复问题,我们可以在原来的方法实现前后增加一些自定的方法调用,或者是修改原方法的调用参数,或者是修改其内部的某一个方法调用即可修复问题。 12 | 13 | ```objc 14 | - (void)sayHelloTo:(NSString *)name 15 | { 16 | // 当 name = nil 会发生 nil 异常。所以我们需要加一个 nil 保护逻辑 17 | // 像这种情况就不需要完全替换原方法实现,只需要在该方法调用前增加一个 if 条件语句即可 18 | 19 | //fix code 20 | // if (name == nil) { 21 | // return; 22 | // } 23 | 24 | [self.nameList addObject:name]; 25 | NSLog(@"Hello %@", name); 26 | } 27 | 28 | ``` 29 | 30 | 综上所述,热修复只需要具备以下几点即可: 31 | 32 | 1. 方法替换为空实现 33 | 2. 方法参数修改 34 | 3. 方法返回值修改 35 | 4. 方法调用前后插入自定义代码 36 | * 支持任意 OC 方法调用 37 | * 支持赋值语句 38 | * 支持 if 语句:**==、!=、>、>=、<、<=、||、&&** 39 | * 支持 super 调用 40 | * 支持自定义局部变量 41 | * 支持 return 语句 42 | 43 | ## 原理 44 | 热修复的核心原理: 45 | 46 | 1. 拦截目标方法调用,让其调用转发到预先埋好的特定方法中 47 | 2. 获取目标方法的调用参数 48 | 49 | 只要完成了上面两步,你就可以随心所欲了。在肆意发挥前,你需要掌握一些 Runtime 的基础理论,下面进入 Runtime 理论速成教程。 50 | 51 | ### Runtime 速成 52 | Runtime 可以在运行时去动态的创建类和方法,因此你可以通过字符串反射的方式去动态调用OC方法、动态的替换方法、动态新增方法等等。下面简单介绍下热修复所需要用到的 Runtime 知识点。 53 | 54 | #### Class 反射创建 55 | 通过字符串创建类:Class 56 | 57 | ```objc 58 | // 方式1 59 | NSClassFromString(@"NSObject"); 60 | 61 | // 方式2 62 | objc_getClass("NSObject"); 63 | ``` 64 | 65 | #### SEL 反射创建 66 | 通过字符串创建方法 selector 67 | 68 | ```objc 69 | // 方式1 70 | @selector(init); 71 | 72 | // 方式2 73 | sel_registerName("init"); 74 | 75 | // 方式3 76 | NSSelectorFromString(@"init"); 77 | ``` 78 | 79 | #### 方法替换/交换 80 | - 方法替换:`class_replaceMethod` 81 | - 方法交换:`method_exchangeImplementations` 82 | 83 | ```objc 84 | // 方法替换 85 | - (void)methodReplace 86 | { 87 | Method methodA = class_getInstanceMethod(self.class, @selector(myMethodA)); 88 | IMP impA = method_getImplementation(methodA); 89 | class_replaceMethod(self.class, @selector(myMethodC), impA, method_getTypeEncoding(methodA)); 90 | 91 | // print: myMethodA 92 | [self myMethodC]; 93 | } 94 | 95 | // 方法交换 96 | - (void)methodExchange 97 | { 98 | Method methodA = class_getInstanceMethod(self.class, @selector(myMethodA)); 99 | Method methodB = class_getInstanceMethod(self.class, @selector(myMethodB)); 100 | method_exchangeImplementations(methodA, methodB); 101 | 102 | // print: myMethodB 103 | [self myMethodA]; 104 | 105 | // print: myMethodA 106 | [self myMethodB]; 107 | } 108 | 109 | - (void)myMethodA 110 | { 111 | NSLog(@"myMethodA"); 112 | } 113 | 114 | - (void)myMethodB 115 | { 116 | NSLog(@"myMethodB"); 117 | } 118 | 119 | - (void)myMethodC 120 | { 121 | NSLog(@"myMethodC"); 122 | } 123 | ``` 124 | 125 | #### 新增类 126 | 通过字符串动态新增一个类 127 | 128 | 1. 首先创建新类:`objc_allocateClassPair` 129 | 2. 然后注册新创建的类:`objc_registerClassPair` 130 | 131 | 这里有个小知识点,为什么类创建的方法名是`objc_allocateClassPair`,而不是`objc_allocateClass`呢?这是因为它同时创建了一个类(class)和元类(metaclass)。关于元类可以看这篇文章:[What is a meta-class in Objective-C?](https://www.cocoawithlove.com/2010/01/what-is-meta-class-in-objective-c.html) 132 | 133 | ```objc 134 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 135 | { 136 | [self addNewClassPair]; 137 | 138 | Class MyObject = NSClassFromString(@"MyObject"); 139 | NSObject *myObj = [[MyObject alloc] init]; 140 | [myObj performSelector:@selector(sayHello)]; 141 | 142 | return YES; 143 | } 144 | 145 | - (void)addNewClassPair 146 | { 147 | Class myCls = objc_allocateClassPair([NSObject class], "MyObject", 0); 148 | objc_registerClassPair(myCls); 149 | [self addNewMethodWithClass:myCls]; 150 | } 151 | ``` 152 | 153 | #### 新增方法 154 | 155 | 新增方法:`class_addMethod` 156 | 157 | 这里也有个小知识点,就是使用特定字符串描述方法返回值和参数,例如:`v@:`。其具体映射关系请移步:[Type Encodings](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html#//apple_ref/doc/uid/TP40008048-CH100-SW1) 158 | 159 | ```objc 160 | void sayHello(id self, SEL _cmd) 161 | { 162 | NSLog(@"%@ %s", self, __func__); 163 | } 164 | 165 | - (void)addNewMethodWithClass:(Class)targetClass 166 | { 167 | class_addMethod(targetClass, @selector(sayHello), (IMP)sayHello, "v@:"); 168 | } 169 | ``` 170 | 171 | #### 消息转发 172 | 173 | 当给对象发送消息时,如果对象没有找到对应的方法实现,那么就会进入正常的消息转发流程。其主要流程如下: 174 | 175 | ```objc 176 | // 1.运行时动态添加方法 177 | + (BOOL)resolveInstanceMethod:(SEL)sel 178 | 179 | // 2.快速转发 180 | - (id)forwardingTargetForSelector:(SEL)aSelector 181 | 182 | // 3.构建方法签名 183 | - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector 184 | 185 | // 4.消息转发 186 | - (void)forwardInvocation:(NSInvocation *)anInvocation 187 | 188 | ``` 189 | 190 | 其中最后的`forwardInvocation:`会传递一个`NSInvocation`对象(**Ps:NSInvocation 可以理解为是消息发送`objc_msgSend(void id self, SEL op, ... )`的对象**)。NSInvocation 包含了这个方法调用的所有信息:selector、参数类型、参数值和返回值类型。此外,你还可以去更改参数值和返回值。 191 | 192 | **除了上面的正常消息转发,我们还可以借助`_objc_msgForward`方法让消息强制转发** 193 | 194 | ```objc 195 | Method methodA = class_getInstanceMethod(self.class, @selector(myMethodA)); 196 | IMP msgForwardIMP = _objc_msgForward; 197 | 198 | // 替换 myMethodA 的实现后,每次调用 myMethodA 都会进入消息转发 199 | class_replaceMethod(self.class, @selector(myMethodA), msgForwardIMP, method_getTypeEncoding(methodA)); 200 | ``` 201 | 202 | ### Method 调用方式 203 | 204 | 1. 常规调用 205 | 2. 反射调用 206 | 3. objc_msgSend 207 | 4. C 函数调用 208 | 5. NSInvocation 调用 209 | 210 | ```objc 211 | @interface People : NSObject 212 | 213 | - (void)helloWorld; 214 | 215 | @end 216 | 217 | // 常规调用 218 | People *people = [[People alloc] init]; 219 | [people helloWorld]; 220 | 221 | // 反射调用 222 | Class cls = NSClassFromString(@"People"); 223 | id obj = [[cls alloc] init]; 224 | [obj performSelector:NSSelectorFromString(@"helloWorld")]; 225 | 226 | // objc_msgSend 227 | ((void(*)(id, SEL))objc_msgSend)(people, sel_registerName("helloWorld")); 228 | 229 | // C 函数调用 230 | Method initMethod = class_getInstanceMethod([People class], @selector(helloWorld)); 231 | IMP imp = method_getImplementation(initMethod); 232 | ((void (*) (id, SEL))imp)(people, @selector(helloWorld)); 233 | 234 | // NSInvocation 调用 235 | NSMethodSignature *sig = [[People class] instanceMethodSignatureForSelector:sel_registerName("helloWorld")]; 236 | NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:sig]; 237 | invocation.target = people; 238 | invocation.selector = sel_registerName("helloWorld"); 239 | [invocation invoke]; 240 | ``` 241 | 242 | 第五种 **NSInvocation 调用** 是热修复调用任意 OC 方法的核心基础。通过 NSInvocation 不但可以自定义函数的参数值和返回值,而且还可以自定义方法选择器(selector) 和消息接收对象(target)。因此,我们可以通过字符串的方式构建任意 OC 方法调用。 243 | 244 | 245 | ## 实战 246 | 掌握了理论知识后,实践起来就不难了。上面说到热修复的核心就是**拦截目标方法调用**并且拿到**方法的参数值**,要实现这一点其实很容易。具体步骤如下: 247 | 248 | 1. 首先新增一个方法实现跟目标方法一致的别名方法,用来调用原目标方法。 249 | 2. 其次将目标方法的函数实现(IMP)替换成 `_objc_msgForward`,目的是让目标方法进行强制转发 250 | 3. 最后将目标方法类的`forwardInvocation:`方法实现替换成通用的自定义实现,其目的是可以在这个自定义实现里面拿到目标方法的 `NSInvocation` 对象。 251 | 252 | 下面是热修复核心代码的简要实现。 253 | 254 | > 实战部分给出的示例代码不考虑异常等情况,只为阐明热修复原理 255 | 256 | ```objc 257 | typedef void(^OCDynamicBlock)(id self, NSInvocation *originalInvocation); 258 | 259 | @implementation NSObject (OCDynamic) 260 | 261 | + (void)dy_hookSelector:(SEL)selector withBlock:(void(^)(id self, NSInvocation *originalInvocation))block 262 | { 263 | // 保存回调 block 264 | [dynamicBlockMap() setObject:block forKey:NSStringFromSelector(selector)]; 265 | 266 | // 1.获取目标方法的 IMP 267 | Method targetMethod = class_getInstanceMethod(self, selector); 268 | IMP targetMethodIMP = method_getImplementation(targetMethod); 269 | 270 | // 2.新增一个目标方法的别名方法 271 | NSString *aliasSelString = [NSString stringWithFormat:@"oc_dynamic_%@", NSStringFromSelector(selector)]; 272 | const char *typeEncoding = method_getTypeEncoding(targetMethod); 273 | BOOL isSuccessed = class_addMethod(self, NSSelectorFromString(aliasSelString), targetMethodIMP, typeEncoding); 274 | NSLog(@"%@ add method successfully: %d", aliasSelString, isSuccessed); 275 | 276 | // 3.将目标方法实现替换成 _objc_msgForward 277 | class_replaceMethod(self, selector, (IMP)_objc_msgForward, typeEncoding); 278 | 279 | // 4.将目标类的 forwardInvocation 替换为自定义 dy_forwardInvocation_center 280 | class_replaceMethod(self, @selector(forwardInvocation:), (IMP)dy_forwardInvocation_center, "v@:@"); 281 | } 282 | 283 | static NSMutableDictionary* dynamicBlockMap(void) 284 | { 285 | static NSMutableDictionary *_dynamicBlockMap; 286 | static dispatch_once_t onceToken; 287 | dispatch_once(&onceToken, ^{ 288 | _dynamicBlockMap = [NSMutableDictionary dictionary]; 289 | }); 290 | 291 | return _dynamicBlockMap; 292 | } 293 | 294 | static void dy_forwardInvocation_center(id self, SEL _cmd, NSInvocation *anInvocation) 295 | { 296 | // 获取回调 block 297 | OCDynamicBlock targetBlock = [dynamicBlockMap() objectForKey:NSStringFromSelector(anInvocation.selector)]; 298 | 299 | // 将 anInvocation 的 sel 设置为别名 sel 300 | NSString *aliasSelString = [NSString stringWithFormat:@"oc_dynamic_%@", NSStringFromSelector(anInvocation.selector)]; 301 | anInvocation.selector = NSSelectorFromString(aliasSelString); 302 | 303 | // 调用回调 block 304 | targetBlock(self, anInvocation); 305 | } 306 | 307 | @end 308 | 309 | ``` 310 | 311 | 下面是 MyClassC 的实现代码 312 | 313 | ```objc 314 | @implementation MyClassC 315 | 316 | - (void)sayHelloTo:(NSString *)name 317 | { 318 | NSLog(@"%s: %@", __func__, name); 319 | } 320 | 321 | @end 322 | ``` 323 | 324 | 下面是 MyClassC 的测试代码 325 | 326 | ```objc 327 | - (void)hookMyClassCMethod 328 | { 329 | [MyClassC dy_hookSelector:@selector(sayHelloTo:) withBlock:^(id _Nonnull self, NSInvocation * _Nonnull originalInvocation) { 330 | __weak id value = nil; 331 | [originalInvocation getArgument:&value atIndex:2]; 332 | NSLog(@"%@ %@", NSStringFromSelector(originalInvocation.selector), value); 333 | }]; 334 | 335 | // 测试 MyClassC 336 | [[MyClassC new] sayHelloTo:@"jack"]; 337 | } 338 | ``` 339 | 340 | 虽然调用了 `[[MyClassC new] sayHelloTo:@"jack"];`,但是你会发现并没有对应的`sayHelloTo: jack`日志输出,而是输出了:`oc_dynamic_sayHelloTo: jack`。这说明了该方法调用被成功拦截并且回调到了对应的 block 中。至此,我们简要的热修复功能已实现了。是不是很简单? 341 | 342 | 上面的示例代码都是本地 Hard Code,下面就来聊聊如何动态的 Hook 指定类的方法及改变修改目标方法的调用行为。从 MyClassC 的测试代码中可以看出,我们可以用字符串反射的方式实现动态 Hook。 343 | 344 | ```objc 345 | [self dy_hookMethodWithHookMap:@{ 346 | @"cls": @"MyClassC", 347 | @"sel": @"sayHelloTo:" 348 | }]; 349 | 350 | // 测试 MyClassC 351 | [[MyClassC new] sayHelloTo:@"jack"]; 352 | 353 | - (void)dy_hookMethodWithHookMap:(NSDictionary *)hookMap { 354 | Class cls = NSClassFromString([hookMap objectForKey:@"cls"]); 355 | SEL sel = NSSelectorFromString([hookMap objectForKey:@"sel"]); 356 | 357 | [cls dy_hookSelector:sel withBlock:^(id _Nonnull self, NSInvocation * _Nonnull originalInvocation) { 358 | __weak id value = nil; 359 | [originalInvocation getArgument:&value atIndex:2]; 360 | NSLog(@"%@ %@", NSStringFromSelector(originalInvocation.selector), value); 361 | }]; 362 | } 363 | ``` 364 | 365 | 上面的示例代码中,我们只需要构建指定规则的 hookMap 即可实现动态 Hook,我们可以根据实际项目实现一套适合自己的 DSL 语法。然后解析对应的 DSL 生成 hookMap。 366 | 367 | 由于我们拿到了目标方法调用的 NSInvocation 对象,所以我们可以任意的修改方法的参数值、返回值、selector 及 target。下面简单介绍下如何实现上面的目标。 368 | 369 | ### 一、方法替换为空实现 370 | 替换为空实现其实很简单,就是不处理回调中的 `originalInvocation` 即可。 371 | 372 | ```objc 373 | [weakSelf dy_hookMethodWithHookMap:@{ 374 | @"cls": @"ViewController", 375 | @"sel": @"myEmptyMethod", 376 | @"isReplcedEmpty": @(YES) 377 | }]; 378 | 379 | // 将不会打印 -[ViewController myEmptyMethod] 380 | [weakSelf myEmptyMethod]; 381 | 382 | [cls dy_hookSelector:sel withBlock:^(id _Nonnull self, NSInvocation * _Nonnull originalInvocation) { 383 | 384 | if ([hookMap[@"isReplcedEmpty"] boolValue]) { 385 | NSLog(@"[%@ %@] replace into empty IMP", cls, NSStringFromSelector(sel)); 386 | return; 387 | } 388 | }]; 389 | ``` 390 | 391 | ### 二、方法参数修改 392 | 通过 NSInvocation 的 `- (void)setArgument:(void *)argumentLocation atIndex:(NSInteger)idx`即可修改方法参数值。例如动态的把 `sayHelloTo:` 方法的参数值`jack` 改为 `Lili`。 393 | 394 | **知识点:** 395 | 396 | > 所有 OC 方法都有两个隐藏的参数:第一个是`self`, 第二个是`selector`,所以我们在设置参数值时 index 是从 2 开始的 397 | 398 | ```objc 399 | [weakSelf dy_hookMethodWithHookMap:@{ 400 | @"cls": @"MyClassC", 401 | @"sel": @"sayHelloTo:", 402 | @"parameters": @[@"Lili"] 403 | }]; 404 | 405 | // 打印信息是-[MyClassC sayHelloTo:]: Lili ,而不是 jack 406 | [[MyClassC new] sayHelloTo:@"jack"]; 407 | 408 | [cls dy_hookSelector:sel withBlock:^(id _Nonnull self, NSInvocation * _Nonnull originalInvocation) { 409 | 410 | if ([hookMap[@"isReplcedEmpty"] boolValue]) { 411 | NSLog(@"[%@ %@] replace into empty IMP", cls, NSStringFromSelector(sel)); 412 | return; 413 | } 414 | 415 | [parameters enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 416 | [originalInvocation setArgument:&obj atIndex:idx + 2]; 417 | }]; 418 | 419 | [originalInvocation invoke]; 420 | }]; 421 | ``` 422 | 423 | ### 三、方法返回值修改 424 | 通过 NSInvocation 的 `- (void)setReturnValue:(void *)retLoc`即可修改方法返回值。例如将 `MyClassC` 的 `className` 方法的返回值改为 `Return value had change` 425 | 426 | ```objc 427 | - (NSString *)className { 428 | return @"MyClassC"; 429 | } 430 | 431 | [weakSelf dy_hookMethodWithHookMap:@{ 432 | @"cls": @"MyClassC", 433 | @"sel": @"className", 434 | @"returnValue": @"Return value had change" 435 | }]; 436 | 437 | // 打印信息是 Return value had change ,而不是 MyClassC 438 | [NSLog(@"%@", [[MyClassC new] className]); 439 | 440 | [cls dy_hookSelector:sel withBlock:^(id _Nonnull self, NSInvocation * _Nonnull originalInvocation) { 441 | if ([hookMap[@"isReplcedEmpty"] boolValue]) { 442 | NSLog(@"[%@ %@] replace into empty IMP", cls, NSStringFromSelector(sel)); 443 | return; 444 | } 445 | 446 | [parameters enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 447 | [originalInvocation setArgument:&obj atIndex:idx + 2]; 448 | }]; 449 | 450 | [originalInvocation invoke]; 451 | 452 | id returnValue = [hookMap objectForKey:@"returnValue"]; 453 | if (returnValue) { 454 | [originalInvocation setReturnValue:&returnValue]; 455 | } 456 | }]; 457 | ``` 458 | 459 | ### 四、方法调用前后插入自定义代码 460 | 我们可以在回调 block 中做一些自定义调用,等这些完成后再调用`[originalInvocation invoke]` 。例如在 `myMethod ` 调用前调用 `dynamicCallMethod `方法 461 | 462 | ```objc 463 | - (void)dynamicCallMethod { 464 | NSLog(@"%s Dynamic call", __func__); 465 | } 466 | 467 | [weakSelf dy_hookMethodWithHookMap:@{ 468 | @"cls": @"MyClassC", 469 | @"sel": @"myMethod", 470 | @"customMethods": @[@"self.dynamicCallMethod"] 471 | }]; 472 | 473 | // 会先打印 -[MyClassC dynamicCallMethod] Dynamic call,然后再打印 -[MyClassC myMethod] 474 | [[MyClassC new] myMethod]; 475 | 476 | [cls dy_hookSelector:sel withBlock:^(id _Nonnull self, NSInvocation * _Nonnull originalInvocation) { 477 | 478 | if ([hookMap[@"isReplcedEmpty"] boolValue]) { 479 | NSLog(@"[%@ %@] replace into empty IMP", cls, NSStringFromSelector(sel)); 480 | return; 481 | } 482 | 483 | [customMethods enumerateObjectsUsingBlock:^(NSString * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 484 | NSArray *targets = [obj componentsSeparatedByString:@"."]; 485 | 486 | id target = nil; 487 | if ([targets.firstObject isEqualToString:@"self"]) { 488 | target = self; 489 | } 490 | 491 | SEL sel = NSSelectorFromString(targets.lastObject); 492 | NSMethodSignature *targetSig = [[target class] instanceMethodSignatureForSelector:sel]; 493 | 494 | NSInvocation *customInvocation = [NSInvocation invocationWithMethodSignature:targetSig]; 495 | customInvocation.target = target; 496 | customInvocation.selector = sel; 497 | [customInvocation invoke]; 498 | 499 | target = nil; 500 | }]; 501 | 502 | [parameters enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 503 | [originalInvocation setArgument:&obj atIndex:idx + 2]; 504 | }]; 505 | 506 | [originalInvocation invoke]; 507 | 508 | id returnValue = [hookMap objectForKey:@"returnValue"]; 509 | if (returnValue) { 510 | [originalInvocation setReturnValue:&returnValue]; 511 | } 512 | }]; 513 | 514 | ``` 515 | 516 | 上面简单的阐述了如何通过字符串方式调用 OC 方法,如果要实现可以调用任意 OC 方法,还需要继续完善上面的解析逻辑,但其中核心点都是通过构建 `NSInvocation`。这里算是抛砖引玉吧。 517 | 518 | OCDynamic 只是简单的实现了热修复的核心逻辑,这是远远不够的。虽然我们可以不断完善,但是业界已经有了完善的开源库:[Aspects](https://github.com/steipete/Aspects)。`Aspects`库是`OCDynamic`的加强完善版。因此,我们只需要站在巨人的肩膀上即可,就没有必要重复造轮子了。下面就来分析下`Aspects`的基本原理及其可以优化的点。 519 | 520 | ## [Aspects](https://github.com/steipete/Aspects) 521 | Aspects 可以拦截目标方法调用,并且将目标方法调用以 NSInvocation 形式返回。 下面简单介绍下其主要构成、Hook 流程、Invoke 流程及该库存在的一些问题。 522 | 523 | * **AspectsContainer**:Tracks all aspects for an object/class 524 | * **AspectIdentifier**:Tracks a single aspect 525 | 526 | ### 一、Hook 流程 527 | 1. 检查 selector 是否可以替换,里面涉及一些黑名单等判断 528 | 2. 获取 AspectsContainer,如果为空则创建并绑定目标类 529 | 3. 创建 AspectIdentifier,用来保存回调`block`和 `AspectOptions` 等信息 530 | 4. 将目标类 `forwardInvocation:` 方法替换为自定义方法(\_\_ASPECTS\_ARE\_BEING\_CALLED\_\_) 531 | 5. 目标类新增一个带有` aspects_`前缀的方法,新方法(aliasSelector)实现跟目标方法相同 532 | 6. 将目标方法实现替换为 `_objc_msgForward` 533 | 534 | ```objc 535 | // 将目标类 forwardInvocation: 方法替换为自定义方法 536 | IMP originalImplementation = class_replaceMethod(klass, @selector(forwardInvocation:), (IMP)__ASPECTS_ARE_BEING_CALLED__, "v@:@"); 537 | if (originalImplementation) { 538 | class_addMethod(klass, NSSelectorFromString(AspectsForwardInvocationSelectorName), originalImplementation, "v@:@"); 539 | } 540 | 541 | // 目标类新增一个带有 aspects_ 前缀的方法,新方法(aliasSelector)实现跟目标方法相同 542 | Method targetMethod = class_getInstanceMethod(klass, selector); 543 | IMP targetMethodIMP = method_getImplementation(targetMethod); 544 | 545 | const char *typeEncoding = method_getTypeEncoding(targetMethod); 546 | SEL aliasSelector = NSSelectorFromString([AspectsMessagePrefix stringByAppendingFormat:@"_%@", NSStringFromSelector(selector)]); 547 | class_addMethod(klass, aliasSelector, method_getImplementation(targetMethod), typeEncoding); 548 | 549 | // 将目标方法实现替换为 _objc_msgForward 550 | class_replaceMethod(klass, selector, aspect_getMsgForwardIMP(self, selector), typeEncoding); 551 | 552 | ``` 553 | 554 | #### 二、Invoke 流程 555 | 1. 调用目标方法进入消息转发流程 556 | 2. 调用自定义 `__ASPECTS_ARE_BEING_CALLED__` 方法 557 | 3. 获取对应 invocation,将 invocation.selector 设置为 aliasSelector 558 | 4. 通过 aliasSelector 获取对应 AspectsContainer 559 | 5. 根据 AspectOptions 调用用户自定实现(目标方法调用前/后/替换) 560 | 561 | #### 三、Aspects 优化 562 | * 使用了自旋锁,存在优先级反转问题,使用 `pthread_mutex_lock` 代替即可 563 | * 特殊 `struct` 判断逻辑不够全面,例如 NSRange, NSPoint 等在 x86-64 位架构下有问题,需要自行兼容 564 | 565 | ```objc 566 | #if defined(__LP64__) && __LP64__ 567 | if (valueSize == 16) { 568 | methodReturnsStructValue = NO; 569 | } 570 | #endif 571 | ``` 572 | 573 | * 类方法无法直接 hook, 不过可以 hook 其 `Meta class` 元类方式进行解决 574 | 575 | ```c 576 | object_getClass(targetCls) 577 | ``` 578 | 579 | * 无法同时 hook 一个类的实例方法和类方法,原因是使用了相同的 `swizzledClasse` key, 解决如下: 580 | 581 | ```objc 582 | static Class aspect_swizzleClassInPlace(Class klass) { 583 | NSCParameterAssert(klass); 584 | NSString *className = [NSString stringWithFormat:@"%@_%p", NSStringFromClass(klass), klass]; 585 | 586 | _aspect_modifySwizzledClasses(^(NSMutableSet *swizzledClasses) { 587 | if (![swizzledClasses containsObject:className]) { 588 | aspect_swizzleForwardInvocation(klass); 589 | [swizzledClasses addObject:className]; 590 | } 591 | }); 592 | return klass; 593 | } 594 | 595 | static void aspect_undoSwizzleClassInPlace(Class klass) { 596 | NSCParameterAssert(klass); 597 | NSString *className = [NSString stringWithFormat:@"%@_%p", NSStringFromClass(klass), klass]; 598 | 599 | _aspect_modifySwizzledClasses(^(NSMutableSet *swizzledClasses) { 600 | if ([swizzledClasses containsObject:className]) { 601 | aspect_undoSwizzleForwardInvocation(klass); 602 | [swizzledClasses removeObject:className]; 603 | } 604 | }); 605 | } 606 | ``` 607 | 608 | ## NSInvocation 的坑 609 | NSInvocation 在取其参数值和返回值的时候需要注意内存管理的问题,下面介绍下在实际开发中所遇到的问题。 610 | 611 | ### 一、`EXC_BAD_ACCESS` 612 | 613 | 从 `-forwardInvocation:` 里的 `NSInvocation` 对象取参数值时,若参数值是id类型,一般会这样取: 614 | 615 | ```objc 616 | id value = nil; 617 | [invocation getArgument:&value atIndex:2]; 618 | ``` 619 | 620 | 但是这种写法存在 `EXC_BAD_ACCESS` 风险。例如:Hook NSMutableArray 的 insertObject:atIndex: 方法。你会发现在有些系统调用会出现野指针崩溃 621 | 622 | ```objc 623 | [NSClassFromString(@"__NSArrayM") aspect_hookSelector:@selector(insertObject:atIndex:) withOptions:AspectPositionInstead usingBlock:^(id info){ 624 | 625 | id value = nil; 626 | [info.originalInvocation getArgument:&value atIndex:2]; 627 | if (value) { 628 | [info.originalInvocation invoke]; 629 | } 630 | } error:NULL]; 631 | ``` 632 | 633 | 开启 `Zombie objects` 下的异常打印 634 | 635 | ``` objc 636 | -[UITraitCollection retain]: message sent to deallocated instance 0x6000007cde00 637 | ``` 638 | 639 | **原因分析:** 640 | 641 | 1. NSInvocation 不会引用参数,详情可以看官方文档(This class does not retain the arguments for the contained invocation by default) 642 | 2. ARC 在隐式赋值不会自动插入 retain 语句。在`[info.originalInvocation getArgument:&value atIndex:2];` 中,因为 value 是通过指针赋值(隐式赋值),所以 ARC 机制并不生效(具体可以参考:[ARC - Retainable object pointers section](https://clang.llvm.org/docs/AutomaticReferenceCounting.html#retainable-object-pointers)),这也导致了 value 没有调用 `retain` 方法 643 | 3. ARC 下 `id value` 相当于 `__strong id vaule`,`__strong` 类型的变量会在当前作用域结束后自动调用 `release`方法进行释放。其实现如下所示: 644 | 645 | ```objc 646 | void objc_storeStrong(id *object, id value) { 647 | id oldValue = *object; 648 | value = [value retain]; 649 | *object = value; 650 | [oldValue release]; 651 | } 652 | ``` 653 | 654 | 综上所述可以得出:value 并没有持有参数对象但又对参数对象进行释放,这导致参数对象被提前释放。如果此时再对该对象发送消息则会发生野指针崩溃 655 | 656 | **解决办法:** 657 | 658 | 1、将 value 变成 `__unsafe_unretained` 或 `__weak`,让 ARC 在它退出作用域时不插入 release 语句 659 | 660 | ```objc 661 | __unsafe_unretained id value = nil; 662 | ``` 663 | 664 | 2、通过 `__bridge` 转换让 value 持有返回对象,显示赋值 665 | 666 | ```objc 667 | id value = nil; 668 | void *result; 669 | [invocation getArgument:&result atIndex:2]; 670 | value = (__bridge id)result; 671 | ``` 672 | 673 | ### 二、Memory Leak 674 | 675 | 使用 `NSInvocation` 调用`alloc/new/copy/mutableCopy`方法时会发生内存泄漏,示例如下: 676 | 677 | ```objc 678 | - (void)memoryLeakA 679 | { 680 | NSMethodSignature *signature = [NSObject methodSignatureForSelector:@selector(new)]; 681 | NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature]; 682 | invocation.target = MyClassA.class; 683 | invocation.selector = @selector(new); 684 | [invocation invoke]; 685 | } 686 | 687 | - (void)memoryLeakB 688 | { 689 | [MyClassB performSelector:@selector(new)]; 690 | } 691 | 692 | ``` 693 | 694 | 使用 **Memory Graph** 查看对象内存时会发现 `MyClassA` 和 `MyClassB` 都被标记为内存泄漏了 695 | 696 | **原因分析:** 697 | 698 | ARC 机制中,当调用 `alloc/new/copy/mutableCopy` 方法返回的对象是直接持有的,其引用计数为`1`。在常规的方法调用时编译器会自动调用 release,而使用`NSInvocation`或`performSelector:`动态调用`alloc/new/copy/mutableCopy`方法时,ARC 并不会自动调用`release`,所以导致内存泄漏。 699 | 700 | **谨记:** 701 | 702 | > ARC 对动态方法调用是无能为力的😅 703 | 704 | **温馨提示:** 705 | > 有兴趣的可以 Xcode 看看这两种方式的汇编实现🤔 (Product -> Perform Action -> Assemble) 706 | 707 | **解决办法:** 708 | 709 | 1. 使用`__bridge_transfer`修饰符将返回对象的内存管理权移交出来,让外部对象管理其内存 710 | 711 | ```objc 712 | // 方法1 713 | id resultObj = nil; 714 | NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature]; 715 | invocation.target = [NSObject class]; 716 | invocation.selector = @selector(new); 717 | [invocation invoke]; 718 | 719 | void *result; 720 | [invocation getReturnValue:&result]; 721 | 722 | if ([selName isEqualToString:@"alloc"] || 723 | [selName isEqualToString:@"new"] || 724 | [selName isEqualToString:@"copy"] || 725 | [selName isEqualToString:@"mutableCopy"]) { 726 | resultObj = (__bridge_transfer id)result; 727 | } else { 728 | resultObj = (__bridge id)result; 729 | } 730 | 731 | ``` 732 | 733 | 2. 采用常规方法调用代替 NSInvocation 734 | 735 | ```objc 736 | // 方法2 737 | id resultObj = nil; 738 | if ([selName isEqualToString:@"alloc"]) { 739 | resultObj = [[target class] alloc]; 740 | } else if ([selName isEqualToString:@"new"]) { 741 | resultObj = [[target class] new]; 742 | } else if ([selName isEqualToString:@"copy"]) { 743 | resultObj = [target copy]; 744 | } else if ([selName isEqualToString:@"mutableCopy"]) { 745 | resultObj = [target mutableCopy]; 746 | } else { 747 | NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature]; 748 | invocation.target = [NSObject class]; 749 | invocation.selector = @selector(new); 750 | [invocation invoke]; 751 | 752 | void *result; 753 | [invocation getReturnValue:&result]; 754 | resultObj = (__bridge id)result; 755 | } 756 | ``` 757 | 758 | ## 审核分析 759 | 其实能不能成功上线是热修复的首要前提,我们辛辛苦苦开的框架如果上不了线,那一切都是徒劳无功。下面就来分析下其审核风险。 760 | 761 | - 首先这个是我们自研的,所以苹果审核无法通过静态代码扫描识别。 762 | - 其次系统库内部也大量使用了消息转发机制。可以通过符号断点验证`_objc_msgForward`和`forwardInvocation:`。所以不存在风险。 763 | - 苹果无法采用动态检验消息转发,非系统调用都不能使用,这个成本太大了,几乎不可能。 764 | - Aspects 库目前线上有大量使用,为此不用担心。就算 Aspects 被禁用,参考 Aspects 自己实现也不难。 765 | 766 | 综上所述:无审核风险。 767 | 768 | 当然热修复框架只是为了更好的控制线上 bug 影响范围和给用户更好的体验。不建议基于其它目的使用🤔 769 | 770 | ## 后记 771 | 随着项目的业务复杂度增加,线上问题可能存在一些 C 函数的动态调用和 block 参数的修改,这边介绍一个强大的库,外部函数接口:[libffi](https://github.com/libffi/libffi),它也可以拦截函数和获取函数调用参数。相比 Aspects,其功能更加强大,不但可以动态调用 C 函数,而且还可以用 libffi 实现一套基于 IMP 替换(拥有更好的性能)的热修复框架。有兴趣的童鞋请参考:[libffi doc](https://sourceware.org/libffi/) 和 [如何动态调用 C 函数](http://blog.cnbang.net/tech/3219/) 772 | 773 | 取名深入只是为了引人注目,实则只是个人的一点心得。由于水平有限,如有不对之处,欢迎大家批评指正。 774 | 775 | **如果觉得文章不错的话,欢迎🌟以资鼓励😄** 776 | 777 | **温馨提示:** 778 | 779 | > 阅读文章的时候建议搭配示例 HotFixDemo,这样理解会更加深刻。 780 | 781 | 782 | ## 参考文献 783 | 1. [Objective-C Runtime Programming Guide](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Introduction/Introduction.html) 784 | 2. [NSInvocation returns value but makes app crash with EXC\_BAD\_ACCESS](https://stackoverflow.com/questions/22018272/nsinvocation-returns-value-but-makes-app-crash-with-exc-bad-access/22034059#22034059) 785 | 3. [JSPatch 实现原理详解](https://github.com/bang590/JSPatch/wiki/JSPatch-%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86%E8%AF%A6%E8%A7%A3) 786 | 4. [objc\_msgSend\_stret](http://sealiesoftware.com/blog/archive/2008/10/30/objc_explain_objc_msgSend_stret.html) 787 | 5. [objc_msgSend() Tour Part 1: The Road Map](http://www.friday.com/bbum/2009/12/18/objc_msgsend-part-1-the-road-map/) 788 | 6. [-rac_signalForSelector: may fail for struct returns](https://github.com/ReactiveCocoa/ReactiveCocoa/issues/783) 789 | 7. [Objective-C Automatic Reference Counting (ARC)](https://clang.llvm.org/docs/AutomaticReferenceCounting.html#arc-runtime-objc-retainautorelease) 790 | 8. [Aspects](https://github.com/steipete/Aspects) 791 | --------------------------------------------------------------------------------