├── LICENSE ├── MessageMock.podspec ├── MessageMock ├── Core │ ├── mm_argument_callback.cpp │ ├── mm_argument_callback.h │ ├── mm_argument_change.cpp │ ├── mm_argument_change.h │ ├── mm_argument_stack.cpp │ ├── mm_argument_stack.h │ ├── mm_define.h │ ├── mm_hook_objc_msgsend.cpp │ ├── mm_hook_objc_msgsend.h │ ├── mm_method_matcher.cpp │ ├── mm_method_matcher.h │ ├── mm_runtime.cpp │ └── mm_runtime.h ├── MessageMocker.h ├── MessageMocker.mm └── fishhook │ ├── mm_fishhook.c │ └── mm_fishhook.h ├── MessageMockDemo ├── MessageMockDemo.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ ├── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ │ └── xcuserdata │ │ │ └── answeryang.xcuserdatad │ │ │ └── UserInterfaceState.xcuserstate │ └── xcuserdata │ │ └── answeryang.xcuserdatad │ │ └── xcschemes │ │ └── xcschememanagement.plist ├── MessageMockDemo.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ └── xcuserdata │ │ └── answeryang.xcuserdatad │ │ ├── UserInterfaceState.xcuserstate │ │ └── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist ├── MessageMockDemo │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ └── Contents.json │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── Info.plist │ ├── SceneDelegate.h │ ├── SceneDelegate.m │ ├── TestObjects │ │ ├── TestObject.h │ │ └── TestObject.m │ ├── ViewController.h │ ├── ViewController.m │ └── main.m ├── MessageMockDemoTests │ ├── Info.plist │ ├── MMArgumentCallbackTests.mm │ ├── MMChangeArgumentTests.mm │ ├── MMMethodReturnTests.mm │ ├── MMReturnCallbackTests.mm │ └── MessageMockerTests.m ├── Podfile ├── Podfile.lock └── Pods │ ├── Local Podspecs │ └── MessageMock.podspec.json │ ├── Manifest.lock │ ├── Pods.xcodeproj │ ├── project.pbxproj │ └── xcuserdata │ │ └── answeryang.xcuserdatad │ │ └── xcschemes │ │ ├── MessageMock.xcscheme │ │ ├── Pods-MessageMockDemoTests.xcscheme │ │ └── xcschememanagement.plist │ └── Target Support Files │ ├── MessageMock │ ├── MessageMock-Info.plist │ ├── MessageMock-dummy.m │ ├── MessageMock-prefix.pch │ ├── MessageMock-umbrella.h │ ├── MessageMock.modulemap │ └── MessageMock.xcconfig │ └── Pods-MessageMockDemoTests │ ├── Pods-MessageMockDemoTests-Info.plist │ ├── Pods-MessageMockDemoTests-acknowledgements.markdown │ ├── Pods-MessageMockDemoTests-acknowledgements.plist │ ├── Pods-MessageMockDemoTests-dummy.m │ ├── Pods-MessageMockDemoTests-frameworks-Debug-input-files.xcfilelist │ ├── Pods-MessageMockDemoTests-frameworks-Debug-output-files.xcfilelist │ ├── Pods-MessageMockDemoTests-frameworks-Release-input-files.xcfilelist │ ├── Pods-MessageMockDemoTests-frameworks-Release-output-files.xcfilelist │ ├── Pods-MessageMockDemoTests-frameworks.sh │ ├── Pods-MessageMockDemoTests-umbrella.h │ ├── Pods-MessageMockDemoTests.debug.xcconfig │ ├── Pods-MessageMockDemoTests.modulemap │ └── Pods-MessageMockDemoTests.release.xcconfig └── README.md /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 波儿菜 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 | -------------------------------------------------------------------------------- /MessageMock.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | 3 | s.name = "MessageMock" 4 | 5 | s.version = "1.0" 6 | 7 | s.summary = "Objective-C 方法模拟工具" 8 | 9 | s.description = <<-DESC 10 | Objective-C 方法模拟工具 11 | DESC 12 | 13 | s.homepage = "https://github.com/indulgeIn" 14 | 15 | s.license = "MIT" 16 | 17 | s.author = { "indulgeIn" => "1106355439@qq.com" } 18 | 19 | s.platform = :ios, "9.0" 20 | 21 | s.source = { :git => "https://github.com/indulgeIn/MessageMock.git", :tag => "#{s.version}" } 22 | 23 | s.requires_arc = true 24 | 25 | s.source_files = "MessageMock/**/*.{h,m,mm,c,cpp,hpp}" 26 | 27 | s.libraries = 'c++.1' 28 | 29 | end 30 | -------------------------------------------------------------------------------- /MessageMock/Core/mm_argument_callback.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // mm_argument_callback.cpp 3 | // MessageMock 4 | // 5 | // Created by 波儿菜 on 2020/7/10. 6 | // 7 | 8 | #import "mm_argument_callback.h" 9 | 10 | #ifdef __aarch64__ 11 | 12 | #import 13 | #import "mm_define.h" 14 | #import "mm_method_matcher.h" 15 | #import "mm_runtime.h" 16 | 17 | using namespace mm_method_matcher; 18 | 19 | void MMArgumentCallbackIfNeeded(uintptr_t self, uintptr_t _cmd) { 20 | #define XS_SIZE 6 21 | #define DS_SIZE 8 22 | uintptr_t xs[XS_SIZE], ds[DS_SIZE]; 23 | __asm volatile 24 | ("mov %0, x2\n" 25 | "mov %1, x3\n" 26 | "mov %2, x4\n" 27 | "mov %3, x5\n" 28 | "mov %4, x6\n" 29 | "mov %5, x7\n" 30 | : "=r"(xs[0]), "=r"(xs[1]), "=r"(xs[2]), "=r"(xs[3]), "=r"(xs[4]), "=r"(xs[5])); 31 | __asm volatile 32 | ("fmov %0, d0\n" 33 | "fmov %1, d1\n" 34 | "fmov %2, d2\n" 35 | "fmov %3, d3\n" 36 | "fmov %4, d4\n" 37 | "fmov %5, d5\n" 38 | "fmov %6, d6\n" 39 | "fmov %7, d7\n" 40 | : "=r"(ds[0]), "=r"(ds[1]), "=r"(ds[2]), "=r"(ds[3]), "=r"(ds[4]), "=r"(ds[5]), "=r"(ds[6]), "=r"(ds[7])); 41 | 42 | ArgumentCallbackMap *callback_map = NULL; 43 | { 44 | MethodMatcherLock lock; 45 | MethodMatcher *matcher = UnsafeGetMethodMatcher(self, _cmd); 46 | if (MM_UNLIKELY(matcher)) { 47 | callback_map = matcher->argument_callback_map; 48 | 49 | ++matcher->using_count; 50 | } 51 | } 52 | if (MM_LIKELY(!callback_map || callback_map->size() <= 0)) { 53 | return; 54 | } 55 | 56 | Method method = MMGetMethod((id)self, (SEL)_cmd); 57 | unsigned int arg_number = method_getNumberOfArguments(method); 58 | 59 | ArgumentCallbackError error = kArgumentCallbackErrorNone; 60 | unsigned int x_idx = 0, d_idx = 0; 61 | for (unsigned int i = 2; i < arg_number; ++i) { 62 | ArgumentCallback callback = callback_map->find(i)->second; 63 | if (!callback) { 64 | continue; 65 | } 66 | 67 | if (error == kArgumentCallbackErrorUnsupported) { 68 | callback(0, error); 69 | continue; 70 | } 71 | 72 | char *type = method_copyArgumentType(method, i); 73 | if (MM_UNLIKELY(!type)) { // 该路径理论上不会走 74 | callback(0, kArgumentCallbackErrorNoIndex); 75 | continue; 76 | } 77 | 78 | uintptr_t arg = 0; 79 | if (MM_UNLIKELY(MMPtrLengthArgumentTypes().count(*type) > 0)) { 80 | if (*type == 'f' || *type == 'd') { 81 | if (MM_UNLIKELY(d_idx >= DS_SIZE)) { 82 | error = kArgumentCallbackErrorOverD7; 83 | } else { 84 | arg = ds[d_idx++]; 85 | } 86 | } else { 87 | if (MM_UNLIKELY(x_idx >= XS_SIZE)) { 88 | error = kArgumentCallbackErrorOverX7; 89 | } else { 90 | arg = xs[x_idx++]; 91 | } 92 | } 93 | } else { 94 | error = kArgumentCallbackErrorUnsupported; 95 | } 96 | 97 | callback(arg, error); 98 | free(type); 99 | } 100 | 101 | #undef XS_SIZE 102 | #undef DS_SIZE 103 | } 104 | 105 | #else 106 | 107 | void MMArgumentCallbackIfNeeded(uintptr_t self, uintptr_t _cmd) {} 108 | 109 | #endif 110 | -------------------------------------------------------------------------------- /MessageMock/Core/mm_argument_callback.h: -------------------------------------------------------------------------------- 1 | // 2 | // mm_argument_callback.h 3 | // MessageMock 4 | // 5 | // Created by 波儿菜 on 2020/7/10. 6 | // 7 | 8 | #ifndef mm_argument_callback_h 9 | #define mm_argument_callback_h 10 | 11 | #include 12 | 13 | void MMArgumentCallbackIfNeeded(uintptr_t self, uintptr_t _cmd); 14 | 15 | #endif /* mm_argument_callback_h */ 16 | -------------------------------------------------------------------------------- /MessageMock/Core/mm_argument_change.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // mm_argument_change.cpp 3 | // MessageMock 4 | // 5 | // Created by 波儿菜 on 2020/7/13. 6 | // 7 | 8 | #import "mm_argument_change.h" 9 | 10 | #ifdef __aarch64__ 11 | 12 | #import 13 | #import "mm_define.h" 14 | #import "mm_runtime.h" 15 | #import "mm_method_matcher.h" 16 | 17 | using namespace mm_method_matcher; 18 | 19 | void MMSetArgumentRegisters(uintptr_t self, uintptr_t _cmd) { 20 | uintptr_t arg_label = 0; 21 | 22 | #define XS_SIZE 6 23 | #define DS_SIZE 8 24 | //通用/浮点寄存器值暂存数组 25 | uintptr_t xs[XS_SIZE] = {0}, ds[DS_SIZE] = {0}, mask = 1; 26 | { 27 | ArgumentList *arguments = NULL; 28 | { 29 | MethodMatcherLock lock; 30 | MethodMatcher *matcher = UnsafeGetMethodMatcher(self, _cmd); 31 | if (MM_UNLIKELY(matcher)) { 32 | arguments = matcher->arguments; 33 | } 34 | } 35 | if (!arguments || arguments->size() <= 0) { 36 | __asm volatile ("b 16f"); 37 | } 38 | 39 | Method method = MMGetMethod((id)self, (SEL)_cmd); 40 | unsigned int arg_number = method_getNumberOfArguments(method); 41 | 42 | std::unordered_map x_register_map, d_register_map; 43 | uintptr_t x_idx = 2, d_idx = 0; 44 | for (unsigned int i = 2; i < arg_number; ++i) { 45 | char *type = method_copyArgumentType(method, i); 46 | if (MM_UNLIKELY(!type || MMPtrLengthArgumentTypes().count(*type) == 0)) { 47 | // Unsupported argument type. 48 | __asm volatile ("b 16f"); 49 | } 50 | if (*type == 'f' || *type == 'd') { 51 | d_register_map[i] = d_idx++; 52 | } else { 53 | x_register_map[i] = x_idx++; 54 | } 55 | free(type); 56 | } 57 | 58 | for (Argument arg : *arguments) { 59 | char *type = method_copyArgumentType(method, arg.idx); 60 | if (MM_UNLIKELY(!type)) { 61 | continue; 62 | } 63 | uintptr_t res_arg = arg.value; 64 | if (arg.object) { 65 | res_arg = (uintptr_t)arg.object; 66 | } 67 | if (*type == 'f' || *type == 'd') { 68 | uintptr_t register_idx = d_register_map[arg.idx]; 69 | if (MM_UNLIKELY(register_idx >= DS_SIZE)) { 70 | continue; 71 | } 72 | arg_label |= (mask << (8 + register_idx)); 73 | ds[register_idx] = res_arg; 74 | } else { 75 | uintptr_t register_idx = x_register_map[arg.idx]; 76 | if (MM_UNLIKELY(register_idx - 2 >= XS_SIZE)) { 77 | continue; 78 | } 79 | arg_label |= (mask << register_idx); 80 | xs[register_idx - 2] = res_arg; 81 | } 82 | free(type); 83 | } 84 | } 85 | #undef XS_SIZE 86 | #undef DS_SIZE 87 | 88 | __asm volatile 89 | ("mov x2, %0\n" 90 | "mov x3, %1\n" 91 | "mov x4, %2\n" 92 | "mov x5, %3\n" 93 | "mov x6, %4\n" 94 | "mov x7, %5\n" 95 | :: "r"(xs[0]), "r"(xs[1]), "r"(xs[2]), "r"(xs[3]), "r"(xs[4]), "r"(xs[5])); 96 | __asm volatile 97 | ("fmov d0, %0\n" 98 | "fmov d1, %1\n" 99 | "fmov d2, %2\n" 100 | "fmov d3, %3\n" 101 | "fmov d4, %4\n" 102 | "fmov d5, %5\n" 103 | "fmov d6, %6\n" 104 | "fmov d7, %7\n" 105 | :: "r"(ds[0]), "r"(ds[1]), "r"(ds[2]), "r"(ds[3]), "r"(ds[4]), "r"(ds[5]), "r"(ds[6]), "r"(ds[7])); 106 | 107 | __asm volatile ("16:"); 108 | __asm volatile ("mov x0, %0" :: "r"(arg_label)); 109 | } 110 | 111 | #else 112 | 113 | void MMSetArgumentRegisters(uintptr_t self, uintptr_t _cmd) {} 114 | 115 | #endif 116 | -------------------------------------------------------------------------------- /MessageMock/Core/mm_argument_change.h: -------------------------------------------------------------------------------- 1 | // 2 | // mm_argument_change.h 3 | // MessageMock 4 | // 5 | // Created by 波儿菜 on 2020/7/13. 6 | // 7 | 8 | #ifndef mm_argument_change_h 9 | #define mm_argument_change_h 10 | 11 | #include 12 | 13 | void MMSetArgumentRegisters(uintptr_t self, uintptr_t _cmd); 14 | 15 | #endif /* mm_argument_change_h */ 16 | -------------------------------------------------------------------------------- /MessageMock/Core/mm_argument_stack.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // mm_argument_stack.cpp 3 | // MessageMock 4 | // 5 | // Created by 波儿菜 on 2020/7/10. 6 | // 7 | 8 | #import "mm_argument_stack.h" 9 | #import 10 | #import 11 | #import "mm_define.h" 12 | 13 | typedef struct { 14 | MMArgumentItem *items; 15 | int index; 16 | int mask; 17 | } MMArgumentStack; 18 | 19 | static pthread_key_t argument_stack_thread_key; 20 | 21 | static void ArgumentStackThreadRelease(void *ptr) { 22 | MMArgumentStack *stack = (MMArgumentStack *)ptr; 23 | if (MM_LIKELY(stack)) { 24 | if (MM_LIKELY(stack->items)) { 25 | free(stack->items); 26 | } 27 | free(stack); 28 | } 29 | } 30 | 31 | void MMThreadKeyCreate(void) { 32 | pthread_key_create(&argument_stack_thread_key, &ArgumentStackThreadRelease); 33 | } 34 | 35 | static MMArgumentStack *GetArgumentStack() { 36 | MMArgumentStack *stack = (MMArgumentStack *)pthread_getspecific(argument_stack_thread_key); 37 | if (MM_UNLIKELY(!stack)) { 38 | stack = (MMArgumentStack *)malloc(sizeof(MMArgumentStack)); 39 | stack->mask = 128; 40 | stack->index = -1; 41 | stack->items = (MMArgumentItem *)calloc(stack->mask, sizeof(MMArgumentItem)); 42 | pthread_setspecific(argument_stack_thread_key, stack); 43 | } 44 | return stack; 45 | } 46 | 47 | void MMArgumentStackPush(MMArgumentItem item) { 48 | MMArgumentStack *stack = GetArgumentStack(); 49 | int index = ++stack->index; 50 | if (MM_UNLIKELY(index >= stack->mask)) { 51 | stack->mask += 128; 52 | stack->items = (MMArgumentItem *)realloc(stack->items, stack->mask * sizeof(MMArgumentItem)); 53 | } 54 | stack->items[index] = item; 55 | } 56 | 57 | MMArgumentItem MMArgumentStackPop() { 58 | MMArgumentStack *stack = GetArgumentStack(); 59 | MMArgumentItem item = stack->items[stack->index--]; 60 | return item; 61 | } 62 | -------------------------------------------------------------------------------- /MessageMock/Core/mm_argument_stack.h: -------------------------------------------------------------------------------- 1 | // 2 | // mm_argument_stack.h 3 | // MessageMock 4 | // 5 | // Created by 波儿菜 on 2020/7/10. 6 | // 7 | 8 | #ifndef mm_argument_stack_h 9 | #define mm_argument_stack_h 10 | 11 | #include 12 | 13 | typedef struct { 14 | uintptr_t target; 15 | uintptr_t selector; 16 | uintptr_t lr; 17 | } MMArgumentItem; 18 | 19 | void MMThreadKeyCreate(void); 20 | 21 | void MMArgumentStackPush(MMArgumentItem item); 22 | 23 | MMArgumentItem MMArgumentStackPop(void); 24 | 25 | #endif /* mm_argument_stack_h */ 26 | -------------------------------------------------------------------------------- /MessageMock/Core/mm_define.h: -------------------------------------------------------------------------------- 1 | // 2 | // mm_define.h 3 | // MessageMock 4 | // 5 | // Created by 波儿菜 on 2020/7/10. 6 | // 7 | 8 | #ifndef mm_define_h 9 | #define mm_define_h 10 | 11 | #define MM_LIKELY(x) (__builtin_expect(!!(x), 1)) 12 | #define MM_UNLIKELY(x) (__builtin_expect(!!(x), 0)) 13 | 14 | #define MM_ASSUME_NONNULL_BEGIN _Pragma("clang assume_nonnull begin") 15 | #define MM_ASSUME_NONNULL_END _Pragma("clang assume_nonnull end") 16 | 17 | #endif /* mm_define_h */ 18 | -------------------------------------------------------------------------------- /MessageMock/Core/mm_hook_objc_msgsend.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // mm_hook_objc_msgsend.cpp 3 | // MessageMock 4 | // 5 | // Created by 波儿菜 on 2020/6/27. 6 | // 7 | 8 | #import "mm_hook_objc_msgsend.h" 9 | 10 | #ifdef __aarch64__ 11 | 12 | #import 13 | #import 14 | #import 15 | #import "mm_fishhook.h" 16 | #import "mm_method_matcher.h" 17 | #import "mm_argument_stack.h" 18 | #import "mm_argument_callback.h" 19 | #import "mm_argument_change.h" 20 | #import "mm_runtime.h" 21 | 22 | using namespace mm_method_matcher; 23 | 24 | #pragma mark - Hook Functions 25 | 26 | static id (*origin_objc_msgSend)(id, SEL, ...); 27 | 28 | static void beforeObjcMsgSend(uintptr_t self, uintptr_t _cmd) { 29 | uintptr_t lr = 0; 30 | __asm volatile ("mov %0, x9" : "=r"(lr)); 31 | { // 必须是第一个调用,保证寄存器不会被修改 32 | MMArgumentCallbackIfNeeded(self, _cmd); 33 | } 34 | bool skip = false; 35 | { 36 | MMArgumentItem item = { 37 | .target = self, 38 | .selector = _cmd, 39 | .lr = lr, 40 | }; 41 | MMArgumentStackPush(item); 42 | { 43 | MethodMatcherLock lock; 44 | MethodMatcher *matcher = UnsafeGetMethodMatcher(self, _cmd); 45 | if (matcher) { 46 | skip = matcher->skip; 47 | } 48 | } 49 | } 50 | { // 必须是最后一个调用,保证寄存器不会被修改 51 | MMSetArgumentRegisters(self, _cmd); 52 | } 53 | __asm volatile ("mov w1, %w0" :: "r"(skip)); 54 | } 55 | 56 | static void AfterObjcMsgSend() { 57 | uintptr_t lr = 0, x0 = 0, d0 = 0; 58 | __asm volatile ("mov %0, x0\n" 59 | "fmov %1, d0\n" 60 | : "=r"(x0), "=r"(d0)); 61 | uintptr_t res_type = 0, res_value = 0; 62 | { 63 | MMArgumentItem item = MMArgumentStackPop(); 64 | lr = item.lr; 65 | 66 | bool change_return = false; 67 | void *return_object = NULL; 68 | uintptr_t return_value = 0; 69 | ReturnCallback return_callback = NULL; 70 | { 71 | MethodMatcherLock lock; 72 | MethodMatcher *matcher = UnsafeGetMethodMatcher(item.target, item.selector); 73 | if (matcher) { 74 | change_return = matcher->change_return; 75 | return_object = matcher->return_object; 76 | return_value = matcher->return_value; 77 | return_callback = matcher->return_callback; 78 | } 79 | } 80 | 81 | if (change_return || return_callback) { 82 | Method method = MMGetMethod((id)item.target, (SEL)item.selector); 83 | char *type = method_copyReturnType(method); 84 | if (MM_LIKELY(type)) { 85 | if (change_return) { 86 | res_type = (*type == 'f' || *type == 'd') ? 0B10 : 0B1; 87 | res_value = return_object ? (uintptr_t)return_object : return_value; 88 | } 89 | 90 | if (return_callback) { 91 | ReturnCallbackError error = kReturnCallbackErrorNone; 92 | uintptr_t callback_value = 0; 93 | if (MMPtrLengthArgumentTypes().count(*type) == 0) { 94 | error = kReturnCallbackErrorNotX0D0; 95 | } else if (*type == 'v') { 96 | error = kReturnCallbackErrorNoReturn; 97 | } else { 98 | callback_value = res_type > 0 ? res_value : ((*type == 'f' || *type == 'd') ? d0 : x0); 99 | } 100 | return_callback(callback_value, error); 101 | } 102 | 103 | free(type); 104 | } 105 | } 106 | 107 | { // 尝试删除 matcher 108 | MethodMatcherLock lock; 109 | MethodMatcher *matcher = UnsafeGetMethodMatcher(item.target, item.selector); 110 | if (matcher) { 111 | ++matcher->hit_count; 112 | --matcher->using_count; 113 | if (matcher->shouldDelete()) { 114 | UnsafeRemoveMethodMatcher(matcher->target, matcher->selector); 115 | } 116 | } 117 | } 118 | } 119 | __asm volatile 120 | ("mov x0, %0\n" 121 | "mov x1, %1\n" 122 | "mov x2, %2\n" 123 | :: "r"(lr), "r"(res_type), "r"(res_value)); 124 | } 125 | 126 | #define BLR(func) \ 127 | __asm volatile ("stp x8, x9, [sp, #-16]!"); \ 128 | __asm volatile ("mov x12, %0" :: "r"(func)); \ 129 | __asm volatile ("ldp x8, x9, [sp], #16"); \ 130 | __asm volatile ("blr x12"); 131 | 132 | #define SAVE_REGISTERS() \ 133 | __asm volatile ( \ 134 | "stp x8, x9, [sp, #-16]!\n" \ 135 | "stp x6, x7, [sp, #-16]!\n" \ 136 | "stp x4, x5, [sp, #-16]!\n" \ 137 | "stp x2, x3, [sp, #-16]!\n" \ 138 | "stp x0, x1, [sp, #-16]!\n" \ 139 | \ 140 | "stp q8, q9, [sp, #-32]!\n" \ 141 | "stp q6, q7, [sp, #-32]!\n" \ 142 | "stp q4, q5, [sp, #-32]!\n" \ 143 | "stp q2, q3, [sp, #-32]!\n" \ 144 | "stp q0, q1, [sp, #-32]!\n"); 145 | 146 | #define LOAD_REGISTERS() \ 147 | __asm volatile ( \ 148 | "ldp q0, q1, [sp], #32\n" \ 149 | "ldp q2, q3, [sp], #32\n" \ 150 | "ldp q4, q5, [sp], #32\n" \ 151 | "ldp q6, q7, [sp], #32\n" \ 152 | "ldp q8, q9, [sp], #32\n" \ 153 | \ 154 | "ldp x0, x1, [sp], #16\n" \ 155 | "ldp x2, x3, [sp], #16\n" \ 156 | "ldp x4, x5, [sp], #16\n" \ 157 | "ldp x6, x7, [sp], #16\n" \ 158 | "ldp x8, x9, [sp], #16\n"); 159 | 160 | #define MARK_FRAME() \ 161 | __asm volatile( \ 162 | ".cfi_def_cfa w29, 16\n" \ 163 | ".cfi_offset w30, -8\n" \ 164 | ".cfi_offset w29, -16\n"); 165 | 166 | #define RET() __asm volatile ("ret"); 167 | 168 | __attribute__((__naked__)) 169 | static void ObjcMsgSend() { 170 | MARK_FRAME() 171 | SAVE_REGISTERS() 172 | 173 | __asm volatile ("mov x9, lr"); 174 | BLR(&beforeObjcMsgSend) 175 | // 是否跳过原函数调用 176 | __asm volatile ("cbnz w1, 40f"); 177 | // 是否修改入参 178 | __asm volatile ("cbz x0, 30f"); 179 | 180 | // 浮点型入参修改 181 | __asm volatile 182 | ("0:" 183 | "and x1, x0, #0b100000000\n" 184 | "cbz x1, 1f\n" 185 | "str d0, [sp, #0]"); 186 | __asm volatile 187 | ("1:" 188 | "and x1, x0, #0b1000000000\n" 189 | "cbz x1, 2f\n" 190 | "str d1, [sp, #16]"); 191 | __asm volatile 192 | ("2:" 193 | "and x1, x0, #0b10000000000\n" 194 | "cbz x1, 3f\n" 195 | "str d2, [sp, #32]"); 196 | __asm volatile 197 | ("3:" 198 | "and x1, x0, #0b100000000000\n" 199 | "cbz x1, 4f\n" 200 | "str d3, [sp, #48]"); 201 | __asm volatile 202 | ("4:" 203 | "and x1, x0, #0b1000000000000\n" 204 | "cbz x1, 5f\n" 205 | "str d4, [sp, #64]"); 206 | __asm volatile 207 | ("5:" 208 | "and x1, x0, #0b10000000000000\n" 209 | "cbz x1, 6f\n" 210 | "str d5, [sp, #80]"); 211 | __asm volatile 212 | ("6:" 213 | "and x1, x0, #0b100000000000000\n" 214 | "cbz x1, 7f\n" 215 | "str d6, [sp, #96]"); 216 | __asm volatile 217 | ("7:" 218 | "and x1, x0, #0b1000000000000000\n" 219 | "cbz x1, 10f\n" 220 | "str d7, [sp, #112]"); 221 | // 通用型入参修改 222 | __asm volatile 223 | ("10:" 224 | "and x1, x0, #0b100\n" 225 | "cbz x1, 11f\n" 226 | "str x2, [sp, #176]"); 227 | __asm volatile 228 | ("11:" 229 | "and x1, x0, #0b1000\n" 230 | "cbz x1, 12f\n" 231 | "str x3, [sp, #184]"); 232 | __asm volatile 233 | ("12:" 234 | "and x1, x0, #0b10000\n" 235 | "cbz x1, 13f\n" 236 | "str x4, [sp, #192]"); 237 | __asm volatile 238 | ("13:" 239 | "and x1, x0, #0b100000\n" 240 | "cbz x1, 14f\n" 241 | "str x5, [sp, #200]"); 242 | __asm volatile 243 | ("14:" 244 | "and x1, x0, #0b1000000\n" 245 | "cbz x1, 15f\n" 246 | "str x6, [sp, #208]"); 247 | __asm volatile 248 | ("15:" 249 | "and x1, x0, #0b10000000\n" 250 | "cbz x1, 30f\n" 251 | "str x7, [sp, #216]"); 252 | 253 | __asm volatile ("30:"); 254 | LOAD_REGISTERS() 255 | BLR(origin_objc_msgSend) 256 | SAVE_REGISTERS() 257 | 258 | __asm volatile ("40:"); 259 | BLR(&AfterObjcMsgSend) 260 | // 是否修改返回值 261 | __asm volatile ("cbz w1, 50f"); 262 | 263 | // 通用型返回值修改 264 | __asm volatile 265 | ("and x3, x1, #0b1\n" 266 | "cbz x3, 41f\n" 267 | "str x2, [sp, #160]\n"); 268 | // 浮点型返回值修改 269 | __asm volatile 270 | ("41:" 271 | "and x3, x1, #0b10\n" 272 | "cbz x3, 50f\n" 273 | "str x2, [sp]\n"); 274 | 275 | __asm volatile ("50:"); 276 | __asm volatile ("mov lr, x0"); 277 | 278 | LOAD_REGISTERS() 279 | RET() 280 | } 281 | 282 | #pragma mark - Public 283 | 284 | void MMStartWorking() { 285 | static dispatch_once_t once_token; 286 | dispatch_once(&once_token, ^{ 287 | MMThreadKeyCreate(); 288 | 289 | struct mm_rebinding info = { 290 | .name = "objc_msgSend", 291 | .replacement = (void *)ObjcMsgSend, 292 | .replaced = (void **)&origin_objc_msgSend, 293 | }; 294 | mm_rebind_symbols((struct mm_rebinding[]){info}, 1); 295 | }); 296 | } 297 | 298 | #else 299 | 300 | void MMStartWorking() {} 301 | 302 | #endif 303 | -------------------------------------------------------------------------------- /MessageMock/Core/mm_hook_objc_msgsend.h: -------------------------------------------------------------------------------- 1 | // 2 | // mm_hook_objc_msgsend.h 3 | // MessageMock 4 | // 5 | // Created by 波儿菜 on 2020/6/27. 6 | // 7 | 8 | #ifndef mm_hook_objc_msgsend_h 9 | #define mm_hook_objc_msgsend_h 10 | 11 | void MMStartWorking(void); 12 | 13 | #endif /* mm_hook_objc_msgsend_h */ 14 | -------------------------------------------------------------------------------- /MessageMock/Core/mm_method_matcher.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // mm_method_matcher.cpp 3 | // MessageMock 4 | // 5 | // Created by 波儿菜 on 2020/6/27. 6 | // 7 | 8 | #import "mm_method_matcher.h" 9 | #import 10 | #import 11 | #import 12 | 13 | namespace mm_method_matcher { 14 | 15 | static pthread_mutex_t *GetMethodMatcherMutex(void) { 16 | static pthread_mutex_t method_matcher_lock; 17 | static dispatch_once_t once_token; 18 | dispatch_once(&once_token, ^{ 19 | pthread_mutexattr_t attr; 20 | pthread_mutexattr_init(&attr); 21 | pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); 22 | pthread_mutex_init(&method_matcher_lock, &attr); 23 | pthread_mutexattr_destroy(&attr); 24 | }); 25 | return &method_matcher_lock; 26 | } 27 | 28 | typedef std::unordered_map> MethodMatcherMap; 29 | 30 | MethodMatcherMap &GetMethodMatcherMap() { 31 | static MethodMatcherMap *matcher_map = NULL; 32 | static dispatch_once_t onceToken; 33 | dispatch_once(&onceToken, ^{ 34 | matcher_map = new MethodMatcherMap(); 35 | }); 36 | return *matcher_map; 37 | }; 38 | 39 | #pragma mark - MethodMatcherLock Implementation 40 | 41 | MethodMatcherLock::MethodMatcherLock() { 42 | pthread_mutex_lock(GetMethodMatcherMutex()); 43 | } 44 | 45 | MethodMatcherLock::~MethodMatcherLock() { 46 | pthread_mutex_unlock(GetMethodMatcherMutex()); 47 | } 48 | 49 | #pragma mark - MethodMatcher Implementation 50 | 51 | bool MethodMatcher::isValid() { 52 | return 0 != target && 0 != selector && hit_count < limit_hit_count; 53 | } 54 | 55 | bool MethodMatcher::shouldDelete() { 56 | return hit_count >= limit_hit_count && using_count <= 0 && reference <= 0; 57 | } 58 | 59 | MethodMatcher::~MethodMatcher() { 60 | if (arguments) { 61 | for (Argument mark : *arguments) { 62 | if (mark.object) { 63 | CFRelease(mark.object); 64 | } 65 | } 66 | delete arguments; 67 | arguments = NULL; 68 | } 69 | if (return_object) { 70 | CFRelease(return_object); 71 | return_object = NULL; 72 | } 73 | } 74 | 75 | #pragma mark - Matcher Access 76 | 77 | void UnsafeRemoveMethodMatcher(uintptr_t target, uintptr_t selector) { 78 | MethodMatcherMap &map = GetMethodMatcherMap(); 79 | auto iterator = map.find(target); 80 | if (map.end() != iterator) { 81 | std::unordered_map &matcher_map = iterator->second; 82 | auto matcher_iterator = matcher_map.find(selector); 83 | 84 | if (matcher_map.end() != matcher_iterator) { 85 | matcher_map.erase(matcher_iterator); 86 | MethodMatcher *matcher = matcher_iterator->second; 87 | delete matcher; 88 | } 89 | } 90 | } 91 | 92 | MethodMatcher * _Nullable UnsafeGetMethodMatcher(uintptr_t target, uintptr_t selector) { 93 | MethodMatcher *matcher = NULL; 94 | MethodMatcherMap &map = GetMethodMatcherMap(); 95 | auto iterator = map.find(target); 96 | if (map.end() != iterator) { 97 | MethodMatcher *tmp = iterator->second[selector]; 98 | if (tmp && tmp->isValid()) { 99 | matcher = tmp; 100 | } 101 | } 102 | return matcher; 103 | } 104 | 105 | bool AddMethodMatcher(MethodMatcher *matcher) { 106 | if (MM_UNLIKELY(!matcher || !matcher->isValid())) { 107 | return false; 108 | } 109 | uintptr_t target = matcher->target, selector = matcher->selector; 110 | 111 | MethodMatcherLock lock; 112 | MethodMatcher *old_matcher = UnsafeGetMethodMatcher(target, selector); 113 | if (old_matcher && !old_matcher->shouldDelete()) { 114 | return false; // old_matcher 由于数据安全问题无法删除 115 | } 116 | if (old_matcher != matcher) { 117 | UnsafeRemoveMethodMatcher(target, selector); 118 | GetMethodMatcherMap()[target][selector] = matcher; 119 | } 120 | return true; 121 | } 122 | 123 | } //namespace mm_method_matcher 124 | -------------------------------------------------------------------------------- /MessageMock/Core/mm_method_matcher.h: -------------------------------------------------------------------------------- 1 | // 2 | // mm_method_matcher.h 3 | // MessageMock 4 | // 5 | // Created by 波儿菜 on 2020/6/27. 6 | // 7 | 8 | #ifndef mm_method_matcher_h 9 | #define mm_method_matcher_h 10 | 11 | #include 12 | #include 13 | #include 14 | #include "mm_define.h" 15 | 16 | MM_ASSUME_NONNULL_BEGIN 17 | 18 | namespace mm_method_matcher { 19 | 20 | typedef struct { 21 | /// 需要改变的参数下标 22 | unsigned int idx = 0; 23 | /// 需要改变的参数值 24 | uintptr_t value = 0; 25 | /// 需要改变的参数指针(用于接管 objc 对象引用计数) 26 | void * _Nullable object = NULL; 27 | } Argument; 28 | 29 | typedef std::vector ArgumentList; 30 | 31 | typedef enum { 32 | /// 无错误 33 | kArgumentCallbackErrorNone, 34 | /// 参数寄存器超过 x0 - x7 35 | kArgumentCallbackErrorOverX7, 36 | /// 参数寄存器超过 d0 - d7 37 | kArgumentCallbackErrorOverD7, 38 | /// 参数列表存在不支持的类型 39 | kArgumentCallbackErrorUnsupported, 40 | /// 没有这个下标的参数 41 | kArgumentCallbackErrorNoIndex, 42 | } ArgumentCallbackError; 43 | 44 | typedef void(^ArgumentCallback)(uintptr_t arg, ArgumentCallbackError error); 45 | 46 | typedef std::unordered_map ArgumentCallbackMap; 47 | 48 | typedef enum { 49 | /// 无错误 50 | kReturnCallbackErrorNone, 51 | /// 返回值不是通过 x0/d0 传递 52 | kReturnCallbackErrorNotX0D0, 53 | /// 没有返回值 54 | kReturnCallbackErrorNoReturn, 55 | } ReturnCallbackError; 56 | 57 | typedef void(^ReturnCallback)(uintptr_t arg, ReturnCallbackError error); 58 | 59 | /// 方法匹配配置对象 60 | class MethodMatcher { 61 | public: 62 | /// 是否跳过该 [target selector] 调用 63 | bool skip = false; 64 | /// 是否改变返回值 65 | bool change_return = false; 66 | /// 被引用的次数(用于上层代码不期望该内存释放) 67 | long reference = 0; 68 | /// 命中次数 69 | long hit_count = 0; 70 | /// 限制命中次数 71 | long limit_hit_count = 1; 72 | /// 正在使用的数量 73 | long using_count = 0; 74 | /// 目标 id 地址 75 | uintptr_t target = 0; 76 | /// 目标 SEL 地址 77 | uintptr_t selector = 0; 78 | /// 需要改变的返回值 79 | uintptr_t return_value = 0; 80 | /// 需要改变的返回指针(用于接管 objc 对象引用计数) 81 | void * _Nullable return_object = NULL; 82 | /// 需要修改的参数表 83 | ArgumentList * _Nullable arguments = NULL; 84 | /// 调用时检查参数的函数指针表 85 | ArgumentCallbackMap * _Nullable argument_callback_map = NULL; 86 | /// 检查返回值的函数指针 87 | ReturnCallback _Nullable return_callback = NULL; 88 | 89 | /// 该 matcher 是否有效 90 | bool isValid(); 91 | /// 是否可以删除 92 | bool shouldDelete(); 93 | 94 | ~MethodMatcher(); 95 | }; 96 | 97 | /// matcher 读写锁 98 | class MethodMatcherLock { 99 | public: 100 | MethodMatcherLock(); 101 | ~MethodMatcherLock(); 102 | }; 103 | 104 | /// 安全的添加 matcher,返回是否成功 105 | bool AddMethodMatcher(MethodMatcher *matcher); 106 | /// 不安全的移除 matcher 107 | void UnsafeRemoveMethodMatcher(uintptr_t target, uintptr_t selector); 108 | /// 不安全的获取 matcher 109 | MethodMatcher * _Nullable UnsafeGetMethodMatcher(uintptr_t target, uintptr_t selector); 110 | 111 | } //namespace mm_method_matcher 112 | 113 | MM_ASSUME_NONNULL_END 114 | 115 | #endif /* mm_method_matcher_h */ 116 | -------------------------------------------------------------------------------- /MessageMock/Core/mm_runtime.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // mm_runtime.cpp 3 | // MessageMock 4 | // 5 | // Created by 波儿菜 on 2020/7/11. 6 | // 7 | 8 | #import "mm_runtime.h" 9 | #import 10 | 11 | Method MMGetMethod(id self, SEL _cmd) { 12 | Method method = NULL; 13 | if (object_isClass(self)) { 14 | method = class_getClassMethod((Class)self, _cmd); 15 | } else { 16 | method = class_getInstanceMethod(object_getClass(self), _cmd); 17 | } 18 | return method; 19 | } 20 | 21 | std::unordered_set MMPtrLengthArgumentTypes() { 22 | static dispatch_once_t once_token; 23 | static std::unordered_set *types; 24 | dispatch_once(&once_token, ^{ 25 | types = new std::unordered_set( 26 | {'c', 'i', 's', 'l', 'q', 'C', 'I', 'S', 'L', 27 | 'Q', 'f', 'd', 'B', 'v', '*', '@', '#', ':', '^'}); 28 | }); 29 | return *types; 30 | } 31 | 32 | -------------------------------------------------------------------------------- /MessageMock/Core/mm_runtime.h: -------------------------------------------------------------------------------- 1 | // 2 | // mm_runtime.h 3 | // MessageMock 4 | // 5 | // Created by 波儿菜 on 2020/7/11. 6 | // 7 | 8 | #ifndef mm_runtime_h 9 | #define mm_runtime_h 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | Method MMGetMethod(id self, SEL _cmd); 16 | 17 | /// 小于等于指针长度的类型前缀集合 18 | std::unordered_set MMPtrLengthArgumentTypes(); 19 | 20 | #endif /* mm_runtime_h */ 21 | -------------------------------------------------------------------------------- /MessageMock/MessageMocker.h: -------------------------------------------------------------------------------- 1 | // 2 | // MessageMocker.h 3 | // MessageMock 4 | // 5 | // Created by 波儿菜 on 2020/7/11. 6 | // 7 | 8 | #import 9 | 10 | NS_ASSUME_NONNULL_BEGIN 11 | 12 | /// 模拟类,根据 [target selector] 命中目标方法,主要能力: 13 | /// - 修改返回值、参数 14 | /// - 验证返回值、参数 15 | /// - 跳过原方法 16 | /// - 获取方法命中次数 17 | @interface MessageMocker : NSObject 18 | 19 | /// 遍历构造(target 目标对象 selector 目标方法) 20 | @property (class, nonatomic, copy, readonly) MessageMocker * _Nullable (^build)(id target, SEL selector); 21 | 22 | /// 修改返回值 23 | @property (nonatomic, copy, readonly) MessageMocker *(^mockReturn)(const char *type, ...); 24 | #define mockReturn(arg) mockReturn(@encode(typeof(arg)), arg, nil) 25 | 26 | /// 修改调用参数 27 | @property (nonatomic, copy, readonly) MessageMocker *(^mockArgument)(unsigned int idx, const char *type, ...); 28 | #define mockArgument(idx, arg) mockArgument(idx, @encode(typeof(arg)), arg, nil) 29 | 30 | /// 检查返回值,callback 是闭包:^(AnyType arg) {} 31 | @property (nonatomic, copy, readonly) MessageMocker *(^checkReturn)(id callback); 32 | 33 | /// 检查参数,callback 是闭包:^(AnyType arg) {} 34 | @property (nonatomic, copy, readonly) MessageMocker *(^checkArgument)(unsigned int idx, id callback); 35 | 36 | /// 是否跳过该 [target selector] 调用,默认 NO 37 | @property (nonatomic, copy, readonly) MessageMocker *(^skip)(BOOL skip); 38 | 39 | /// 限制命中次数,默认 1(超限则该伪装对象失效) 40 | @property (nonatomic, copy, readonly) MessageMocker *(^limitHitCount)(NSInteger count); 41 | 42 | /// 方法命中次数(并发过高可能获取的值大于预期) 43 | @property (nonatomic, assign, readonly) long hitCount; 44 | 45 | /// 启用(只能启用一次,启用后该对象任何修改无效) 46 | @property (nonatomic, copy, readonly) void(^start)(void); 47 | 48 | - (instancetype)init UNAVAILABLE_ATTRIBUTE; 49 | + (instancetype)new UNAVAILABLE_ATTRIBUTE; 50 | 51 | @end 52 | 53 | NS_ASSUME_NONNULL_END 54 | -------------------------------------------------------------------------------- /MessageMock/MessageMocker.mm: -------------------------------------------------------------------------------- 1 | // 2 | // MessageMocker.m 3 | // MessageMock 4 | // 5 | // Created by 波儿菜 on 2020/7/11. 6 | // 7 | 8 | #import "MessageMocker.h" 9 | #import "mm_method_matcher.h" 10 | #import "mm_hook_objc_msgsend.h" 11 | #import "mm_runtime.h" 12 | 13 | #define MM_CHECK_START if (self->_isStarted) { return self; } 14 | 15 | using namespace mm_method_matcher; 16 | 17 | static void AutoCallback(const char * _Nullable type, uintptr_t arg, id callback) { 18 | switch (*type) { 19 | case 'f': { 20 | float tmp = *((float *)&arg); 21 | ((void(^)(float))callback)(tmp); 22 | } break; 23 | case 'd': { 24 | double tmp = *((double *)&arg); 25 | ((void(^)(double))callback)(tmp); 26 | } break; 27 | default: { 28 | ((void(^)(uintptr_t))callback)(arg); 29 | } break; 30 | } 31 | } 32 | 33 | static BOOL SetNextArgument(const char *type, va_list argList, uintptr_t *nextValue, void **nextObject) { 34 | if (!type || MMPtrLengthArgumentTypes().count(*type) == 0) { 35 | return NO; 36 | } 37 | switch (*type) { 38 | case 'f': { 39 | #pragma clang push 40 | #pragma clang diagnostic ignored "-Wvarargs" 41 | float arg = va_arg(argList, float); 42 | #pragma clang pop 43 | *nextValue = *(uintptr_t *)&arg; 44 | } break; 45 | case 'd': { 46 | double arg = va_arg(argList, double); 47 | *nextValue = *(uintptr_t *)&arg; 48 | } break; 49 | case '@': { 50 | if (*nextObject) { //把之前的对象清理掉 51 | CFRelease(*nextObject); 52 | } 53 | id arg = va_arg(argList, id); 54 | *nextObject = (__bridge_retained void *)arg; 55 | } break; 56 | default: { 57 | uintptr_t arg = va_arg(argList, uintptr_t); 58 | *nextValue = (uintptr_t)arg; 59 | } break; 60 | } 61 | return YES; 62 | } 63 | 64 | @implementation MessageMocker { 65 | MethodMatcher *_matcher; 66 | BOOL _isStarted; 67 | id _target; //避免 target 先于 mocker 释放 68 | SEL _selector; 69 | } 70 | 71 | #pragma mark - Life Cycle 72 | 73 | - (nullable instancetype)initWithTarget:(id)target selector:(SEL)selector { 74 | if (!target || !selector) { 75 | return nil; 76 | } 77 | self = [super init]; 78 | if (self) { 79 | _target = target; 80 | _selector = selector; 81 | 82 | _matcher = new MethodMatcher(); 83 | _matcher->target = (uintptr_t)target; 84 | _matcher->selector = (uintptr_t)selector; 85 | ++_matcher->reference; 86 | } 87 | return self; 88 | } 89 | 90 | - (void)dealloc { 91 | MethodMatcherLock lock; 92 | if (_matcher) { 93 | --_matcher->reference; 94 | if (_matcher->shouldDelete()) { 95 | UnsafeRemoveMethodMatcher(_matcher->target, _matcher->selector); 96 | } 97 | } 98 | } 99 | 100 | #pragma mark - Getters 101 | 102 | + (MessageMocker * _Nullable (^)(id _Nonnull, SEL _Nonnull))build { 103 | return ^MessageMocker *(id target, SEL selector) { 104 | return [[MessageMocker alloc] initWithTarget:target selector:selector]; 105 | }; 106 | } 107 | 108 | - (MessageMocker * _Nonnull (^)(const char * _Nonnull, ...))mockReturn { 109 | return ^MessageMocker *(const char *type, ...) { 110 | MM_CHECK_START 111 | 112 | va_list argList; 113 | va_start(argList, type); 114 | BOOL succeed = SetNextArgument(type, argList, &(self->_matcher->return_value), &(self->_matcher->return_object)); 115 | va_end(argList); 116 | 117 | if (succeed) { 118 | self->_matcher->change_return = true; 119 | } 120 | return self; 121 | }; 122 | } 123 | 124 | - (MessageMocker * _Nonnull (^)(unsigned int, const char * _Nonnull, ...))mockArgument { 125 | return ^MessageMocker *(unsigned int idx, const char *type, ...) { 126 | MM_CHECK_START 127 | 128 | Argument mark; 129 | mark.idx = idx + 2; 130 | 131 | va_list argList; 132 | va_start(argList, type); 133 | BOOL succeed = SetNextArgument(type, argList, &(mark.value), &(mark.object)); 134 | va_end(argList); 135 | 136 | if (succeed) { 137 | if (!self->_matcher->arguments) { 138 | self->_matcher->arguments = new ArgumentList(); 139 | } 140 | self->_matcher->arguments->push_back(mark); 141 | } 142 | return self; 143 | }; 144 | } 145 | 146 | - (MessageMocker * _Nonnull (^)(id _Nonnull))checkReturn { 147 | return ^MessageMocker *(id callback) { 148 | MM_CHECK_START 149 | 150 | Method method = MMGetMethod(self->_target, self->_selector); 151 | char *type = method_copyReturnType(method); 152 | NSString *typeStr = nil; 153 | if (type) { 154 | typeStr = [NSString stringWithUTF8String:type]; 155 | free(type); 156 | } 157 | 158 | self->_matcher->return_callback = ^(uintptr_t arg, ReturnCallbackError error) { 159 | if (error == kReturnCallbackErrorNone && typeStr.length > 0) { 160 | AutoCallback(typeStr.UTF8String, arg, callback); 161 | } 162 | }; 163 | return self; 164 | }; 165 | } 166 | 167 | - (MessageMocker * _Nonnull (^)(unsigned int, id _Nonnull))checkArgument { 168 | return ^MessageMocker *(unsigned int idx, id callback) { 169 | MM_CHECK_START 170 | 171 | unsigned int realIdx = idx + 2; 172 | 173 | Method method = MMGetMethod(self->_target, self->_selector); 174 | char *type = method_copyArgumentType(method, realIdx); 175 | NSString *typeStr = nil; 176 | if (type) { 177 | typeStr = [NSString stringWithUTF8String:type]; 178 | free(type); 179 | } 180 | 181 | ArgumentCallback insideCallback = ^(uintptr_t arg, ArgumentCallbackError error) { 182 | if (error == kArgumentCallbackErrorNone && typeStr.length > 0) { 183 | AutoCallback(typeStr.UTF8String, arg, callback); 184 | } 185 | }; 186 | if (!self->_matcher->argument_callback_map) { 187 | self->_matcher->argument_callback_map = new ArgumentCallbackMap(); 188 | } 189 | self->_matcher->argument_callback_map->insert(std::make_pair(realIdx, insideCallback)); 190 | return self; 191 | }; 192 | } 193 | 194 | - (MessageMocker * _Nonnull (^)(BOOL))skip { 195 | return ^MessageMocker *(BOOL skip) { 196 | MM_CHECK_START 197 | 198 | self->_matcher->skip = skip; 199 | return self; 200 | }; 201 | } 202 | 203 | - (MessageMocker * _Nonnull (^)(NSInteger))limitHitCount { 204 | return ^MessageMocker *(NSInteger count) { 205 | MM_CHECK_START 206 | 207 | self->_matcher->limit_hit_count = count; 208 | return self; 209 | }; 210 | } 211 | 212 | - (long)hitCount { 213 | return self->_matcher->hit_count; 214 | } 215 | 216 | - (void (^)())start { 217 | return ^void() { 218 | if (self->_isStarted) { 219 | return; 220 | } 221 | self->_isStarted = YES; 222 | 223 | AddMethodMatcher(self->_matcher); 224 | MMStartWorking(); 225 | }; 226 | } 227 | 228 | @end 229 | 230 | -------------------------------------------------------------------------------- /MessageMock/fishhook/mm_fishhook.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013, Facebook, Inc. 2 | // All rights reserved. 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions are met: 5 | // * Redistributions of source code must retain the above copyright notice, 6 | // this list of conditions and the following disclaimer. 7 | // * Redistributions in binary form must reproduce the above copyright notice, 8 | // this list of conditions and the following disclaimer in the documentation 9 | // and/or other materials provided with the distribution. 10 | // * Neither the name Facebook nor the names of its contributors may be used to 11 | // endorse or promote products derived from this software without specific 12 | // prior written permission. 13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 17 | // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 20 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 21 | // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 22 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | #include "mm_fishhook.h" 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #ifdef __LP64__ 35 | typedef struct mach_header_64 mach_header_t; 36 | typedef struct segment_command_64 segment_command_t; 37 | typedef struct section_64 section_t; 38 | typedef struct nlist_64 nlist_t; 39 | #define LC_SEGMENT_ARCH_DEPENDENT LC_SEGMENT_64 40 | #else 41 | typedef struct mach_header mach_header_t; 42 | typedef struct segment_command segment_command_t; 43 | typedef struct section section_t; 44 | typedef struct nlist nlist_t; 45 | #define LC_SEGMENT_ARCH_DEPENDENT LC_SEGMENT 46 | #endif 47 | 48 | #ifndef SEG_DATA_CONST 49 | #define SEG_DATA_CONST "__DATA_CONST" 50 | #endif 51 | 52 | struct rebindings_entry { 53 | struct mm_rebinding *rebindings; 54 | size_t rebindings_nel; 55 | struct rebindings_entry *next; 56 | }; 57 | 58 | static struct rebindings_entry *_rebindings_head; 59 | 60 | static int prepend_rebindings(struct rebindings_entry **rebindings_head, 61 | struct mm_rebinding rebindings[], 62 | size_t nel) { 63 | struct rebindings_entry *new_entry = (struct rebindings_entry *) malloc(sizeof(struct rebindings_entry)); 64 | if (!new_entry) { 65 | return -1; 66 | } 67 | new_entry->rebindings = (struct mm_rebinding *) malloc(sizeof(struct mm_rebinding) * nel); 68 | if (!new_entry->rebindings) { 69 | free(new_entry); 70 | return -1; 71 | } 72 | memcpy(new_entry->rebindings, rebindings, sizeof(struct mm_rebinding) * nel); 73 | new_entry->rebindings_nel = nel; 74 | new_entry->next = *rebindings_head; 75 | *rebindings_head = new_entry; 76 | return 0; 77 | } 78 | 79 | static void perform_rebinding_with_section(struct rebindings_entry *rebindings, 80 | section_t *section, 81 | intptr_t slide, 82 | nlist_t *symtab, 83 | char *strtab, 84 | uint32_t *indirect_symtab) { 85 | uint32_t *indirect_symbol_indices = indirect_symtab + section->reserved1; 86 | void **indirect_symbol_bindings = (void **)((uintptr_t)slide + section->addr); 87 | for (uint i = 0; i < section->size / sizeof(void *); i++) { 88 | uint32_t symtab_index = indirect_symbol_indices[i]; 89 | if (symtab_index == INDIRECT_SYMBOL_ABS || symtab_index == INDIRECT_SYMBOL_LOCAL || 90 | symtab_index == (INDIRECT_SYMBOL_LOCAL | INDIRECT_SYMBOL_ABS)) { 91 | continue; 92 | } 93 | uint32_t strtab_offset = symtab[symtab_index].n_un.n_strx; 94 | char *symbol_name = strtab + strtab_offset; 95 | bool symbol_name_longer_than_1 = symbol_name[0] && symbol_name[1]; 96 | struct rebindings_entry *cur = rebindings; 97 | while (cur) { 98 | for (uint j = 0; j < cur->rebindings_nel; j++) { 99 | if (symbol_name_longer_than_1 && 100 | strcmp(&symbol_name[1], cur->rebindings[j].name) == 0) { 101 | if (cur->rebindings[j].replaced != NULL && 102 | indirect_symbol_bindings[i] != cur->rebindings[j].replacement) { 103 | *(cur->rebindings[j].replaced) = indirect_symbol_bindings[i]; 104 | } 105 | indirect_symbol_bindings[i] = cur->rebindings[j].replacement; 106 | goto symbol_loop; 107 | } 108 | } 109 | cur = cur->next; 110 | } 111 | symbol_loop:; 112 | } 113 | } 114 | 115 | static void rebind_symbols_for_image(struct rebindings_entry *rebindings, 116 | const struct mach_header *header, 117 | intptr_t slide) { 118 | Dl_info info; 119 | if (dladdr(header, &info) == 0) { 120 | return; 121 | } 122 | 123 | segment_command_t *cur_seg_cmd; 124 | segment_command_t *linkedit_segment = NULL; 125 | struct symtab_command* symtab_cmd = NULL; 126 | struct dysymtab_command* dysymtab_cmd = NULL; 127 | 128 | uintptr_t cur = (uintptr_t)header + sizeof(mach_header_t); 129 | for (uint i = 0; i < header->ncmds; i++, cur += cur_seg_cmd->cmdsize) { 130 | cur_seg_cmd = (segment_command_t *)cur; 131 | if (cur_seg_cmd->cmd == LC_SEGMENT_ARCH_DEPENDENT) { 132 | if (strcmp(cur_seg_cmd->segname, SEG_LINKEDIT) == 0) { 133 | linkedit_segment = cur_seg_cmd; 134 | } 135 | } else if (cur_seg_cmd->cmd == LC_SYMTAB) { 136 | symtab_cmd = (struct symtab_command*)cur_seg_cmd; 137 | } else if (cur_seg_cmd->cmd == LC_DYSYMTAB) { 138 | dysymtab_cmd = (struct dysymtab_command*)cur_seg_cmd; 139 | } 140 | } 141 | 142 | if (!symtab_cmd || !dysymtab_cmd || !linkedit_segment || 143 | !dysymtab_cmd->nindirectsyms) { 144 | return; 145 | } 146 | 147 | // Find base symbol/string table addresses 148 | uintptr_t linkedit_base = (uintptr_t)slide + linkedit_segment->vmaddr - linkedit_segment->fileoff; 149 | nlist_t *symtab = (nlist_t *)(linkedit_base + symtab_cmd->symoff); 150 | char *strtab = (char *)(linkedit_base + symtab_cmd->stroff); 151 | 152 | // Get indirect symbol table (array of uint32_t indices into symbol table) 153 | uint32_t *indirect_symtab = (uint32_t *)(linkedit_base + dysymtab_cmd->indirectsymoff); 154 | 155 | cur = (uintptr_t)header + sizeof(mach_header_t); 156 | for (uint i = 0; i < header->ncmds; i++, cur += cur_seg_cmd->cmdsize) { 157 | cur_seg_cmd = (segment_command_t *)cur; 158 | if (cur_seg_cmd->cmd == LC_SEGMENT_ARCH_DEPENDENT) { 159 | if (strcmp(cur_seg_cmd->segname, SEG_DATA) != 0 && 160 | strcmp(cur_seg_cmd->segname, SEG_DATA_CONST) != 0) { 161 | continue; 162 | } 163 | for (uint j = 0; j < cur_seg_cmd->nsects; j++) { 164 | section_t *sect = 165 | (section_t *)(cur + sizeof(segment_command_t)) + j; 166 | if ((sect->flags & SECTION_TYPE) == S_LAZY_SYMBOL_POINTERS) { 167 | perform_rebinding_with_section(rebindings, sect, slide, symtab, strtab, indirect_symtab); 168 | } 169 | if ((sect->flags & SECTION_TYPE) == S_NON_LAZY_SYMBOL_POINTERS) { 170 | perform_rebinding_with_section(rebindings, sect, slide, symtab, strtab, indirect_symtab); 171 | } 172 | } 173 | } 174 | } 175 | } 176 | 177 | static void _rebind_symbols_for_image(const struct mach_header *header, 178 | intptr_t slide) { 179 | rebind_symbols_for_image(_rebindings_head, header, slide); 180 | } 181 | 182 | int mm_rebind_symbols_image(void *header, 183 | intptr_t slide, 184 | struct mm_rebinding rebindings[], 185 | size_t rebindings_nel) { 186 | struct rebindings_entry *rebindings_head = NULL; 187 | int retval = prepend_rebindings(&rebindings_head, rebindings, rebindings_nel); 188 | rebind_symbols_for_image(rebindings_head, (const struct mach_header *) header, slide); 189 | if (rebindings_head) { 190 | free(rebindings_head->rebindings); 191 | } 192 | free(rebindings_head); 193 | return retval; 194 | } 195 | 196 | int mm_rebind_symbols(struct mm_rebinding rebindings[], size_t rebindings_nel) { 197 | int retval = prepend_rebindings(&_rebindings_head, rebindings, rebindings_nel); 198 | if (retval < 0) { 199 | return retval; 200 | } 201 | // If this was the first call, register callback for image additions (which is also invoked for 202 | // existing images, otherwise, just run on existing images 203 | if (!_rebindings_head->next) { 204 | _dyld_register_func_for_add_image(_rebind_symbols_for_image); 205 | } else { 206 | uint32_t c = _dyld_image_count(); 207 | for (uint32_t i = 0; i < c; i++) { 208 | _rebind_symbols_for_image(_dyld_get_image_header(i), _dyld_get_image_vmaddr_slide(i)); 209 | } 210 | } 211 | return retval; 212 | } 213 | -------------------------------------------------------------------------------- /MessageMock/fishhook/mm_fishhook.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013, Facebook, Inc. 2 | // All rights reserved. 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions are met: 5 | // * Redistributions of source code must retain the above copyright notice, 6 | // this list of conditions and the following disclaimer. 7 | // * Redistributions in binary form must reproduce the above copyright notice, 8 | // this list of conditions and the following disclaimer in the documentation 9 | // and/or other materials provided with the distribution. 10 | // * Neither the name Facebook nor the names of its contributors may be used to 11 | // endorse or promote products derived from this software without specific 12 | // prior written permission. 13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 17 | // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 20 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 21 | // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 22 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | #ifndef mm_fishhook_h 25 | #define mm_fishhook_h 26 | 27 | #include 28 | #include 29 | 30 | #ifdef __cplusplus 31 | extern "C" { 32 | #endif //__cplusplus 33 | 34 | /* 35 | * A structure representing a particular intended mm_rebinding from a symbol 36 | * name to its replacement 37 | */ 38 | struct mm_rebinding { 39 | const char *name; 40 | void *replacement; 41 | void **replaced; 42 | }; 43 | 44 | /* 45 | * For each mm_rebinding in rebindings, rebinds references to external, indirect 46 | * symbols with the specified name to instead point at replacement for each 47 | * image in the calling process as well as for all future images that are loaded 48 | * by the process. If rebind_functions is called more than once, the symbols to 49 | * rebind are added to the existing list of rebindings, and if a given symbol 50 | * is rebound more than once, the later mm_rebinding will take precedence. 51 | */ 52 | int mm_rebind_symbols(struct mm_rebinding rebindings[], size_t rebindings_nel); 53 | 54 | /* 55 | * Rebinds as above, but only in the specified image. The header should point 56 | * to the mach-o header, the slide should be the slide offset. Others as above. 57 | */ 58 | int mm_rebind_symbols_image(void *header, 59 | intptr_t slide, 60 | struct mm_rebinding rebindings[], 61 | size_t rebindings_nel); 62 | 63 | #ifdef __cplusplus 64 | } 65 | #endif //__cplusplus 66 | 67 | #endif //mm_fishhook_h 68 | 69 | -------------------------------------------------------------------------------- /MessageMockDemo/MessageMockDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 51; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 4BBEEA8C24D672E500BF51FC /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BBEEA8B24D672E500BF51FC /* AppDelegate.m */; }; 11 | 4BBEEA8F24D672E500BF51FC /* SceneDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BBEEA8E24D672E500BF51FC /* SceneDelegate.m */; }; 12 | 4BBEEA9224D672E500BF51FC /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BBEEA9124D672E500BF51FC /* ViewController.m */; }; 13 | 4BBEEA9524D672E500BF51FC /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4BBEEA9324D672E500BF51FC /* Main.storyboard */; }; 14 | 4BBEEA9724D672E600BF51FC /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4BBEEA9624D672E600BF51FC /* Assets.xcassets */; }; 15 | 4BBEEA9A24D672E600BF51FC /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4BBEEA9824D672E600BF51FC /* LaunchScreen.storyboard */; }; 16 | 4BBEEA9D24D672E600BF51FC /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BBEEA9C24D672E600BF51FC /* main.m */; }; 17 | 4BBEEAB624D6758F00BF51FC /* MMReturnCallbackTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BBEEAB124D6758F00BF51FC /* MMReturnCallbackTests.mm */; }; 18 | 4BBEEAB724D6758F00BF51FC /* MMMethodReturnTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BBEEAB224D6758F00BF51FC /* MMMethodReturnTests.mm */; }; 19 | 4BBEEAB824D6758F00BF51FC /* MessageMockerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BBEEAB324D6758F00BF51FC /* MessageMockerTests.m */; }; 20 | 4BBEEAB924D6758F00BF51FC /* MMArgumentCallbackTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BBEEAB424D6758F00BF51FC /* MMArgumentCallbackTests.mm */; }; 21 | 4BBEEABA24D6758F00BF51FC /* MMChangeArgumentTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BBEEAB524D6758F00BF51FC /* MMChangeArgumentTests.mm */; }; 22 | 4BBEEABE24D6759A00BF51FC /* TestObject.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BBEEABD24D6759A00BF51FC /* TestObject.m */; }; 23 | 788824398C4F1A526B692317 /* Pods_MessageMockDemoTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 85204ADEF3AF5EED2111AFA2 /* Pods_MessageMockDemoTests.framework */; }; 24 | /* End PBXBuildFile section */ 25 | 26 | /* Begin PBXContainerItemProxy section */ 27 | 4BBEEAA324D672E600BF51FC /* PBXContainerItemProxy */ = { 28 | isa = PBXContainerItemProxy; 29 | containerPortal = 4BBEEA7F24D672E500BF51FC /* Project object */; 30 | proxyType = 1; 31 | remoteGlobalIDString = 4BBEEA8624D672E500BF51FC; 32 | remoteInfo = MessageMockDemo; 33 | }; 34 | /* End PBXContainerItemProxy section */ 35 | 36 | /* Begin PBXFileReference section */ 37 | 262BF739578A3A33058AFD27 /* Pods-MessageMockDemoTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MessageMockDemoTests.release.xcconfig"; path = "Target Support Files/Pods-MessageMockDemoTests/Pods-MessageMockDemoTests.release.xcconfig"; sourceTree = ""; }; 38 | 4BBEEA8724D672E500BF51FC /* MessageMockDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MessageMockDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 39 | 4BBEEA8A24D672E500BF51FC /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 40 | 4BBEEA8B24D672E500BF51FC /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 41 | 4BBEEA8D24D672E500BF51FC /* SceneDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SceneDelegate.h; sourceTree = ""; }; 42 | 4BBEEA8E24D672E500BF51FC /* SceneDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SceneDelegate.m; sourceTree = ""; }; 43 | 4BBEEA9024D672E500BF51FC /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 44 | 4BBEEA9124D672E500BF51FC /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 45 | 4BBEEA9424D672E500BF51FC /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 46 | 4BBEEA9624D672E600BF51FC /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 47 | 4BBEEA9924D672E600BF51FC /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 48 | 4BBEEA9B24D672E600BF51FC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 49 | 4BBEEA9C24D672E600BF51FC /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 50 | 4BBEEAA224D672E600BF51FC /* MessageMockDemoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MessageMockDemoTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 51 | 4BBEEAA824D672E600BF51FC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 52 | 4BBEEAB124D6758F00BF51FC /* MMReturnCallbackTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MMReturnCallbackTests.mm; sourceTree = ""; }; 53 | 4BBEEAB224D6758F00BF51FC /* MMMethodReturnTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MMMethodReturnTests.mm; sourceTree = ""; }; 54 | 4BBEEAB324D6758F00BF51FC /* MessageMockerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MessageMockerTests.m; sourceTree = ""; }; 55 | 4BBEEAB424D6758F00BF51FC /* MMArgumentCallbackTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MMArgumentCallbackTests.mm; sourceTree = ""; }; 56 | 4BBEEAB524D6758F00BF51FC /* MMChangeArgumentTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MMChangeArgumentTests.mm; sourceTree = ""; }; 57 | 4BBEEABC24D6759A00BF51FC /* TestObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TestObject.h; sourceTree = ""; }; 58 | 4BBEEABD24D6759A00BF51FC /* TestObject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TestObject.m; sourceTree = ""; }; 59 | 85204ADEF3AF5EED2111AFA2 /* Pods_MessageMockDemoTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_MessageMockDemoTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 60 | F15F084354D2FA76553B028E /* Pods-MessageMockDemoTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MessageMockDemoTests.debug.xcconfig"; path = "Target Support Files/Pods-MessageMockDemoTests/Pods-MessageMockDemoTests.debug.xcconfig"; sourceTree = ""; }; 61 | /* End PBXFileReference section */ 62 | 63 | /* Begin PBXFrameworksBuildPhase section */ 64 | 4BBEEA8424D672E500BF51FC /* Frameworks */ = { 65 | isa = PBXFrameworksBuildPhase; 66 | buildActionMask = 2147483647; 67 | files = ( 68 | ); 69 | runOnlyForDeploymentPostprocessing = 0; 70 | }; 71 | 4BBEEA9F24D672E600BF51FC /* Frameworks */ = { 72 | isa = PBXFrameworksBuildPhase; 73 | buildActionMask = 2147483647; 74 | files = ( 75 | 788824398C4F1A526B692317 /* Pods_MessageMockDemoTests.framework in Frameworks */, 76 | ); 77 | runOnlyForDeploymentPostprocessing = 0; 78 | }; 79 | /* End PBXFrameworksBuildPhase section */ 80 | 81 | /* Begin PBXGroup section */ 82 | 4BBEEA7E24D672E500BF51FC = { 83 | isa = PBXGroup; 84 | children = ( 85 | 4BBEEA8924D672E500BF51FC /* MessageMockDemo */, 86 | 4BBEEAA524D672E600BF51FC /* MessageMockDemoTests */, 87 | 4BBEEA8824D672E500BF51FC /* Products */, 88 | 88F83CC6A351B0D316702CB8 /* Pods */, 89 | C32F0D5F36833E2214BF6D03 /* Frameworks */, 90 | ); 91 | sourceTree = ""; 92 | }; 93 | 4BBEEA8824D672E500BF51FC /* Products */ = { 94 | isa = PBXGroup; 95 | children = ( 96 | 4BBEEA8724D672E500BF51FC /* MessageMockDemo.app */, 97 | 4BBEEAA224D672E600BF51FC /* MessageMockDemoTests.xctest */, 98 | ); 99 | name = Products; 100 | sourceTree = ""; 101 | }; 102 | 4BBEEA8924D672E500BF51FC /* MessageMockDemo */ = { 103 | isa = PBXGroup; 104 | children = ( 105 | 4BBEEABB24D6759A00BF51FC /* TestObjects */, 106 | 4BBEEA8A24D672E500BF51FC /* AppDelegate.h */, 107 | 4BBEEA8B24D672E500BF51FC /* AppDelegate.m */, 108 | 4BBEEA8D24D672E500BF51FC /* SceneDelegate.h */, 109 | 4BBEEA8E24D672E500BF51FC /* SceneDelegate.m */, 110 | 4BBEEA9024D672E500BF51FC /* ViewController.h */, 111 | 4BBEEA9124D672E500BF51FC /* ViewController.m */, 112 | 4BBEEA9324D672E500BF51FC /* Main.storyboard */, 113 | 4BBEEA9624D672E600BF51FC /* Assets.xcassets */, 114 | 4BBEEA9824D672E600BF51FC /* LaunchScreen.storyboard */, 115 | 4BBEEA9B24D672E600BF51FC /* Info.plist */, 116 | 4BBEEA9C24D672E600BF51FC /* main.m */, 117 | ); 118 | path = MessageMockDemo; 119 | sourceTree = ""; 120 | }; 121 | 4BBEEAA524D672E600BF51FC /* MessageMockDemoTests */ = { 122 | isa = PBXGroup; 123 | children = ( 124 | 4BBEEAB124D6758F00BF51FC /* MMReturnCallbackTests.mm */, 125 | 4BBEEAB424D6758F00BF51FC /* MMArgumentCallbackTests.mm */, 126 | 4BBEEAB524D6758F00BF51FC /* MMChangeArgumentTests.mm */, 127 | 4BBEEAB224D6758F00BF51FC /* MMMethodReturnTests.mm */, 128 | 4BBEEAB324D6758F00BF51FC /* MessageMockerTests.m */, 129 | 4BBEEAA824D672E600BF51FC /* Info.plist */, 130 | ); 131 | path = MessageMockDemoTests; 132 | sourceTree = ""; 133 | }; 134 | 4BBEEABB24D6759A00BF51FC /* TestObjects */ = { 135 | isa = PBXGroup; 136 | children = ( 137 | 4BBEEABC24D6759A00BF51FC /* TestObject.h */, 138 | 4BBEEABD24D6759A00BF51FC /* TestObject.m */, 139 | ); 140 | path = TestObjects; 141 | sourceTree = ""; 142 | }; 143 | 88F83CC6A351B0D316702CB8 /* Pods */ = { 144 | isa = PBXGroup; 145 | children = ( 146 | F15F084354D2FA76553B028E /* Pods-MessageMockDemoTests.debug.xcconfig */, 147 | 262BF739578A3A33058AFD27 /* Pods-MessageMockDemoTests.release.xcconfig */, 148 | ); 149 | path = Pods; 150 | sourceTree = ""; 151 | }; 152 | C32F0D5F36833E2214BF6D03 /* Frameworks */ = { 153 | isa = PBXGroup; 154 | children = ( 155 | 85204ADEF3AF5EED2111AFA2 /* Pods_MessageMockDemoTests.framework */, 156 | ); 157 | name = Frameworks; 158 | sourceTree = ""; 159 | }; 160 | /* End PBXGroup section */ 161 | 162 | /* Begin PBXNativeTarget section */ 163 | 4BBEEA8624D672E500BF51FC /* MessageMockDemo */ = { 164 | isa = PBXNativeTarget; 165 | buildConfigurationList = 4BBEEAAB24D672E600BF51FC /* Build configuration list for PBXNativeTarget "MessageMockDemo" */; 166 | buildPhases = ( 167 | 4BBEEA8324D672E500BF51FC /* Sources */, 168 | 4BBEEA8424D672E500BF51FC /* Frameworks */, 169 | 4BBEEA8524D672E500BF51FC /* Resources */, 170 | ); 171 | buildRules = ( 172 | ); 173 | dependencies = ( 174 | ); 175 | name = MessageMockDemo; 176 | productName = MessageMockDemo; 177 | productReference = 4BBEEA8724D672E500BF51FC /* MessageMockDemo.app */; 178 | productType = "com.apple.product-type.application"; 179 | }; 180 | 4BBEEAA124D672E600BF51FC /* MessageMockDemoTests */ = { 181 | isa = PBXNativeTarget; 182 | buildConfigurationList = 4BBEEAAE24D672E600BF51FC /* Build configuration list for PBXNativeTarget "MessageMockDemoTests" */; 183 | buildPhases = ( 184 | 6BF2D91D93E65C2A7E021BAA /* [CP] Check Pods Manifest.lock */, 185 | 4BBEEA9E24D672E600BF51FC /* Sources */, 186 | 4BBEEA9F24D672E600BF51FC /* Frameworks */, 187 | 4BBEEAA024D672E600BF51FC /* Resources */, 188 | 2D84A30AADD66651E1C588B5 /* [CP] Embed Pods Frameworks */, 189 | ); 190 | buildRules = ( 191 | ); 192 | dependencies = ( 193 | 4BBEEAA424D672E600BF51FC /* PBXTargetDependency */, 194 | ); 195 | name = MessageMockDemoTests; 196 | productName = MessageMockDemoTests; 197 | productReference = 4BBEEAA224D672E600BF51FC /* MessageMockDemoTests.xctest */; 198 | productType = "com.apple.product-type.bundle.unit-test"; 199 | }; 200 | /* End PBXNativeTarget section */ 201 | 202 | /* Begin PBXProject section */ 203 | 4BBEEA7F24D672E500BF51FC /* Project object */ = { 204 | isa = PBXProject; 205 | attributes = { 206 | LastUpgradeCheck = 1150; 207 | ORGANIZATIONNAME = yangbo; 208 | TargetAttributes = { 209 | 4BBEEA8624D672E500BF51FC = { 210 | CreatedOnToolsVersion = 11.5; 211 | }; 212 | 4BBEEAA124D672E600BF51FC = { 213 | CreatedOnToolsVersion = 11.5; 214 | TestTargetID = 4BBEEA8624D672E500BF51FC; 215 | }; 216 | }; 217 | }; 218 | buildConfigurationList = 4BBEEA8224D672E500BF51FC /* Build configuration list for PBXProject "MessageMockDemo" */; 219 | compatibilityVersion = "Xcode 9.3"; 220 | developmentRegion = en; 221 | hasScannedForEncodings = 0; 222 | knownRegions = ( 223 | en, 224 | Base, 225 | ); 226 | mainGroup = 4BBEEA7E24D672E500BF51FC; 227 | productRefGroup = 4BBEEA8824D672E500BF51FC /* Products */; 228 | projectDirPath = ""; 229 | projectRoot = ""; 230 | targets = ( 231 | 4BBEEA8624D672E500BF51FC /* MessageMockDemo */, 232 | 4BBEEAA124D672E600BF51FC /* MessageMockDemoTests */, 233 | ); 234 | }; 235 | /* End PBXProject section */ 236 | 237 | /* Begin PBXResourcesBuildPhase section */ 238 | 4BBEEA8524D672E500BF51FC /* Resources */ = { 239 | isa = PBXResourcesBuildPhase; 240 | buildActionMask = 2147483647; 241 | files = ( 242 | 4BBEEA9A24D672E600BF51FC /* LaunchScreen.storyboard in Resources */, 243 | 4BBEEA9724D672E600BF51FC /* Assets.xcassets in Resources */, 244 | 4BBEEA9524D672E500BF51FC /* Main.storyboard in Resources */, 245 | ); 246 | runOnlyForDeploymentPostprocessing = 0; 247 | }; 248 | 4BBEEAA024D672E600BF51FC /* Resources */ = { 249 | isa = PBXResourcesBuildPhase; 250 | buildActionMask = 2147483647; 251 | files = ( 252 | ); 253 | runOnlyForDeploymentPostprocessing = 0; 254 | }; 255 | /* End PBXResourcesBuildPhase section */ 256 | 257 | /* Begin PBXShellScriptBuildPhase section */ 258 | 2D84A30AADD66651E1C588B5 /* [CP] Embed Pods Frameworks */ = { 259 | isa = PBXShellScriptBuildPhase; 260 | buildActionMask = 2147483647; 261 | files = ( 262 | ); 263 | inputFileListPaths = ( 264 | "${PODS_ROOT}/Target Support Files/Pods-MessageMockDemoTests/Pods-MessageMockDemoTests-frameworks-${CONFIGURATION}-input-files.xcfilelist", 265 | ); 266 | name = "[CP] Embed Pods Frameworks"; 267 | outputFileListPaths = ( 268 | "${PODS_ROOT}/Target Support Files/Pods-MessageMockDemoTests/Pods-MessageMockDemoTests-frameworks-${CONFIGURATION}-output-files.xcfilelist", 269 | ); 270 | runOnlyForDeploymentPostprocessing = 0; 271 | shellPath = /bin/sh; 272 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-MessageMockDemoTests/Pods-MessageMockDemoTests-frameworks.sh\"\n"; 273 | showEnvVarsInLog = 0; 274 | }; 275 | 6BF2D91D93E65C2A7E021BAA /* [CP] Check Pods Manifest.lock */ = { 276 | isa = PBXShellScriptBuildPhase; 277 | buildActionMask = 2147483647; 278 | files = ( 279 | ); 280 | inputFileListPaths = ( 281 | ); 282 | inputPaths = ( 283 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 284 | "${PODS_ROOT}/Manifest.lock", 285 | ); 286 | name = "[CP] Check Pods Manifest.lock"; 287 | outputFileListPaths = ( 288 | ); 289 | outputPaths = ( 290 | "$(DERIVED_FILE_DIR)/Pods-MessageMockDemoTests-checkManifestLockResult.txt", 291 | ); 292 | runOnlyForDeploymentPostprocessing = 0; 293 | shellPath = /bin/sh; 294 | 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"; 295 | showEnvVarsInLog = 0; 296 | }; 297 | /* End PBXShellScriptBuildPhase section */ 298 | 299 | /* Begin PBXSourcesBuildPhase section */ 300 | 4BBEEA8324D672E500BF51FC /* Sources */ = { 301 | isa = PBXSourcesBuildPhase; 302 | buildActionMask = 2147483647; 303 | files = ( 304 | 4BBEEA9224D672E500BF51FC /* ViewController.m in Sources */, 305 | 4BBEEA8C24D672E500BF51FC /* AppDelegate.m in Sources */, 306 | 4BBEEABE24D6759A00BF51FC /* TestObject.m in Sources */, 307 | 4BBEEA9D24D672E600BF51FC /* main.m in Sources */, 308 | 4BBEEA8F24D672E500BF51FC /* SceneDelegate.m in Sources */, 309 | ); 310 | runOnlyForDeploymentPostprocessing = 0; 311 | }; 312 | 4BBEEA9E24D672E600BF51FC /* Sources */ = { 313 | isa = PBXSourcesBuildPhase; 314 | buildActionMask = 2147483647; 315 | files = ( 316 | 4BBEEAB724D6758F00BF51FC /* MMMethodReturnTests.mm in Sources */, 317 | 4BBEEAB924D6758F00BF51FC /* MMArgumentCallbackTests.mm in Sources */, 318 | 4BBEEAB624D6758F00BF51FC /* MMReturnCallbackTests.mm in Sources */, 319 | 4BBEEAB824D6758F00BF51FC /* MessageMockerTests.m in Sources */, 320 | 4BBEEABA24D6758F00BF51FC /* MMChangeArgumentTests.mm in Sources */, 321 | ); 322 | runOnlyForDeploymentPostprocessing = 0; 323 | }; 324 | /* End PBXSourcesBuildPhase section */ 325 | 326 | /* Begin PBXTargetDependency section */ 327 | 4BBEEAA424D672E600BF51FC /* PBXTargetDependency */ = { 328 | isa = PBXTargetDependency; 329 | target = 4BBEEA8624D672E500BF51FC /* MessageMockDemo */; 330 | targetProxy = 4BBEEAA324D672E600BF51FC /* PBXContainerItemProxy */; 331 | }; 332 | /* End PBXTargetDependency section */ 333 | 334 | /* Begin PBXVariantGroup section */ 335 | 4BBEEA9324D672E500BF51FC /* Main.storyboard */ = { 336 | isa = PBXVariantGroup; 337 | children = ( 338 | 4BBEEA9424D672E500BF51FC /* Base */, 339 | ); 340 | name = Main.storyboard; 341 | sourceTree = ""; 342 | }; 343 | 4BBEEA9824D672E600BF51FC /* LaunchScreen.storyboard */ = { 344 | isa = PBXVariantGroup; 345 | children = ( 346 | 4BBEEA9924D672E600BF51FC /* Base */, 347 | ); 348 | name = LaunchScreen.storyboard; 349 | sourceTree = ""; 350 | }; 351 | /* End PBXVariantGroup section */ 352 | 353 | /* Begin XCBuildConfiguration section */ 354 | 4BBEEAA924D672E600BF51FC /* Debug */ = { 355 | isa = XCBuildConfiguration; 356 | buildSettings = { 357 | ALWAYS_SEARCH_USER_PATHS = NO; 358 | CLANG_ANALYZER_NONNULL = YES; 359 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 360 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 361 | CLANG_CXX_LIBRARY = "libc++"; 362 | CLANG_ENABLE_MODULES = YES; 363 | CLANG_ENABLE_OBJC_ARC = YES; 364 | CLANG_ENABLE_OBJC_WEAK = YES; 365 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 366 | CLANG_WARN_BOOL_CONVERSION = YES; 367 | CLANG_WARN_COMMA = YES; 368 | CLANG_WARN_CONSTANT_CONVERSION = YES; 369 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 370 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 371 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 372 | CLANG_WARN_EMPTY_BODY = YES; 373 | CLANG_WARN_ENUM_CONVERSION = YES; 374 | CLANG_WARN_INFINITE_RECURSION = YES; 375 | CLANG_WARN_INT_CONVERSION = YES; 376 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 377 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 378 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 379 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 380 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 381 | CLANG_WARN_STRICT_PROTOTYPES = YES; 382 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 383 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 384 | CLANG_WARN_UNREACHABLE_CODE = YES; 385 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 386 | COPY_PHASE_STRIP = NO; 387 | DEBUG_INFORMATION_FORMAT = dwarf; 388 | ENABLE_STRICT_OBJC_MSGSEND = YES; 389 | ENABLE_TESTABILITY = YES; 390 | GCC_C_LANGUAGE_STANDARD = gnu11; 391 | GCC_DYNAMIC_NO_PIC = NO; 392 | GCC_NO_COMMON_BLOCKS = YES; 393 | GCC_OPTIMIZATION_LEVEL = 0; 394 | GCC_PREPROCESSOR_DEFINITIONS = ( 395 | "DEBUG=1", 396 | "$(inherited)", 397 | ); 398 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 399 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 400 | GCC_WARN_UNDECLARED_SELECTOR = YES; 401 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 402 | GCC_WARN_UNUSED_FUNCTION = YES; 403 | GCC_WARN_UNUSED_VARIABLE = YES; 404 | IPHONEOS_DEPLOYMENT_TARGET = 13.5; 405 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 406 | MTL_FAST_MATH = YES; 407 | ONLY_ACTIVE_ARCH = YES; 408 | SDKROOT = iphoneos; 409 | }; 410 | name = Debug; 411 | }; 412 | 4BBEEAAA24D672E600BF51FC /* Release */ = { 413 | isa = XCBuildConfiguration; 414 | buildSettings = { 415 | ALWAYS_SEARCH_USER_PATHS = NO; 416 | CLANG_ANALYZER_NONNULL = YES; 417 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 418 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 419 | CLANG_CXX_LIBRARY = "libc++"; 420 | CLANG_ENABLE_MODULES = YES; 421 | CLANG_ENABLE_OBJC_ARC = YES; 422 | CLANG_ENABLE_OBJC_WEAK = YES; 423 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 424 | CLANG_WARN_BOOL_CONVERSION = YES; 425 | CLANG_WARN_COMMA = YES; 426 | CLANG_WARN_CONSTANT_CONVERSION = YES; 427 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 428 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 429 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 430 | CLANG_WARN_EMPTY_BODY = YES; 431 | CLANG_WARN_ENUM_CONVERSION = YES; 432 | CLANG_WARN_INFINITE_RECURSION = YES; 433 | CLANG_WARN_INT_CONVERSION = YES; 434 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 435 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 436 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 437 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 438 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 439 | CLANG_WARN_STRICT_PROTOTYPES = YES; 440 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 441 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 442 | CLANG_WARN_UNREACHABLE_CODE = YES; 443 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 444 | COPY_PHASE_STRIP = NO; 445 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 446 | ENABLE_NS_ASSERTIONS = NO; 447 | ENABLE_STRICT_OBJC_MSGSEND = YES; 448 | GCC_C_LANGUAGE_STANDARD = gnu11; 449 | GCC_NO_COMMON_BLOCKS = YES; 450 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 451 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 452 | GCC_WARN_UNDECLARED_SELECTOR = YES; 453 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 454 | GCC_WARN_UNUSED_FUNCTION = YES; 455 | GCC_WARN_UNUSED_VARIABLE = YES; 456 | IPHONEOS_DEPLOYMENT_TARGET = 13.5; 457 | MTL_ENABLE_DEBUG_INFO = NO; 458 | MTL_FAST_MATH = YES; 459 | SDKROOT = iphoneos; 460 | VALIDATE_PRODUCT = YES; 461 | }; 462 | name = Release; 463 | }; 464 | 4BBEEAAC24D672E600BF51FC /* Debug */ = { 465 | isa = XCBuildConfiguration; 466 | buildSettings = { 467 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 468 | CODE_SIGN_STYLE = Automatic; 469 | DEVELOPMENT_TEAM = UU8H9EQ986; 470 | INFOPLIST_FILE = MessageMockDemo/Info.plist; 471 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 472 | LD_RUNPATH_SEARCH_PATHS = ( 473 | "$(inherited)", 474 | "@executable_path/Frameworks", 475 | ); 476 | OTHER_LDFLAGS = ( 477 | "-ObjC", 478 | "$(inherited)", 479 | ); 480 | PRODUCT_BUNDLE_IDENTIFIER = com.yangbo.MessageMockDemo; 481 | PRODUCT_NAME = "$(TARGET_NAME)"; 482 | TARGETED_DEVICE_FAMILY = "1,2"; 483 | }; 484 | name = Debug; 485 | }; 486 | 4BBEEAAD24D672E600BF51FC /* Release */ = { 487 | isa = XCBuildConfiguration; 488 | buildSettings = { 489 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 490 | CODE_SIGN_STYLE = Automatic; 491 | DEVELOPMENT_TEAM = UU8H9EQ986; 492 | INFOPLIST_FILE = MessageMockDemo/Info.plist; 493 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 494 | LD_RUNPATH_SEARCH_PATHS = ( 495 | "$(inherited)", 496 | "@executable_path/Frameworks", 497 | ); 498 | OTHER_LDFLAGS = ( 499 | "-ObjC", 500 | "$(inherited)", 501 | ); 502 | PRODUCT_BUNDLE_IDENTIFIER = com.yangbo.MessageMockDemo; 503 | PRODUCT_NAME = "$(TARGET_NAME)"; 504 | TARGETED_DEVICE_FAMILY = "1,2"; 505 | }; 506 | name = Release; 507 | }; 508 | 4BBEEAAF24D672E600BF51FC /* Debug */ = { 509 | isa = XCBuildConfiguration; 510 | baseConfigurationReference = F15F084354D2FA76553B028E /* Pods-MessageMockDemoTests.debug.xcconfig */; 511 | buildSettings = { 512 | BUNDLE_LOADER = "$(TEST_HOST)"; 513 | CODE_SIGN_STYLE = Automatic; 514 | DEVELOPMENT_TEAM = UU8H9EQ986; 515 | INFOPLIST_FILE = MessageMockDemoTests/Info.plist; 516 | IPHONEOS_DEPLOYMENT_TARGET = 13.5; 517 | LD_RUNPATH_SEARCH_PATHS = ( 518 | "$(inherited)", 519 | "@executable_path/Frameworks", 520 | "@loader_path/Frameworks", 521 | ); 522 | PRODUCT_BUNDLE_IDENTIFIER = com.yangbo.MessageMockDemoTests; 523 | PRODUCT_NAME = "$(TARGET_NAME)"; 524 | TARGETED_DEVICE_FAMILY = "1,2"; 525 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/MessageMockDemo.app/MessageMockDemo"; 526 | }; 527 | name = Debug; 528 | }; 529 | 4BBEEAB024D672E600BF51FC /* Release */ = { 530 | isa = XCBuildConfiguration; 531 | baseConfigurationReference = 262BF739578A3A33058AFD27 /* Pods-MessageMockDemoTests.release.xcconfig */; 532 | buildSettings = { 533 | BUNDLE_LOADER = "$(TEST_HOST)"; 534 | CODE_SIGN_STYLE = Automatic; 535 | DEVELOPMENT_TEAM = UU8H9EQ986; 536 | INFOPLIST_FILE = MessageMockDemoTests/Info.plist; 537 | IPHONEOS_DEPLOYMENT_TARGET = 13.5; 538 | LD_RUNPATH_SEARCH_PATHS = ( 539 | "$(inherited)", 540 | "@executable_path/Frameworks", 541 | "@loader_path/Frameworks", 542 | ); 543 | PRODUCT_BUNDLE_IDENTIFIER = com.yangbo.MessageMockDemoTests; 544 | PRODUCT_NAME = "$(TARGET_NAME)"; 545 | TARGETED_DEVICE_FAMILY = "1,2"; 546 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/MessageMockDemo.app/MessageMockDemo"; 547 | }; 548 | name = Release; 549 | }; 550 | /* End XCBuildConfiguration section */ 551 | 552 | /* Begin XCConfigurationList section */ 553 | 4BBEEA8224D672E500BF51FC /* Build configuration list for PBXProject "MessageMockDemo" */ = { 554 | isa = XCConfigurationList; 555 | buildConfigurations = ( 556 | 4BBEEAA924D672E600BF51FC /* Debug */, 557 | 4BBEEAAA24D672E600BF51FC /* Release */, 558 | ); 559 | defaultConfigurationIsVisible = 0; 560 | defaultConfigurationName = Release; 561 | }; 562 | 4BBEEAAB24D672E600BF51FC /* Build configuration list for PBXNativeTarget "MessageMockDemo" */ = { 563 | isa = XCConfigurationList; 564 | buildConfigurations = ( 565 | 4BBEEAAC24D672E600BF51FC /* Debug */, 566 | 4BBEEAAD24D672E600BF51FC /* Release */, 567 | ); 568 | defaultConfigurationIsVisible = 0; 569 | defaultConfigurationName = Release; 570 | }; 571 | 4BBEEAAE24D672E600BF51FC /* Build configuration list for PBXNativeTarget "MessageMockDemoTests" */ = { 572 | isa = XCConfigurationList; 573 | buildConfigurations = ( 574 | 4BBEEAAF24D672E600BF51FC /* Debug */, 575 | 4BBEEAB024D672E600BF51FC /* Release */, 576 | ); 577 | defaultConfigurationIsVisible = 0; 578 | defaultConfigurationName = Release; 579 | }; 580 | /* End XCConfigurationList section */ 581 | }; 582 | rootObject = 4BBEEA7F24D672E500BF51FC /* Project object */; 583 | } 584 | -------------------------------------------------------------------------------- /MessageMockDemo/MessageMockDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /MessageMockDemo/MessageMockDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /MessageMockDemo/MessageMockDemo.xcodeproj/project.xcworkspace/xcuserdata/answeryang.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/indulgeIn/MessageMock/06e88694ba73fe0aa4448ba9bbb4cafe64662275/MessageMockDemo/MessageMockDemo.xcodeproj/project.xcworkspace/xcuserdata/answeryang.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /MessageMockDemo/MessageMockDemo.xcodeproj/xcuserdata/answeryang.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | MessageMockDemo.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 2 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /MessageMockDemo/MessageMockDemo.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /MessageMockDemo/MessageMockDemo.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /MessageMockDemo/MessageMockDemo.xcworkspace/xcuserdata/answeryang.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/indulgeIn/MessageMock/06e88694ba73fe0aa4448ba9bbb4cafe64662275/MessageMockDemo/MessageMockDemo.xcworkspace/xcuserdata/answeryang.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /MessageMockDemo/MessageMockDemo.xcworkspace/xcuserdata/answeryang.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 9 | 21 | 22 | 23 | 25 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /MessageMockDemo/MessageMockDemo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // MessageMockDemo 4 | // 5 | // Created by 波儿菜 on 2020/8/2. 6 | // 7 | 8 | #import 9 | 10 | @interface AppDelegate : UIResponder 11 | 12 | 13 | @end 14 | 15 | -------------------------------------------------------------------------------- /MessageMockDemo/MessageMockDemo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // MessageMockDemo 4 | // 5 | // Created by 波儿菜 on 2020/8/2. 6 | // 7 | 8 | #import "AppDelegate.h" 9 | 10 | @interface AppDelegate () 11 | 12 | @end 13 | 14 | @implementation AppDelegate 15 | 16 | 17 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 18 | // Override point for customization after application launch. 19 | return YES; 20 | } 21 | 22 | 23 | #pragma mark - UISceneSession lifecycle 24 | 25 | 26 | - (UISceneConfiguration *)application:(UIApplication *)application configurationForConnectingSceneSession:(UISceneSession *)connectingSceneSession options:(UISceneConnectionOptions *)options API_AVAILABLE(ios(13.0)) { 27 | // Called when a new scene session is being created. 28 | // Use this method to select a configuration to create the new scene with. 29 | return [[UISceneConfiguration alloc] initWithName:@"Default Configuration" sessionRole:connectingSceneSession.role]; 30 | } 31 | 32 | 33 | - (void)application:(UIApplication *)application didDiscardSceneSessions:(NSSet *)sceneSessions API_AVAILABLE(ios(13.0)) { 34 | // Called when the user discards a scene session. 35 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. 36 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return. 37 | } 38 | 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /MessageMockDemo/MessageMockDemo/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 | -------------------------------------------------------------------------------- /MessageMockDemo/MessageMockDemo/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /MessageMockDemo/MessageMockDemo/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /MessageMockDemo/MessageMockDemo/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 | -------------------------------------------------------------------------------- /MessageMockDemo/MessageMockDemo/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 | UISupportedInterfaceOrientations 51 | 52 | UIInterfaceOrientationPortrait 53 | UIInterfaceOrientationLandscapeLeft 54 | UIInterfaceOrientationLandscapeRight 55 | 56 | UISupportedInterfaceOrientations~ipad 57 | 58 | UIInterfaceOrientationPortrait 59 | UIInterfaceOrientationPortraitUpsideDown 60 | UIInterfaceOrientationLandscapeLeft 61 | UIInterfaceOrientationLandscapeRight 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /MessageMockDemo/MessageMockDemo/SceneDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.h 3 | // MessageMockDemo 4 | // 5 | // Created by 波儿菜 on 2020/8/2. 6 | // 7 | 8 | #import 9 | 10 | @interface SceneDelegate : UIResponder 11 | 12 | @property (strong, nonatomic) UIWindow * window; 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /MessageMockDemo/MessageMockDemo/SceneDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.m 3 | // MessageMockDemo 4 | // 5 | // Created by 波儿菜 on 2020/8/2. 6 | // 7 | 8 | #import "SceneDelegate.h" 9 | 10 | @interface SceneDelegate () 11 | 12 | @end 13 | 14 | @implementation SceneDelegate 15 | 16 | 17 | - (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions API_AVAILABLE(ios(13.0)) { 18 | // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. 19 | // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. 20 | // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). 21 | } 22 | 23 | 24 | - (void)sceneDidDisconnect:(UIScene *)scene API_AVAILABLE(ios(13.0)) { 25 | // Called as the scene is being released by the system. 26 | // This occurs shortly after the scene enters the background, or when its session is discarded. 27 | // Release any resources associated with this scene that can be re-created the next time the scene connects. 28 | // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead). 29 | } 30 | 31 | 32 | - (void)sceneDidBecomeActive:(UIScene *)scene API_AVAILABLE(ios(13.0)) { 33 | // Called when the scene has moved from an inactive state to an active state. 34 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. 35 | } 36 | 37 | 38 | - (void)sceneWillResignActive:(UIScene *)scene API_AVAILABLE(ios(13.0)) { 39 | // Called when the scene will move from an active state to an inactive state. 40 | // This may occur due to temporary interruptions (ex. an incoming phone call). 41 | } 42 | 43 | 44 | - (void)sceneWillEnterForeground:(UIScene *)scene API_AVAILABLE(ios(13.0)) { 45 | // Called as the scene transitions from the background to the foreground. 46 | // Use this method to undo the changes made on entering the background. 47 | } 48 | 49 | 50 | - (void)sceneDidEnterBackground:(UIScene *)scene API_AVAILABLE(ios(13.0)) { 51 | // Called as the scene transitions from the foreground to the background. 52 | // Use this method to save data, release shared resources, and store enough scene-specific state information 53 | // to restore the scene back to its current state. 54 | } 55 | 56 | 57 | @end 58 | -------------------------------------------------------------------------------- /MessageMockDemo/MessageMockDemo/TestObjects/TestObject.h: -------------------------------------------------------------------------------- 1 | // 2 | // TestObject.h 3 | // MessageMockDemo 4 | // 5 | // Created by 波儿菜 on 2020/6/27. 6 | // 7 | 8 | #import 9 | 10 | NS_ASSUME_NONNULL_BEGIN 11 | 12 | @interface TestObject : NSObject 13 | 14 | + (TestObject *)createObj; 15 | 16 | + (char *)createPointer; 17 | 18 | + (long)createLong; 19 | 20 | + (int)createInt; 21 | 22 | + (int16_t)createInt16; 23 | 24 | + (int8_t)createInt8; 25 | 26 | + (double)createDouble; 27 | 28 | + (float)createFloat; 29 | 30 | /* 31 | 参数基本规则 32 | 仅使用寄存器情况: 33 | 通用寄存器参数最多 6 个 x2 - x7 34 | 浮点寄存器参数最多 8 个 d0 - d7 (编译器限制不能连续超过6个) 35 | */ 36 | 37 | @property (nonatomic, copy) NSString *name; 38 | @property (nonatomic, assign) int age; 39 | @property (nonatomic, assign) NSInteger phone; 40 | @property (nonatomic, assign) BOOL isMan; 41 | @property (nonatomic, assign) double height; 42 | @property (nonatomic, assign) float weight; 43 | @property (nonatomic, strong) NSObject *extra; 44 | 45 | + (TestObject *)createWithName:(NSString *)name 46 | age:(int)age 47 | phone:(NSInteger)phone 48 | isMan:(BOOL)isMan 49 | height:(double)height 50 | weight:(float)weight 51 | extra:(NSObject *)extra; 52 | 53 | + (TestObject *)defaultArgument; 54 | 55 | @end 56 | 57 | NS_ASSUME_NONNULL_END 58 | -------------------------------------------------------------------------------- /MessageMockDemo/MessageMockDemo/TestObjects/TestObject.m: -------------------------------------------------------------------------------- 1 | // 2 | // TestObject.m 3 | // MessageMockDemo 4 | // 5 | // Created by 波儿菜 on 2020/6/27. 6 | // 7 | 8 | #import "TestObject.h" 9 | 10 | @implementation TestObject 11 | 12 | - (void)dealloc { 13 | NSLog(@"dealloc: %@", self); 14 | } 15 | 16 | + (TestObject *)createObj { 17 | TestObject *obj = [TestObject new]; 18 | return obj; 19 | } 20 | 21 | + (char *)createPointer { 22 | char *value = (char *)malloc(sizeof(char)); 23 | value = "aaaaa"; 24 | return value; 25 | } 26 | 27 | + (long)createLong { 28 | return 100; 29 | } 30 | 31 | + (int)createInt { 32 | return -100; 33 | } 34 | 35 | + (int16_t)createInt16 { 36 | return -100; 37 | } 38 | 39 | + (int8_t)createInt8 { 40 | return (int8_t)(1 << 7); 41 | } 42 | 43 | + (double)createDouble { 44 | return 1000000000000.0001192873912731982; 45 | } 46 | 47 | + (float)createFloat { 48 | return 97249213.23141234; 49 | } 50 | 51 | + (TestObject *)createWithName:(NSString *)name age:(int)age phone:(NSInteger)phone isMan:(BOOL)isMan height:(double)height weight:(float)weight extra:(nonnull NSObject *)extra { 52 | TestObject *res = [TestObject new]; 53 | res.name = name; 54 | res.phone = phone; 55 | res.age = age; 56 | res.height = height; 57 | res.weight = weight; 58 | res.isMan = isMan; 59 | res.extra = extra; 60 | return res; 61 | } 62 | 63 | + (TestObject *)defaultArgument { 64 | TestObject *res = [TestObject new]; 65 | res.name = @"波儿菜"; 66 | res.phone = 66666666666; 67 | res.age = 27; 68 | res.isMan = YES; 69 | res.height = 170; 70 | res.weight = -105; 71 | static NSObject *extra = nil; 72 | if (!extra) { 73 | extra = [NSObject new]; 74 | } 75 | res.extra = extra; 76 | return res; 77 | } 78 | 79 | - (BOOL)isEqual:(TestObject *)object { 80 | return 81 | self.name == object.name 82 | && self.phone == object.phone 83 | && self.age == object.age 84 | && self.height == object.height 85 | && self.weight == object.weight 86 | && self.isMan == object.isMan 87 | && self.extra == object.extra; 88 | } 89 | 90 | @end 91 | -------------------------------------------------------------------------------- /MessageMockDemo/MessageMockDemo/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // MessageMockDemo 4 | // 5 | // Created by 波儿菜 on 2020/8/2. 6 | // 7 | 8 | #import 9 | 10 | @interface ViewController : UIViewController 11 | 12 | 13 | @end 14 | 15 | -------------------------------------------------------------------------------- /MessageMockDemo/MessageMockDemo/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // MessageMockDemo 4 | // 5 | // Created by 波儿菜 on 2020/8/2. 6 | // 7 | 8 | #import "ViewController.h" 9 | 10 | @interface ViewController () 11 | 12 | @end 13 | 14 | @implementation ViewController 15 | 16 | - (void)viewDidLoad { 17 | [super viewDidLoad]; 18 | // Do any additional setup after loading the view. 19 | } 20 | 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /MessageMockDemo/MessageMockDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // MessageMockDemo 4 | // 5 | // Created by 波儿菜 on 2020/8/2. 6 | // 7 | 8 | #import 9 | #import "AppDelegate.h" 10 | 11 | int main(int argc, char * argv[]) { 12 | NSString * appDelegateClassName; 13 | @autoreleasepool { 14 | // Setup code that might create autoreleased objects goes here. 15 | appDelegateClassName = NSStringFromClass([AppDelegate class]); 16 | } 17 | return UIApplicationMain(argc, argv, nil, appDelegateClassName); 18 | } 19 | -------------------------------------------------------------------------------- /MessageMockDemo/MessageMockDemoTests/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 | -------------------------------------------------------------------------------- /MessageMockDemo/MessageMockDemoTests/MMArgumentCallbackTests.mm: -------------------------------------------------------------------------------- 1 | // 2 | // MMArgumentCallbackTests.m 3 | // MessageMockDemoTests 4 | // 5 | // Created by 波儿菜 on 2020/7/4. 6 | // 7 | 8 | #import 9 | #import 10 | #import 11 | #import "TestObject.h" 12 | 13 | using namespace mm_method_matcher; 14 | 15 | @interface MMArgumentCallbackTests : XCTestCase 16 | 17 | @end 18 | 19 | @implementation MMArgumentCallbackTests 20 | 21 | - (void)setUp { 22 | MMStartWorking(); 23 | } 24 | 25 | - (void)tearDown {} 26 | 27 | - (void)testArgumentCallback { 28 | TestObject *argument = [TestObject defaultArgument]; 29 | 30 | MethodMatcher *matcher = new MethodMatcher(); 31 | matcher->target = (uintptr_t)TestObject.self; 32 | matcher->selector = (uintptr_t)(void *)(@selector(createWithName:age:phone:isMan:height:weight:extra:)); 33 | matcher->skip = false; 34 | matcher->limit_hit_count = 1; 35 | 36 | ArgumentCallback callBack2 = ^(uintptr_t arg, ArgumentCallbackError error) { 37 | void *argPointer = (void *)arg; 38 | NSString *name = (__bridge NSString *)argPointer; 39 | XCTAssertTrue(name == argument.name); 40 | NSLog(@"\n callBack2 NSString:%@\n", name); 41 | }; 42 | ArgumentCallback callBack3 = ^(uintptr_t arg, ArgumentCallbackError error) { 43 | int age = (int)arg; 44 | XCTAssertTrue(age == argument.age); 45 | NSLog(@"\n callBack3 int:%d\n", age); 46 | }; 47 | ArgumentCallback callBack4 = ^(uintptr_t arg, ArgumentCallbackError error) { 48 | NSInteger phone = (NSInteger)arg; 49 | XCTAssertTrue(phone == argument.phone); 50 | NSLog(@"\n callBack4 NSInteger:%ld\n", phone); 51 | }; 52 | ArgumentCallback callBack5 = ^(uintptr_t arg, ArgumentCallbackError error) { 53 | BOOL isMan = (BOOL)arg; 54 | XCTAssertTrue(isMan == argument.isMan); 55 | NSLog(@"\n callBack5 BOOL:%@\n", @(isMan)); 56 | }; 57 | ArgumentCallback callBack6 = ^(uintptr_t arg, ArgumentCallbackError error) { 58 | double height = *((double *)&arg); 59 | XCTAssertTrue(height == argument.height); 60 | NSLog(@"\n callBack6 double:%lf\n", height); 61 | }; 62 | ArgumentCallback callBack7 = ^(uintptr_t arg, ArgumentCallbackError error) { 63 | float weight = *((float *)&arg); 64 | XCTAssertTrue(weight == argument.weight); 65 | NSLog(@"\n callBack7 float:%f\n", weight); 66 | }; 67 | ArgumentCallback callBack8 = ^(uintptr_t arg, ArgumentCallbackError error) { 68 | void *argPointer = (void *)arg; 69 | NSObject *extra = (__bridge NSObject *)argPointer; 70 | XCTAssertTrue(extra == argument.extra); 71 | NSLog(@"\n callBack8 NSObject:%@\n", extra); 72 | }; 73 | ArgumentCallbackMap *callbackMap = new ArgumentCallbackMap(); 74 | (*callbackMap)[2] = callBack2; 75 | (*callbackMap)[3] = callBack3; 76 | (*callbackMap)[4] = callBack4; 77 | (*callbackMap)[5] = callBack5; 78 | (*callbackMap)[6] = callBack6; 79 | (*callbackMap)[7] = callBack7; 80 | (*callbackMap)[8] = callBack8; 81 | 82 | matcher->argument_callback_map = callbackMap; 83 | 84 | AddMethodMatcher(matcher); 85 | 86 | [TestObject createWithName:argument.name age:argument.age phone:argument.phone isMan:argument.isMan height:argument.height weight:argument.weight extra:argument.extra]; 87 | } 88 | 89 | @end 90 | -------------------------------------------------------------------------------- /MessageMockDemo/MessageMockDemoTests/MMChangeArgumentTests.mm: -------------------------------------------------------------------------------- 1 | // 2 | // MMChangeArgumentTests.m 3 | // MessageMockDemoTests 4 | // 5 | // Created by 波儿菜 on 2020/7/10. 6 | // 7 | 8 | #import 9 | #import 10 | #import 11 | #import "TestObject.h" 12 | 13 | using namespace mm_method_matcher; 14 | 15 | @interface MMChangeArgumentTests : XCTestCase 16 | 17 | @end 18 | 19 | @implementation MMChangeArgumentTests 20 | 21 | - (void)setUp { 22 | MMStartWorking(); 23 | } 24 | 25 | - (void)tearDown {} 26 | 27 | - (void)testChangeArgument { 28 | MethodMatcher *matcher = new MethodMatcher(); 29 | matcher->target = (uintptr_t)TestObject.self; 30 | matcher->selector = (uintptr_t)(void *)(@selector(createWithName:age:phone:isMan:height:weight:extra:)); 31 | matcher->skip = false; 32 | matcher->limit_hit_count = 1; 33 | 34 | TestObject *argument = [TestObject defaultArgument]; 35 | Argument mark0 = {.idx = 2, .object = (__bridge_retained void *)argument.name}; 36 | Argument mark1 = {.idx = 3, .value = (uintptr_t)argument.age}; 37 | Argument mark2 = {.idx = 4, .value = (uintptr_t)argument.phone}; 38 | Argument mark3 = {.idx = 5, .value = (uintptr_t)argument.isMan}; 39 | double height = argument.height; 40 | Argument mark4 = {.idx = 6, .value = *(uintptr_t *)&height}; 41 | float weight = argument.weight; 42 | Argument mark5 = {.idx = 7, .value = *(uintptr_t *)&weight}; 43 | Argument mark6 = {.idx = 8, .object = (__bridge_retained void *)argument.extra}; 44 | matcher->arguments = new ArgumentList({mark0, mark1, mark2, mark3, mark4, mark5, mark6}); 45 | 46 | AddMethodMatcher(matcher); 47 | 48 | TestObject *res = [TestObject createWithName:@"小芳" age:20 phone:55555555555 isMan:NO height:165 weight:100 extra:[NSObject new]]; 49 | 50 | XCTAssertTrue([res isEqual:argument]); 51 | } 52 | 53 | @end 54 | -------------------------------------------------------------------------------- /MessageMockDemo/MessageMockDemoTests/MMMethodReturnTests.mm: -------------------------------------------------------------------------------- 1 | // 2 | // MMMethodReturnTests.m 3 | // MessageMockDemoTests 4 | // 5 | // Created by 波儿菜 on 2020/6/28. 6 | // 7 | 8 | #import 9 | #import 10 | #import 11 | #import "TestObject.h" 12 | 13 | using namespace mm_method_matcher; 14 | 15 | @interface MMMethodReturnTests : XCTestCase 16 | 17 | @end 18 | 19 | @implementation MMMethodReturnTests 20 | 21 | - (void)setUp { 22 | MMStartWorking(); 23 | } 24 | 25 | - (void)tearDown {} 26 | 27 | #pragma mark - 指针返回值改为其它指针 28 | 29 | - (void)testChangeReturnPointer { 30 | TestObject *returnValue = [TestObject createObj]; 31 | 32 | MethodMatcher *matcher = new MethodMatcher(); 33 | matcher->target = (uintptr_t)TestObject.self; 34 | matcher->selector = (uintptr_t)(void *)(@selector(createObj)); 35 | matcher->skip = false; 36 | matcher->change_return = true; 37 | matcher->return_object = (__bridge_retained void *)returnValue; 38 | matcher->limit_hit_count = 1; 39 | 40 | AddMethodMatcher(matcher); 41 | 42 | TestObject *value = [TestObject createObj]; 43 | XCTAssertTrue(returnValue == value); 44 | 45 | TestObject *value1 = [TestObject createObj]; 46 | XCTAssertFalse(returnValue == value1); 47 | } 48 | 49 | #pragma mark - 指针返回值改为 C 指针 50 | 51 | - (void)testChangeReturnCPointer { 52 | char *returnValue = [TestObject createPointer]; 53 | returnValue = (char *)"dddddddd"; 54 | 55 | MethodMatcher *matcher = new MethodMatcher(); 56 | matcher->target = (uintptr_t)TestObject.self; 57 | matcher->selector = (uintptr_t)(void *)(@selector(createPointer)); 58 | matcher->skip = true; 59 | matcher->change_return = true; 60 | matcher->return_value = (uintptr_t)returnValue; 61 | matcher->limit_hit_count = 1; 62 | 63 | AddMethodMatcher(matcher); 64 | 65 | char *value = [TestObject createPointer]; 66 | XCTAssertTrue(returnValue == value); 67 | 68 | char *value1 = [TestObject createPointer]; 69 | XCTAssertFalse(returnValue == value1); 70 | } 71 | 72 | #pragma mark - 指针返回值改为 nil 73 | 74 | - (void)testChangeReturnPointerToNil { 75 | MethodMatcher *matcher = new MethodMatcher(); 76 | matcher->target = (uintptr_t)TestObject.self; 77 | matcher->selector = (uintptr_t)(void *)(@selector(createObj)); 78 | matcher->skip = true; 79 | matcher->change_return = true; 80 | matcher->return_value = (uintptr_t)nil; 81 | matcher->limit_hit_count = 1; 82 | 83 | AddMethodMatcher(matcher); 84 | 85 | TestObject *value = [TestObject createObj]; 86 | XCTAssertTrue(nil == value); 87 | 88 | TestObject *value1 = [TestObject createObj]; 89 | XCTAssertFalse(nil == value1); 90 | } 91 | 92 | #pragma mark - 整形返回值修改 93 | 94 | - (void)testChangeReturnLong { 95 | long returnValue = [TestObject createLong] + 10086; 96 | 97 | MethodMatcher *matcher = new MethodMatcher(); 98 | matcher->target = (uintptr_t)TestObject.self; 99 | matcher->selector = (uintptr_t)(void *)(@selector(createLong)); 100 | matcher->skip = true; 101 | matcher->change_return = true; 102 | matcher->return_value = returnValue; 103 | matcher->limit_hit_count = 1; 104 | 105 | AddMethodMatcher(matcher); 106 | 107 | long value = [TestObject createLong]; 108 | XCTAssertTrue(returnValue == value); 109 | 110 | long value1 = [TestObject createLong]; 111 | XCTAssertFalse(returnValue == value1); 112 | } 113 | 114 | - (void)testChangeReturnInt { 115 | int returnValue = [TestObject createInt] - 20000; 116 | 117 | MethodMatcher *matcher = new MethodMatcher(); 118 | matcher->target = (uintptr_t)TestObject.self; 119 | matcher->selector = (uintptr_t)(void *)(@selector(createInt)); 120 | matcher->skip = true; 121 | matcher->change_return = true; 122 | matcher->return_value = returnValue; 123 | matcher->limit_hit_count = 1; 124 | 125 | AddMethodMatcher(matcher); 126 | 127 | int value = [TestObject createInt]; 128 | XCTAssertTrue(returnValue == value); 129 | 130 | int value1 = [TestObject createInt]; 131 | XCTAssertFalse(returnValue == value1); 132 | } 133 | 134 | - (void)testChangeReturnInt16 { 135 | int returnValue = [TestObject createInt16] - 200; 136 | 137 | MethodMatcher *matcher = new MethodMatcher(); 138 | matcher->target = (uintptr_t)TestObject.self; 139 | matcher->selector = (uintptr_t)(void *)(@selector(createInt16)); 140 | matcher->skip = true; 141 | matcher->change_return = true; 142 | matcher->return_value = returnValue; 143 | matcher->limit_hit_count = 1; 144 | 145 | AddMethodMatcher(matcher); 146 | 147 | int value = [TestObject createInt16]; 148 | XCTAssertTrue(returnValue == value); 149 | 150 | int value1 = [TestObject createInt16]; 151 | XCTAssertFalse(returnValue == value1); 152 | } 153 | 154 | - (void)testChangeReturnInt8 { 155 | int returnValue = [TestObject createInt8] + 10; 156 | 157 | MethodMatcher *matcher = new MethodMatcher(); 158 | matcher->target = (uintptr_t)TestObject.self; 159 | matcher->selector = (uintptr_t)(void *)(@selector(createInt8)); 160 | matcher->skip = true; 161 | matcher->change_return = true; 162 | matcher->return_value = returnValue; 163 | matcher->limit_hit_count = 1; 164 | 165 | AddMethodMatcher(matcher); 166 | 167 | int value = [TestObject createInt8]; 168 | XCTAssertTrue(returnValue == value); 169 | 170 | int value1 = [TestObject createInt8]; 171 | XCTAssertFalse(returnValue == value1); 172 | } 173 | 174 | #pragma mark - 浮点型返回值修改 175 | 176 | - (void)testChangeReturnDouble { 177 | double returnValue = [TestObject createDouble] - 1000000000000.000002394713984; 178 | 179 | MethodMatcher *matcher = new MethodMatcher(); 180 | matcher->target = (uintptr_t)TestObject.self; 181 | matcher->selector = (uintptr_t)(void *)(@selector(createDouble)); 182 | matcher->skip = true; 183 | matcher->change_return = true; 184 | matcher->return_value = *(uintptr_t *)&returnValue; 185 | matcher->limit_hit_count = 1; 186 | 187 | AddMethodMatcher(matcher); 188 | 189 | double value = [TestObject createDouble]; 190 | XCTAssertTrue(returnValue == value); 191 | 192 | double value1 = [TestObject createDouble]; 193 | XCTAssertFalse(returnValue == value1); 194 | } 195 | 196 | - (void)testChangeReturnFloat { 197 | float returnValue = [TestObject createFloat] - 2000000000000.000002394713984; 198 | 199 | MethodMatcher *matcher = new MethodMatcher(); 200 | matcher->target = (uintptr_t)TestObject.self; 201 | matcher->selector = (uintptr_t)(void *)(@selector(createFloat)); 202 | matcher->skip = true; 203 | matcher->change_return = true; 204 | matcher->return_value = *(uintptr_t *)&returnValue; 205 | matcher->limit_hit_count = 1; 206 | 207 | AddMethodMatcher(matcher); 208 | 209 | float value = [TestObject createFloat]; 210 | XCTAssertTrue(returnValue == value); 211 | 212 | float value1 = [TestObject createFloat]; 213 | XCTAssertFalse(returnValue == value1); 214 | } 215 | 216 | @end 217 | -------------------------------------------------------------------------------- /MessageMockDemo/MessageMockDemoTests/MMReturnCallbackTests.mm: -------------------------------------------------------------------------------- 1 | // 2 | // MMReturnCallbackTests.m 3 | // MessageMockDemoTests 4 | // 5 | // Created by 波儿菜 on 2020/7/18. 6 | // 7 | 8 | #import 9 | #import 10 | #import 11 | #import "TestObject.h" 12 | 13 | using namespace mm_method_matcher; 14 | 15 | @interface MMReturnCallbackTests : XCTestCase 16 | 17 | @end 18 | 19 | @implementation MMReturnCallbackTests 20 | 21 | - (void)setUp { 22 | MMStartWorking(); 23 | } 24 | 25 | - (void)tearDown {} 26 | 27 | - (void)testReturnObjectCallback { 28 | __block TestObject *checkObj = nil; 29 | 30 | MethodMatcher *matcher = new MethodMatcher(); 31 | matcher->target = (uintptr_t)TestObject.self; 32 | matcher->selector = (uintptr_t)(void *)(@selector(createObj)); 33 | matcher->skip = false; 34 | 35 | ReturnCallback returnCallback = ^(uintptr_t arg, ReturnCallbackError error) { 36 | void *argPointer = (void *)arg; 37 | checkObj = (__bridge TestObject *)argPointer; 38 | }; 39 | matcher->return_callback = returnCallback; 40 | 41 | AddMethodMatcher(matcher); 42 | 43 | TestObject *returnObj = [TestObject createObj]; 44 | 45 | XCTAssertTrue(returnObj == checkObj); 46 | } 47 | 48 | - (void)testReturnFloatCallback { 49 | __block float value = 0; 50 | 51 | MethodMatcher *matcher = new MethodMatcher(); 52 | matcher->target = (uintptr_t)TestObject.self; 53 | matcher->selector = (uintptr_t)(void *)(@selector(createFloat)); 54 | matcher->skip = false; 55 | 56 | ReturnCallback returnCallback = ^(uintptr_t arg, ReturnCallbackError error) { 57 | float tmp = *(float *)&arg; 58 | value = tmp; 59 | }; 60 | matcher->return_callback = returnCallback; 61 | 62 | AddMethodMatcher(matcher); 63 | 64 | float res = [TestObject createFloat]; 65 | 66 | XCTAssertTrue(value == res); 67 | } 68 | 69 | - (void)testChangeReturnObjectCallback { 70 | TestObject *changeObj = [TestObject new]; 71 | 72 | MethodMatcher *matcher = new MethodMatcher(); 73 | matcher->target = (uintptr_t)TestObject.self; 74 | matcher->selector = (uintptr_t)(void *)(@selector(createObj)); 75 | matcher->skip = false; 76 | matcher->change_return = true; 77 | matcher->return_object = (__bridge_retained void *)changeObj; 78 | 79 | ReturnCallback returnCallback = ^(uintptr_t arg, ReturnCallbackError error) { 80 | void *argPointer = (void *)arg; 81 | TestObject *tmp = (__bridge TestObject *)argPointer; 82 | XCTAssertTrue(changeObj == tmp); 83 | }; 84 | matcher->return_callback = returnCallback; 85 | 86 | AddMethodMatcher(matcher); 87 | 88 | TestObject *returnObj = [TestObject createObj]; 89 | 90 | XCTAssertTrue(returnObj == changeObj); 91 | } 92 | 93 | @end 94 | -------------------------------------------------------------------------------- /MessageMockDemo/MessageMockDemoTests/MessageMockerTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // MessageMockerTests.m 3 | // MessageMockDemoTests 4 | // 5 | // Created by 波儿菜 on 2020/7/12. 6 | // 7 | 8 | #import 9 | #import 10 | #import "TestObject.h" 11 | 12 | @interface MessageMockerTests : XCTestCase 13 | 14 | @end 15 | 16 | @implementation MessageMockerTests 17 | 18 | - (void)setUp {} 19 | 20 | - (void)tearDown {} 21 | 22 | - (void)testChangeReturnFloat { 23 | float value = 0.384189347192384789; 24 | 25 | MessageMocker.build(NSObject.self, @selector(new)).mockReturn(nil).skip(YES).start(); 26 | 27 | MessageMocker.build(TestObject.self, @selector(createFloat)).mockReturn(value).start(); 28 | 29 | XCTAssertTrue(value == [TestObject createFloat]); 30 | XCTAssertTrue(value != [TestObject createFloat]); 31 | } 32 | 33 | - (void)testChangeReturnDouble { 34 | double value = 0.48591734913287498; 35 | 36 | MessageMocker.build(TestObject.self, @selector(createDouble)).mockReturn(value).start(); 37 | 38 | XCTAssertTrue(value == [TestObject createDouble]); 39 | XCTAssertTrue(value != [TestObject createDouble]); 40 | } 41 | 42 | - (void)testChangeReturnObject { 43 | NSObject *value = [NSObject new]; 44 | 45 | MessageMocker.build(TestObject.self, @selector(createObj)).mockReturn(value).start(); 46 | 47 | XCTAssertTrue(value == [TestObject createObj]); 48 | XCTAssertTrue(value != [TestObject createObj]); 49 | } 50 | 51 | - (void)testChangeReturnObject1 { 52 | NSObject *value = [NSObject new]; 53 | 54 | MessageMocker.build(NSNotificationCenter.self, @selector(defaultCenter)).mockReturn(value).start(); 55 | 56 | XCTAssertTrue(value == [NSNotificationCenter defaultCenter]); 57 | XCTAssertTrue(value != [NSNotificationCenter defaultCenter]); 58 | 59 | MessageMocker.build([NSUserDefaults standardUserDefaults], @selector(objectForKey:)).mockReturn(value).start(); 60 | 61 | XCTAssertTrue(value == [[NSUserDefaults standardUserDefaults] objectForKey:@"AnyKey"]); 62 | XCTAssertTrue(value != [[NSUserDefaults standardUserDefaults] objectForKey:@"AnyKey"]); 63 | } 64 | 65 | - (void)testChangeArgument { 66 | TestObject *value = [TestObject defaultArgument]; 67 | 68 | MessageMocker.build(TestObject.self, @selector(createWithName:age:phone:isMan:height:weight:extra:)) 69 | .mockArgument(0, value.name) 70 | .mockArgument(1, value.age) 71 | .mockArgument(2, value.phone) 72 | .mockArgument(3, value.isMan) 73 | .mockArgument(4, value.height) 74 | .mockArgument(5, value.weight) 75 | .mockArgument(6, value.extra) 76 | .start(); 77 | 78 | TestObject *res = [TestObject createWithName:@"小芳" age:20 phone:55555555555 isMan:NO height:165 weight:100 extra:[NSObject new]]; 79 | 80 | XCTAssertTrue([res isEqual:value]); 81 | } 82 | 83 | - (void)testChangeArgument1 { 84 | UIView *view = [UIView new]; 85 | NSInteger value = 999; 86 | 87 | MessageMocker.build(view, @selector(setTag:)).mockArgument(0, value).start(); 88 | 89 | view.tag = 666; 90 | XCTAssertTrue(view.tag == value); 91 | } 92 | 93 | - (void)testCheckReturnObject { 94 | __block id value = nil; 95 | 96 | MessageMocker.build(TestObject.self, @selector(createObj)) 97 | .checkReturn(^(TestObject *arg) { 98 | value = arg; 99 | }) 100 | .start(); 101 | 102 | id res = [TestObject createObj]; 103 | XCTAssertTrue(value == res); 104 | } 105 | 106 | - (void)testCheckReturnInt { 107 | __block int value = 0; 108 | 109 | MessageMocker.build(TestObject.self, @selector(createInt)) 110 | .checkReturn(^(int arg) { 111 | value = arg; 112 | }) 113 | .start(); 114 | 115 | int res = [TestObject createInt]; 116 | XCTAssertTrue(value == res); 117 | } 118 | 119 | - (void)testCheckReturnFloat { 120 | __block float value = 0; 121 | 122 | MessageMocker.build(TestObject.self, @selector(createFloat)) 123 | .checkReturn(^(float arg) { 124 | value = arg; 125 | }) 126 | .start(); 127 | 128 | float res = [TestObject createFloat]; 129 | XCTAssertTrue(value == res); 130 | } 131 | 132 | - (void)testCheckReturnLong { 133 | __block unsigned long value = 0; 134 | 135 | NSObject *obj = [NSObject new]; 136 | 137 | MessageMocker.build(obj, @selector(hash)) 138 | .checkReturn(^(unsigned long arg) { 139 | value = arg; 140 | }) 141 | .start(); 142 | 143 | unsigned long res = [obj hash]; 144 | XCTAssertTrue(value == res); 145 | } 146 | 147 | - (void)testCheckArgument { 148 | TestObject *argument = [TestObject defaultArgument]; 149 | 150 | MessageMocker.build(TestObject.self, @selector(createWithName:age:phone:isMan:height:weight:extra:)) 151 | .checkArgument(0, ^(NSString *arg) { 152 | XCTAssertTrue(arg == argument.name); 153 | }) 154 | .checkArgument(1, ^(int arg) { 155 | XCTAssertTrue(arg == argument.age); 156 | }) 157 | .checkArgument(2, ^(NSInteger arg) { 158 | XCTAssertTrue(arg == argument.phone); 159 | }) 160 | .checkArgument(3, ^(BOOL arg) { 161 | XCTAssertTrue(arg == argument.isMan); 162 | }) 163 | .checkArgument(4, ^(double arg) { 164 | XCTAssertTrue(arg == argument.height); 165 | }) 166 | .checkArgument(5, ^(float arg) { 167 | XCTAssertTrue(arg == argument.weight); 168 | }) 169 | .checkArgument(6, ^(id arg) { 170 | XCTAssertTrue(arg == argument.extra); 171 | }) 172 | .start(); 173 | 174 | [TestObject createWithName:argument.name age:argument.age phone:argument.phone isMan:argument.isMan height:argument.height weight:argument.weight extra:argument.extra]; 175 | } 176 | 177 | - (void)testCheckArgument1 { 178 | Class anyCls = NSString.self; 179 | 180 | MessageMocker.build(NSObject.self, @selector(isSubclassOfClass:)) 181 | .checkArgument(0, ^(Class cls){ 182 | XCTAssertTrue(anyCls == cls); 183 | }) 184 | .start(); 185 | 186 | [NSObject isSubclassOfClass:anyCls]; 187 | } 188 | 189 | - (void)testSkip { 190 | UIView *view = [UIView new]; 191 | NSInteger tag = 666; 192 | view.tag = tag; 193 | 194 | MessageMocker.build(view, @selector(setTag:)).skip(YES).start(); 195 | 196 | view.tag = 999; 197 | XCTAssertTrue(view.tag = tag); 198 | } 199 | 200 | - (void)testHitCount { 201 | NSObject *value = [NSObject new]; 202 | 203 | MessageMocker *mocker = MessageMocker.build(TestObject.self, @selector(createObj)).mockReturn(value).limitHitCount(3); 204 | mocker.start(); 205 | 206 | XCTAssertTrue(value == [TestObject createObj]); 207 | XCTAssertTrue(mocker.hitCount == 1); 208 | XCTAssertTrue(value == [TestObject createObj]); 209 | XCTAssertTrue(mocker.hitCount == 2); 210 | XCTAssertTrue(value == [TestObject createObj]); 211 | XCTAssertTrue(mocker.hitCount == 3); 212 | 213 | XCTAssertTrue(value != [TestObject createObj]); 214 | } 215 | 216 | @end 217 | -------------------------------------------------------------------------------- /MessageMockDemo/Podfile: -------------------------------------------------------------------------------- 1 | platform:ios, '9.0' 2 | 3 | target ‘MessageMockDemoTests’ do 4 | use_frameworks! 5 | 6 | pod 'MessageMock', :path => '../', :configurations => ['Debug'] 7 | 8 | end 9 | -------------------------------------------------------------------------------- /MessageMockDemo/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - MessageMock (1.0) 3 | 4 | DEPENDENCIES: 5 | - MessageMock (from `../`) 6 | 7 | EXTERNAL SOURCES: 8 | MessageMock: 9 | :path: "../" 10 | 11 | SPEC CHECKSUMS: 12 | MessageMock: 17efd42346cf9d04792dc4e06791908b9ca0fd3e 13 | 14 | PODFILE CHECKSUM: d82d697e487c7dfc1ca5bd1d4efd186eff3368c2 15 | 16 | COCOAPODS: 1.8.4 17 | -------------------------------------------------------------------------------- /MessageMockDemo/Pods/Local Podspecs/MessageMock.podspec.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "MessageMock", 3 | "version": "1.0", 4 | "summary": "Objective-C 方法模拟工具", 5 | "description": "Objective-C 方法模拟工具", 6 | "homepage": "https://github.com/indulgeIn/MessageMock", 7 | "license": "MIT", 8 | "authors": { 9 | "indulgeIn": "1106355439@qq.com" 10 | }, 11 | "platforms": { 12 | "ios": "9.0" 13 | }, 14 | "source": { 15 | "git": "https://github.com/indulgeIn/MessageMock.git", 16 | "tag": "1.0" 17 | }, 18 | "requires_arc": true, 19 | "source_files": "MessageMock/**/*.{h,m,mm,c,cpp,hpp}", 20 | "libraries": "c++.1" 21 | } 22 | -------------------------------------------------------------------------------- /MessageMockDemo/Pods/Manifest.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - MessageMock (1.0) 3 | 4 | DEPENDENCIES: 5 | - MessageMock (from `../`) 6 | 7 | EXTERNAL SOURCES: 8 | MessageMock: 9 | :path: "../" 10 | 11 | SPEC CHECKSUMS: 12 | MessageMock: 17efd42346cf9d04792dc4e06791908b9ca0fd3e 13 | 14 | PODFILE CHECKSUM: d82d697e487c7dfc1ca5bd1d4efd186eff3368c2 15 | 16 | COCOAPODS: 1.8.4 17 | -------------------------------------------------------------------------------- /MessageMockDemo/Pods/Pods.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 51; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 006A5202DA8D63735802BD2DD2C81B80 /* MessageMock-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FF3921DC37497A1D856E9B0CC4BD2E4 /* MessageMock-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; 11 | 1FB88F3A82E998FD8F31858CC74B9A07 /* Pods-MessageMockDemoTests-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 45E0B4DDD172B91873020A2B30AA8D1A /* Pods-MessageMockDemoTests-dummy.m */; }; 12 | 21D7E7B9291AC10E2621008046B218B2 /* mm_argument_change.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 44CAFB591123437B7177E491FE9DE8E5 /* mm_argument_change.cpp */; }; 13 | 28B7C014DF07364EFEB74DDE0185C692 /* mm_argument_change.h in Headers */ = {isa = PBXBuildFile; fileRef = 5CB91277397839001E6C2AE08958132A /* mm_argument_change.h */; settings = {ATTRIBUTES = (Public, ); }; }; 14 | 383AA9C531C51F74F200DE0A3FBD54C6 /* MessageMocker.mm in Sources */ = {isa = PBXBuildFile; fileRef = C3230046D05486195C0F952243562BF5 /* MessageMocker.mm */; }; 15 | 3BD9F144AA4E0A613A923F5DA7B0107E /* mm_argument_stack.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FE8ED299CC65AD093896ACBF67B2A8C6 /* mm_argument_stack.cpp */; }; 16 | 50CD9E644C6614610A92E0F772E0E8BF /* mm_argument_callback.h in Headers */ = {isa = PBXBuildFile; fileRef = A2BEFE37965F08F8C9AD669F09ED9A87 /* mm_argument_callback.h */; settings = {ATTRIBUTES = (Public, ); }; }; 17 | 6BBAFF22B26B96CF328F9E8D4BE798F3 /* mm_define.h in Headers */ = {isa = PBXBuildFile; fileRef = 2F1354228863C85F4C5EB6F2A9E6E8B0 /* mm_define.h */; settings = {ATTRIBUTES = (Public, ); }; }; 18 | 76B3018681383CF81C8BB87BADE929DB /* mm_fishhook.c in Sources */ = {isa = PBXBuildFile; fileRef = E2AE909801B176A8E29B55A197FF03B6 /* mm_fishhook.c */; }; 19 | 7792573CA56A65C99F012C4C9D505574 /* mm_fishhook.h in Headers */ = {isa = PBXBuildFile; fileRef = A32D253029562D93A04B0DC43B5E4A63 /* mm_fishhook.h */; settings = {ATTRIBUTES = (Public, ); }; }; 20 | 7C111FF3CA1D631663095E374C733C19 /* mm_runtime.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 081F7669A053E41E9FC46B57774EF931 /* mm_runtime.cpp */; }; 21 | A882763C285C889F0B1896C4B591DD2B /* Pods-MessageMockDemoTests-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 7F0318F1F0A0C63F1CB8B965F29A35FF /* Pods-MessageMockDemoTests-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; 22 | B485BDE7ED3F98853804F0D534738C06 /* mm_runtime.h in Headers */ = {isa = PBXBuildFile; fileRef = E943472018DAF2DA0358B50252166BEF /* mm_runtime.h */; settings = {ATTRIBUTES = (Public, ); }; }; 23 | B9A5715005DE3F27192EB4C66E8A82D8 /* mm_argument_stack.h in Headers */ = {isa = PBXBuildFile; fileRef = 72959443356585CF350E4AF0F7BC6E84 /* mm_argument_stack.h */; settings = {ATTRIBUTES = (Public, ); }; }; 24 | C209FE0D8DCF71D006231BC1BFC3CA11 /* MessageMocker.h in Headers */ = {isa = PBXBuildFile; fileRef = ACAD3467482F46A240385688B20E0026 /* MessageMocker.h */; settings = {ATTRIBUTES = (Public, ); }; }; 25 | CB6D436365207738A6F4532ACDB22876 /* mm_argument_callback.cpp in Sources */ = {isa = PBXBuildFile; fileRef = EA69A9FDF41912A7E86D0B18145F9E9D /* mm_argument_callback.cpp */; }; 26 | D9492F1EDA0B5C385E97D61E6F30F2FA /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3212113385A8FBBDB272BD23C409FF61 /* Foundation.framework */; }; 27 | E432AAC418E66927B7DB578D9341E6E6 /* mm_hook_objc_msgsend.h in Headers */ = {isa = PBXBuildFile; fileRef = 7F03BE7D90077EF143676DA820F2E1AA /* mm_hook_objc_msgsend.h */; settings = {ATTRIBUTES = (Public, ); }; }; 28 | E56356525F1D64A28CEFA0C98F2D7DFC /* mm_method_matcher.h in Headers */ = {isa = PBXBuildFile; fileRef = 1ECD0CE6EC95ACB1948E5255DA921AB9 /* mm_method_matcher.h */; settings = {ATTRIBUTES = (Public, ); }; }; 29 | EB8FEDCA9E91E53D5E59024CD8965E5F /* MessageMock-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 72E87D0112CC0C80183153D0D6A849DF /* MessageMock-dummy.m */; }; 30 | F057FE746667651224C4405544DBBB5F /* mm_hook_objc_msgsend.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 62606F77B15DBFB5A276B250B813E8F4 /* mm_hook_objc_msgsend.cpp */; }; 31 | F4428DDD1438A3BEB19D98099F53E0ED /* mm_method_matcher.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 76AC65AF62D34E922BCD3085A9675878 /* mm_method_matcher.cpp */; }; 32 | F67036B46B58ACDF88076B5B2287F87C /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3212113385A8FBBDB272BD23C409FF61 /* Foundation.framework */; }; 33 | /* End PBXBuildFile section */ 34 | 35 | /* Begin PBXContainerItemProxy section */ 36 | C7A485C171BA07567E26569795CDB8A0 /* PBXContainerItemProxy */ = { 37 | isa = PBXContainerItemProxy; 38 | containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; 39 | proxyType = 1; 40 | remoteGlobalIDString = 0DC72AA7DFB4148188F5D3D63C80F621; 41 | remoteInfo = MessageMock; 42 | }; 43 | /* End PBXContainerItemProxy section */ 44 | 45 | /* Begin PBXFileReference section */ 46 | 081F7669A053E41E9FC46B57774EF931 /* mm_runtime.cpp */ = {isa = PBXFileReference; includeInIndex = 1; path = mm_runtime.cpp; sourceTree = ""; }; 47 | 0A870BDF92896BDE64683AF4F71CB96A /* Pods-MessageMockDemoTests.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = "Pods-MessageMockDemoTests.modulemap"; sourceTree = ""; }; 48 | 0E4F714EC593ECF5453250160A393349 /* MessageMock.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = MessageMock.framework; path = MessageMock.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 49 | 0FF3921DC37497A1D856E9B0CC4BD2E4 /* MessageMock-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "MessageMock-umbrella.h"; sourceTree = ""; }; 50 | 193C1AC6FDC933128082A5C8DFC49A8D /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; path = README.md; sourceTree = ""; }; 51 | 1ECD0CE6EC95ACB1948E5255DA921AB9 /* mm_method_matcher.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = mm_method_matcher.h; sourceTree = ""; }; 52 | 2F1354228863C85F4C5EB6F2A9E6E8B0 /* mm_define.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = mm_define.h; sourceTree = ""; }; 53 | 3212113385A8FBBDB272BD23C409FF61 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS12.2.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; 54 | 37D7595A2AE54D14872185009BC4F5F2 /* Pods-MessageMockDemoTests-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-MessageMockDemoTests-Info.plist"; sourceTree = ""; }; 55 | 44CAFB591123437B7177E491FE9DE8E5 /* mm_argument_change.cpp */ = {isa = PBXFileReference; includeInIndex = 1; path = mm_argument_change.cpp; sourceTree = ""; }; 56 | 45E0B4DDD172B91873020A2B30AA8D1A /* Pods-MessageMockDemoTests-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-MessageMockDemoTests-dummy.m"; sourceTree = ""; }; 57 | 48EEC4E470179B9DC44B1BD48C95B733 /* MessageMock.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = MessageMock.xcconfig; sourceTree = ""; }; 58 | 4AF519972B919C142AE9B773E45291A8 /* MessageMock.podspec */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; indentWidth = 2; lastKnownFileType = text; path = MessageMock.podspec; sourceTree = ""; tabWidth = 2; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; 59 | 5CB91277397839001E6C2AE08958132A /* mm_argument_change.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = mm_argument_change.h; sourceTree = ""; }; 60 | 62606F77B15DBFB5A276B250B813E8F4 /* mm_hook_objc_msgsend.cpp */ = {isa = PBXFileReference; includeInIndex = 1; path = mm_hook_objc_msgsend.cpp; sourceTree = ""; }; 61 | 67769E141D99397F27D9AAC2A28E02E7 /* Pods-MessageMockDemoTests-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-MessageMockDemoTests-acknowledgements.markdown"; sourceTree = ""; }; 62 | 72959443356585CF350E4AF0F7BC6E84 /* mm_argument_stack.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = mm_argument_stack.h; sourceTree = ""; }; 63 | 72E87D0112CC0C80183153D0D6A849DF /* MessageMock-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "MessageMock-dummy.m"; sourceTree = ""; }; 64 | 76AC65AF62D34E922BCD3085A9675878 /* mm_method_matcher.cpp */ = {isa = PBXFileReference; includeInIndex = 1; path = mm_method_matcher.cpp; sourceTree = ""; }; 65 | 7F0318F1F0A0C63F1CB8B965F29A35FF /* Pods-MessageMockDemoTests-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-MessageMockDemoTests-umbrella.h"; sourceTree = ""; }; 66 | 7F03BE7D90077EF143676DA820F2E1AA /* mm_hook_objc_msgsend.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = mm_hook_objc_msgsend.h; sourceTree = ""; }; 67 | 8B4163FC65ED75060468B05BE08BD5B3 /* Pods-MessageMockDemoTests-frameworks.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-MessageMockDemoTests-frameworks.sh"; sourceTree = ""; }; 68 | 9612836D2E24DD4A4F2BFF3225820F30 /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; path = LICENSE; sourceTree = ""; }; 69 | 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; }; 70 | 9DB5937B7D6FE9575700E937D57595D2 /* MessageMock-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "MessageMock-prefix.pch"; sourceTree = ""; }; 71 | A2BEFE37965F08F8C9AD669F09ED9A87 /* mm_argument_callback.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = mm_argument_callback.h; sourceTree = ""; }; 72 | A32D253029562D93A04B0DC43B5E4A63 /* mm_fishhook.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = mm_fishhook.h; sourceTree = ""; }; 73 | ACAD3467482F46A240385688B20E0026 /* MessageMocker.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; name = MessageMocker.h; path = MessageMock/MessageMocker.h; sourceTree = ""; }; 74 | B27452342D018A7B6017C418581E288A /* Pods_MessageMockDemoTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Pods_MessageMockDemoTests.framework; path = "Pods-MessageMockDemoTests.framework"; sourceTree = BUILT_PRODUCTS_DIR; }; 75 | BC7258C913072156F5364BF28592EEA9 /* MessageMock.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = MessageMock.modulemap; sourceTree = ""; }; 76 | BE1E37FD9AEA6EE788FF0AB458D51D86 /* MessageMock-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "MessageMock-Info.plist"; sourceTree = ""; }; 77 | C3230046D05486195C0F952243562BF5 /* MessageMocker.mm */ = {isa = PBXFileReference; includeInIndex = 1; name = MessageMocker.mm; path = MessageMock/MessageMocker.mm; sourceTree = ""; }; 78 | C499D9FA0F99944F924A98A0B832DD0F /* Pods-MessageMockDemoTests-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-MessageMockDemoTests-acknowledgements.plist"; sourceTree = ""; }; 79 | E2AE909801B176A8E29B55A197FF03B6 /* mm_fishhook.c */ = {isa = PBXFileReference; includeInIndex = 1; path = mm_fishhook.c; sourceTree = ""; }; 80 | E943472018DAF2DA0358B50252166BEF /* mm_runtime.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = mm_runtime.h; sourceTree = ""; }; 81 | EA69A9FDF41912A7E86D0B18145F9E9D /* mm_argument_callback.cpp */ = {isa = PBXFileReference; includeInIndex = 1; path = mm_argument_callback.cpp; sourceTree = ""; }; 82 | EE119006FF2E3126B33DE859B44E3EEA /* Pods-MessageMockDemoTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-MessageMockDemoTests.release.xcconfig"; sourceTree = ""; }; 83 | F6D8B468295743E6B9837C8A54608760 /* Pods-MessageMockDemoTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-MessageMockDemoTests.debug.xcconfig"; sourceTree = ""; }; 84 | FE8ED299CC65AD093896ACBF67B2A8C6 /* mm_argument_stack.cpp */ = {isa = PBXFileReference; includeInIndex = 1; path = mm_argument_stack.cpp; sourceTree = ""; }; 85 | /* End PBXFileReference section */ 86 | 87 | /* Begin PBXFrameworksBuildPhase section */ 88 | 2BE8084F2AD80591444CC51C67272690 /* Frameworks */ = { 89 | isa = PBXFrameworksBuildPhase; 90 | buildActionMask = 2147483647; 91 | files = ( 92 | F67036B46B58ACDF88076B5B2287F87C /* Foundation.framework in Frameworks */, 93 | ); 94 | runOnlyForDeploymentPostprocessing = 0; 95 | }; 96 | 6BD51F928FDF1CF116AB117FD1807F49 /* Frameworks */ = { 97 | isa = PBXFrameworksBuildPhase; 98 | buildActionMask = 2147483647; 99 | files = ( 100 | D9492F1EDA0B5C385E97D61E6F30F2FA /* Foundation.framework in Frameworks */, 101 | ); 102 | runOnlyForDeploymentPostprocessing = 0; 103 | }; 104 | /* End PBXFrameworksBuildPhase section */ 105 | 106 | /* Begin PBXGroup section */ 107 | 0AC9AE219A0277325E677B52AEE0BAD8 /* MessageMock */ = { 108 | isa = PBXGroup; 109 | children = ( 110 | ACAD3467482F46A240385688B20E0026 /* MessageMocker.h */, 111 | C3230046D05486195C0F952243562BF5 /* MessageMocker.mm */, 112 | 222A2AAF822822BF920B4B481684ADCC /* Core */, 113 | 712574555A87162701DB9E904A3175C7 /* fishhook */, 114 | 206DE43C7B0E3DF04C44E5F983882E1E /* Pod */, 115 | AD4B3B939834FAC653B8D67ABCF1682D /* Support Files */, 116 | ); 117 | name = MessageMock; 118 | path = ../..; 119 | sourceTree = ""; 120 | }; 121 | 206DE43C7B0E3DF04C44E5F983882E1E /* Pod */ = { 122 | isa = PBXGroup; 123 | children = ( 124 | 9612836D2E24DD4A4F2BFF3225820F30 /* LICENSE */, 125 | 4AF519972B919C142AE9B773E45291A8 /* MessageMock.podspec */, 126 | 193C1AC6FDC933128082A5C8DFC49A8D /* README.md */, 127 | ); 128 | name = Pod; 129 | sourceTree = ""; 130 | }; 131 | 222A2AAF822822BF920B4B481684ADCC /* Core */ = { 132 | isa = PBXGroup; 133 | children = ( 134 | EA69A9FDF41912A7E86D0B18145F9E9D /* mm_argument_callback.cpp */, 135 | A2BEFE37965F08F8C9AD669F09ED9A87 /* mm_argument_callback.h */, 136 | 44CAFB591123437B7177E491FE9DE8E5 /* mm_argument_change.cpp */, 137 | 5CB91277397839001E6C2AE08958132A /* mm_argument_change.h */, 138 | FE8ED299CC65AD093896ACBF67B2A8C6 /* mm_argument_stack.cpp */, 139 | 72959443356585CF350E4AF0F7BC6E84 /* mm_argument_stack.h */, 140 | 2F1354228863C85F4C5EB6F2A9E6E8B0 /* mm_define.h */, 141 | 62606F77B15DBFB5A276B250B813E8F4 /* mm_hook_objc_msgsend.cpp */, 142 | 7F03BE7D90077EF143676DA820F2E1AA /* mm_hook_objc_msgsend.h */, 143 | 76AC65AF62D34E922BCD3085A9675878 /* mm_method_matcher.cpp */, 144 | 1ECD0CE6EC95ACB1948E5255DA921AB9 /* mm_method_matcher.h */, 145 | 081F7669A053E41E9FC46B57774EF931 /* mm_runtime.cpp */, 146 | E943472018DAF2DA0358B50252166BEF /* mm_runtime.h */, 147 | ); 148 | name = Core; 149 | path = MessageMock/Core; 150 | sourceTree = ""; 151 | }; 152 | 45295D8DEF41133486C8B14ED69034BF /* Targets Support Files */ = { 153 | isa = PBXGroup; 154 | children = ( 155 | AACE813D43FF64ECEC52E32A00A3BE2B /* Pods-MessageMockDemoTests */, 156 | ); 157 | name = "Targets Support Files"; 158 | sourceTree = ""; 159 | }; 160 | 655736BB2CE9AE1D504E8D0CF146FEE1 /* Products */ = { 161 | isa = PBXGroup; 162 | children = ( 163 | 0E4F714EC593ECF5453250160A393349 /* MessageMock.framework */, 164 | B27452342D018A7B6017C418581E288A /* Pods_MessageMockDemoTests.framework */, 165 | ); 166 | name = Products; 167 | sourceTree = ""; 168 | }; 169 | 712574555A87162701DB9E904A3175C7 /* fishhook */ = { 170 | isa = PBXGroup; 171 | children = ( 172 | E2AE909801B176A8E29B55A197FF03B6 /* mm_fishhook.c */, 173 | A32D253029562D93A04B0DC43B5E4A63 /* mm_fishhook.h */, 174 | ); 175 | name = fishhook; 176 | path = MessageMock/fishhook; 177 | sourceTree = ""; 178 | }; 179 | 9B238F32725C2DC2FABEB92E9EA93C2A /* Development Pods */ = { 180 | isa = PBXGroup; 181 | children = ( 182 | 0AC9AE219A0277325E677B52AEE0BAD8 /* MessageMock */, 183 | ); 184 | name = "Development Pods"; 185 | sourceTree = ""; 186 | }; 187 | AACE813D43FF64ECEC52E32A00A3BE2B /* Pods-MessageMockDemoTests */ = { 188 | isa = PBXGroup; 189 | children = ( 190 | 0A870BDF92896BDE64683AF4F71CB96A /* Pods-MessageMockDemoTests.modulemap */, 191 | 67769E141D99397F27D9AAC2A28E02E7 /* Pods-MessageMockDemoTests-acknowledgements.markdown */, 192 | C499D9FA0F99944F924A98A0B832DD0F /* Pods-MessageMockDemoTests-acknowledgements.plist */, 193 | 45E0B4DDD172B91873020A2B30AA8D1A /* Pods-MessageMockDemoTests-dummy.m */, 194 | 8B4163FC65ED75060468B05BE08BD5B3 /* Pods-MessageMockDemoTests-frameworks.sh */, 195 | 37D7595A2AE54D14872185009BC4F5F2 /* Pods-MessageMockDemoTests-Info.plist */, 196 | 7F0318F1F0A0C63F1CB8B965F29A35FF /* Pods-MessageMockDemoTests-umbrella.h */, 197 | F6D8B468295743E6B9837C8A54608760 /* Pods-MessageMockDemoTests.debug.xcconfig */, 198 | EE119006FF2E3126B33DE859B44E3EEA /* Pods-MessageMockDemoTests.release.xcconfig */, 199 | ); 200 | name = "Pods-MessageMockDemoTests"; 201 | path = "Target Support Files/Pods-MessageMockDemoTests"; 202 | sourceTree = ""; 203 | }; 204 | AD4B3B939834FAC653B8D67ABCF1682D /* Support Files */ = { 205 | isa = PBXGroup; 206 | children = ( 207 | BC7258C913072156F5364BF28592EEA9 /* MessageMock.modulemap */, 208 | 48EEC4E470179B9DC44B1BD48C95B733 /* MessageMock.xcconfig */, 209 | 72E87D0112CC0C80183153D0D6A849DF /* MessageMock-dummy.m */, 210 | BE1E37FD9AEA6EE788FF0AB458D51D86 /* MessageMock-Info.plist */, 211 | 9DB5937B7D6FE9575700E937D57595D2 /* MessageMock-prefix.pch */, 212 | 0FF3921DC37497A1D856E9B0CC4BD2E4 /* MessageMock-umbrella.h */, 213 | ); 214 | name = "Support Files"; 215 | path = "MessageMockDemo/Pods/Target Support Files/MessageMock"; 216 | sourceTree = ""; 217 | }; 218 | C0834CEBB1379A84116EF29F93051C60 /* iOS */ = { 219 | isa = PBXGroup; 220 | children = ( 221 | 3212113385A8FBBDB272BD23C409FF61 /* Foundation.framework */, 222 | ); 223 | name = iOS; 224 | sourceTree = ""; 225 | }; 226 | CF1408CF629C7361332E53B88F7BD30C = { 227 | isa = PBXGroup; 228 | children = ( 229 | 9D940727FF8FB9C785EB98E56350EF41 /* Podfile */, 230 | 9B238F32725C2DC2FABEB92E9EA93C2A /* Development Pods */, 231 | D210D550F4EA176C3123ED886F8F87F5 /* Frameworks */, 232 | 655736BB2CE9AE1D504E8D0CF146FEE1 /* Products */, 233 | 45295D8DEF41133486C8B14ED69034BF /* Targets Support Files */, 234 | ); 235 | sourceTree = ""; 236 | }; 237 | D210D550F4EA176C3123ED886F8F87F5 /* Frameworks */ = { 238 | isa = PBXGroup; 239 | children = ( 240 | C0834CEBB1379A84116EF29F93051C60 /* iOS */, 241 | ); 242 | name = Frameworks; 243 | sourceTree = ""; 244 | }; 245 | /* End PBXGroup section */ 246 | 247 | /* Begin PBXHeadersBuildPhase section */ 248 | 0E1D2749020AB6F89831EF95F4EB5BB3 /* Headers */ = { 249 | isa = PBXHeadersBuildPhase; 250 | buildActionMask = 2147483647; 251 | files = ( 252 | 006A5202DA8D63735802BD2DD2C81B80 /* MessageMock-umbrella.h in Headers */, 253 | C209FE0D8DCF71D006231BC1BFC3CA11 /* MessageMocker.h in Headers */, 254 | 50CD9E644C6614610A92E0F772E0E8BF /* mm_argument_callback.h in Headers */, 255 | 28B7C014DF07364EFEB74DDE0185C692 /* mm_argument_change.h in Headers */, 256 | B9A5715005DE3F27192EB4C66E8A82D8 /* mm_argument_stack.h in Headers */, 257 | 6BBAFF22B26B96CF328F9E8D4BE798F3 /* mm_define.h in Headers */, 258 | 7792573CA56A65C99F012C4C9D505574 /* mm_fishhook.h in Headers */, 259 | E432AAC418E66927B7DB578D9341E6E6 /* mm_hook_objc_msgsend.h in Headers */, 260 | E56356525F1D64A28CEFA0C98F2D7DFC /* mm_method_matcher.h in Headers */, 261 | B485BDE7ED3F98853804F0D534738C06 /* mm_runtime.h in Headers */, 262 | ); 263 | runOnlyForDeploymentPostprocessing = 0; 264 | }; 265 | D34163734298E55BFE9B7EF75955F309 /* Headers */ = { 266 | isa = PBXHeadersBuildPhase; 267 | buildActionMask = 2147483647; 268 | files = ( 269 | A882763C285C889F0B1896C4B591DD2B /* Pods-MessageMockDemoTests-umbrella.h in Headers */, 270 | ); 271 | runOnlyForDeploymentPostprocessing = 0; 272 | }; 273 | /* End PBXHeadersBuildPhase section */ 274 | 275 | /* Begin PBXNativeTarget section */ 276 | 0DC72AA7DFB4148188F5D3D63C80F621 /* MessageMock */ = { 277 | isa = PBXNativeTarget; 278 | buildConfigurationList = 891E52FC4734C81DEC7CEC07FE8C53CA /* Build configuration list for PBXNativeTarget "MessageMock" */; 279 | buildPhases = ( 280 | 0E1D2749020AB6F89831EF95F4EB5BB3 /* Headers */, 281 | ADD6DFC5D55455FA5B9B31A63D5D6E24 /* Sources */, 282 | 6BD51F928FDF1CF116AB117FD1807F49 /* Frameworks */, 283 | 5EBDBF80CD9F1B56E09246F86B058121 /* Resources */, 284 | ); 285 | buildRules = ( 286 | ); 287 | dependencies = ( 288 | ); 289 | name = MessageMock; 290 | productName = MessageMock; 291 | productReference = 0E4F714EC593ECF5453250160A393349 /* MessageMock.framework */; 292 | productType = "com.apple.product-type.framework"; 293 | }; 294 | 8751FC505D38D6521F525192EDA257C7 /* Pods-MessageMockDemoTests */ = { 295 | isa = PBXNativeTarget; 296 | buildConfigurationList = C6B905825BE92CBC95730E2C030DF486 /* Build configuration list for PBXNativeTarget "Pods-MessageMockDemoTests" */; 297 | buildPhases = ( 298 | D34163734298E55BFE9B7EF75955F309 /* Headers */, 299 | EE334356D54A910A60783FFC83E44E60 /* Sources */, 300 | 2BE8084F2AD80591444CC51C67272690 /* Frameworks */, 301 | 6AD4DCBCE56FAB083CB85A15933951B2 /* Resources */, 302 | ); 303 | buildRules = ( 304 | ); 305 | dependencies = ( 306 | 9B04BB158ECD3F467E3E2A4208167A14 /* PBXTargetDependency */, 307 | ); 308 | name = "Pods-MessageMockDemoTests"; 309 | productName = "Pods-MessageMockDemoTests"; 310 | productReference = B27452342D018A7B6017C418581E288A /* Pods_MessageMockDemoTests.framework */; 311 | productType = "com.apple.product-type.framework"; 312 | }; 313 | /* End PBXNativeTarget section */ 314 | 315 | /* Begin PBXProject section */ 316 | BFDFE7DC352907FC980B868725387E98 /* Project object */ = { 317 | isa = PBXProject; 318 | attributes = { 319 | LastSwiftUpdateCheck = 1100; 320 | LastUpgradeCheck = 1100; 321 | }; 322 | buildConfigurationList = 4821239608C13582E20E6DA73FD5F1F9 /* Build configuration list for PBXProject "Pods" */; 323 | compatibilityVersion = "Xcode 10.0"; 324 | developmentRegion = en; 325 | hasScannedForEncodings = 0; 326 | knownRegions = ( 327 | en, 328 | Base, 329 | ); 330 | mainGroup = CF1408CF629C7361332E53B88F7BD30C; 331 | productRefGroup = 655736BB2CE9AE1D504E8D0CF146FEE1 /* Products */; 332 | projectDirPath = ""; 333 | projectRoot = ""; 334 | targets = ( 335 | 0DC72AA7DFB4148188F5D3D63C80F621 /* MessageMock */, 336 | 8751FC505D38D6521F525192EDA257C7 /* Pods-MessageMockDemoTests */, 337 | ); 338 | }; 339 | /* End PBXProject section */ 340 | 341 | /* Begin PBXResourcesBuildPhase section */ 342 | 5EBDBF80CD9F1B56E09246F86B058121 /* Resources */ = { 343 | isa = PBXResourcesBuildPhase; 344 | buildActionMask = 2147483647; 345 | files = ( 346 | ); 347 | runOnlyForDeploymentPostprocessing = 0; 348 | }; 349 | 6AD4DCBCE56FAB083CB85A15933951B2 /* Resources */ = { 350 | isa = PBXResourcesBuildPhase; 351 | buildActionMask = 2147483647; 352 | files = ( 353 | ); 354 | runOnlyForDeploymentPostprocessing = 0; 355 | }; 356 | /* End PBXResourcesBuildPhase section */ 357 | 358 | /* Begin PBXSourcesBuildPhase section */ 359 | ADD6DFC5D55455FA5B9B31A63D5D6E24 /* Sources */ = { 360 | isa = PBXSourcesBuildPhase; 361 | buildActionMask = 2147483647; 362 | files = ( 363 | EB8FEDCA9E91E53D5E59024CD8965E5F /* MessageMock-dummy.m in Sources */, 364 | 383AA9C531C51F74F200DE0A3FBD54C6 /* MessageMocker.mm in Sources */, 365 | CB6D436365207738A6F4532ACDB22876 /* mm_argument_callback.cpp in Sources */, 366 | 21D7E7B9291AC10E2621008046B218B2 /* mm_argument_change.cpp in Sources */, 367 | 3BD9F144AA4E0A613A923F5DA7B0107E /* mm_argument_stack.cpp in Sources */, 368 | 76B3018681383CF81C8BB87BADE929DB /* mm_fishhook.c in Sources */, 369 | F057FE746667651224C4405544DBBB5F /* mm_hook_objc_msgsend.cpp in Sources */, 370 | F4428DDD1438A3BEB19D98099F53E0ED /* mm_method_matcher.cpp in Sources */, 371 | 7C111FF3CA1D631663095E374C733C19 /* mm_runtime.cpp in Sources */, 372 | ); 373 | runOnlyForDeploymentPostprocessing = 0; 374 | }; 375 | EE334356D54A910A60783FFC83E44E60 /* Sources */ = { 376 | isa = PBXSourcesBuildPhase; 377 | buildActionMask = 2147483647; 378 | files = ( 379 | 1FB88F3A82E998FD8F31858CC74B9A07 /* Pods-MessageMockDemoTests-dummy.m in Sources */, 380 | ); 381 | runOnlyForDeploymentPostprocessing = 0; 382 | }; 383 | /* End PBXSourcesBuildPhase section */ 384 | 385 | /* Begin PBXTargetDependency section */ 386 | 9B04BB158ECD3F467E3E2A4208167A14 /* PBXTargetDependency */ = { 387 | isa = PBXTargetDependency; 388 | name = MessageMock; 389 | target = 0DC72AA7DFB4148188F5D3D63C80F621 /* MessageMock */; 390 | targetProxy = C7A485C171BA07567E26569795CDB8A0 /* PBXContainerItemProxy */; 391 | }; 392 | /* End PBXTargetDependency section */ 393 | 394 | /* Begin XCBuildConfiguration section */ 395 | 014E9B349A99F0CE39806D8C6CF705E9 /* Release */ = { 396 | isa = XCBuildConfiguration; 397 | baseConfigurationReference = 48EEC4E470179B9DC44B1BD48C95B733 /* MessageMock.xcconfig */; 398 | buildSettings = { 399 | CODE_SIGN_IDENTITY = ""; 400 | "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; 401 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 402 | "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; 403 | CURRENT_PROJECT_VERSION = 1; 404 | DEFINES_MODULE = YES; 405 | DYLIB_COMPATIBILITY_VERSION = 1; 406 | DYLIB_CURRENT_VERSION = 1; 407 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 408 | GCC_PREFIX_HEADER = "Target Support Files/MessageMock/MessageMock-prefix.pch"; 409 | INFOPLIST_FILE = "Target Support Files/MessageMock/MessageMock-Info.plist"; 410 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 411 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 412 | LD_RUNPATH_SEARCH_PATHS = ( 413 | "$(inherited)", 414 | "@executable_path/Frameworks", 415 | "@loader_path/Frameworks", 416 | ); 417 | MODULEMAP_FILE = "Target Support Files/MessageMock/MessageMock.modulemap"; 418 | PRODUCT_MODULE_NAME = MessageMock; 419 | PRODUCT_NAME = MessageMock; 420 | SDKROOT = iphoneos; 421 | SKIP_INSTALL = YES; 422 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; 423 | TARGETED_DEVICE_FAMILY = "1,2"; 424 | VALIDATE_PRODUCT = YES; 425 | VERSIONING_SYSTEM = "apple-generic"; 426 | VERSION_INFO_PREFIX = ""; 427 | }; 428 | name = Release; 429 | }; 430 | 16A38ADD9D4879B101D3C7757F0E49F7 /* Debug */ = { 431 | isa = XCBuildConfiguration; 432 | baseConfigurationReference = 48EEC4E470179B9DC44B1BD48C95B733 /* MessageMock.xcconfig */; 433 | buildSettings = { 434 | CODE_SIGN_IDENTITY = ""; 435 | "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; 436 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 437 | "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; 438 | CURRENT_PROJECT_VERSION = 1; 439 | DEFINES_MODULE = YES; 440 | DYLIB_COMPATIBILITY_VERSION = 1; 441 | DYLIB_CURRENT_VERSION = 1; 442 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 443 | GCC_PREFIX_HEADER = "Target Support Files/MessageMock/MessageMock-prefix.pch"; 444 | INFOPLIST_FILE = "Target Support Files/MessageMock/MessageMock-Info.plist"; 445 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 446 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 447 | LD_RUNPATH_SEARCH_PATHS = ( 448 | "$(inherited)", 449 | "@executable_path/Frameworks", 450 | "@loader_path/Frameworks", 451 | ); 452 | MODULEMAP_FILE = "Target Support Files/MessageMock/MessageMock.modulemap"; 453 | PRODUCT_MODULE_NAME = MessageMock; 454 | PRODUCT_NAME = MessageMock; 455 | SDKROOT = iphoneos; 456 | SKIP_INSTALL = YES; 457 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; 458 | TARGETED_DEVICE_FAMILY = "1,2"; 459 | VERSIONING_SYSTEM = "apple-generic"; 460 | VERSION_INFO_PREFIX = ""; 461 | }; 462 | name = Debug; 463 | }; 464 | 4F0AFF0E33B3BC1AD41EC25765D7AAFE /* Release */ = { 465 | isa = XCBuildConfiguration; 466 | baseConfigurationReference = EE119006FF2E3126B33DE859B44E3EEA /* Pods-MessageMockDemoTests.release.xcconfig */; 467 | buildSettings = { 468 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; 469 | CODE_SIGN_IDENTITY = ""; 470 | "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; 471 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 472 | "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; 473 | CURRENT_PROJECT_VERSION = 1; 474 | DEFINES_MODULE = YES; 475 | DYLIB_COMPATIBILITY_VERSION = 1; 476 | DYLIB_CURRENT_VERSION = 1; 477 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 478 | INFOPLIST_FILE = "Target Support Files/Pods-MessageMockDemoTests/Pods-MessageMockDemoTests-Info.plist"; 479 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 480 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 481 | LD_RUNPATH_SEARCH_PATHS = ( 482 | "$(inherited)", 483 | "@executable_path/Frameworks", 484 | "@loader_path/Frameworks", 485 | ); 486 | MACH_O_TYPE = staticlib; 487 | MODULEMAP_FILE = "Target Support Files/Pods-MessageMockDemoTests/Pods-MessageMockDemoTests.modulemap"; 488 | OTHER_LDFLAGS = ""; 489 | OTHER_LIBTOOLFLAGS = ""; 490 | PODS_ROOT = "$(SRCROOT)"; 491 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; 492 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 493 | SDKROOT = iphoneos; 494 | SKIP_INSTALL = YES; 495 | TARGETED_DEVICE_FAMILY = "1,2"; 496 | VALIDATE_PRODUCT = YES; 497 | VERSIONING_SYSTEM = "apple-generic"; 498 | VERSION_INFO_PREFIX = ""; 499 | }; 500 | name = Release; 501 | }; 502 | 5BE66E32DBC615D507AE7B4757774964 /* Debug */ = { 503 | isa = XCBuildConfiguration; 504 | baseConfigurationReference = F6D8B468295743E6B9837C8A54608760 /* Pods-MessageMockDemoTests.debug.xcconfig */; 505 | buildSettings = { 506 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; 507 | CODE_SIGN_IDENTITY = ""; 508 | "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; 509 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 510 | "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; 511 | CURRENT_PROJECT_VERSION = 1; 512 | DEFINES_MODULE = YES; 513 | DYLIB_COMPATIBILITY_VERSION = 1; 514 | DYLIB_CURRENT_VERSION = 1; 515 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 516 | INFOPLIST_FILE = "Target Support Files/Pods-MessageMockDemoTests/Pods-MessageMockDemoTests-Info.plist"; 517 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 518 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 519 | LD_RUNPATH_SEARCH_PATHS = ( 520 | "$(inherited)", 521 | "@executable_path/Frameworks", 522 | "@loader_path/Frameworks", 523 | ); 524 | MACH_O_TYPE = staticlib; 525 | MODULEMAP_FILE = "Target Support Files/Pods-MessageMockDemoTests/Pods-MessageMockDemoTests.modulemap"; 526 | OTHER_LDFLAGS = ""; 527 | OTHER_LIBTOOLFLAGS = ""; 528 | PODS_ROOT = "$(SRCROOT)"; 529 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; 530 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 531 | SDKROOT = iphoneos; 532 | SKIP_INSTALL = YES; 533 | TARGETED_DEVICE_FAMILY = "1,2"; 534 | VERSIONING_SYSTEM = "apple-generic"; 535 | VERSION_INFO_PREFIX = ""; 536 | }; 537 | name = Debug; 538 | }; 539 | 8F17DC3A99F99FBAD606CE6963886315 /* Release */ = { 540 | isa = XCBuildConfiguration; 541 | buildSettings = { 542 | ALWAYS_SEARCH_USER_PATHS = NO; 543 | CLANG_ANALYZER_NONNULL = YES; 544 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 545 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 546 | CLANG_CXX_LIBRARY = "libc++"; 547 | CLANG_ENABLE_MODULES = YES; 548 | CLANG_ENABLE_OBJC_ARC = YES; 549 | CLANG_ENABLE_OBJC_WEAK = YES; 550 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 551 | CLANG_WARN_BOOL_CONVERSION = YES; 552 | CLANG_WARN_COMMA = YES; 553 | CLANG_WARN_CONSTANT_CONVERSION = YES; 554 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 555 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 556 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 557 | CLANG_WARN_EMPTY_BODY = YES; 558 | CLANG_WARN_ENUM_CONVERSION = YES; 559 | CLANG_WARN_INFINITE_RECURSION = YES; 560 | CLANG_WARN_INT_CONVERSION = YES; 561 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 562 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 563 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 564 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 565 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 566 | CLANG_WARN_STRICT_PROTOTYPES = YES; 567 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 568 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 569 | CLANG_WARN_UNREACHABLE_CODE = YES; 570 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 571 | COPY_PHASE_STRIP = NO; 572 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 573 | ENABLE_NS_ASSERTIONS = NO; 574 | ENABLE_STRICT_OBJC_MSGSEND = YES; 575 | GCC_C_LANGUAGE_STANDARD = gnu11; 576 | GCC_NO_COMMON_BLOCKS = YES; 577 | GCC_PREPROCESSOR_DEFINITIONS = ( 578 | "POD_CONFIGURATION_RELEASE=1", 579 | "$(inherited)", 580 | ); 581 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 582 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 583 | GCC_WARN_UNDECLARED_SELECTOR = YES; 584 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 585 | GCC_WARN_UNUSED_FUNCTION = YES; 586 | GCC_WARN_UNUSED_VARIABLE = YES; 587 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 588 | MTL_ENABLE_DEBUG_INFO = NO; 589 | MTL_FAST_MATH = YES; 590 | PRODUCT_NAME = "$(TARGET_NAME)"; 591 | STRIP_INSTALLED_PRODUCT = NO; 592 | SWIFT_COMPILATION_MODE = wholemodule; 593 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 594 | SWIFT_VERSION = 5.0; 595 | SYMROOT = "${SRCROOT}/../build"; 596 | }; 597 | name = Release; 598 | }; 599 | 916E0404255105F480DC4950B7625F7A /* Debug */ = { 600 | isa = XCBuildConfiguration; 601 | buildSettings = { 602 | ALWAYS_SEARCH_USER_PATHS = NO; 603 | CLANG_ANALYZER_NONNULL = YES; 604 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 605 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 606 | CLANG_CXX_LIBRARY = "libc++"; 607 | CLANG_ENABLE_MODULES = YES; 608 | CLANG_ENABLE_OBJC_ARC = YES; 609 | CLANG_ENABLE_OBJC_WEAK = YES; 610 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 611 | CLANG_WARN_BOOL_CONVERSION = YES; 612 | CLANG_WARN_COMMA = YES; 613 | CLANG_WARN_CONSTANT_CONVERSION = YES; 614 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 615 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 616 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 617 | CLANG_WARN_EMPTY_BODY = YES; 618 | CLANG_WARN_ENUM_CONVERSION = YES; 619 | CLANG_WARN_INFINITE_RECURSION = YES; 620 | CLANG_WARN_INT_CONVERSION = YES; 621 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 622 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 623 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 624 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 625 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 626 | CLANG_WARN_STRICT_PROTOTYPES = YES; 627 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 628 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 629 | CLANG_WARN_UNREACHABLE_CODE = YES; 630 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 631 | COPY_PHASE_STRIP = NO; 632 | DEBUG_INFORMATION_FORMAT = dwarf; 633 | ENABLE_STRICT_OBJC_MSGSEND = YES; 634 | ENABLE_TESTABILITY = YES; 635 | GCC_C_LANGUAGE_STANDARD = gnu11; 636 | GCC_DYNAMIC_NO_PIC = NO; 637 | GCC_NO_COMMON_BLOCKS = YES; 638 | GCC_OPTIMIZATION_LEVEL = 0; 639 | GCC_PREPROCESSOR_DEFINITIONS = ( 640 | "POD_CONFIGURATION_DEBUG=1", 641 | "DEBUG=1", 642 | "$(inherited)", 643 | ); 644 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 645 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 646 | GCC_WARN_UNDECLARED_SELECTOR = YES; 647 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 648 | GCC_WARN_UNUSED_FUNCTION = YES; 649 | GCC_WARN_UNUSED_VARIABLE = YES; 650 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 651 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 652 | MTL_FAST_MATH = YES; 653 | ONLY_ACTIVE_ARCH = YES; 654 | PRODUCT_NAME = "$(TARGET_NAME)"; 655 | STRIP_INSTALLED_PRODUCT = NO; 656 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 657 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 658 | SWIFT_VERSION = 5.0; 659 | SYMROOT = "${SRCROOT}/../build"; 660 | }; 661 | name = Debug; 662 | }; 663 | /* End XCBuildConfiguration section */ 664 | 665 | /* Begin XCConfigurationList section */ 666 | 4821239608C13582E20E6DA73FD5F1F9 /* Build configuration list for PBXProject "Pods" */ = { 667 | isa = XCConfigurationList; 668 | buildConfigurations = ( 669 | 916E0404255105F480DC4950B7625F7A /* Debug */, 670 | 8F17DC3A99F99FBAD606CE6963886315 /* Release */, 671 | ); 672 | defaultConfigurationIsVisible = 0; 673 | defaultConfigurationName = Release; 674 | }; 675 | 891E52FC4734C81DEC7CEC07FE8C53CA /* Build configuration list for PBXNativeTarget "MessageMock" */ = { 676 | isa = XCConfigurationList; 677 | buildConfigurations = ( 678 | 16A38ADD9D4879B101D3C7757F0E49F7 /* Debug */, 679 | 014E9B349A99F0CE39806D8C6CF705E9 /* Release */, 680 | ); 681 | defaultConfigurationIsVisible = 0; 682 | defaultConfigurationName = Release; 683 | }; 684 | C6B905825BE92CBC95730E2C030DF486 /* Build configuration list for PBXNativeTarget "Pods-MessageMockDemoTests" */ = { 685 | isa = XCConfigurationList; 686 | buildConfigurations = ( 687 | 5BE66E32DBC615D507AE7B4757774964 /* Debug */, 688 | 4F0AFF0E33B3BC1AD41EC25765D7AAFE /* Release */, 689 | ); 690 | defaultConfigurationIsVisible = 0; 691 | defaultConfigurationName = Release; 692 | }; 693 | /* End XCConfigurationList section */ 694 | }; 695 | rootObject = BFDFE7DC352907FC980B868725387E98 /* Project object */; 696 | } 697 | -------------------------------------------------------------------------------- /MessageMockDemo/Pods/Pods.xcodeproj/xcuserdata/answeryang.xcuserdatad/xcschemes/MessageMock.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 | -------------------------------------------------------------------------------- /MessageMockDemo/Pods/Pods.xcodeproj/xcuserdata/answeryang.xcuserdatad/xcschemes/Pods-MessageMockDemoTests.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 50 | 51 | 53 | 54 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /MessageMockDemo/Pods/Pods.xcodeproj/xcuserdata/answeryang.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | MessageMock.xcscheme 8 | 9 | isShown 10 | 11 | orderHint 12 | 0 13 | 14 | Pods-MessageMockDemoTests.xcscheme 15 | 16 | isShown 17 | 18 | orderHint 19 | 1 20 | 21 | 22 | SuppressBuildableAutocreation 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /MessageMockDemo/Pods/Target Support Files/MessageMock/MessageMock-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /MessageMockDemo/Pods/Target Support Files/MessageMock/MessageMock-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_MessageMock : NSObject 3 | @end 4 | @implementation PodsDummy_MessageMock 5 | @end 6 | -------------------------------------------------------------------------------- /MessageMockDemo/Pods/Target Support Files/MessageMock/MessageMock-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 | -------------------------------------------------------------------------------- /MessageMockDemo/Pods/Target Support Files/MessageMock/MessageMock-umbrella.h: -------------------------------------------------------------------------------- 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 | #import "mm_argument_callback.h" 14 | #import "mm_argument_change.h" 15 | #import "mm_argument_stack.h" 16 | #import "mm_define.h" 17 | #import "mm_hook_objc_msgsend.h" 18 | #import "mm_method_matcher.h" 19 | #import "mm_runtime.h" 20 | #import "mm_fishhook.h" 21 | #import "MessageMocker.h" 22 | 23 | FOUNDATION_EXPORT double MessageMockVersionNumber; 24 | FOUNDATION_EXPORT const unsigned char MessageMockVersionString[]; 25 | 26 | -------------------------------------------------------------------------------- /MessageMockDemo/Pods/Target Support Files/MessageMock/MessageMock.modulemap: -------------------------------------------------------------------------------- 1 | framework module MessageMock { 2 | umbrella header "MessageMock-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /MessageMockDemo/Pods/Target Support Files/MessageMock/MessageMock.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/MessageMock 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | OTHER_LDFLAGS = $(inherited) -l"c++.1" 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}/../.. 8 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 9 | SKIP_INSTALL = YES 10 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 11 | -------------------------------------------------------------------------------- /MessageMockDemo/Pods/Target Support Files/Pods-MessageMockDemoTests/Pods-MessageMockDemoTests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /MessageMockDemo/Pods/Target Support Files/Pods-MessageMockDemoTests/Pods-MessageMockDemoTests-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | 4 | ## MessageMock 5 | 6 | MIT License 7 | 8 | Copyright (c) 2020 波儿菜 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 | 28 | Generated by CocoaPods - https://cocoapods.org 29 | -------------------------------------------------------------------------------- /MessageMockDemo/Pods/Target Support Files/Pods-MessageMockDemoTests/Pods-MessageMockDemoTests-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 | MIT License 18 | 19 | Copyright (c) 2020 波儿菜 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 | 39 | License 40 | MIT 41 | Title 42 | MessageMock 43 | Type 44 | PSGroupSpecifier 45 | 46 | 47 | FooterText 48 | Generated by CocoaPods - https://cocoapods.org 49 | Title 50 | 51 | Type 52 | PSGroupSpecifier 53 | 54 | 55 | StringsTable 56 | Acknowledgements 57 | Title 58 | Acknowledgements 59 | 60 | 61 | -------------------------------------------------------------------------------- /MessageMockDemo/Pods/Target Support Files/Pods-MessageMockDemoTests/Pods-MessageMockDemoTests-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_MessageMockDemoTests : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_MessageMockDemoTests 5 | @end 6 | -------------------------------------------------------------------------------- /MessageMockDemo/Pods/Target Support Files/Pods-MessageMockDemoTests/Pods-MessageMockDemoTests-frameworks-Debug-input-files.xcfilelist: -------------------------------------------------------------------------------- 1 | ${PODS_ROOT}/Target Support Files/Pods-MessageMockDemoTests/Pods-MessageMockDemoTests-frameworks.sh 2 | ${BUILT_PRODUCTS_DIR}/MessageMock/MessageMock.framework -------------------------------------------------------------------------------- /MessageMockDemo/Pods/Target Support Files/Pods-MessageMockDemoTests/Pods-MessageMockDemoTests-frameworks-Debug-output-files.xcfilelist: -------------------------------------------------------------------------------- 1 | ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MessageMock.framework -------------------------------------------------------------------------------- /MessageMockDemo/Pods/Target Support Files/Pods-MessageMockDemoTests/Pods-MessageMockDemoTests-frameworks-Release-input-files.xcfilelist: -------------------------------------------------------------------------------- 1 | ${PODS_ROOT}/Target Support Files/Pods-MessageMockDemoTests/Pods-MessageMockDemoTests-frameworks.sh -------------------------------------------------------------------------------- /MessageMockDemo/Pods/Target Support Files/Pods-MessageMockDemoTests/Pods-MessageMockDemoTests-frameworks-Release-output-files.xcfilelist: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/indulgeIn/MessageMock/06e88694ba73fe0aa4448ba9bbb4cafe64662275/MessageMockDemo/Pods/Target Support Files/Pods-MessageMockDemoTests/Pods-MessageMockDemoTests-frameworks-Release-output-files.xcfilelist -------------------------------------------------------------------------------- /MessageMockDemo/Pods/Target Support Files/Pods-MessageMockDemoTests/Pods-MessageMockDemoTests-frameworks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | set -u 4 | set -o pipefail 5 | 6 | function on_error { 7 | echo "$(realpath -mq "${0}"):$1: error: Unexpected failure" 8 | } 9 | trap 'on_error $LINENO' ERR 10 | 11 | if [ -z ${FRAMEWORKS_FOLDER_PATH+x} ]; then 12 | # If FRAMEWORKS_FOLDER_PATH is not set, then there's nowhere for us to copy 13 | # frameworks to, so exit 0 (signalling the script phase was successful). 14 | exit 0 15 | fi 16 | 17 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 18 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 19 | 20 | COCOAPODS_PARALLEL_CODE_SIGN="${COCOAPODS_PARALLEL_CODE_SIGN:-false}" 21 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" 22 | 23 | # Used as a return value for each invocation of `strip_invalid_archs` function. 24 | STRIP_BINARY_RETVAL=0 25 | 26 | # This protects against multiple targets copying the same framework dependency at the same time. The solution 27 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html 28 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") 29 | 30 | # Copies and strips a vendored framework 31 | install_framework() 32 | { 33 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then 34 | local source="${BUILT_PRODUCTS_DIR}/$1" 35 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then 36 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" 37 | elif [ -r "$1" ]; then 38 | local source="$1" 39 | fi 40 | 41 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 42 | 43 | if [ -L "${source}" ]; then 44 | echo "Symlinked..." 45 | source="$(readlink "${source}")" 46 | fi 47 | 48 | # Use filter instead of exclude so missing patterns don't throw errors. 49 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" 50 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" 51 | 52 | local basename 53 | basename="$(basename -s .framework "$1")" 54 | binary="${destination}/${basename}.framework/${basename}" 55 | 56 | if ! [ -r "$binary" ]; then 57 | binary="${destination}/${basename}" 58 | elif [ -L "${binary}" ]; then 59 | echo "Destination binary is symlinked..." 60 | dirname="$(dirname "${binary}")" 61 | binary="${dirname}/$(readlink "${binary}")" 62 | fi 63 | 64 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 65 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then 66 | strip_invalid_archs "$binary" 67 | fi 68 | 69 | # Resign the code if required by the build settings to avoid unstable apps 70 | code_sign_if_enabled "${destination}/$(basename "$1")" 71 | 72 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. 73 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then 74 | local swift_runtime_libs 75 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u) 76 | for lib in $swift_runtime_libs; do 77 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" 78 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" 79 | code_sign_if_enabled "${destination}/${lib}" 80 | done 81 | fi 82 | } 83 | 84 | # Copies and strips a vendored dSYM 85 | install_dsym() { 86 | local source="$1" 87 | if [ -r "$source" ]; then 88 | # Copy the dSYM into a the targets temp dir. 89 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\"" 90 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}" 91 | 92 | local basename 93 | basename="$(basename -s .framework.dSYM "$source")" 94 | binary="${DERIVED_FILES_DIR}/${basename}.framework.dSYM/Contents/Resources/DWARF/${basename}" 95 | 96 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 97 | if [[ "$(file "$binary")" == *"Mach-O "*"dSYM companion"* ]]; then 98 | strip_invalid_archs "$binary" 99 | fi 100 | 101 | if [[ $STRIP_BINARY_RETVAL == 1 ]]; then 102 | # Move the stripped file into its final destination. 103 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\"" 104 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.framework.dSYM" "${DWARF_DSYM_FOLDER_PATH}" 105 | else 106 | # The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing. 107 | touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.framework.dSYM" 108 | fi 109 | fi 110 | } 111 | 112 | # Copies the bcsymbolmap files of a vendored framework 113 | install_bcsymbolmap() { 114 | local bcsymbolmap_path="$1" 115 | local destination="${BUILT_PRODUCTS_DIR}" 116 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}"" 117 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}" 118 | } 119 | 120 | # Signs a framework with the provided identity 121 | code_sign_if_enabled() { 122 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY:-}" -a "${CODE_SIGNING_REQUIRED:-}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then 123 | # Use the current code_sign_identity 124 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" 125 | local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS:-} --preserve-metadata=identifier,entitlements '$1'" 126 | 127 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 128 | code_sign_cmd="$code_sign_cmd &" 129 | fi 130 | echo "$code_sign_cmd" 131 | eval "$code_sign_cmd" 132 | fi 133 | } 134 | 135 | # Strip invalid architectures 136 | strip_invalid_archs() { 137 | binary="$1" 138 | # Get architectures for current target binary 139 | binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)" 140 | # Intersect them with the architectures we are building for 141 | intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)" 142 | # If there are no archs supported by this binary then warn the user 143 | if [[ -z "$intersected_archs" ]]; then 144 | echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)." 145 | STRIP_BINARY_RETVAL=0 146 | return 147 | fi 148 | stripped="" 149 | for arch in $binary_archs; do 150 | if ! [[ "${ARCHS}" == *"$arch"* ]]; then 151 | # Strip non-valid architectures in-place 152 | lipo -remove "$arch" -output "$binary" "$binary" 153 | stripped="$stripped $arch" 154 | fi 155 | done 156 | if [[ "$stripped" ]]; then 157 | echo "Stripped $binary of architectures:$stripped" 158 | fi 159 | STRIP_BINARY_RETVAL=1 160 | } 161 | 162 | 163 | if [[ "$CONFIGURATION" == "Debug" ]]; then 164 | install_framework "${BUILT_PRODUCTS_DIR}/MessageMock/MessageMock.framework" 165 | fi 166 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 167 | wait 168 | fi 169 | -------------------------------------------------------------------------------- /MessageMockDemo/Pods/Target Support Files/Pods-MessageMockDemoTests/Pods-MessageMockDemoTests-umbrella.h: -------------------------------------------------------------------------------- 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 | 14 | FOUNDATION_EXPORT double Pods_MessageMockDemoTestsVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_MessageMockDemoTestsVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /MessageMockDemo/Pods/Target Support Files/Pods-MessageMockDemoTests/Pods-MessageMockDemoTests.debug.xcconfig: -------------------------------------------------------------------------------- 1 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/MessageMock" 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/MessageMock/MessageMock.framework/Headers" 4 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 5 | OTHER_LDFLAGS = $(inherited) -l"c++.1" -framework "MessageMock" 6 | PODS_BUILD_DIR = ${BUILD_DIR} 7 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 8 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 9 | PODS_ROOT = ${SRCROOT}/Pods 10 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 11 | -------------------------------------------------------------------------------- /MessageMockDemo/Pods/Target Support Files/Pods-MessageMockDemoTests/Pods-MessageMockDemoTests.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_MessageMockDemoTests { 2 | umbrella header "Pods-MessageMockDemoTests-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /MessageMockDemo/Pods/Target Support Files/Pods-MessageMockDemoTests/Pods-MessageMockDemoTests.release.xcconfig: -------------------------------------------------------------------------------- 1 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 2 | PODS_BUILD_DIR = ${BUILD_DIR} 3 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 4 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 5 | PODS_ROOT = ${SRCROOT}/Pods 6 | USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # MessageMock 3 | 4 | [![CocoaPods](https://img.shields.io/cocoapods/v/MessageMock.svg)](https://cocoapods.org/pods/MessageMock)  5 | [![CocoaPods](https://img.shields.io/cocoapods/p/MessageMock.svg)](https://github.com/indulgeIn/MessageMock)  6 | [![License](https://img.shields.io/github/license/indulgeIn/MessageMock.svg)](https://github.com/indulgeIn/MessageMock)  7 | 8 | #### 优雅的模拟 Objective-C 方法,对参数、返回值进行任意修改和检查。 9 | 10 | #### 原理分析文章:[MessageMock : 优雅的模拟 Objective-C 方法](https://juejin.im/post/6856324772303273992) 11 | 12 | 13 | # 特性 14 | 15 | 通过任意`[target selector]`调用命中目标方法。 16 | 17 | **功能:** 18 | - 修改目标方法返回值、参数 19 | - 验证目标方法返回值、参数 20 | - 跳过目标方法调用 21 | - 获取目标方法命中次数 22 | 23 | **TODO** 24 | - 支持 x86 25 | - 支持大于指针长度的返回值修改 26 | - 支持大于指针长度的参数修改 27 | 28 | 29 | # 安装 30 | 31 | ## CocoaPods 32 | 33 | 1. 在 Podfile 中添加: 34 | ``` 35 | pod 'MessageMocker' 36 | ``` 37 | 2. 执行 `pod install` 或 `pod update`。 38 | 3. 导入 `#import `。 39 | 40 | ## 手动导入 41 | 42 | 1. 把 MessageMock 源码文件夹整个拖入工程。 43 | 2. 导入文件`#import "MessageMocker.h"` 44 | 45 | 46 | # 基本用法 47 | 48 | 通过`MessageMocker.h`的接口进行链式配置(参考`MessageMockerTests.m`的单元测试代码)。 49 | 50 | ### 修改返回值 51 | 52 | ``` 53 | NSObject *value = [NSObject new]; 54 | 55 | MessageMocker.build(NSNotificationCenter.self, @selector(defaultCenter)).mockReturn(value).start(); 56 | 57 | XCTAssertTrue(value == [NSNotificationCenter defaultCenter]); 58 | ``` 59 | 60 | ### 修改参数 61 | ``` 62 | UIView *view = [UIView new]; 63 | NSInteger value = 999; 64 | 65 | MessageMocker.build(view, @selector(setTag:)).mockArgument(0, value).start(); 66 | 67 | view.tag = 666; 68 | XCTAssertTrue(view.tag == value); 69 | ``` 70 | 71 | ### 检查返回值 72 | 73 | ``` 74 | __block unsigned long value = 0; 75 | 76 | NSObject *obj = [NSObject new]; 77 | 78 | MessageMocker.build(obj, @selector(hash)) 79 | .checkReturn(^(unsigned long arg) { 80 | value = arg; 81 | }) 82 | .start(); 83 | 84 | unsigned long res = [obj hash]; 85 | XCTAssertTrue(value == res); 86 | ``` 87 | 88 | ### 检查参数 89 | 90 | ``` 91 | Class anyCls = NSString.self; 92 | 93 | MessageMocker.build(NSObject.self, @selector(isSubclassOfClass:)) 94 | .checkArgument(0, ^(Class cls){ 95 | XCTAssertTrue(anyCls == cls); 96 | }) 97 | .start(); 98 | 99 | [NSObject isSubclassOfClass:anyCls]; 100 | ``` 101 | 102 | ### 跳过方法调用 103 | 104 | ``` 105 | UIView *view = [UIView new]; 106 | NSInteger tag = 666; 107 | view.tag = tag; 108 | 109 | MessageMocker.build(view, @selector(setTag:)).skip(YES).start(); 110 | 111 | view.tag = 999; 112 | XCTAssertTrue(view.tag = tag); 113 | ``` 114 | 115 | ### 设置/读取命中次数 116 | 117 | ``` 118 | NSObject *value = [NSObject new]; 119 | 120 | MessageMocker *mocker = MessageMocker.build(TestObject.self, @selector(createObj)) 121 | .mockReturn(value) 122 | .limitHitCount(2); 123 | mocker.start(); 124 | 125 | XCTAssertTrue(value == [TestObject createObj]); 126 | XCTAssertTrue(mocker.hitCount == 1); 127 | XCTAssertTrue(value == [TestObject createObj]); 128 | XCTAssertTrue(mocker.hitCount == 2); 129 | 130 | XCTAssertTrue(value != [TestObject createObj]); 131 | ``` 132 | --------------------------------------------------------------------------------