├── .gitignore ├── .travis.yml ├── Changelog.md ├── Example ├── Podfile ├── Podfile.lock ├── Tests │ ├── ContainerSpec.m │ ├── DanglingPointerSpec.m │ ├── NotificationSpec.m │ ├── NullSpec.m │ ├── SDKSetup.m │ ├── Tests-Info.plist │ ├── Tests-Prefix.pch │ ├── UnrecognizedSelectorSpec.m │ ├── XXNotificationObserver.h │ ├── XXNotificationObserver.m │ ├── XXShield_Tests-Bridging-Header.h │ ├── XXSpec.swift │ └── en.lproj │ │ ├── InfoPlist.strings │ │ └── XXTestNotification.m ├── XXShield.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcshareddata │ │ └── xcschemes │ │ ├── XXShield-Example.xcscheme │ │ └── XXShield_Tests.xcscheme ├── XXShield.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── XXShield │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── File.swift │ ├── Images.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json │ ├── Person.h │ ├── Person.m │ ├── Student.h │ ├── Student.m │ ├── XXAppDelegate.h │ ├── XXAppDelegate.m │ ├── XXKVOViewController.h │ ├── XXKVOViewController.m │ ├── XXShield-Info.plist │ ├── XXShield-Prefix.pch │ ├── XXShield_Example-Bridging-Header.h │ ├── XXTimerViewController.h │ ├── XXTimerViewController.m │ ├── XXViewController.h │ ├── XXViewController.m │ ├── en.lproj │ └── InfoPlist.strings │ └── main.m ├── LICENSE ├── README.md ├── XXShield.podspec ├── XXShield ├── Assets │ └── .gitkeep ├── Classes │ ├── .gitkeep │ ├── DanglingPointerShield │ │ ├── NSObject+DanglingPointer.h │ │ ├── NSObject+DanglingPointer.mm │ │ ├── XXDanglingPointStub.h │ │ ├── XXDanglingPointStub.m │ │ ├── XXDanglingPonterService.h │ │ └── XXDanglingPonterService.m │ ├── FoundationContainer │ │ ├── NSArray+Shield.m │ │ ├── NSCache+Shield.m │ │ ├── NSDictionary+Shield.m │ │ ├── NSMutableArray+Shield.m │ │ └── NSMutableDictionary+Shield.m │ ├── KVO │ │ └── NSObject+KVOShield.m │ ├── NSNull │ │ └── NSNull+Shield.m │ ├── NSTimer │ │ └── NSTimer+Shield.m │ ├── Notification │ │ └── NSNotificationCenter+Shield.m │ ├── Record │ │ ├── XXRecord.h │ │ └── XXRecord.m │ ├── SmartKit │ │ ├── NSObject+Forward.m │ │ ├── XXShieldStubObject.h │ │ └── XXShieldStubObject.m │ ├── Swizzle │ │ ├── XXMetamacros.h │ │ ├── XXShieldSwizzling.h │ │ └── XXShieldSwizzling.m │ ├── XXShield.h │ ├── XXShieldSDK.h │ ├── XXShieldSDK.m │ └── template │ │ ├── NSArrayObjectAtIndex.h │ │ └── NSArrayWithObjects.h └── XXShield.modulemap └── _Pods.xcodeproj /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xccheckout 23 | *.xcscmblueprint 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | *.ipa 28 | *.dSYM.zip 29 | *.dSYM 30 | 31 | # CocoaPods 32 | # 33 | # We recommend against adding the Pods directory to your .gitignore. However 34 | # you should judge for yourself, the pros and cons are mentioned at: 35 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 36 | # 37 | Example/Pods/* 38 | 39 | # Carthage 40 | # 41 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 42 | # Carthage/Checkouts 43 | 44 | Carthage/Build 45 | 46 | # fastlane 47 | # 48 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 49 | # screenshots whenever they are needed. 50 | # For more information about the recommended setup visit: 51 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 52 | 53 | fastlane/report.xml 54 | fastlane/Preview.html 55 | fastlane/screenshots 56 | fastlane/test_output 57 | 58 | # Code Injection 59 | # 60 | # After new code Injection tools there's a generated folder /iOSInjectionProject 61 | # https://github.com/johnno1962/injectionforxcode 62 | 63 | iOSInjectionProject/ 64 | Example/Pods 65 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # references: 2 | # * http://www.objc.io/issue-6/travis-ci.html 3 | # * https://github.com/supermarin/xcpretty#usage 4 | 5 | osx_image: xcode11.3 6 | language: objective-c 7 | cache: cocoapods 8 | podfile: Example/Podfile 9 | before_install: 10 | - gem install cocoapods # Since Travis is not always on latest version 11 | - pod install --project-directory=Example --repo-update 12 | script: 13 | - set -o pipefail && xcodebuild test -enableCodeCoverage YES -workspace Example/XXShield.xcworkspace -scheme XXShield_Tests -sdk iphonesimulator13.3 -destination 'platform=iOS Simulator,name=iPhone 6s Plus,OS=13.3' ONLY_ACTIVE_ARCH=NO | xcpretty 14 | - pod lib lint 15 | -------------------------------------------------------------------------------- /Changelog.md: -------------------------------------------------------------------------------- 1 | # XXShield 2 | 3 | # 修改日志 4 | 5 | ## 2.2.0 6 | 7 | 修复dli_frame 造成的crash问题 8 | 9 | ## 2.1.1 10 | 11 | 修复NSMutableArray在插入索引切好为count的时候导致插入失败的bug 12 | 13 | ## 2.1.0 14 | 15 | 启动白名单机制 只支持主程序所在类 16 | 17 | ## 2.0.2 18 | 19 | 消息转发改用白名单机制,只支持主程序所在bundle,修复野指针小bug。 20 | 21 | ## 2.0.2 22 | 23 | 修改代码风格 支持travisCI, 调整野指针转发对象使用单例即可 24 | 25 | 26 | ## 2.0.0 27 | 28 | 修改代码风格 添加对应的测试用例 29 | -------------------------------------------------------------------------------- /Example/Podfile: -------------------------------------------------------------------------------- 1 | platform :ios, '9.0' 2 | use_frameworks! 3 | target 'XXShield_Example' do 4 | pod 'XXShield', :path => '../' 5 | 6 | target 'XXShield_Tests' do 7 | inherit! :search_paths 8 | pod 'Quick' 9 | pod 'Nimble' 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /Example/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Nimble (8.0.4) 3 | - Quick (2.2.0) 4 | - XXShield (2.3.0) 5 | 6 | DEPENDENCIES: 7 | - Nimble 8 | - Quick 9 | - XXShield (from `../`) 10 | 11 | SPEC REPOS: 12 | trunk: 13 | - Nimble 14 | - Quick 15 | 16 | EXTERNAL SOURCES: 17 | XXShield: 18 | :path: "../" 19 | 20 | SPEC CHECKSUMS: 21 | Nimble: 18d5360282923225d62b09d781f63abc1a0111fc 22 | Quick: 7fb19e13be07b5dfb3b90d4f9824c855a11af40e 23 | XXShield: b0608906e060154f9078df922a665199e7cf08b3 24 | 25 | PODFILE CHECKSUM: 1beab47862b66bbe8ade576f32e614911c11544f 26 | 27 | COCOAPODS: 1.8.4 28 | -------------------------------------------------------------------------------- /Example/Tests/ContainerSpec.m: -------------------------------------------------------------------------------- 1 | // 2 | // ContainerSpec.m 3 | // XXShield_Tests 4 | // 5 | // Created by nero on 2017/11/1. 6 | // Copyright © 2017年 ValiantCat. All rights reserved. 7 | // 8 | 9 | QuickSpecBegin(ContainerSpec) 10 | 11 | describe(@"Container test", ^{ 12 | context(@"NSCache test", ^{ 13 | it(@"should avoid crash by insert nil value to NSCache", ^{ 14 | NSCache *cache = [[NSCache alloc] init]; 15 | id stub = nil; 16 | [cache setObject:@"val" forKey:@"key"]; // 17 | [cache setObject:stub forKey:@"key"]; 18 | 19 | expect([cache objectForKey:@"key"]).to(equal(@"val")); 20 | 21 | [cache setObject:@"value" forKey:@"anotherKey" cost:10]; 22 | [cache setObject:stub forKey:@"anotherKey" cost:10]; 23 | expect([cache objectForKey:@"anotherKey"]).to(equal(@"value")); 24 | }); 25 | }); 26 | 27 | context(@"NSArray(Private SubClass) test", ^{ 28 | it(@"should avoid crash by using subscribe method objectAtIndex while index out of bounds.", ^{ 29 | NSArray *arr = @[]; 30 | NSString *clazzName = [[NSString alloc] initWithUTF8String:object_getClassName(arr)]; 31 | expect(clazzName).to(equal(@"__NSArray0")); 32 | expect(arr[10]).to(beNil()); 33 | 34 | arr = @[@1]; 35 | clazzName = [[NSString alloc] initWithUTF8String:object_getClassName(arr)]; 36 | expect(clazzName).to(equal(@"__NSSingleObjectArrayI")); 37 | expect(arr[10]).to(beNil()); 38 | 39 | arr = @[@1, @2]; 40 | clazzName = [[NSString alloc] initWithUTF8String:object_getClassName(arr)]; 41 | expect(clazzName).to(equal(@"__NSArrayI")); 42 | expect(arr[10]).to(beNil()); 43 | }); 44 | 45 | it(@"should avoid crash by using convience constructor arrayWithObjects:count: while object appear nil.", ^{ 46 | const id os[] = { @"1",nil}; 47 | NSArray *arr = [NSArray arrayWithObjects:os count:2]; 48 | expect(arr).to(equal(@[@"1"])); 49 | }); 50 | }); 51 | 52 | context(@"NSDictionary test", ^{ 53 | it(@"should avoid crash by using convience constructor dictionaryWithObjects:forKeys:count: while object or key appear nil.", ^{ 54 | NSString *nilValue = nil; 55 | NSDictionary *dict = @{ 56 | @"name" : @"zs", 57 | @"age" : nilValue, 58 | nilValue : @"" 59 | }; 60 | 61 | expect(dict).to(equal(@{@"name" : @"zs"})); 62 | }); 63 | }); 64 | 65 | context(@"NSMutableArray(Private SubClass) test", ^{ 66 | it(@"should avoid crash by using addObject: while object appear nil.", ^{ 67 | NSMutableArray *arr = @[].mutableCopy; 68 | NSString *clazzName = [[NSString alloc] initWithUTF8String:object_getClassName(arr)]; 69 | expect(clazzName).to(equal(@"__NSArrayM")); 70 | id nilValue = nil; 71 | [arr addObject:@1]; 72 | [arr addObject:nilValue]; 73 | [arr addObject:@3]; 74 | expect(arr).to(equal(@[@1, @3])); 75 | }); 76 | 77 | it(@"should avoid crash by using objectAtIndex: while index out of bounds.", ^{ 78 | NSMutableArray *arr = @[].mutableCopy; 79 | NSString *clazzName = [[NSString alloc] initWithUTF8String:object_getClassName(arr)]; 80 | expect(clazzName).to(equal(@"__NSArrayM")); 81 | [arr addObject:@1]; 82 | [arr addObject:@3]; 83 | expect(arr[100]).to(beNil()); 84 | }); 85 | 86 | it(@"should avoid crash by using removeObjectAtIndex: while index out of bounds.", ^{ 87 | NSMutableArray *arr = @[].mutableCopy; 88 | NSString *clazzName = [[NSString alloc] initWithUTF8String:object_getClassName(arr)]; 89 | expect(clazzName).to(equal(@"__NSArrayM")); 90 | [arr addObject:@1]; 91 | [arr addObject:@3]; 92 | [arr removeObjectAtIndex:2]; 93 | expect(arr).to(equal(@[@1, @3])); 94 | }); 95 | 96 | it(@"should avoid crash by using insertObject:atIndex: while object appear nil.", ^{ 97 | NSMutableArray *arr = @[].mutableCopy; 98 | NSString *clazzName = [[NSString alloc] initWithUTF8String:object_getClassName(arr)]; 99 | expect(clazzName).to(equal(@"__NSArrayM")); 100 | [arr addObject:@1]; 101 | [arr addObject:@3]; 102 | id nilValue = nil; 103 | [arr insertObject:nilValue atIndex:10]; 104 | expect(arr).to(equal(@[@1, @3])); 105 | }); 106 | 107 | it(@"should avoid crash by using setObject:atIndexedSubscript: while object appear nil or idx out of bounds.", ^{ 108 | NSMutableArray *arr = @[].mutableCopy; 109 | NSString *clazzName = [[NSString alloc] initWithUTF8String:object_getClassName(arr)]; 110 | expect(clazzName).to(equal(@"__NSArrayM")); 111 | [arr addObject:@1]; 112 | [arr addObject:@3]; 113 | 114 | id nilValue = nil; 115 | [arr setObject:nilValue atIndexedSubscript:1]; 116 | expect(arr).to(equal(@[@1, @3])); 117 | arr[1] = nilValue; 118 | expect(arr).to(equal(@[@1, @3])); 119 | 120 | [arr setObject:@"xx" atIndexedSubscript:100]; 121 | expect(arr).to(equal(@[@1, @3])); 122 | arr[100] = @"xx"; 123 | expect(arr).to(equal(@[@1, @3])); 124 | }); 125 | }); 126 | 127 | context(@"NSMutableDictionary(Private SubClass) test", ^{ 128 | it(@"should avoid crash by using setObject:forKey: while object or key appear nil.", ^{ 129 | NSMutableDictionary *dict = @{ 130 | @"name" : @"zs" 131 | }.mutableCopy; 132 | NSString *clazzName = [[NSString alloc] initWithUTF8String:object_getClassName(dict)]; 133 | expect(clazzName).to(equal(@"__NSDictionaryM")); 134 | 135 | id nilValue = nil; 136 | [dict setObject:nilValue forKey:@"xx"]; 137 | expect(dict).to(equal(@{ @"name" : @"zs"})); 138 | dict[@"xx"] = nilValue; 139 | expect(dict).to(equal(@{ @"name" : @"zs"})); 140 | 141 | [dict setObject:@"xx" forKey:nilValue]; 142 | expect(dict).to(equal(@{ @"name" : @"zs"})); 143 | dict[nilValue] = @"xx"; 144 | expect(dict).to(equal(@{ @"name" : @"zs"})); 145 | }); 146 | }); 147 | 148 | }); 149 | 150 | QuickSpecEnd 151 | -------------------------------------------------------------------------------- /Example/Tests/DanglingPointerSpec.m: -------------------------------------------------------------------------------- 1 | // 2 | // DanglingPointerSpec.m 3 | // XXShield_Tests 4 | // 5 | // Created by nero on 2017/11/1. 6 | // Copyright © 2017年 ValiantCat. All rights reserved. 7 | // 8 | 9 | #import "Person.h" 10 | 11 | QuickSpecBegin(DanglingPointerSpec) 12 | 13 | describe(@"Notification test", ^{ 14 | it(@"should avoid crash by send message to a danglingPointer.", ^{ 15 | __unsafe_unretained Person *obj = nil; 16 | @autoreleasepool { 17 | Person *o = [[Person alloc] init]; 18 | obj = o; 19 | [o release]; 20 | } 21 | int result = [obj performSelector:@selector(sayHello)]; 22 | expect(result).to(equal(0)); 23 | }); 24 | }); 25 | 26 | QuickSpecEnd 27 | 28 | -------------------------------------------------------------------------------- /Example/Tests/NotificationSpec.m: -------------------------------------------------------------------------------- 1 | // 2 | // NotificationSpec.m 3 | // XXShield_Tests 4 | // 5 | // Created by nero on 2017/11/1. 6 | // Copyright © 2017年 ValiantCat. All rights reserved. 7 | // 8 | #import "XXNotificationObserver.h" 9 | 10 | QuickSpecBegin(NotificationSpec) 11 | 12 | describe(@"Notification test", ^{ 13 | it(@"should avoid crash by post a notification to an alred dealloced object.", ^{ 14 | @autoreleasepool { 15 | XXNotificationObserver *_observer = [XXNotificationObserver new]; 16 | [[NSNotificationCenter defaultCenter] addObserver:_observer selector:@selector(noti:) name:@"noti" object:nil]; 17 | } 18 | waitUntilTimeout(2, ^(void (^done)(void)) { 19 | dispatch_after(0.5, dispatch_get_main_queue(), ^{ 20 | [[NSNotificationCenter defaultCenter] postNotificationName:@"noti" object:nil]; 21 | done(); 22 | }); 23 | }); 24 | }); 25 | }); 26 | 27 | QuickSpecEnd 28 | 29 | -------------------------------------------------------------------------------- /Example/Tests/NullSpec.m: -------------------------------------------------------------------------------- 1 | // 2 | // NullSpec.m 3 | // XXShield_Tests 4 | // 5 | // Created by nero on 2017/11/1. 6 | // Copyright © 2017年 ValiantCat. All rights reserved. 7 | // 8 | 9 | QuickSpecBegin(NullSpec) 10 | 11 | describe(@"NullSpec test", ^{ 12 | it(@"should avoid crash by send message to 'NSString' class", ^{ 13 | NSString *null = (NSString *)[NSNull null]; 14 | NSString *result = [null stringByAppendingString:@"fafa"]; 15 | expect(result).to(equal(@"fafa")); 16 | }); 17 | 18 | it(@"should avoid crash by send message to 'NSNumber' class", ^{ 19 | NSNumber *null = (NSNumber *)[NSNull null]; 20 | BOOL result = [null boolValue]; 21 | expect(result).to(beFalsy()); 22 | }); 23 | 24 | it(@"should avoid crash by send message to 'NSArray' class", ^{ 25 | NSArray *null = (NSArray *)[NSNull null]; 26 | NSArray *result = [null arrayByAddingObjectsFromArray:@[@"xx"]]; 27 | expect(result).to(equal(@[@"xx"])); 28 | }); 29 | 30 | it(@"should avoid crash by send message to 'NSDictionary' class", ^{ 31 | NSDictionary *null = (NSDictionary *)[NSNull null]; 32 | NSArray *allKeys = [null allKeys]; 33 | NSArray *allValues = [null allValues]; 34 | expect(allKeys).to(equal(@[])); 35 | expect(allValues).to(equal(@[])); 36 | }); 37 | }); 38 | 39 | QuickSpecEnd 40 | -------------------------------------------------------------------------------- /Example/Tests/SDKSetup.m: -------------------------------------------------------------------------------- 1 | // 2 | // SDKSetup.m 3 | // XXShield_Tests 4 | // 5 | // Created by nero on 2017/11/1. 6 | // Copyright © 2017年 ValiantCat. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "Person.h" 11 | 12 | @interface SDKSetup : NSObject 13 | 14 | @end 15 | 16 | @implementation SDKSetup 17 | 18 | + (void)load { 19 | [XXShieldSDK registerRecordHandler:[self new]]; 20 | [XXShieldSDK registerStabilityWithAbility:(EXXShieldTypeDangLingPointer) withClassNames:@[NSStringFromClass([Person class])]]; 21 | [XXShieldSDK registerStabilitySDK]; 22 | } 23 | 24 | - (void)recordWithReason:(NSError *)reason { 25 | NSLog(@"----------------------------------------------------------------------------------------------------"); 26 | NSLog(@"XXShield has catch a non-fatal error: error is %@", reason); 27 | } 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /Example/Tests/Tests-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 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Example/Tests/Tests-Prefix.pch: -------------------------------------------------------------------------------- 1 | // The contents of this file are implicitly included at the beginning of every test case source file. 2 | 3 | #ifdef __OBJC__ 4 | 5 | #import "XXShield_Tests-Swift.h" 6 | @import Quick; 7 | @import Nimble; 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /Example/Tests/UnrecognizedSelectorSpec.m: -------------------------------------------------------------------------------- 1 | // 2 | // UnrecognizedSelectorSpec.m 3 | // XXShield_Tests 4 | // 5 | // Created by nero on 2017/11/1. 6 | // Copyright © 2017年 ValiantCat. All rights reserved. 7 | // 8 | 9 | #import "Student.h" 10 | 11 | @interface NSObject (XX_Private) 12 | 13 | - (int)doesnotExistMethodWithArg1:(NSObject *)obj arg2:(int)xx; 14 | 15 | @end; 16 | 17 | QuickSpecBegin(UnrecognizedSelectorSpec) 18 | 19 | describe(@"UnrecognizedSelector test", ^{ 20 | it(@"should raise an exception when send message to an doesn't exist method while the class from system.", ^{ 21 | expectAction(^{ 22 | NSObject *object = NSObject.new; 23 | [object doesnotExistMethodWithArg1:nil arg2:100]; 24 | }).to(raiseException().named(@"NSInvalidArgumentException")); 25 | }); 26 | it(@"should avoid crash when send message to an doesn't exist method while the class from system.", ^{ 27 | Student *object = Student.new; 28 | int result = [object doesnotExistMethodWithArg1:nil arg2:100]; 29 | expect(result).to(equal(0)); 30 | }); 31 | }); 32 | 33 | QuickSpecEnd 34 | -------------------------------------------------------------------------------- /Example/Tests/XXNotificationObserver.h: -------------------------------------------------------------------------------- 1 | // 2 | // XXNotificationObserver.h 3 | // XXShield 4 | // 5 | // Created by nero on 2017/7/19. 6 | // Copyright © 2017年 XXShield. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface XXNotificationObserver : NSObject 12 | 13 | - (void)noti:(NSNotification*)noti; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /Example/Tests/XXNotificationObserver.m: -------------------------------------------------------------------------------- 1 | // 2 | // XXNotificationObserver.m 3 | // XXShield 4 | // 5 | // Created by nero on 2017/7/19. 6 | // Copyright © 2017年 XXShield. All rights reserved. 7 | // 8 | 9 | #import "XXNotificationObserver.h" 10 | 11 | @implementation XXNotificationObserver 12 | 13 | - (void)noti:(NSNotification*)noti { 14 | NSLog(@"hello"); 15 | } 16 | 17 | - (void)dealloc { 18 | NSLog(@"dealloc"); 19 | } 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /Example/Tests/XXShield_Tests-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | 5 | -------------------------------------------------------------------------------- /Example/Tests/XXSpec.swift: -------------------------------------------------------------------------------- 1 | // 2 | // XXSpec.swift 3 | // XXShield_Tests 4 | // 5 | // Created by nero on 2017/10/31. 6 | // Copyright © 2017年 ValiantCat. All rights reserved. 7 | // 8 | 9 | import Quick; 10 | import Nimble; 11 | -------------------------------------------------------------------------------- /Example/Tests/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /Example/Tests/en.lproj/XXTestNotification.m: -------------------------------------------------------------------------------- 1 | // 2 | // XXTestNotification.m 3 | // XXShield 4 | // 5 | // Created by nero on 2017/7/19. 6 | // Copyright © 2017年 XXShield. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "XXShieldSDK.h" 11 | #import "XXNotificationObserved.h" 12 | 13 | @interface XXTestNotification : XCTestCase 14 | 15 | @end 16 | 17 | @implementation XXTestNotification 18 | 19 | - (void)setUp { 20 | [super setUp]; 21 | [XXShieldSDK registerStabilityWithAbility:(EXXShieldTypeNotification)]; 22 | } 23 | 24 | - (void)tearDown { 25 | // Put teardown code here. This method is called after the invocation of each test method in the class. 26 | [super tearDown]; 27 | } 28 | 29 | - (void)testNotification { 30 | [XXNotificationObserved new]; 31 | } 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /Example/XXShield.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 117CC1C5C9DA0B4061B90035 /* Pods_XXShield_Tests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B26E2A514A5A1E4049AA321D /* Pods_XXShield_Tests.framework */; }; 11 | 6003F58E195388D20070C39A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58D195388D20070C39A /* Foundation.framework */; }; 12 | 6003F590195388D20070C39A /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58F195388D20070C39A /* CoreGraphics.framework */; }; 13 | 6003F592195388D20070C39A /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F591195388D20070C39A /* UIKit.framework */; }; 14 | 6003F598195388D20070C39A /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 6003F596195388D20070C39A /* InfoPlist.strings */; }; 15 | 6003F59A195388D20070C39A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 6003F599195388D20070C39A /* main.m */; }; 16 | 6003F59E195388D20070C39A /* XXAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 6003F59D195388D20070C39A /* XXAppDelegate.m */; }; 17 | 6003F5A7195388D20070C39A /* XXViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6003F5A6195388D20070C39A /* XXViewController.m */; }; 18 | 6003F5A9195388D20070C39A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6003F5A8195388D20070C39A /* Images.xcassets */; }; 19 | 6003F5B0195388D20070C39A /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F5AF195388D20070C39A /* XCTest.framework */; }; 20 | 6003F5B1195388D20070C39A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58D195388D20070C39A /* Foundation.framework */; }; 21 | 6003F5B2195388D20070C39A /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F591195388D20070C39A /* UIKit.framework */; }; 22 | 6003F5BA195388D20070C39A /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 6003F5B8195388D20070C39A /* InfoPlist.strings */; }; 23 | 71719F9F1E33DC2100824A3D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 71719F9D1E33DC2100824A3D /* LaunchScreen.storyboard */; }; 24 | 873B8AEB1B1F5CCA007FD442 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 873B8AEA1B1F5CCA007FD442 /* Main.storyboard */; }; 25 | C3865EC4D336ABDC5AC479B5 /* Pods_XXShield_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9F65CB4687CAC266B048D03C /* Pods_XXShield_Example.framework */; }; 26 | CE0438D01FB044C10025F7C7 /* SDKSetup.m in Sources */ = {isa = PBXBuildFile; fileRef = CE8331A71FA9663000CA41A4 /* SDKSetup.m */; }; 27 | CE10249F1FA8A18C00F0EDA7 /* XXSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE10249E1FA8A18C00F0EDA7 /* XXSpec.swift */; }; 28 | CE1C77A220FDB0D700377C2A /* Changelog.md in Resources */ = {isa = PBXBuildFile; fileRef = CE1C77A120FDB0D700377C2A /* Changelog.md */; }; 29 | CE648A941FA9CE7E00B6BABD /* UnrecognizedSelectorSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = CE648A931FA9CE7E00B6BABD /* UnrecognizedSelectorSpec.m */; }; 30 | CE648A961FA9D14300B6BABD /* NotificationSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = CE648A951FA9D14300B6BABD /* NotificationSpec.m */; }; 31 | CE648A981FA9EC9F00B6BABD /* DanglingPointerSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = CE648A971FA9EC9F00B6BABD /* DanglingPointerSpec.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; 32 | CE8331A51FA9661400CA41A4 /* NullSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = CE8331A41FA9661400CA41A4 /* NullSpec.m */; }; 33 | CE8331AA1FA972A400CA41A4 /* ContainerSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = CE8331A91FA972A400CA41A4 /* ContainerSpec.m */; }; 34 | CE96440C1F1F50FC004EB2F2 /* XXNotificationObserver.m in Sources */ = {isa = PBXBuildFile; fileRef = CE96440B1F1F50FC004EB2F2 /* XXNotificationObserver.m */; }; 35 | CE9644161F1F535F004EB2F2 /* XXKVOViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = CE9644151F1F535F004EB2F2 /* XXKVOViewController.m */; }; 36 | CE96441B1F1F5390004EB2F2 /* Person.m in Sources */ = {isa = PBXBuildFile; fileRef = CE9644181F1F5390004EB2F2 /* Person.m */; }; 37 | CE96441C1F1F5390004EB2F2 /* Student.m in Sources */ = {isa = PBXBuildFile; fileRef = CE96441A1F1F5390004EB2F2 /* Student.m */; }; 38 | CE9644221F1F53D2004EB2F2 /* XXTimerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = CE9644211F1F53D2004EB2F2 /* XXTimerViewController.m */; }; 39 | CECD211A23A36E360071A7A7 /* File.swift in Sources */ = {isa = PBXBuildFile; fileRef = CECD211923A36E360071A7A7 /* File.swift */; }; 40 | /* End PBXBuildFile section */ 41 | 42 | /* Begin PBXContainerItemProxy section */ 43 | 6003F5B3195388D20070C39A /* PBXContainerItemProxy */ = { 44 | isa = PBXContainerItemProxy; 45 | containerPortal = 6003F582195388D10070C39A /* Project object */; 46 | proxyType = 1; 47 | remoteGlobalIDString = 6003F589195388D20070C39A; 48 | remoteInfo = XXShield; 49 | }; 50 | /* End PBXContainerItemProxy section */ 51 | 52 | /* Begin PBXFileReference section */ 53 | 24F7ABBBD291BB26F5220B32 /* Pods-XXShield_Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-XXShield_Example.debug.xcconfig"; path = "Pods/Target Support Files/Pods-XXShield_Example/Pods-XXShield_Example.debug.xcconfig"; sourceTree = ""; }; 54 | 3AA5DDFEFC7DA2D12EBD18CC /* Pods-XXShield_Tests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-XXShield_Tests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-XXShield_Tests/Pods-XXShield_Tests.debug.xcconfig"; sourceTree = ""; }; 55 | 46EEA05012F18039114BAB98 /* XXShield.podspec */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; name = XXShield.podspec; path = ../XXShield.podspec; sourceTree = ""; }; 56 | 6003F58A195388D20070C39A /* XXShield_Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = XXShield_Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; 57 | 6003F58D195388D20070C39A /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 58 | 6003F58F195388D20070C39A /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 59 | 6003F591195388D20070C39A /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 60 | 6003F595195388D20070C39A /* XXShield-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "XXShield-Info.plist"; sourceTree = ""; }; 61 | 6003F597195388D20070C39A /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 62 | 6003F599195388D20070C39A /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 63 | 6003F59B195388D20070C39A /* XXShield-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "XXShield-Prefix.pch"; sourceTree = ""; }; 64 | 6003F59C195388D20070C39A /* XXAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = XXAppDelegate.h; sourceTree = ""; }; 65 | 6003F59D195388D20070C39A /* XXAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = XXAppDelegate.m; sourceTree = ""; }; 66 | 6003F5A5195388D20070C39A /* XXViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = XXViewController.h; sourceTree = ""; }; 67 | 6003F5A6195388D20070C39A /* XXViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = XXViewController.m; sourceTree = ""; }; 68 | 6003F5A8195388D20070C39A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 69 | 6003F5AE195388D20070C39A /* XXShield_Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = XXShield_Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 70 | 6003F5AF195388D20070C39A /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; 71 | 6003F5B7195388D20070C39A /* Tests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Tests-Info.plist"; sourceTree = ""; }; 72 | 6003F5B9195388D20070C39A /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 73 | 605E5EA041E249575181AD27 /* Pods-XXShield_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-XXShield_Example.release.xcconfig"; path = "Pods/Target Support Files/Pods-XXShield_Example/Pods-XXShield_Example.release.xcconfig"; sourceTree = ""; }; 74 | 606FC2411953D9B200FFA9A0 /* Tests-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Tests-Prefix.pch"; sourceTree = ""; }; 75 | 71719F9E1E33DC2100824A3D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 76 | 873B8AEA1B1F5CCA007FD442 /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = Main.storyboard; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 77 | 8ED4B5E1A4E690247385A142 /* Pods-XXShield_Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-XXShield_Tests.release.xcconfig"; path = "Pods/Target Support Files/Pods-XXShield_Tests/Pods-XXShield_Tests.release.xcconfig"; sourceTree = ""; }; 78 | 9F65CB4687CAC266B048D03C /* Pods_XXShield_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_XXShield_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 79 | A3E63806E16CFC48D7969CE1 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = ""; }; 80 | B26E2A514A5A1E4049AA321D /* Pods_XXShield_Tests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_XXShield_Tests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 81 | CE10249D1FA8A18C00F0EDA7 /* XXShield_Tests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "XXShield_Tests-Bridging-Header.h"; sourceTree = ""; }; 82 | CE10249E1FA8A18C00F0EDA7 /* XXSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XXSpec.swift; sourceTree = ""; }; 83 | CE1C77A120FDB0D700377C2A /* Changelog.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; name = Changelog.md; path = ../Changelog.md; sourceTree = ""; }; 84 | CE648A931FA9CE7E00B6BABD /* UnrecognizedSelectorSpec.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = UnrecognizedSelectorSpec.m; sourceTree = ""; }; 85 | CE648A951FA9D14300B6BABD /* NotificationSpec.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NotificationSpec.m; sourceTree = ""; }; 86 | CE648A971FA9EC9F00B6BABD /* DanglingPointerSpec.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DanglingPointerSpec.m; sourceTree = ""; }; 87 | CE8331A41FA9661400CA41A4 /* NullSpec.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NullSpec.m; sourceTree = ""; }; 88 | CE8331A71FA9663000CA41A4 /* SDKSetup.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDKSetup.m; sourceTree = ""; }; 89 | CE8331A91FA972A400CA41A4 /* ContainerSpec.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ContainerSpec.m; sourceTree = ""; }; 90 | CE96440A1F1F50FC004EB2F2 /* XXNotificationObserver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XXNotificationObserver.h; sourceTree = ""; }; 91 | CE96440B1F1F50FC004EB2F2 /* XXNotificationObserver.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XXNotificationObserver.m; sourceTree = ""; }; 92 | CE9644141F1F535F004EB2F2 /* XXKVOViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XXKVOViewController.h; sourceTree = ""; }; 93 | CE9644151F1F535F004EB2F2 /* XXKVOViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XXKVOViewController.m; sourceTree = ""; }; 94 | CE9644171F1F5390004EB2F2 /* Person.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Person.h; sourceTree = ""; }; 95 | CE9644181F1F5390004EB2F2 /* Person.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Person.m; sourceTree = ""; }; 96 | CE9644191F1F5390004EB2F2 /* Student.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Student.h; sourceTree = ""; }; 97 | CE96441A1F1F5390004EB2F2 /* Student.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Student.m; sourceTree = ""; }; 98 | CE9644201F1F53D2004EB2F2 /* XXTimerViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XXTimerViewController.h; sourceTree = ""; }; 99 | CE9644211F1F53D2004EB2F2 /* XXTimerViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XXTimerViewController.m; sourceTree = ""; }; 100 | CECD211823A36E360071A7A7 /* XXShield_Example-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "XXShield_Example-Bridging-Header.h"; sourceTree = ""; }; 101 | CECD211923A36E360071A7A7 /* File.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = File.swift; sourceTree = ""; }; 102 | D5D64BCBDD700A05544EEBE3 /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = ""; }; 103 | /* End PBXFileReference section */ 104 | 105 | /* Begin PBXFrameworksBuildPhase section */ 106 | 6003F587195388D20070C39A /* Frameworks */ = { 107 | isa = PBXFrameworksBuildPhase; 108 | buildActionMask = 2147483647; 109 | files = ( 110 | 6003F590195388D20070C39A /* CoreGraphics.framework in Frameworks */, 111 | 6003F592195388D20070C39A /* UIKit.framework in Frameworks */, 112 | 6003F58E195388D20070C39A /* Foundation.framework in Frameworks */, 113 | C3865EC4D336ABDC5AC479B5 /* Pods_XXShield_Example.framework in Frameworks */, 114 | ); 115 | runOnlyForDeploymentPostprocessing = 0; 116 | }; 117 | 6003F5AB195388D20070C39A /* Frameworks */ = { 118 | isa = PBXFrameworksBuildPhase; 119 | buildActionMask = 2147483647; 120 | files = ( 121 | 6003F5B0195388D20070C39A /* XCTest.framework in Frameworks */, 122 | 6003F5B2195388D20070C39A /* UIKit.framework in Frameworks */, 123 | 6003F5B1195388D20070C39A /* Foundation.framework in Frameworks */, 124 | 117CC1C5C9DA0B4061B90035 /* Pods_XXShield_Tests.framework in Frameworks */, 125 | ); 126 | runOnlyForDeploymentPostprocessing = 0; 127 | }; 128 | /* End PBXFrameworksBuildPhase section */ 129 | 130 | /* Begin PBXGroup section */ 131 | 17BF97ABE93938B2ED5393D0 /* Pods */ = { 132 | isa = PBXGroup; 133 | children = ( 134 | 24F7ABBBD291BB26F5220B32 /* Pods-XXShield_Example.debug.xcconfig */, 135 | 605E5EA041E249575181AD27 /* Pods-XXShield_Example.release.xcconfig */, 136 | 3AA5DDFEFC7DA2D12EBD18CC /* Pods-XXShield_Tests.debug.xcconfig */, 137 | 8ED4B5E1A4E690247385A142 /* Pods-XXShield_Tests.release.xcconfig */, 138 | ); 139 | name = Pods; 140 | sourceTree = ""; 141 | }; 142 | 6003F581195388D10070C39A = { 143 | isa = PBXGroup; 144 | children = ( 145 | 60FF7A9C1954A5C5007DD14C /* Podspec Metadata */, 146 | 6003F593195388D20070C39A /* Example for XXShield */, 147 | 6003F5B5195388D20070C39A /* Tests */, 148 | 6003F58C195388D20070C39A /* Frameworks */, 149 | 6003F58B195388D20070C39A /* Products */, 150 | 17BF97ABE93938B2ED5393D0 /* Pods */, 151 | ); 152 | sourceTree = ""; 153 | }; 154 | 6003F58B195388D20070C39A /* Products */ = { 155 | isa = PBXGroup; 156 | children = ( 157 | 6003F58A195388D20070C39A /* XXShield_Example.app */, 158 | 6003F5AE195388D20070C39A /* XXShield_Tests.xctest */, 159 | ); 160 | name = Products; 161 | sourceTree = ""; 162 | }; 163 | 6003F58C195388D20070C39A /* Frameworks */ = { 164 | isa = PBXGroup; 165 | children = ( 166 | 6003F58D195388D20070C39A /* Foundation.framework */, 167 | 6003F58F195388D20070C39A /* CoreGraphics.framework */, 168 | 6003F591195388D20070C39A /* UIKit.framework */, 169 | 6003F5AF195388D20070C39A /* XCTest.framework */, 170 | 9F65CB4687CAC266B048D03C /* Pods_XXShield_Example.framework */, 171 | B26E2A514A5A1E4049AA321D /* Pods_XXShield_Tests.framework */, 172 | ); 173 | name = Frameworks; 174 | sourceTree = ""; 175 | }; 176 | 6003F593195388D20070C39A /* Example for XXShield */ = { 177 | isa = PBXGroup; 178 | children = ( 179 | CE9644201F1F53D2004EB2F2 /* XXTimerViewController.h */, 180 | CE9644211F1F53D2004EB2F2 /* XXTimerViewController.m */, 181 | CE9644171F1F5390004EB2F2 /* Person.h */, 182 | CE9644181F1F5390004EB2F2 /* Person.m */, 183 | CE9644191F1F5390004EB2F2 /* Student.h */, 184 | CE96441A1F1F5390004EB2F2 /* Student.m */, 185 | 6003F59C195388D20070C39A /* XXAppDelegate.h */, 186 | 6003F59D195388D20070C39A /* XXAppDelegate.m */, 187 | 873B8AEA1B1F5CCA007FD442 /* Main.storyboard */, 188 | 6003F5A5195388D20070C39A /* XXViewController.h */, 189 | 6003F5A6195388D20070C39A /* XXViewController.m */, 190 | 71719F9D1E33DC2100824A3D /* LaunchScreen.storyboard */, 191 | 6003F5A8195388D20070C39A /* Images.xcassets */, 192 | 6003F594195388D20070C39A /* Supporting Files */, 193 | CE9644141F1F535F004EB2F2 /* XXKVOViewController.h */, 194 | CE9644151F1F535F004EB2F2 /* XXKVOViewController.m */, 195 | CECD211923A36E360071A7A7 /* File.swift */, 196 | CECD211823A36E360071A7A7 /* XXShield_Example-Bridging-Header.h */, 197 | ); 198 | name = "Example for XXShield"; 199 | path = XXShield; 200 | sourceTree = ""; 201 | }; 202 | 6003F594195388D20070C39A /* Supporting Files */ = { 203 | isa = PBXGroup; 204 | children = ( 205 | 6003F595195388D20070C39A /* XXShield-Info.plist */, 206 | 6003F596195388D20070C39A /* InfoPlist.strings */, 207 | 6003F599195388D20070C39A /* main.m */, 208 | 6003F59B195388D20070C39A /* XXShield-Prefix.pch */, 209 | ); 210 | name = "Supporting Files"; 211 | sourceTree = ""; 212 | }; 213 | 6003F5B5195388D20070C39A /* Tests */ = { 214 | isa = PBXGroup; 215 | children = ( 216 | CE648A9C1FA9ED2600B6BABD /* lib */, 217 | 6003F5B6195388D20070C39A /* Supporting Files */, 218 | CE8331A41FA9661400CA41A4 /* NullSpec.m */, 219 | CE8331A91FA972A400CA41A4 /* ContainerSpec.m */, 220 | CE648A931FA9CE7E00B6BABD /* UnrecognizedSelectorSpec.m */, 221 | CE648A951FA9D14300B6BABD /* NotificationSpec.m */, 222 | CE648A971FA9EC9F00B6BABD /* DanglingPointerSpec.m */, 223 | ); 224 | path = Tests; 225 | sourceTree = ""; 226 | }; 227 | 6003F5B6195388D20070C39A /* Supporting Files */ = { 228 | isa = PBXGroup; 229 | children = ( 230 | 6003F5B7195388D20070C39A /* Tests-Info.plist */, 231 | 6003F5B8195388D20070C39A /* InfoPlist.strings */, 232 | 606FC2411953D9B200FFA9A0 /* Tests-Prefix.pch */, 233 | CE10249E1FA8A18C00F0EDA7 /* XXSpec.swift */, 234 | CE10249D1FA8A18C00F0EDA7 /* XXShield_Tests-Bridging-Header.h */, 235 | ); 236 | name = "Supporting Files"; 237 | sourceTree = ""; 238 | }; 239 | 60FF7A9C1954A5C5007DD14C /* Podspec Metadata */ = { 240 | isa = PBXGroup; 241 | children = ( 242 | CE1C77A120FDB0D700377C2A /* Changelog.md */, 243 | 46EEA05012F18039114BAB98 /* XXShield.podspec */, 244 | A3E63806E16CFC48D7969CE1 /* README.md */, 245 | D5D64BCBDD700A05544EEBE3 /* LICENSE */, 246 | ); 247 | name = "Podspec Metadata"; 248 | sourceTree = ""; 249 | }; 250 | CE648A9C1FA9ED2600B6BABD /* lib */ = { 251 | isa = PBXGroup; 252 | children = ( 253 | CE8331A71FA9663000CA41A4 /* SDKSetup.m */, 254 | CE96440A1F1F50FC004EB2F2 /* XXNotificationObserver.h */, 255 | CE96440B1F1F50FC004EB2F2 /* XXNotificationObserver.m */, 256 | ); 257 | name = lib; 258 | sourceTree = ""; 259 | }; 260 | /* End PBXGroup section */ 261 | 262 | /* Begin PBXNativeTarget section */ 263 | 6003F589195388D20070C39A /* XXShield_Example */ = { 264 | isa = PBXNativeTarget; 265 | buildConfigurationList = 6003F5BF195388D20070C39A /* Build configuration list for PBXNativeTarget "XXShield_Example" */; 266 | buildPhases = ( 267 | 6104433839D6A71BF719C7D4 /* [CP] Check Pods Manifest.lock */, 268 | 6003F586195388D20070C39A /* Sources */, 269 | 6003F587195388D20070C39A /* Frameworks */, 270 | 6003F588195388D20070C39A /* Resources */, 271 | EE3680AFA08B0B5A30F1F7AA /* [CP] Embed Pods Frameworks */, 272 | ); 273 | buildRules = ( 274 | ); 275 | dependencies = ( 276 | ); 277 | name = XXShield_Example; 278 | productName = XXShield; 279 | productReference = 6003F58A195388D20070C39A /* XXShield_Example.app */; 280 | productType = "com.apple.product-type.application"; 281 | }; 282 | 6003F5AD195388D20070C39A /* XXShield_Tests */ = { 283 | isa = PBXNativeTarget; 284 | buildConfigurationList = 6003F5C2195388D20070C39A /* Build configuration list for PBXNativeTarget "XXShield_Tests" */; 285 | buildPhases = ( 286 | 781938AC0F93748FA4A58294 /* [CP] Check Pods Manifest.lock */, 287 | 6003F5AA195388D20070C39A /* Sources */, 288 | 6003F5AB195388D20070C39A /* Frameworks */, 289 | 6003F5AC195388D20070C39A /* Resources */, 290 | 07C67A25D660A60C7C175F51 /* [CP] Embed Pods Frameworks */, 291 | ); 292 | buildRules = ( 293 | ); 294 | dependencies = ( 295 | 6003F5B4195388D20070C39A /* PBXTargetDependency */, 296 | ); 297 | name = XXShield_Tests; 298 | productName = XXShieldTests; 299 | productReference = 6003F5AE195388D20070C39A /* XXShield_Tests.xctest */; 300 | productType = "com.apple.product-type.bundle.unit-test"; 301 | }; 302 | /* End PBXNativeTarget section */ 303 | 304 | /* Begin PBXProject section */ 305 | 6003F582195388D10070C39A /* Project object */ = { 306 | isa = PBXProject; 307 | attributes = { 308 | CLASSPREFIX = XX; 309 | LastUpgradeCheck = 1130; 310 | ORGANIZATIONNAME = ValiantCat; 311 | TargetAttributes = { 312 | 6003F589195388D20070C39A = { 313 | LastSwiftMigration = 1130; 314 | }; 315 | 6003F5AD195388D20070C39A = { 316 | LastSwiftMigration = 0900; 317 | TestTargetID = 6003F589195388D20070C39A; 318 | }; 319 | }; 320 | }; 321 | buildConfigurationList = 6003F585195388D10070C39A /* Build configuration list for PBXProject "XXShield" */; 322 | compatibilityVersion = "Xcode 3.2"; 323 | developmentRegion = English; 324 | hasScannedForEncodings = 0; 325 | knownRegions = ( 326 | English, 327 | en, 328 | Base, 329 | ); 330 | mainGroup = 6003F581195388D10070C39A; 331 | productRefGroup = 6003F58B195388D20070C39A /* Products */; 332 | projectDirPath = ""; 333 | projectRoot = ""; 334 | targets = ( 335 | 6003F589195388D20070C39A /* XXShield_Example */, 336 | 6003F5AD195388D20070C39A /* XXShield_Tests */, 337 | ); 338 | }; 339 | /* End PBXProject section */ 340 | 341 | /* Begin PBXResourcesBuildPhase section */ 342 | 6003F588195388D20070C39A /* Resources */ = { 343 | isa = PBXResourcesBuildPhase; 344 | buildActionMask = 2147483647; 345 | files = ( 346 | 873B8AEB1B1F5CCA007FD442 /* Main.storyboard in Resources */, 347 | 71719F9F1E33DC2100824A3D /* LaunchScreen.storyboard in Resources */, 348 | 6003F5A9195388D20070C39A /* Images.xcassets in Resources */, 349 | 6003F598195388D20070C39A /* InfoPlist.strings in Resources */, 350 | CE1C77A220FDB0D700377C2A /* Changelog.md in Resources */, 351 | ); 352 | runOnlyForDeploymentPostprocessing = 0; 353 | }; 354 | 6003F5AC195388D20070C39A /* Resources */ = { 355 | isa = PBXResourcesBuildPhase; 356 | buildActionMask = 2147483647; 357 | files = ( 358 | 6003F5BA195388D20070C39A /* InfoPlist.strings in Resources */, 359 | ); 360 | runOnlyForDeploymentPostprocessing = 0; 361 | }; 362 | /* End PBXResourcesBuildPhase section */ 363 | 364 | /* Begin PBXShellScriptBuildPhase section */ 365 | 07C67A25D660A60C7C175F51 /* [CP] Embed Pods Frameworks */ = { 366 | isa = PBXShellScriptBuildPhase; 367 | buildActionMask = 2147483647; 368 | files = ( 369 | ); 370 | inputPaths = ( 371 | "${PODS_ROOT}/Target Support Files/Pods-XXShield_Tests/Pods-XXShield_Tests-frameworks.sh", 372 | "${BUILT_PRODUCTS_DIR}/Nimble/Nimble.framework", 373 | "${BUILT_PRODUCTS_DIR}/Quick/Quick.framework", 374 | ); 375 | name = "[CP] Embed Pods Frameworks"; 376 | outputPaths = ( 377 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Nimble.framework", 378 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Quick.framework", 379 | ); 380 | runOnlyForDeploymentPostprocessing = 0; 381 | shellPath = /bin/sh; 382 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-XXShield_Tests/Pods-XXShield_Tests-frameworks.sh\"\n"; 383 | showEnvVarsInLog = 0; 384 | }; 385 | 6104433839D6A71BF719C7D4 /* [CP] Check Pods Manifest.lock */ = { 386 | isa = PBXShellScriptBuildPhase; 387 | buildActionMask = 2147483647; 388 | files = ( 389 | ); 390 | inputPaths = ( 391 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 392 | "${PODS_ROOT}/Manifest.lock", 393 | ); 394 | name = "[CP] Check Pods Manifest.lock"; 395 | outputPaths = ( 396 | "$(DERIVED_FILE_DIR)/Pods-XXShield_Example-checkManifestLockResult.txt", 397 | ); 398 | runOnlyForDeploymentPostprocessing = 0; 399 | shellPath = /bin/sh; 400 | 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"; 401 | showEnvVarsInLog = 0; 402 | }; 403 | 781938AC0F93748FA4A58294 /* [CP] Check Pods Manifest.lock */ = { 404 | isa = PBXShellScriptBuildPhase; 405 | buildActionMask = 2147483647; 406 | files = ( 407 | ); 408 | inputPaths = ( 409 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 410 | "${PODS_ROOT}/Manifest.lock", 411 | ); 412 | name = "[CP] Check Pods Manifest.lock"; 413 | outputPaths = ( 414 | "$(DERIVED_FILE_DIR)/Pods-XXShield_Tests-checkManifestLockResult.txt", 415 | ); 416 | runOnlyForDeploymentPostprocessing = 0; 417 | shellPath = /bin/sh; 418 | 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"; 419 | showEnvVarsInLog = 0; 420 | }; 421 | EE3680AFA08B0B5A30F1F7AA /* [CP] Embed Pods Frameworks */ = { 422 | isa = PBXShellScriptBuildPhase; 423 | buildActionMask = 2147483647; 424 | files = ( 425 | ); 426 | inputPaths = ( 427 | "${PODS_ROOT}/Target Support Files/Pods-XXShield_Example/Pods-XXShield_Example-frameworks.sh", 428 | "${BUILT_PRODUCTS_DIR}/XXShield/XXShield.framework", 429 | ); 430 | name = "[CP] Embed Pods Frameworks"; 431 | outputPaths = ( 432 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/XXShield.framework", 433 | ); 434 | runOnlyForDeploymentPostprocessing = 0; 435 | shellPath = /bin/sh; 436 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-XXShield_Example/Pods-XXShield_Example-frameworks.sh\"\n"; 437 | showEnvVarsInLog = 0; 438 | }; 439 | /* End PBXShellScriptBuildPhase section */ 440 | 441 | /* Begin PBXSourcesBuildPhase section */ 442 | 6003F586195388D20070C39A /* Sources */ = { 443 | isa = PBXSourcesBuildPhase; 444 | buildActionMask = 2147483647; 445 | files = ( 446 | CE9644221F1F53D2004EB2F2 /* XXTimerViewController.m in Sources */, 447 | 6003F59E195388D20070C39A /* XXAppDelegate.m in Sources */, 448 | 6003F5A7195388D20070C39A /* XXViewController.m in Sources */, 449 | CE96441B1F1F5390004EB2F2 /* Person.m in Sources */, 450 | CE96441C1F1F5390004EB2F2 /* Student.m in Sources */, 451 | CE96440C1F1F50FC004EB2F2 /* XXNotificationObserver.m in Sources */, 452 | 6003F59A195388D20070C39A /* main.m in Sources */, 453 | CECD211A23A36E360071A7A7 /* File.swift in Sources */, 454 | CE9644161F1F535F004EB2F2 /* XXKVOViewController.m in Sources */, 455 | ); 456 | runOnlyForDeploymentPostprocessing = 0; 457 | }; 458 | 6003F5AA195388D20070C39A /* Sources */ = { 459 | isa = PBXSourcesBuildPhase; 460 | buildActionMask = 2147483647; 461 | files = ( 462 | CE8331AA1FA972A400CA41A4 /* ContainerSpec.m in Sources */, 463 | CE0438D01FB044C10025F7C7 /* SDKSetup.m in Sources */, 464 | CE648A941FA9CE7E00B6BABD /* UnrecognizedSelectorSpec.m in Sources */, 465 | CE10249F1FA8A18C00F0EDA7 /* XXSpec.swift in Sources */, 466 | CE8331A51FA9661400CA41A4 /* NullSpec.m in Sources */, 467 | CE648A981FA9EC9F00B6BABD /* DanglingPointerSpec.m in Sources */, 468 | CE648A961FA9D14300B6BABD /* NotificationSpec.m in Sources */, 469 | ); 470 | runOnlyForDeploymentPostprocessing = 0; 471 | }; 472 | /* End PBXSourcesBuildPhase section */ 473 | 474 | /* Begin PBXTargetDependency section */ 475 | 6003F5B4195388D20070C39A /* PBXTargetDependency */ = { 476 | isa = PBXTargetDependency; 477 | target = 6003F589195388D20070C39A /* XXShield_Example */; 478 | targetProxy = 6003F5B3195388D20070C39A /* PBXContainerItemProxy */; 479 | }; 480 | /* End PBXTargetDependency section */ 481 | 482 | /* Begin PBXVariantGroup section */ 483 | 6003F596195388D20070C39A /* InfoPlist.strings */ = { 484 | isa = PBXVariantGroup; 485 | children = ( 486 | 6003F597195388D20070C39A /* en */, 487 | ); 488 | name = InfoPlist.strings; 489 | sourceTree = ""; 490 | }; 491 | 6003F5B8195388D20070C39A /* InfoPlist.strings */ = { 492 | isa = PBXVariantGroup; 493 | children = ( 494 | 6003F5B9195388D20070C39A /* en */, 495 | ); 496 | name = InfoPlist.strings; 497 | sourceTree = ""; 498 | }; 499 | 71719F9D1E33DC2100824A3D /* LaunchScreen.storyboard */ = { 500 | isa = PBXVariantGroup; 501 | children = ( 502 | 71719F9E1E33DC2100824A3D /* Base */, 503 | ); 504 | name = LaunchScreen.storyboard; 505 | sourceTree = ""; 506 | }; 507 | /* End PBXVariantGroup section */ 508 | 509 | /* Begin XCBuildConfiguration section */ 510 | 6003F5BD195388D20070C39A /* Debug */ = { 511 | isa = XCBuildConfiguration; 512 | buildSettings = { 513 | ALWAYS_SEARCH_USER_PATHS = NO; 514 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 515 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 516 | CLANG_CXX_LIBRARY = "libc++"; 517 | CLANG_ENABLE_MODULES = YES; 518 | CLANG_ENABLE_OBJC_ARC = YES; 519 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 520 | CLANG_WARN_BOOL_CONVERSION = YES; 521 | CLANG_WARN_COMMA = YES; 522 | CLANG_WARN_CONSTANT_CONVERSION = YES; 523 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 524 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 525 | CLANG_WARN_EMPTY_BODY = YES; 526 | CLANG_WARN_ENUM_CONVERSION = YES; 527 | CLANG_WARN_INFINITE_RECURSION = YES; 528 | CLANG_WARN_INT_CONVERSION = YES; 529 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 530 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 531 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 532 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 533 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 534 | CLANG_WARN_STRICT_PROTOTYPES = YES; 535 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 536 | CLANG_WARN_UNREACHABLE_CODE = YES; 537 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 538 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 539 | COPY_PHASE_STRIP = NO; 540 | ENABLE_STRICT_OBJC_MSGSEND = YES; 541 | ENABLE_TESTABILITY = YES; 542 | GCC_C_LANGUAGE_STANDARD = gnu99; 543 | GCC_DYNAMIC_NO_PIC = NO; 544 | GCC_NO_COMMON_BLOCKS = YES; 545 | GCC_OPTIMIZATION_LEVEL = 0; 546 | GCC_PREPROCESSOR_DEFINITIONS = ( 547 | "DEBUG=1", 548 | "$(inherited)", 549 | ); 550 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 551 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 552 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 553 | GCC_WARN_UNDECLARED_SELECTOR = YES; 554 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 555 | GCC_WARN_UNUSED_FUNCTION = YES; 556 | GCC_WARN_UNUSED_VARIABLE = YES; 557 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 558 | ONLY_ACTIVE_ARCH = YES; 559 | SDKROOT = iphoneos; 560 | TARGETED_DEVICE_FAMILY = "1,2"; 561 | }; 562 | name = Debug; 563 | }; 564 | 6003F5BE195388D20070C39A /* Release */ = { 565 | isa = XCBuildConfiguration; 566 | buildSettings = { 567 | ALWAYS_SEARCH_USER_PATHS = NO; 568 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 569 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 570 | CLANG_CXX_LIBRARY = "libc++"; 571 | CLANG_ENABLE_MODULES = YES; 572 | CLANG_ENABLE_OBJC_ARC = YES; 573 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 574 | CLANG_WARN_BOOL_CONVERSION = YES; 575 | CLANG_WARN_COMMA = YES; 576 | CLANG_WARN_CONSTANT_CONVERSION = YES; 577 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 578 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 579 | CLANG_WARN_EMPTY_BODY = YES; 580 | CLANG_WARN_ENUM_CONVERSION = YES; 581 | CLANG_WARN_INFINITE_RECURSION = YES; 582 | CLANG_WARN_INT_CONVERSION = YES; 583 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 584 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 585 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 586 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 587 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 588 | CLANG_WARN_STRICT_PROTOTYPES = YES; 589 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 590 | CLANG_WARN_UNREACHABLE_CODE = YES; 591 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 592 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 593 | COPY_PHASE_STRIP = YES; 594 | ENABLE_NS_ASSERTIONS = NO; 595 | ENABLE_STRICT_OBJC_MSGSEND = YES; 596 | GCC_C_LANGUAGE_STANDARD = gnu99; 597 | GCC_NO_COMMON_BLOCKS = YES; 598 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 599 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 600 | GCC_WARN_UNDECLARED_SELECTOR = YES; 601 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 602 | GCC_WARN_UNUSED_FUNCTION = YES; 603 | GCC_WARN_UNUSED_VARIABLE = YES; 604 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 605 | SDKROOT = iphoneos; 606 | SWIFT_COMPILATION_MODE = wholemodule; 607 | TARGETED_DEVICE_FAMILY = "1,2"; 608 | VALIDATE_PRODUCT = YES; 609 | }; 610 | name = Release; 611 | }; 612 | 6003F5C0195388D20070C39A /* Debug */ = { 613 | isa = XCBuildConfiguration; 614 | baseConfigurationReference = 24F7ABBBD291BB26F5220B32 /* Pods-XXShield_Example.debug.xcconfig */; 615 | buildSettings = { 616 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 617 | CLANG_ENABLE_MODULES = YES; 618 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 619 | GCC_PREFIX_HEADER = "XXShield/XXShield-Prefix.pch"; 620 | INFOPLIST_FILE = "XXShield/XXShield-Info.plist"; 621 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 622 | MODULE_NAME = ExampleApp; 623 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.${PRODUCT_NAME:rfc1034identifier}"; 624 | PRODUCT_NAME = "$(TARGET_NAME)"; 625 | SWIFT_OBJC_BRIDGING_HEADER = "XXShield/XXShield_Example-Bridging-Header.h"; 626 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 627 | SWIFT_VERSION = 5.0; 628 | WRAPPER_EXTENSION = app; 629 | }; 630 | name = Debug; 631 | }; 632 | 6003F5C1195388D20070C39A /* Release */ = { 633 | isa = XCBuildConfiguration; 634 | baseConfigurationReference = 605E5EA041E249575181AD27 /* Pods-XXShield_Example.release.xcconfig */; 635 | buildSettings = { 636 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 637 | CLANG_ENABLE_MODULES = YES; 638 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 639 | GCC_PREFIX_HEADER = "XXShield/XXShield-Prefix.pch"; 640 | INFOPLIST_FILE = "XXShield/XXShield-Info.plist"; 641 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 642 | MODULE_NAME = ExampleApp; 643 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.${PRODUCT_NAME:rfc1034identifier}"; 644 | PRODUCT_NAME = "$(TARGET_NAME)"; 645 | SWIFT_OBJC_BRIDGING_HEADER = "XXShield/XXShield_Example-Bridging-Header.h"; 646 | SWIFT_VERSION = 5.0; 647 | WRAPPER_EXTENSION = app; 648 | }; 649 | name = Release; 650 | }; 651 | 6003F5C3195388D20070C39A /* Debug */ = { 652 | isa = XCBuildConfiguration; 653 | baseConfigurationReference = 3AA5DDFEFC7DA2D12EBD18CC /* Pods-XXShield_Tests.debug.xcconfig */; 654 | buildSettings = { 655 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 656 | BUNDLE_LOADER = "$(TEST_HOST)"; 657 | CLANG_ENABLE_MODULES = YES; 658 | FRAMEWORK_SEARCH_PATHS = ( 659 | "$(SDKROOT)/Developer/Library/Frameworks", 660 | "$(inherited)", 661 | "$(DEVELOPER_FRAMEWORKS_DIR)", 662 | ); 663 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 664 | GCC_PREFIX_HEADER = "Tests/Tests-Prefix.pch"; 665 | GCC_PREPROCESSOR_DEFINITIONS = ( 666 | "DEBUG=1", 667 | "$(inherited)", 668 | ); 669 | INFOPLIST_FILE = "Tests/Tests-Info.plist"; 670 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 671 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.${PRODUCT_NAME:rfc1034identifier}"; 672 | PRODUCT_NAME = "$(TARGET_NAME)"; 673 | SWIFT_OBJC_BRIDGING_HEADER = "Tests/XXShield_Tests-Bridging-Header.h"; 674 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 675 | SWIFT_VERSION = 5.0; 676 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/XXShield_Example.app/XXShield_Example"; 677 | WRAPPER_EXTENSION = xctest; 678 | }; 679 | name = Debug; 680 | }; 681 | 6003F5C4195388D20070C39A /* Release */ = { 682 | isa = XCBuildConfiguration; 683 | baseConfigurationReference = 8ED4B5E1A4E690247385A142 /* Pods-XXShield_Tests.release.xcconfig */; 684 | buildSettings = { 685 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 686 | BUNDLE_LOADER = "$(TEST_HOST)"; 687 | CLANG_ENABLE_MODULES = YES; 688 | FRAMEWORK_SEARCH_PATHS = ( 689 | "$(SDKROOT)/Developer/Library/Frameworks", 690 | "$(inherited)", 691 | "$(DEVELOPER_FRAMEWORKS_DIR)", 692 | ); 693 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 694 | GCC_PREFIX_HEADER = "Tests/Tests-Prefix.pch"; 695 | INFOPLIST_FILE = "Tests/Tests-Info.plist"; 696 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 697 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.${PRODUCT_NAME:rfc1034identifier}"; 698 | PRODUCT_NAME = "$(TARGET_NAME)"; 699 | SWIFT_OBJC_BRIDGING_HEADER = "Tests/XXShield_Tests-Bridging-Header.h"; 700 | SWIFT_VERSION = 5.0; 701 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/XXShield_Example.app/XXShield_Example"; 702 | WRAPPER_EXTENSION = xctest; 703 | }; 704 | name = Release; 705 | }; 706 | /* End XCBuildConfiguration section */ 707 | 708 | /* Begin XCConfigurationList section */ 709 | 6003F585195388D10070C39A /* Build configuration list for PBXProject "XXShield" */ = { 710 | isa = XCConfigurationList; 711 | buildConfigurations = ( 712 | 6003F5BD195388D20070C39A /* Debug */, 713 | 6003F5BE195388D20070C39A /* Release */, 714 | ); 715 | defaultConfigurationIsVisible = 0; 716 | defaultConfigurationName = Release; 717 | }; 718 | 6003F5BF195388D20070C39A /* Build configuration list for PBXNativeTarget "XXShield_Example" */ = { 719 | isa = XCConfigurationList; 720 | buildConfigurations = ( 721 | 6003F5C0195388D20070C39A /* Debug */, 722 | 6003F5C1195388D20070C39A /* Release */, 723 | ); 724 | defaultConfigurationIsVisible = 0; 725 | defaultConfigurationName = Release; 726 | }; 727 | 6003F5C2195388D20070C39A /* Build configuration list for PBXNativeTarget "XXShield_Tests" */ = { 728 | isa = XCConfigurationList; 729 | buildConfigurations = ( 730 | 6003F5C3195388D20070C39A /* Debug */, 731 | 6003F5C4195388D20070C39A /* Release */, 732 | ); 733 | defaultConfigurationIsVisible = 0; 734 | defaultConfigurationName = Release; 735 | }; 736 | /* End XCConfigurationList section */ 737 | }; 738 | rootObject = 6003F582195388D10070C39A /* Project object */; 739 | } 740 | -------------------------------------------------------------------------------- /Example/XXShield.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/XXShield.xcodeproj/xcshareddata/xcschemes/XXShield-Example.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 42 | 48 | 49 | 50 | 51 | 52 | 62 | 64 | 70 | 71 | 72 | 73 | 79 | 81 | 87 | 88 | 89 | 90 | 92 | 93 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /Example/XXShield.xcodeproj/xcshareddata/xcschemes/XXShield_Tests.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 14 | 15 | 17 | 23 | 24 | 25 | 26 | 27 | 37 | 38 | 44 | 45 | 47 | 48 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /Example/XXShield.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Example/XXShield.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Example/XXShield/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 | 31 | 32 | -------------------------------------------------------------------------------- /Example/XXShield/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /Example/XXShield/File.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // XXShield_Example 4 | // 5 | // Created by nero on 2019/12/13. 6 | // Copyright © 2019 ValiantCat. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | -------------------------------------------------------------------------------- /Example/XXShield/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | } 88 | ], 89 | "info" : { 90 | "version" : 1, 91 | "author" : "xcode" 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /Example/XXShield/Person.h: -------------------------------------------------------------------------------- 1 | // 2 | // Person.h 3 | // XXShield 4 | // 5 | // Created by nero on 2017/2/7. 6 | // Copyright © 2017年 XXShield. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface Person : NSObject 12 | 13 | @property (nonatomic, copy) NSString *name; 14 | 15 | - (void)sayHello; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /Example/XXShield/Person.m: -------------------------------------------------------------------------------- 1 | // 2 | // Person.m 3 | // XXShield 4 | // 5 | // Created by nero on 2017/2/7. 6 | // Copyright © 2017年 XXShield. All rights reserved. 7 | // 8 | 9 | #import "Person.h" 10 | 11 | @implementation Person 12 | 13 | - (void)fireTimer:(NSTimer *)timer { 14 | NSLog(@"userinfo is %@",timer.userInfo); 15 | } 16 | 17 | - (void)fireTimer { 18 | NSLog(@"fire"); 19 | } 20 | 21 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { 22 | 23 | } 24 | 25 | - (void)dealloc { 26 | NSLog(@"person dealloced"); 27 | } 28 | 29 | - (void)sayHello { 30 | NSLog(@"person say hello"); 31 | } 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /Example/XXShield/Student.h: -------------------------------------------------------------------------------- 1 | // 2 | // Student.h 3 | // ios_black_magic_homework 4 | // 5 | // Created by nero on 2017/2/20. 6 | // Copyright © 2017年 nero. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface Student : NSObject 12 | 13 | @property (nonatomic, copy) NSString *name; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /Example/XXShield/Student.m: -------------------------------------------------------------------------------- 1 | // 2 | // Student.m 3 | // ios_black_magic_homework 4 | // 5 | // Created by nero on 2017/2/20. 6 | // Copyright © 2017年 nero. All rights reserved. 7 | // 8 | 9 | #import "Student.h" 10 | 11 | @implementation Student 12 | 13 | - (void)dealloc { 14 | NSLog(@"student dealloced"); 15 | } 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /Example/XXShield/XXAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // XXAppDelegate.h 3 | // XXShield 4 | // 5 | // Created by XXShield on 07/10/2017. 6 | // Copyright (c) 2017 XXShield. All rights reserved. 7 | // 8 | 9 | @import UIKit; 10 | 11 | @interface XXAppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /Example/XXShield/XXAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // XXAppDelegate.m 3 | // XXShield 4 | // 5 | // Created by XXShield on 07/10/2017. 6 | // Copyright (c) 2017 XXShield. All rights reserved. 7 | // 8 | 9 | #import "XXAppDelegate.h" 10 | @import ObjectiveC.runtime; 11 | 12 | @implementation XXAppDelegate 13 | 14 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 15 | { 16 | // NSLog(@"%@", [self findAllOf:[NSArray class]]); 17 | 18 | // WebCascadeList, 19 | // CSSearchableItemCodedArray, 20 | // "_PFEncodedArray", 21 | // "_PFBatchFaultingArray", 22 | // "_PFArray", 23 | // "_NSMetadataQueryResultGroupArray", 24 | // "_NSMetadataQueryResultArray", 25 | // "_NSCallStackArray", 26 | // NSKeyValueArray, 27 | // "__NSArrayI_Transfer", 28 | // "__NSArrayReversed", 29 | // "__NSOrderedSetArrayProxy", 30 | // "_CTFontFallbacksArray", 31 | // "_NSConstantArray", 32 | // CALayerArray, 33 | // "__NSArrayI", 34 | // "__NSFrozenArrayM", 35 | // "__NSArray0", 36 | // "__NSSingleObjectArrayI", 37 | // NSMutableArray 38 | return YES; 39 | } 40 | 41 | //- (NSArray *)findAllOf:(Class)defaultClass { 42 | // 43 | // int count = objc_getClassList(NULL, 0); 44 | // 45 | // if (count <= 0) { 46 | // 47 | // @throw@"Couldn't retrieve Obj-C class-list"; 48 | // 49 | // return @[defaultClass]; 50 | // } 51 | // 52 | // NSMutableArray *output = @[].mutableCopy; 53 | // 54 | // Class *classes = (Class *) malloc(sizeof(Class) * count); 55 | // 56 | // objc_getClassList(classes, count); 57 | // 58 | // for (int i = 0; i < count; ++i) { 59 | // 60 | // if (defaultClass == class_getSuperclass(classes[i]))//子类 61 | // { 62 | // [output addObject:classes[i]]; 63 | // } 64 | // 65 | // } 66 | // 67 | // free(classes); 68 | // 69 | // return output.copy; 70 | // 71 | //} 72 | 73 | @end 74 | -------------------------------------------------------------------------------- /Example/XXShield/XXKVOViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // XXKVOViewController.h 3 | // XXShield 4 | // 5 | // Created by nero on 2017/7/19. 6 | // Copyright © 2017年 XXShield. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface XXKVOViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Example/XXShield/XXKVOViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // XXKVOViewController.m 3 | // XXShield 4 | // 5 | // Created by nero on 2017/7/19. 6 | // Copyright © 2017年 XXShield. All rights reserved. 7 | // 8 | 9 | #import "XXKVOViewController.h" 10 | #import "Person.h" 11 | #import "Student.h" 12 | #import 13 | 14 | @interface XXKVOViewController () 15 | 16 | @end 17 | 18 | @implementation XXKVOViewController 19 | 20 | - (void)viewDidLoad { 21 | [super viewDidLoad]; 22 | static dispatch_once_t onceToken; 23 | dispatch_once(&onceToken, ^{ 24 | [XXShieldSDK registerStabilityWithAbility:(EXXShieldTypeKVO)]; 25 | }); 26 | } 27 | 28 | - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { 29 | // [self testKVO1]; 30 | // [self testKVO2]; 31 | [self testKVO3]; 32 | } 33 | - (void)testKVO1 { 34 | //name: 35 | // NSInternalInconsistencyException 36 | //reason: 37 | // An instance 0x60800001b5d0 of class Student was deallocated while key value observers were still registered with it. Current observation info: ( Context: 0x0, Property: 0x608000051a30> 38 | // 被观察者提前释放 Crash 39 | Student *s = [[Student alloc] init]; 40 | s.name = @"zs"; 41 | 42 | [s addObserver:self 43 | forKeyPath:@"name" 44 | options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld 45 | context:nil]; 46 | 47 | // 延时1000ms后改变stu的name属性值 48 | // dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 49 | // p.name = @"Jane"; 50 | // }); 51 | } 52 | 53 | - (void)testKVO2 { 54 | [self addObserver:[Person new ] forKeyPath:@"view" options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld) context:NULL]; 55 | 56 | self.view = [UIView new]; // Crash 57 | // 被观察者是局部变量 触发KVOCrash 58 | } 59 | 60 | - (void)testKVO3 { 61 | 62 | // for test 重复添加 63 | // [self addObserver:self forKeyPath:@"view" options:(NSKeyValueObservingOptionNew) context:NULL]; 64 | [self addObserver:self forKeyPath:@"view" options:(NSKeyValueObservingOptionNew) context:NULL]; 65 | 66 | 67 | self.view = [UIView new]; 68 | // 会触发多次响应事件 69 | 70 | // for test 多余的移除会导致Crash because it is not registered as an observer.' 71 | [self removeObserver:self forKeyPath:@"view"]; 72 | [self removeObserver:self forKeyPath:@"view"]; 73 | } 74 | 75 | //- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { 76 | // NSLog(@"%@",object); 77 | //} 78 | 79 | @end 80 | -------------------------------------------------------------------------------- /Example/XXShield/XXShield-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1.0 25 | LSRequiresIPhoneOS 26 | 27 | UILaunchStoryboardName 28 | LaunchScreen 29 | UIMainStoryboardFile 30 | Main 31 | UIRequiredDeviceCapabilities 32 | 33 | armv7 34 | 35 | UISupportedInterfaceOrientations 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationLandscapeLeft 39 | UIInterfaceOrientationLandscapeRight 40 | 41 | UISupportedInterfaceOrientations~ipad 42 | 43 | UIInterfaceOrientationPortrait 44 | UIInterfaceOrientationPortraitUpsideDown 45 | UIInterfaceOrientationLandscapeLeft 46 | UIInterfaceOrientationLandscapeRight 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /Example/XXShield/XXShield-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header 3 | // 4 | // The contents of this file are implicitly included at the beginning of every source file. 5 | // 6 | 7 | #import 8 | 9 | #ifndef __IPHONE_5_0 10 | #warning "This project uses features only available in iOS SDK 5.0 and later." 11 | #endif 12 | 13 | #ifdef __OBJC__ 14 | @import UIKit; 15 | @import Foundation; 16 | #endif 17 | -------------------------------------------------------------------------------- /Example/XXShield/XXShield_Example-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | 5 | -------------------------------------------------------------------------------- /Example/XXShield/XXTimerViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // XXTimerViewController.h 3 | // XXShield 4 | // 5 | // Created by nero on 2017/6/23. 6 | // Copyright © 2017年 XXShield. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface XXTimerViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Example/XXShield/XXTimerViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // XXTimerViewController.m 3 | // XXShield 4 | // 5 | // Created by nero on 2017/6/23. 6 | // Copyright © 2017年 XXShield. All rights reserved. 7 | // 8 | 9 | #import "XXTimerViewController.h" 10 | #import "Person.h" 11 | #import 12 | 13 | @interface XXTimerViewController () 14 | 15 | @property (nonatomic, strong) NSTimer *timer; 16 | @property (nonatomic, strong) Person *timerPerson; 17 | 18 | @end 19 | 20 | @implementation XXTimerViewController 21 | 22 | - (void)viewDidLoad { 23 | [super viewDidLoad]; 24 | static dispatch_once_t onceToken; 25 | dispatch_once(&onceToken, ^{ 26 | [XXShieldSDK registerStabilityWithAbility:(EXXShieldTypeTimer)]; 27 | }); 28 | } 29 | 30 | - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { 31 | [self testTimer]; 32 | } 33 | 34 | - (void)testTimer { 35 | // 1 正常使用 36 | // self.timerPerson = [Person new]; 37 | // [NSTimer scheduledTimerWithTimeInterval:1 target:self.timerPerson selector:@selector(fireTimer) userInfo:@{@"hah":@"jaj"} repeats:YES]; 38 | // 2 target会被runloop持有 造成隐式的内存泄漏 开启防护之后会自动注销timer 39 | [NSTimer scheduledTimerWithTimeInterval:1 target:[Person new] selector:@selector(fireTimer) userInfo:@{@"hah":@"jaj"} repeats:YES]; 40 | 41 | } 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /Example/XXShield/XXViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // XXViewController.h 3 | // XXShield 4 | // 5 | // Created by XXShield on 07/10/2017. 6 | // Copyright (c) 2017 XXShield. All rights reserved. 7 | // 8 | 9 | @import UIKit; 10 | 11 | @interface XXViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Example/XXShield/XXViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // XXViewController.m 3 | // XXShield 4 | // 5 | // Created by XXShield on 07/10/2017. 6 | // Copyright (c) 2017 XXShield. All rights reserved. 7 | // 8 | 9 | #import "XXViewController.h" 10 | 11 | @interface XXViewController () 12 | 13 | @end 14 | 15 | @implementation XXViewController 16 | 17 | - (void)viewDidLoad 18 | { 19 | [super viewDidLoad]; 20 | // Do any additional setup after loading the view, typically from a nib. 21 | } 22 | 23 | - (void)didReceiveMemoryWarning 24 | { 25 | [super didReceiveMemoryWarning]; 26 | // Dispose of any resources that can be recreated. 27 | } 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /Example/XXShield/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /Example/XXShield/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // XXShield 4 | // 5 | // Created by XXShield on 07/10/2017. 6 | // Copyright (c) 2017 XXShield. All rights reserved. 7 | // 8 | 9 | @import UIKit; 10 | #import "XXAppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) 13 | { 14 | @autoreleasepool { 15 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([XXAppDelegate class])); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | Copyright (c) 2017 ValiantCat <519224747@qq.com> 203 | 204 | Permission is hereby granted, free of charge, to any person obtaining a copy 205 | of this software and associated documentation files (the "Software"), to deal 206 | in the Software without restriction, including without limitation the rights 207 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 208 | copies of the Software, and to permit persons to whom the Software is 209 | furnished to do so, subject to the following conditions: 210 | 211 | The above copyright notice and this permission notice shall be included in 212 | all copies or substantial portions of the Software. 213 | 214 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 215 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 216 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 217 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 218 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 219 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 220 | THE SOFTWARE. 221 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | [![CI Status](http://img.shields.io/travis/ValiantCat/XXShield.svg?style=flat)](https://travis-ci.org/ValiantCat/XXShield) [![Version](https://img.shields.io/cocoapods/v/XXShield.svg?style=flat)](http://cocoapods.org/pods/XXShield) [![License](https://img.shields.io/cocoapods/l/XXShield.svg?style=flat)](http://cocoapods.org/pods/XXShield) [![Platform](https://img.shields.io/cocoapods/p/XXShield.svg?style=flat)](http://cocoapods.org/pods/XXShield) 3 | 4 | 5 | 6 | 7 | 8 | 9 | # 前言 10 | 11 | 正在运行的 APP 突然 Crash,是一件令人不爽的事,会流失用户,影响公司发展,所以 APP 运行时拥有防 Crash 功能能有效降低 Crash 率,提升 APP 稳定性。但是有时候 APP Crash 是应有的表现,我们不让 APPCrash 可能会导致别的逻辑错误,不过我们可以抓取到应用当前的堆栈信息并上传至相关的服务器,分析并修复这些 BUG。 12 | 13 | 所以本文介绍的 XXShield 库有两个重要的功能: 14 | 15 | 1. 防止Crash 16 | 2. 捕获异常状态下的崩溃信息 17 | 18 | 类似的相关技术分析也有 [网易iOS App运行时Crash自动防护实践](https://mp.weixin.qq.com/s?__biz=MzA3ODg4MDk0Ng==&mid=2651113088&idx=1&sn=10b28d7fbcdf0def1a47113e5505728d&chksm=844c6f5db33be64b57fc9b4013cdbb7122d7791f3bbca9b4b29e80cce295eb341b03a9fc0f2f&mpshare=1&scene=23&srcid=0207njmGS2HWHI0BZmpjNKhv%23rd) 19 | 20 | 21 | # 目前已经实现的功能 22 | 23 | 1. Unrecognized Selector Crash 24 | 2. KVO Crash 25 | 3. Container Crash 26 | 4. NSNotification Crash 27 | 5. NSNull Crash 28 | 6. NSTimer Crash 29 | 7. 野指针 Crash 30 | 31 | 32 | ---- 33 | 34 | # 1 Unrecoginzed Selector Crash 35 | 36 | 37 | ## 出现原因 38 | 39 | 由于 Objective-C 是动态语言,所有的消息发送都会放在运行时去解析,有时候我们把一个信息传递给了错误的类型,就会导致这个错误。 40 | 41 | ## 解决办法 42 | 43 | Objective-C 在出现无法解析的方法时有三部曲来进行消息转发。 44 | 详见[Objective-C Runtime 运行时之三:方法与消息](https://southpeak.github.io/2014/11/03/objective-c-runtime-3/) 45 | 46 | 1. 动态方法解析 47 | 2. 备用接收者 48 | 3. 完整转发 49 | 50 | 1 一般适用与 Dynamic 修饰的 Property 51 | 2 一般适用与将方法转发至其他对象 52 | 3 一般适用与消息可以转发多个对象,可以实现类似多继承或者转发中心的概念。 53 | 54 | 这里选择的是方案二,因为三里面用到了 NSInvocation 对象,此对象性能开销较大,而且这种异常如果出现必然频次较高。最适合将消息转发到一个备用者对象上。 55 | 56 | 这里新建一个智能转发类。此对象将在其他对象无法解析数据时,返回一个 0 来防止 Crash。返回 0 是因为这个通用的智能转发类做的操作接近向 nil 发送一个消息。 57 | 58 | 代码如下 59 | 60 | ```Objc 61 | 62 | #import 63 | 64 | /** 65 | default Implement 66 | @param target trarget 67 | @param cmd cmd 68 | @param ... other param 69 | @return default Implement is zero 70 | */ 71 | int smartFunction(id target, SEL cmd, ...) { 72 | return 0; 73 | } 74 | 75 | static BOOL __addMethod(Class clazz, SEL sel) { 76 | NSString *selName = NSStringFromSelector(sel); 77 | 78 | NSMutableString *tmpString = [[NSMutableString alloc] initWithFormat:@"%@", selName]; 79 | 80 | int count = (int)[tmpString replaceOccurrencesOfString:@":" 81 | withString:@"_" 82 | options:NSCaseInsensitiveSearch 83 | range:NSMakeRange(0, selName.length)]; 84 | 85 | NSMutableString *val = [[NSMutableString alloc] initWithString:@"i@:"]; 86 | 87 | for (int i = 0; i < count; i++) { 88 | [val appendString:@"@"]; 89 | } 90 | const char *funcTypeEncoding = [val UTF8String]; 91 | return class_addMethod(clazz, sel, (IMP)smartFunction, funcTypeEncoding); 92 | } 93 | 94 | @implementation XXShieldStubObject 95 | 96 | + (XXShieldStubObject *)shareInstance { 97 | static XXShieldStubObject *singleton; 98 | if (!singleton) { 99 | static dispatch_once_t onceToken; 100 | dispatch_once(&onceToken, ^{ 101 | singleton = [XXShieldStubObject new]; 102 | }); 103 | } 104 | return singleton; 105 | } 106 | 107 | - (BOOL)addFunc:(SEL)sel { 108 | return __addMethod([XXShieldStubObject class], sel); 109 | } 110 | 111 | + (BOOL)addClassFunc:(SEL)sel { 112 | Class metaClass = objc_getMetaClass(class_getName([XXShieldStubObject class])); 113 | return __addMethod(metaClass, sel); 114 | } 115 | 116 | @end 117 | 118 | ``` 119 | 120 | 我们这里需要 Hook NSObject的 `- (id)forwardingTargetForSelector:(SEL)aSelector` 方法启动消息转发。 121 | 很多人不知道的是如果想要转发类方法,只需要实现一个同名的类方法即可,虽然在头文件中此方法并未声明。 122 | 123 | ```Objc 124 | 125 | XXStaticHookClass(NSObject, ProtectFW, id, @selector(forwardingTargetForSelector:), (SEL)aSelector) { 126 | // 1 如果是NSSNumber 和NSString没找到就是类型不对 切换下类型就好了 127 | if ([self isKindOfClass:[NSNumber class]] && [NSString instancesRespondToSelector:aSelector]) { 128 | NSNumber *number = (NSNumber *)self; 129 | NSString *str = [number stringValue]; 130 | return str; 131 | } else if ([self isKindOfClass:[NSString class]] && [NSNumber instancesRespondToSelector:aSelector]) { 132 | NSString *str = (NSString *)self; 133 | NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init]; 134 | NSNumber *number = [formatter numberFromString:str]; 135 | return number; 136 | } 137 | 138 | BOOL aBool = [self respondsToSelector:aSelector]; 139 | NSMethodSignature *signatrue = [self methodSignatureForSelector:aSelector]; 140 | 141 | if (aBool || signatrue) { 142 | return XXHookOrgin(aSelector); 143 | } else { 144 | XXShieldStubObject *stub = [XXShieldStubObject shareInstance]; 145 | [stub addFunc:aSelector]; 146 | 147 | NSString *reason = [NSString stringWithFormat:@"*****Warning***** logic error.target is %@ method is %@, reason : method forword to SmartFunction Object default implement like send message to nil.", 148 | [self class], NSStringFromSelector(aSelector)]; 149 | [XXRecord recordFatalWithReason:reason errorType:EXXShieldTypeUnrecognizedSelector]; 150 | 151 | return stub; 152 | } 153 | } 154 | XXStaticHookEnd 155 | 156 | ``` 157 | 158 | 这里汇报了 Crash 信息,出现消息转发一般是一个 logic 错误,为必须修复的Bug,上报尤为重要。 159 | 160 | ---- 161 | 162 | # 2 KVO Crash 163 | 164 | ## 出现原因 165 | 166 | KVOCrash总结下来有以下2大类。 167 | 168 | 1. 不匹配的移除和添加关系。 169 | 2. 观察者和被观察者释放的时候没有及时断开观察者关系。 170 | 171 | 172 | ## 解决办法 173 | 174 | > 尼古拉斯赵四说过 :![赵四](https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1487130185&di=41675c8578b69177ee9172e488c803f7&imgtype=jpg&er=1&src=http%3A%2F%2Fimgsrc.baidu.com%2Fforum%2Fw%3D580%2Fsign%3Dd1ac5ff09e25bc312b5d01906edf8de7%2Fcedafc039245d6882866389aa3c27d1ed21b244b.jpg) 175 | > 对比到程序世界就是,程序世界没有什么难以解决的问题都是不可以通过抽象层次来解决的,如果有,那就两层。 176 | > 纵观程序的架构设计,计算机网络协议分层设计,操作系统内核设计等等都是如此。 177 | 178 | 问题1 : 不成对的添加观察者和移除观察者会导致 Crash,以往我们使用 KVO,观察者和被观察者都是直接交互的。这里的设计方案是我们找一个 Proxy 用来做转发, 真正的观察者是 Proxy,被观察者出现了通知信息,由 Proxy 做分发。所以 Proxy 里面要保存一个数据结构 {keypath : [observer1, observer2,...]} 。 179 | 180 | 181 | ```Objc 182 | 183 | @interface XXKVOProxy : NSObject { 184 | __unsafe_unretained NSObject *_observed; 185 | } 186 | 187 | /** 188 | {keypath : [ob1,ob2](NSHashTable)} 189 | */ 190 | @property (nonatomic, strong) NSMutableDictionary *> *kvoInfoMap; 191 | 192 | @end 193 | 194 | ``` 195 | 196 | 我们需要 Hook NSObject的 KVO 相关方法。 197 | 198 | ```Objc 199 | 200 | - (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context; 201 | 202 | - (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath; 203 | 204 | ``` 205 | 206 | 1. 在添加观察者时 207 | ![addObserver](http://ompeszjl2.bkt.clouddn.com/iOS-APP-%E8%BF%90%E8%A1%8C%E6%97%B6%E9%98%B2Crash%E5%B7%A5%E5%85%B7XXShield%E7%BB%83%E5%B0%B1//KVO-Add.png) 208 | 209 | 210 | 2. 在移除观察者时 211 | 212 | ![removeObserver](http://ompeszjl2.bkt.clouddn.com/iOS-APP-%E8%BF%90%E8%A1%8C%E6%97%B6%E9%98%B2Crash%E5%B7%A5%E5%85%B7XXShield%E7%BB%83%E5%B0%B1/KVO-Remove.png) 213 | 214 | 215 | 问题2: 观察者和被观察者释放的时候没有断开观察者关系。 216 | 对于观察者, 既然我们是自己用 Proxy 做的分发,我们自己就需要保存观察者,这里我们简单的使用 `NSHashTable` 指定指针持有策略为 `weak` 即可。 217 | 218 | 对于被观察者,我们使用 [iOS 界的毒瘤-MethodSwizzling](https://www.valiantcat.cn/index.php/2017/11/03/53.html) 219 | 一文中到的方法。我们在被观察者上绑定一个关联对象,在关联对象的 dealloc 方法中做相关操作即可。 220 | 221 | ```Objc 222 | 223 | - (void)dealloc { 224 | @autoreleasepool { 225 | NSDictionary *> *kvoinfos = self.kvoInfoMap.copy; 226 | for (NSString *keyPath in kvoinfos) { 227 | // call original IMP 228 | __xx_hook_orgin_function_removeObserver(_observed,@selector(removeObserver:forKeyPath:),self, keyPath); 229 | } 230 | } 231 | } 232 | 233 | ``` 234 | 235 | ---- 236 | 237 | # 3 Container Crash 238 | 239 | 240 | ## 出现原因 241 | 242 | 容器在任何编程语言中都尤为重要,容器是数据的载体,很多容器对容器放空值都做了容错处理。不幸的是 Objective-C 并没有,容器插入了 `nil` 就会导致 Crash,容器还有另外一个最容易 Crash 的原因就是下标越界。 243 | 244 | ## 解决办法 245 | 246 | 常见的容器有 NS(Mutable)Array , NS(Mutable)Dictionary, NSCache 等。我们需要 hook 常见的方法加入检测功能并且捕获堆栈信息上报。 247 | 248 | 例如 249 | 250 | ```Objc 251 | 252 | XXStaticHookClass(NSArray, ProtectCont, id, @selector(objectAtIndex:),(NSUInteger)index) { 253 | if (self.count == 0) { 254 | 255 | NSString *reason = [NSString stringWithFormat:@"target is %@ method is %@,reason : index %@ out of count %@ of array ", 256 | [self class], XXSEL2Str(@selector(objectAtIndex:)), @(index), @(self.count)]; 257 | [XXRecord recordFatalWithReason:reason errorType:EXXShieldTypeContainer]; 258 | return nil; 259 | } 260 | 261 | if (index >= self.count) { 262 | NSString *reason = [NSString stringWithFormat:@"target is %@ method is %@,reason : index %@ out of count %@ of array ", 263 | [self class], XXSEL2Str(@selector(objectAtIndex:)), @(index), @(self.count)]; 264 | [XXRecord recordFatalWithReason:reason errorType:EXXShieldTypeContainer]; 265 | return nil; 266 | } 267 | 268 | return XXHookOrgin(index); 269 | } 270 | XXStaticHookEnd 271 | 272 | ``` 273 | 274 | 但是需要注意的是 NSArray 是一个 Class Cluster 的抽象父类,所以我们需要 Hook 到我们真正的子类。 275 | 276 | 这里给出一个辅助方法,获取一个类的所有直接子类: 277 | 278 | ```Objc 279 | + (NSArray *)findAllOf:(Class)defaultClass { 280 | 281 | int count = objc_getClassList(NULL, 0); 282 | 283 | if (count <= 0) { 284 | 285 | @throw@"Couldn't retrieve Obj-C class-list"; 286 | 287 | return @[defaultClass]; 288 | } 289 | 290 | NSMutableArray *output = @[].mutableCopy; 291 | 292 | Class *classes = (Class *) malloc(sizeof(Class) * count); 293 | 294 | objc_getClassList(classes, count); 295 | 296 | for (int i = 0; i < count; ++i) { 297 | 298 | if (defaultClass == class_getSuperclass(classes[i]))//子类 299 | { 300 | [output addObject:classes[i]]; 301 | } 302 | 303 | } 304 | 305 | free(classes); 306 | 307 | return output.copy; 308 | 309 | } 310 | 311 | // 对于NSarray : 312 | 313 | //[NSarray array] 和 @[] 的类型是__NSArray0 314 | //只有一个元素的数组类型 __NSSingleObjectArrayI, 315 | // 其他的大部分是//__NSArrayI, 316 | 317 | 318 | 319 | // 对于NSMutableArray : 320 | //[NSMutableDictionary dictionary] 和 @[].mutableCopy__NSArrayM 321 | 322 | 323 | 324 | // 对于NSDictionary: : 325 | 326 | //[NSDictionary dictionary];。 @{}; __NSDictionary0 327 | // 其他一般是 __NSDictionaryI 328 | 329 | // 对于NSMutableDictionary: : 330 | // 一般用到的是 __NSDictionaryM 331 | ``` 332 | 333 | 334 | ---- 335 | 336 | 337 | # 4 NSNotification Crash 338 | 339 | ## 出现原因 340 | 341 | 在 iOS8 及以下的操作系统中添加的观察者一般需要在 dealloc 的时候做移除,如果开发者忘记移除,则在发送通知的时候会导致 Crash,而在 iOS9 上即使移忘记除也无所谓,猜想可能是 iOS9 之后系统将通知中心持有对象由 `assign` 变为了`weak`。 342 | 343 | 344 | ## 解决办法 345 | 346 | 所以这里两种解决办法 347 | 348 | 1. 类似 KVO 中间加上 Proxy 层,使用 weak 指针来持有对象 349 | 2. 在 dealloc 的时候将未被移除的观察者移除 350 | 351 | 这里我们使用 [iOS 界的毒瘤-MethodSwizzling](https://www.valiantcat.cn/index.php/2017/11/03/53.html) 352 | 一文中到的方法。 353 | 354 | 355 | - - - - - 356 | 357 | # 5 NSNull Crash 358 | 359 | ## 出现原因 360 | 361 | 虽然 Objecttive-C 不允许开发者将 nil 放进容器内,但是另外一个代表用户态 `空` 的类 NSNull 却可以放进容器,但令人不爽的是这个类的实例,并不能响应任何方法。 362 | 363 | 容器中出现 NSNull 一般是 API 接口返回了含有 null 的 JSON 数据, 364 | 调用方通常将其理解为 NSNumber,NSString,NSDictionary 和 NSArray。 这时开发者如果没有做好防御 一旦对 NSNull 这个类型调用任何方法都会出现 unrecongized selector 错误。 365 | 366 | 367 | 368 | ## 解决办法 369 | 370 | 我们在 NSNull 的转发方法中可以判断上面的四种类型是否可以解析。如果可以解析直接将其转发给这几种对象,如果不能则调用父类的默认实现。 371 | 372 | 373 | ```Objc 374 | 375 | XXStaticHookClass(NSNull, ProtectNull, id, @selector(forwardingTargetForSelector:), (SEL) aSelector) { 376 | static NSArray *sTmpOutput = nil; 377 | if (sTmpOutput == nil) { 378 | sTmpOutput = @[@"", @0, @[], @{}]; 379 | } 380 | 381 | for (id tmpObj in sTmpOutput) { 382 | if ([tmpObj respondsToSelector:aSelector]) { 383 | return tmpObj; 384 | } 385 | } 386 | return XXHookOrgin(aSelector); 387 | } 388 | XXStaticHookEnd 389 | 390 | ``` 391 | # 6. NSTimer Crash 392 | 393 | 394 | ## 出现原因 395 | 396 | 在使用 `+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo` 创建定时任务的时候,target 一般都会持有 timer,timer又会持有 target 对象,在我们没有正确关闭定时器的时候,timer 会一直持有target 导致内存泄漏。 397 | 398 | ## 解决办法 399 | 400 | 同 KVO 一样,既然 timer 和 target 直接交互容易出现问题,我们就再找个代理将 target 和 selctor 等信息保存到 Proxy 里,并且是弱引用 target。 401 | 这样避免因为循环引用造成的内存泄漏。然后在触发真正 target 事件的时候如果 target 置为 nil 了这时候手动去关闭定时器。 402 | 403 | ```Objc 404 | 405 | XXStaticHookMetaClass(NSTimer, ProtectTimer, NSTimer * ,@selector(scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:), 406 | (NSTimeInterval)ti , (id)aTarget, (SEL)aSelector, (id)userInfo, (BOOL)yesOrNo ) { 407 | if (yesOrNo) { 408 | NSTimer *timer = nil ; 409 | @autoreleasepool { 410 | XXTimerProxy *proxy = [XXTimerProxy new]; 411 | proxy.target = aTarget; 412 | proxy.aSelector = aSelector; 413 | timer.timerProxy = proxy; 414 | timer = XXHookOrgin(ti, proxy, @selector(trigger:), userInfo, yesOrNo); 415 | proxy.sourceTimer = timer; 416 | } 417 | return timer; 418 | } 419 | return XXHookOrgin(ti, aTarget, aSelector, userInfo, yesOrNo); 420 | } 421 | XXStaticHookEnd 422 | @implementation XXTimerProxy 423 | 424 | - (void)trigger:(id)userinfo { 425 | id strongTarget = self.target; 426 | if (strongTarget && ([strongTarget respondsToSelector:self.aSelector])) { 427 | #pragma clang diagnostic push 428 | #pragma clang diagnostic ignored "-Warc-performSelector-leaks" 429 | [strongTarget performSelector:self.aSelector withObject:userinfo]; 430 | #pragma clang diagnostic pop 431 | } else { 432 | NSTimer *sourceTimer = self.sourceTimer; 433 | if (sourceTimer) { 434 | [sourceTimer invalidate]; 435 | } 436 | NSString *reason = [NSString stringWithFormat:@"*****Warning***** logic error target is %@ method is %@, reason : an object dealloc not invalidate Timer.", 437 | [self class], NSStringFromSelector(self.aSelector)]; 438 | 439 | [XXRecord recordFatalWithReason:reason errorType:(EXXShieldTypeTimer)]; 440 | } 441 | } 442 | 443 | @end 444 | 445 | ``` 446 | 447 | # 7. 野指针 Crash 448 | 449 | ## 出现原因 450 | 451 | 一般在单线程条件下使用 ARC 正确的处理引用关系野指针出现的并不频繁, 但是多线程下则不尽然,通常在一个线程中释放了对象,另外一个线程还没有更新指针状态 后续访问就可能会造成随机性 bug。 452 | 453 | 之所以是随机 bug 是因为被回收的内存不一定立马被使用。而且崩溃的位置可能也与原来的逻辑相聚很远,因此收集的堆栈信息也可能是杂乱无章没有什么价值。 454 | 具体的分类请看Bugly整理的脑图。 455 | ![x](http://upload-images.jianshu.io/upload_images/783864-9fa6e25efbe248e8.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 456 | 457 | 更多关于野指针的文章请参考: 458 | 459 | 1. [如何定位Obj-C野指针随机Crash(一)](https://dev.qq.com/topic/59141e56ca95d00d727ba750) 460 | 1. [如何定位Obj-C野指针随机Crash(二)](https://dev.qq.com/topic/59142d61ca95d00d727ba752) 461 | 1. [如何定位Obj-C野指针随机Crash(三)](https://dev.qq.com/topic/5915134b75d11c055ca7fca0) 462 | 463 | ## 解决办法 464 | 465 | 这里我们可以借用系统的NSZombies对象的设计。 466 | 参考[buildNSZombie](https://mikeash.com/pyblog/friday-qa-2014-11-07-lets-build-nszombie.html) 467 | 468 | 469 | 解决过程 470 | 471 | 1. 建立白名单机制,由于系统的类基本不会出现野指针,而且 hook 所有的类开销较大。所以我们只过滤开发者自定义的类。 472 | 2. hook dealloc 方法 这些需要保护的类我们并不让其释放,而是调用objc_desctructInstance 方法释放实例内部所持有属性的引用和关联对象。 473 | 3. 利用 object_setClass(id,Class) 修改 isa 指针将其指向一个Proxy 对象(类比系统的 KVO 实现),此 Proxy 实现了一个和前面所说的智能转发类一样的 `return 0`的函数。 474 | 4. 在 Proxy 对象内的 `- (void)forwardInvocation:(NSInvocation *)anInvocation` 中收集 Crash 信息。 475 | 476 | 5. 缓存的对象是有成本的,我们在缓存对象到达一定数量时候将其释放(object_dispose)。 477 | 478 | ## 存在问题 479 | 480 | 1. 延迟释放内存会造成性能浪费,所以默认缓存会造成野指针的Class实例的对象限制是50,超出之后会释放,如果这时候再此触发了刚好释放掉的野指针,还是会造成Crash的, 481 | 482 | 2. 建议使用的时候如果近期没有野指针的Crash可以不必开启,如果野指针类型的Crash突然增多,可以考虑在 hot Patch 中开启野指针防护,待收取异常信息之后,再关闭此开关。 483 | 484 | - - - - 485 | 486 | # 收集信息 487 | 488 | 由于希望此库没有任何外部依赖,所以并未实现响应的上报逻辑。使用者如果需要上报信息 只需要自行实现 `XXRecordProtocol` 即可,然后在开启 SDK 之前将其注册进入 SDK。 489 | 在实现方法里面会接收到 XXShield 内部定义的错误信息。 490 | 开发者无论可以使用诸如 CrashLytics,友盟, bugly等第三库,或者自行 dump堆栈信息都可。 491 | 492 | ```objc 493 | @protocol XXRecordProtocol 494 | 495 | - (void)recordWithReason:(NSError * )reason userInfo:(NSDictionary *)userInfo; 496 | 497 | @end 498 | ``` 499 | 500 | 501 | 502 | 503 | # 使用方法 504 | 505 | ## 示例工程 506 | 507 | ```sh 508 | 509 | git clone git@github.com:ValiantCat/XXShield.git 510 | cd Example 511 | pod install 512 | open XXShield.xcworkspace 513 | 514 | ``` 515 | 516 | 517 | ## Install 518 | 519 | ```ruby 520 | 521 | pod "XXShield" 522 | 523 | ``` 524 | 525 | ## Usage 526 | 527 | ```Objc 528 | 529 | /** 530 | 注册汇报中心 531 | 532 | @param record 汇报中心 533 | */ 534 | + (void)registerRecordHandler:(id)record; 535 | 536 | /** 537 | 注册SDK,默认只要开启就打开防Crash,如果需要DEBUG关闭,请在调用处使用条件编译 538 | 本注册方式不包含EXXShieldTypeDangLingPointer类型 539 | */ 540 | + (void)registerStabilitySDK; 541 | 542 | /** 543 | 本注册方式不包含EXXShieldTypeDangLingPointer类型 544 | 545 | @param ability ability 546 | */ 547 | + (void)registerStabilityWithAbility:(EXXShieldType)ability; 548 | 549 | /** 550 | ///注册EXXShieldTypeDangLingPointer需要传入存储类名的array,暂时请不要传入系统框架类 551 | 552 | @param ability ability description 553 | @param classNames 野指针类列表 554 | */ 555 | + (void)registerStabilityWithAbility:(EXXShieldType)ability withClassNames:(nonnull NSArray *)classNames; 556 | 557 | 558 | ``` 559 | 560 | ## ChangeLog 561 | 562 | [ChangeLog](https://github.com/ValiantCat/XXShield/blob/develop/Changelog.md) 563 | 564 | ## 单元测试 565 | 566 | 相关的单元测试在示例工程的Test Target下,有兴趣的开发者可以自行查看。并且已经接入 [TrivisCI](https://travis-ci.org/ValiantCat/XXShield)保证了代码质量。 567 | 568 | ## Bug&Feature 569 | 570 | 如果有相关的 Bug 请提 [Issue](https://github.com/ValiantCat/XXShield/issues)。 571 | 572 | 如果觉得可以扩充新的防护类型,请提 [PR](https://github.com/ValiantCat/XXShield/pulls) 给我。 573 | 574 | ## 作者 575 | 576 | ValiantCat, 519224747@qq.com 577 | 578 | [个人博客](https://www.valiantcat.cn/index.php/2017/11/03/53.html) 579 | 580 | [南栀倾寒的简书](http://www.jianshu.com/u/cc1e4faec5f7) 581 | 582 | 583 | 584 | # License 585 | 586 | XXShield 使用 Apache-2.0 开源协议. 587 | 588 | -------------------------------------------------------------------------------- /XXShield.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'XXShield' 3 | s.version = '2.3.1' 4 | s.summary = 'Avoid Crash' 5 | s.description = <<-DESC 6 | 1. unrecoginzed Selector Crash 7 | 2. KVO Crash 8 | 3. Container Crash 9 | 4. NSNotification Crash 10 | 5. NSNull Crash 11 | 6. NSTimer Crash 12 | 7. 野指针 Crash 13 | DESC 14 | s.homepage = 'https://github.com/ValiantCat/XXShield' 15 | s.license = { :type => 'Apache', :file => 'LICENSE' } 16 | s.author = { 'ValiantCat' => '519224747@qq.com' } 17 | s.source = { :git => 'https://github.com/ValiantCat/XXShield.git', :tag => s.version.to_s } 18 | s.platform = :ios, '9.0' 19 | s.ios.deployment_target = '9.0' 20 | s.module_map = 'XXShield/XXShield.modulemap' 21 | s.public_header_files = 'XXShield/Classes/*.h' 22 | s.private_header_files = 'XXShield/Classes/template/*.h' 23 | s.source_files = "XXShield/Classes/*/*.{h,m,mm}", "XXShield/Classes/*.{h,m,mm}" 24 | s.requires_arc = 25 | ['XXShield/Classes/*.m', 26 | 'XXShield/Classes/FoundationContainer/*.m', 27 | 'XXShield/Classes/KVO/*.m', 28 | 'XXShield/Classes/NSTimer/*.m', 29 | 'XXShield/Classes/Notification/*.m', 30 | 'XXShield/Classes/NSNull/*.m', 31 | 'XXShield/Classes/Record/*.m', 32 | 'XXShield/Classes/SmartKit/*.m', 33 | 'XXShield/Classes/Swizzle/*.m', 34 | 'XXShield/Classes/DanglingPointerShield/ForwordingCenterForDanglingPoint.m', 35 | 'XXShield/Classes/DanglingPointerShield/XXDanglingPonterClassService.m' 36 | ] 37 | s.libraries = 'c++' 38 | s.pod_target_xcconfig = { 39 | 'CLANG_WARN_STRICT_PROTOTYPES' => 'NO', 40 | 'DEFINES_MODULE' => 'YES' 41 | # 'SWIFT_VERSION' => '' 42 | } 43 | 44 | end 45 | -------------------------------------------------------------------------------- /XXShield/Assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ValiantCat/XXShield/c6c36974cd29a41ae0482606ebc905fad2698d31/XXShield/Assets/.gitkeep -------------------------------------------------------------------------------- /XXShield/Classes/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ValiantCat/XXShield/c6c36974cd29a41ae0482606ebc905fad2698d31/XXShield/Classes/.gitkeep -------------------------------------------------------------------------------- /XXShield/Classes/DanglingPointerShield/NSObject+DanglingPointer.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+DanglingPointer.h 3 | // XXShield 4 | // 5 | // Created by nero on 2017/1/18. 6 | // Copyright © 2017年 XXShield. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSObject (DanglingPointer) 12 | 13 | - (void)xx_danglingPointer_dealloc; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /XXShield/Classes/DanglingPointerShield/NSObject+DanglingPointer.mm: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+DanglingPointer.h 3 | // XXShield 4 | // 5 | // Created by nero on 2017/1/18. 6 | // Copyright © 2017年 XXShield. All rights reserved. 7 | // 8 | 9 | 10 | #import 11 | #import "NSObject+DanglingPointer.h" 12 | #import "XXDanglingPointStub.h" 13 | #import "XXDanglingPonterService.h" 14 | #import 15 | 16 | static NSInteger const threshold = 100; 17 | 18 | static std::list undellocedList; 19 | 20 | @implementation NSObject (DanglingPointer) 21 | 22 | - (void)xx_danglingPointer_dealloc { 23 | Class selfClazz = object_getClass(self); 24 | 25 | BOOL needProtect = NO; 26 | for (NSString *className in [XXDanglingPonterService getInstance].classArr) { 27 | Class clazz = objc_getClass([className UTF8String]); 28 | if (clazz == selfClazz) { 29 | needProtect = YES; 30 | break; 31 | } 32 | } 33 | 34 | if (needProtect) { 35 | objc_destructInstance(self); 36 | object_setClass(self, [XXDanglingPointStub class]); 37 | 38 | undellocedList.size(); 39 | if (undellocedList.size() >= threshold) { 40 | id object = undellocedList.front(); 41 | undellocedList.pop_front(); 42 | free(object); 43 | } 44 | undellocedList.push_back(self); 45 | } else { 46 | [self xx_danglingPointer_dealloc]; 47 | } 48 | } 49 | 50 | @end 51 | -------------------------------------------------------------------------------- /XXShield/Classes/DanglingPointerShield/XXDanglingPointStub.h: -------------------------------------------------------------------------------- 1 | // 2 | // ForwordingCenterForDanglingPoint.h 3 | // XXShield 4 | // 5 | // Created by nero on 2017/1/18. 6 | // Copyright © 2017年 XXShield. All rights reserved. 7 | // 8 | 9 | 10 | #import 11 | 12 | @interface XXDanglingPointStub : NSProxy 13 | 14 | - (instancetype)init; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /XXShield/Classes/DanglingPointerShield/XXDanglingPointStub.m: -------------------------------------------------------------------------------- 1 | // 2 | // ForwordingCenterForDanglingPoint.m 3 | // XXShield 4 | // 5 | // Created by nero on 2017/1/18. 6 | // Copyright © 2017年 XXShield. All rights reserved. 7 | // 8 | 9 | #import "XXDanglingPointStub.h" 10 | #import 11 | #import "XXShieldStubObject.h" 12 | #import "XXRecord.h" 13 | 14 | @implementation XXDanglingPointStub 15 | 16 | - (instancetype)init { 17 | return self; 18 | } 19 | 20 | - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector { 21 | XXShieldStubObject *stub = [XXShieldStubObject shareInstance]; 22 | [stub addFunc:aSelector]; 23 | 24 | return [[XXShieldStubObject class] instanceMethodSignatureForSelector:aSelector]; 25 | } 26 | 27 | - (void)forwardInvocation:(NSInvocation *)anInvocation { 28 | NSString *reason = [NSString stringWithFormat:@"target is %@ method is %@, reason : DangLingPointer .", 29 | [self class], NSStringFromSelector(_cmd)]; 30 | 31 | [XXRecord recordFatalWithReason:reason errorType:(EXXShieldTypeDangLingPointer)]; 32 | [anInvocation invokeWithTarget:[XXShieldStubObject shareInstance]]; 33 | } 34 | 35 | @end 36 | 37 | -------------------------------------------------------------------------------- /XXShield/Classes/DanglingPointerShield/XXDanglingPonterService.h: -------------------------------------------------------------------------------- 1 | // 2 | // XXDanglingPonterClassService.h 3 | // XXShield 4 | // 5 | // Created by nero on 2017/1/18. 6 | // Copyright © 2017年 XXShield. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface XXDanglingPonterService : NSObject 12 | 13 | @property (nonatomic, copy) NSArray *classArr; 14 | 15 | + (instancetype)getInstance; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /XXShield/Classes/DanglingPointerShield/XXDanglingPonterService.m: -------------------------------------------------------------------------------- 1 | // 2 | // XXDanglingPonterClassService.m 3 | // XXShield 4 | // 5 | // Created by nero on 2017/1/18. 6 | // Copyright © 2017年 XXShield. All rights reserved. 7 | // 8 | 9 | #import "XXDanglingPonterService.h" 10 | #import 11 | 12 | @implementation XXDanglingPonterService 13 | 14 | + (instancetype)getInstance { 15 | static XXDanglingPonterService *service = nil; 16 | 17 | static dispatch_once_t onceToken; 18 | dispatch_once(&onceToken, ^{ 19 | service = [[XXDanglingPonterService alloc] init]; 20 | }); 21 | return service; 22 | } 23 | 24 | - (instancetype)init { 25 | self = [super init]; 26 | if (self) { 27 | _classArr = @[]; 28 | } 29 | return self; 30 | } 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /XXShield/Classes/FoundationContainer/NSArray+Shield.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSArray+Shield.m 3 | // XXShield 4 | // 5 | // Created by nero on 2017/1/19. 6 | // Copyright © 2017年 XXShield. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "XXShieldSwizzling.h" 11 | #import "XXRecord.h" 12 | 13 | //NSArray *arrayClasses = @[@"__NSSingleObjectArrayI",@"__NSArrayI",@"__NSArray0"]; 14 | 15 | /** 16 | hook @selector(objectAtIndex:) 17 | 18 | @param 19 | @param NSArray 20 | @param ProtectCont 21 | @param id 22 | @param objectAtIndex: 23 | @return safe 24 | */ 25 | 26 | XXStaticHookClass(NSArray, ProtectCont, id, @selector(objectAtIndex:), (NSUInteger)index) { 27 | #include "NSArrayObjectAtIndex.h" 28 | } 29 | XXStaticHookEnd 30 | 31 | XXStaticHookPrivateClass(__NSSingleObjectArrayI, NSArray *, ProtectCont, id, @selector(objectAtIndex:), (NSUInteger)index) { 32 | #include "NSArrayObjectAtIndex.h" 33 | } 34 | XXStaticHookEnd 35 | 36 | XXStaticHookPrivateClass(__NSArrayI, NSArray *, ProtectCont, id, @selector(objectAtIndexedSubscript:), (NSUInteger)index) { 37 | #include "NSArrayObjectAtIndex.h" 38 | } 39 | XXStaticHookEnd 40 | 41 | XXStaticHookPrivateClass(__NSArray0, NSArray *, ProtectCont, id, @selector(objectAtIndex:), (NSUInteger)index) { 42 | #include "NSArrayObjectAtIndex.h" 43 | } 44 | XXStaticHookEnd 45 | 46 | 47 | //NSArray *arrayClasses = @[@"__NSSingleObjectArrayI",@"__NSArrayI",@"__NSArray0"]; 48 | 49 | /** 50 | hook @selector(arrayWithObjects:count:), 51 | 52 | @param 53 | @param NSArray 54 | @param ProtectCont 55 | @param id 56 | @param objectAtIndex: 57 | @return safe 58 | */ 59 | 60 | XXStaticHookPrivateMetaClass(__NSSingleObjectArrayI, NSArray *, ProtectCont, NSArray *, 61 | @selector(arrayWithObjects:count:), (const id *)objects, (NSUInteger)cnt) { 62 | #include "NSArrayWithObjects.h" 63 | } 64 | XXStaticHookEnd 65 | 66 | XXStaticHookPrivateMetaClass(__NSArrayI, NSArray *, ProtectCont, NSArray *, @selector(arrayWithObjects:count:), 67 | (const id *)objects, (NSUInteger)cnt) { 68 | #include "NSArrayWithObjects.h" 69 | } 70 | XXStaticHookEnd 71 | 72 | XXStaticHookPrivateMetaClass(__NSArray0, NSArray *, ProtectCont, NSArray *, @selector(arrayWithObjects:count:), 73 | (const id *)objects, (NSUInteger)cnt) { 74 | #include "NSArrayWithObjects.h" 75 | } 76 | XXStaticHookEnd 77 | 78 | XXStaticHookPrivateClass(__NSPlaceholderArray, NSArray *, ProtectCont, NSArray *, @selector(initWithObjects:count:), 79 | (const id *)objects, (NSUInteger)cnt) { 80 | #include "NSArrayWithObjects.h" 81 | } 82 | XXStaticHookEnd 83 | -------------------------------------------------------------------------------- /XXShield/Classes/FoundationContainer/NSCache+Shield.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSCache+Shield.m 3 | // Shield 4 | // 5 | // Created by nero on 2017/1/19. 6 | // Copyright © 2017年 XXShield. All rights reserved. 7 | // 8 | 9 | #import "XXShieldSwizzling.h" 10 | #import "XXRecord.h" 11 | 12 | 13 | XXStaticHookClass(NSCache, ProtectCont, void, @selector(setObject:forKey:), (id)obj, (id)key) { 14 | if (obj && key) { 15 | XXHookOrgin(obj,key); 16 | } else { 17 | NSString *reason = [NSString stringWithFormat:@"target is %@ method is %@, reason : key or value appear nil- key is %@, obj is %@", 18 | [self class], XXSEL2Str(@selector(setObject:forKey:)),key, obj]; 19 | [XXRecord recordFatalWithReason:reason errorType:(EXXShieldTypeContainer)]; 20 | } 21 | } 22 | XXStaticHookEnd 23 | 24 | XXStaticHookClass(NSCache, ProtectCont, void, @selector(setObject:forKey:cost:), (id)obj, (id)key, (NSUInteger)g) { 25 | if (obj && key) { 26 | XXHookOrgin(obj,key,g); 27 | } else { 28 | NSString *reason = [NSString stringWithFormat:@"target is %@ method is %@, reason : key or value appear nil- key is %@, obj is %@", 29 | [self class], XXSEL2Str(@selector(setObject:forKey:cost:)), key, obj]; 30 | [XXRecord recordFatalWithReason:reason errorType:(EXXShieldTypeContainer)]; 31 | } 32 | } 33 | XXStaticHookEnd 34 | 35 | -------------------------------------------------------------------------------- /XXShield/Classes/FoundationContainer/NSDictionary+Shield.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSDictionary+Shield.m 3 | // XXShield 4 | // 5 | // Created by nero on 2017/1/19. 6 | // Copyright © 2017年 XXShield. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "XXRecord.h" 11 | #import "XXShieldSwizzling.h" 12 | 13 | XXStaticHookMetaClass(NSDictionary, ProtectCont, NSDictionary *, @selector(dictionaryWithObjects:forKeys:count:), 14 | (const id *) objects, (const id *) keys, (NSUInteger)cnt) { 15 | NSUInteger index = 0; 16 | id _Nonnull __unsafe_unretained newObjects[cnt]; 17 | id _Nonnull __unsafe_unretained newkeys[cnt]; 18 | for (int i = 0; i < cnt; i++) { 19 | id tmpItem = objects[i]; 20 | id tmpKey = keys[i]; 21 | if (tmpItem == nil || tmpKey == nil) { 22 | NSString *reason = [NSString stringWithFormat:@"target is %@ method is %@, reason : NSDictionary constructor appear nil", 23 | [self class], XXSEL2Str(@selector(dictionaryWithObjects:forKeys:count:))]; 24 | [XXRecord recordFatalWithReason:reason errorType:EXXShieldTypeContainer]; 25 | continue; 26 | } 27 | newObjects[index] = objects[i]; 28 | newkeys[index] = keys[i]; 29 | index++; 30 | } 31 | 32 | return XXHookOrgin(newObjects, newkeys,index); 33 | } 34 | XXStaticHookEnd 35 | 36 | -------------------------------------------------------------------------------- /XXShield/Classes/FoundationContainer/NSMutableArray+Shield.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSMutableArray+Shield.m 3 | // XXShield 4 | // 5 | // Created by nero on 2017/1/19. 6 | // Copyright © 2017年 XXShield. All rights reserved. 7 | // 8 | 9 | #import "XXRecord.h" 10 | #import "XXShieldSwizzling.h" 11 | 12 | XXStaticHookPrivateClass(__NSArrayM, NSMutableArray *, ProtectCont, id, @selector(objectAtIndexedSubscript:), (NSUInteger)index) { 13 | if (index >= self.count) { 14 | NSString *reason = [NSString stringWithFormat:@"target is %@ method is %@,reason : index %@ out of count %@ of marray ", 15 | [self class], XXSEL2Str(@selector(objectAtIndex:)),@(index), @(self.count)]; 16 | [XXRecord recordFatalWithReason:reason errorType:(EXXShieldTypeContainer)]; 17 | return nil; 18 | } 19 | 20 | return XXHookOrgin(index); 21 | } 22 | XXStaticHookEnd 23 | 24 | XXStaticHookPrivateClass(__NSArrayM, NSMutableArray *, ProtectCont, void, @selector(addObject:), (id)anObject ) { 25 | if (anObject) { 26 | XXHookOrgin(anObject); 27 | } 28 | } 29 | XXStaticHookEnd 30 | 31 | XXStaticHookPrivateClass(__NSArrayM, NSMutableArray *, ProtectCont, void, @selector(insertObject:atIndex:), (id)anObject, (NSUInteger)index) { 32 | if (anObject) { 33 | XXHookOrgin(anObject, index); 34 | } 35 | } 36 | XXStaticHookEnd 37 | 38 | XXStaticHookPrivateClass(__NSArrayM,NSMutableArray *, ProtectCont, void, @selector(removeObjectAtIndex:), (NSUInteger)index) { 39 | if (index >= self.count) { 40 | NSString *reason = [NSString stringWithFormat:@"target is %@ method is %@,reason : index %@ out of count %@ of marray ", 41 | [self class], XXSEL2Str(@selector(removeObjectAtIndex:)) ,@(index), @(self.count)]; 42 | 43 | [XXRecord recordFatalWithReason:reason errorType:(EXXShieldTypeContainer)]; 44 | } else { 45 | XXHookOrgin(index); 46 | } 47 | } 48 | XXStaticHookEnd 49 | 50 | XXStaticHookPrivateClass(__NSArrayM, NSMutableArray *, ProtectCont, void, @selector(setObject:atIndexedSubscript:), (id) obj, (NSUInteger) idx) { 51 | if (obj) { 52 | if (idx > self.count) { 53 | NSString *reason = [NSString stringWithFormat:@"target is %@ method is %@,reason : index %@ out of count %@ of marray ", 54 | [self class], XXSEL2Str(@selector(setObject:atIndexedSubscript:)) ,@(idx), @(self.count)]; 55 | [XXRecord recordFatalWithReason:reason errorType:(EXXShieldTypeContainer)]; 56 | } else { 57 | XXHookOrgin(obj, idx); 58 | } 59 | } else { 60 | NSString *reason = [NSString stringWithFormat:@"target is %@ method is %@, reason : object appear nil obj is %@", 61 | [self class], XXSEL2Str(@selector(setObject:atIndexedSubscript:)), obj]; 62 | [XXRecord recordFatalWithReason:reason errorType:(EXXShieldTypeContainer)]; 63 | } 64 | } 65 | XXStaticHookEnd 66 | 67 | -------------------------------------------------------------------------------- /XXShield/Classes/FoundationContainer/NSMutableDictionary+Shield.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSMutableDictionary+Shield.m 3 | // XXShield 4 | // 5 | // Created by nero on 2017/1/19. 6 | // Copyright © 2017年 XXShield. All rights reserved. 7 | // 8 | 9 | #import "XXRecord.h" 10 | #import "XXShieldSwizzling.h" 11 | 12 | XXStaticHookPrivateClass(__NSDictionaryM, NSMutableDictionary *, ProtectCont, void, @selector(setObject:forKey:), (id)anObject, (id)aKey ) { 13 | if (anObject && aKey) { 14 | XXHookOrgin(anObject,aKey); 15 | } else { 16 | NSString *reason = [NSString stringWithFormat:@"target is %@ method is %@, reason : key or value appear nil- key is %@, obj is %@", 17 | [self class], XXSEL2Str(@selector(setObject:forKey:)),aKey, anObject]; 18 | 19 | [XXRecord recordFatalWithReason:reason errorType:EXXShieldTypeContainer]; 20 | } 21 | 22 | } 23 | XXStaticHookEnd 24 | 25 | XXStaticHookPrivateClass(__NSDictionaryM, NSMutableDictionary *, ProtectCont, void, @selector(setObject:forKeyedSubscript:), (id)anObject, (id)aKey ) { 26 | if (anObject && aKey) { 27 | XXHookOrgin(anObject,aKey); 28 | } else { 29 | NSString *reason = [NSString stringWithFormat:@"target is %@ method is %@, reason : key or value appear nil- key is %@, obj is %@", 30 | [self class], XXSEL2Str(@selector(setObject:forKeyedSubscript:)),aKey, anObject]; 31 | 32 | [XXRecord recordFatalWithReason:reason errorType:EXXShieldTypeContainer]; 33 | } 34 | } 35 | XXStaticHookEnd 36 | 37 | XXStaticHookPrivateClass(__NSDictionaryM, NSMutableDictionary *, ProtectCont, void, @selector(removeObjectForKey:), (id)aKey ) { 38 | if (aKey) { 39 | XXHookOrgin(aKey); 40 | } else { 41 | NSString *reason = [NSString stringWithFormat:@"target is %@ method is %@, reason : key appear nil- key is %@.", 42 | [self class], XXSEL2Str(@selector(setObject:forKey:)),aKey]; 43 | 44 | [XXRecord recordFatalWithReason:reason errorType:EXXShieldTypeContainer]; 45 | } 46 | 47 | } 48 | XXStaticHookEnd 49 | -------------------------------------------------------------------------------- /XXShield/Classes/KVO/NSObject+KVOShield.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+KVOShield.m 3 | // XXShield 4 | // 5 | // Created by nero on 2017/2/7. 6 | // Copyright © 2017年 XXShield. All rights reserved. 7 | // 8 | 9 | #define KVOADDIgnoreMarco() \ 10 | autoreleasepool {} \ 11 | if (object_getClass(observer) == objc_getClass("RACKVOProxy") ) { \ 12 | XXHookOrgin(observer, keyPath, options, context); \ 13 | return; \ 14 | } 15 | 16 | 17 | #define KVORemoveIgnoreMarco() \ 18 | autoreleasepool {} \ 19 | if (object_getClass(observer) == objc_getClass("RACKVOProxy") ) { \ 20 | XXHookOrgin(observer, keyPath);\ 21 | return; \ 22 | } 23 | 24 | #import "XXShieldSwizzling.h" 25 | #import 26 | #import "XXRecord.h" 27 | 28 | #import 29 | 30 | static void(*__xx_hook_orgin_function_removeObserver)(NSObject* self, SEL _cmd ,NSObject *observer ,NSString *keyPath) = ((void*)0); 31 | 32 | @interface XXKVOProxy : NSObject { 33 | __unsafe_unretained NSObject *_observed; 34 | pthread_mutex_t _mutex; 35 | } 36 | 37 | /** 38 | {keypath : [ob1,ob2](NSHashTable)} 39 | */ 40 | @property (nonatomic, strong) NSMutableDictionary *> *kvoInfoMap; 41 | 42 | @end 43 | 44 | @implementation XXKVOProxy 45 | 46 | + (instancetype)proxyWithObserverd:(NSObject *)observed { 47 | static dispatch_once_t onceToken; 48 | static NSMapTable *proxyTable; 49 | static dispatch_semaphore_t lock; 50 | 51 | dispatch_once(&onceToken, ^{ 52 | proxyTable = [NSMapTable weakToWeakObjectsMapTable]; 53 | lock = dispatch_semaphore_create(1); 54 | }); 55 | XXKVOProxy *proxy = nil; 56 | dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER); 57 | proxy = [proxyTable objectForKey:observed]; 58 | if (proxy == nil) { 59 | proxy = [[XXKVOProxy alloc] initWithObserverd:observed]; 60 | [proxyTable setObject:proxy forKey:observed]; 61 | } 62 | dispatch_semaphore_signal(lock); 63 | return proxy; 64 | } 65 | 66 | - (instancetype)initWithObserverd:(NSObject *)observed { 67 | if (self = [super init]) { 68 | _observed = observed; 69 | _kvoInfoMap = [NSMutableDictionary dictionary]; 70 | 71 | pthread_mutexattr_t mta; 72 | pthread_mutexattr_init(&mta); 73 | pthread_mutexattr_settype(&mta, PTHREAD_MUTEX_RECURSIVE); 74 | pthread_mutex_init(&_mutex, &mta); 75 | pthread_mutexattr_destroy(&mta); 76 | } 77 | return self; 78 | } 79 | 80 | - (void)dealloc { 81 | @autoreleasepool { 82 | NSDictionary *> *kvoinfos = _kvoInfoMap.copy; 83 | for (NSString *keyPath in kvoinfos) { 84 | // call original IMP 85 | __xx_hook_orgin_function_removeObserver(_observed,@selector(removeObserver:forKeyPath:),self, keyPath); 86 | } 87 | } 88 | pthread_mutex_destroy(&_mutex); 89 | } 90 | 91 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { 92 | // dispatch to origina observers 93 | [self lock:^(NSMutableDictionary *> *kvoInfoMap) { 94 | NSHashTable *os = [kvoInfoMap[keyPath] copy]; 95 | for (NSObject *observer in os) { 96 | @try { 97 | [observer observeValueForKeyPath:keyPath ofObject:object change:change context:context]; 98 | } @catch (NSException *exception) { 99 | NSString *reason = [NSString stringWithFormat:@"non fatal Error%@",[exception description]]; 100 | [XXRecord recordFatalWithReason:reason errorType:(EXXShieldTypeKVO)]; 101 | } 102 | } 103 | }]; 104 | } 105 | 106 | - (void)lock:(void (^)(NSMutableDictionary *> *kvoInfoMap))blk { 107 | if (blk) { 108 | pthread_mutex_lock(&_mutex); 109 | blk(_kvoInfoMap); 110 | pthread_mutex_unlock(&_mutex); 111 | } 112 | } 113 | 114 | @end 115 | 116 | #pragma mark - KVOStabilityProperty 117 | 118 | @interface NSObject (KVOShieldProperty) 119 | 120 | @property (nonatomic, strong) XXKVOProxy *kvoProxy; 121 | 122 | @end 123 | 124 | @implementation NSObject (KVOShield) 125 | 126 | - (void)setKvoProxy:(XXKVOProxy *)kvoProxy { 127 | objc_setAssociatedObject(self, @selector(kvoProxy), kvoProxy, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 128 | } 129 | 130 | - (XXKVOProxy *)kvoProxy { 131 | return objc_getAssociatedObject(self, @selector(kvoProxy)); 132 | } 133 | 134 | @end 135 | 136 | #pragma mark - hook KVO 137 | 138 | XXStaticHookClass(NSObject, ProtectKVO, void, @selector(addObserver:forKeyPath:options:context:), 139 | (NSObject *)observer, (NSString *)keyPath,(NSKeyValueObservingOptions)options, (void *)context) { 140 | @KVOADDIgnoreMarco() 141 | if (!self.kvoProxy) { 142 | @autoreleasepool { 143 | self.kvoProxy = [XXKVOProxy proxyWithObserverd:self]; 144 | } 145 | } 146 | 147 | __block BOOL contained = NO; 148 | [self.kvoProxy lock:^(NSMutableDictionary *> *kvoInfoMap) { 149 | BOOL shouldHookOrigin = NO; 150 | NSHashTable *os = kvoInfoMap[keyPath]; 151 | if (os.count == 0) { 152 | os = [[NSHashTable alloc] initWithOptions:(NSPointerFunctionsWeakMemory) capacity:0]; 153 | kvoInfoMap[keyPath] = os; 154 | shouldHookOrigin = YES; 155 | } 156 | if ([os containsObject:observer]) { 157 | contained = YES; 158 | } 159 | else { 160 | [os addObject:observer]; 161 | } 162 | if (shouldHookOrigin) { 163 | // hook origin if needed after kvoInfoMap updated 164 | XXHookOrgin(self.kvoProxy, keyPath, options, context); 165 | } 166 | }]; 167 | if (contained) { 168 | NSString *reason = [NSString stringWithFormat:@"target is %@ method is %@, reason : KVO add Observer to many timers.", 169 | [self class], XXSEL2Str(@selector(addObserver:forKeyPath:options:context:))]; 170 | 171 | [XXRecord recordFatalWithReason:reason errorType:(EXXShieldTypeKVO)]; 172 | } 173 | } 174 | XXStaticHookEnd 175 | 176 | XXStaticHookClass(NSObject, ProtectKVO, void, @selector(removeObserver:forKeyPath:), 177 | (NSObject *)observer, (NSString *)keyPath) { 178 | @KVORemoveIgnoreMarco() 179 | __block BOOL removed = NO; 180 | [self.kvoProxy lock:^(NSMutableDictionary *> *kvoInfoMap) { 181 | NSHashTable *os = kvoInfoMap[keyPath]; 182 | 183 | if (os.count == 0) { 184 | removed = YES; 185 | return; 186 | } 187 | 188 | [os removeObject:observer]; 189 | 190 | if (os.count == 0) { 191 | [kvoInfoMap removeObjectForKey:keyPath]; 192 | XXHookOrgin(self.kvoProxy, keyPath); 193 | } 194 | }]; 195 | if (removed) { 196 | NSString *reason = [NSString stringWithFormat:@"target is %@ method is %@, reason : KVO remove Observer to many times.", 197 | [self class], XXSEL2Str(@selector(removeObserver:forKeyPath:))]; 198 | [XXRecord recordFatalWithReason:reason errorType:(EXXShieldTypeKVO)]; 199 | } 200 | } 201 | XXStaticHookEnd_SaveOri(__xx_hook_orgin_function_removeObserver) 202 | -------------------------------------------------------------------------------- /XXShield/Classes/NSNull/NSNull+Shield.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSNull+Shield.m 3 | // XXShield 4 | // 5 | // Created by nero on 2017/1/19. 6 | // Copyright © 2017年 XXShield. All rights reserved. 7 | // 8 | 9 | #import "XXShieldSwizzling.h" 10 | 11 | XXStaticHookClass(NSNull, ProtectNull, id, @selector(forwardingTargetForSelector:), (SEL) aSelector) { 12 | static NSArray *sTmpOutput = nil; 13 | if (sTmpOutput == nil) { 14 | sTmpOutput = @[@"", @0, @[], @{}]; 15 | } 16 | 17 | for (id tmpObj in sTmpOutput) { 18 | if ([tmpObj respondsToSelector:aSelector]) { 19 | return tmpObj; 20 | } 21 | } 22 | return XXHookOrgin(aSelector); 23 | } 24 | XXStaticHookEnd 25 | 26 | -------------------------------------------------------------------------------- /XXShield/Classes/NSTimer/NSTimer+Shield.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSTimer+Shield.m 3 | // XXShield 4 | // 5 | // Created by nero on 2017/2/8. 6 | // Copyright © 2017年 XXShield. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "XXRecord.h" 11 | #import "XXShieldSwizzling.h" 12 | 13 | @interface XXTimerProxy : NSObject 14 | 15 | @property (nonatomic, weak) NSTimer *sourceTimer; 16 | @property (nonatomic, weak) id target; 17 | @property (nonatomic) SEL aSelector; 18 | 19 | @end 20 | 21 | @implementation XXTimerProxy 22 | 23 | - (void)trigger:(id)userinfo { 24 | id strongTarget = self.target; 25 | if (strongTarget && ([strongTarget respondsToSelector:self.aSelector])) { 26 | #pragma clang diagnostic push 27 | #pragma clang diagnostic ignored "-Warc-performSelector-leaks" 28 | [strongTarget performSelector:self.aSelector withObject:userinfo]; 29 | #pragma clang diagnostic pop 30 | } else { 31 | NSTimer *sourceTimer = self.sourceTimer; 32 | if (sourceTimer) { 33 | [sourceTimer invalidate]; 34 | } 35 | NSString *reason = [NSString stringWithFormat:@"*****Warning***** logic error target is %@ method is %@, reason : an object dealloc not invalidate Timer.",[self class], NSStringFromSelector(self.aSelector)]; 36 | 37 | [XXRecord recordFatalWithReason:reason errorType:(EXXShieldTypeTimer)]; 38 | } 39 | } 40 | 41 | @end 42 | 43 | @interface NSTimer (ShieldProperty) 44 | 45 | @property (nonatomic, strong) XXTimerProxy *timerProxy; 46 | 47 | @end 48 | 49 | @implementation NSTimer (Shield) 50 | 51 | - (void)setTimerProxy:(XXTimerProxy *)timerProxy { 52 | objc_setAssociatedObject(self, @selector(timerProxy), timerProxy, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 53 | } 54 | 55 | - (XXTimerProxy *)timerProxy { 56 | return objc_getAssociatedObject(self, @selector(timerProxy)); 57 | } 58 | 59 | @end 60 | 61 | XXStaticHookMetaClass(NSTimer, ProtectTimer, NSTimer * ,@selector(scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:), 62 | (NSTimeInterval)ti , (id)aTarget, (SEL)aSelector, (id)userInfo, (BOOL)yesOrNo ) { 63 | if (yesOrNo) { 64 | NSTimer *timer = nil ; 65 | @autoreleasepool { 66 | XXTimerProxy *proxy = [XXTimerProxy new]; 67 | proxy.target = aTarget; 68 | proxy.aSelector = aSelector; 69 | timer.timerProxy = proxy; 70 | timer = XXHookOrgin(ti, proxy, @selector(trigger:), userInfo, yesOrNo); 71 | proxy.sourceTimer = timer; 72 | } 73 | return timer; 74 | } 75 | return XXHookOrgin(ti, aTarget, aSelector, userInfo, yesOrNo); 76 | } 77 | XXStaticHookEnd 78 | -------------------------------------------------------------------------------- /XXShield/Classes/Notification/NSNotificationCenter+Shield.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSNotificationCenter+Stability.m 3 | // XXShield 4 | // 5 | // Created by nero on 2017/2/8. 6 | // Copyright © 2017年 XXShield. All rights reserved. 7 | // 8 | 9 | #import "XXShieldSwizzling.h" 10 | 11 | @interface XXObserverRemover : NSObject 12 | 13 | @end 14 | 15 | @implementation XXObserverRemover { 16 | __strong NSMutableArray *_centers; 17 | __unsafe_unretained id _obs; 18 | } 19 | 20 | - (instancetype)initWithObserver:(id)obs { 21 | if (self = [super init]) { 22 | _obs = obs; 23 | _centers = @[].mutableCopy; 24 | } 25 | return self; 26 | } 27 | 28 | - (void)addCenter:(NSNotificationCenter*)center { 29 | if (center) { 30 | [_centers addObject:center]; 31 | } 32 | } 33 | 34 | - (void)dealloc { 35 | @autoreleasepool { 36 | for (NSNotificationCenter *center in _centers) { 37 | [center removeObserver:_obs]; 38 | } 39 | } 40 | } 41 | 42 | @end 43 | 44 | // why autorelasePool 45 | // an instance life dead 46 | // *********************1 release property 47 | // *********************2 remove AssociatedObject 48 | // *********************3 destory self 49 | // Once u want use assobciedObject's dealloc method do something , 50 | // must in a custome autorelease Pool let associate 51 | // release immediately. 52 | // AssociatedObject retain source target strategy must be __unsafe_unretain. (weak will be nil ) 53 | void addCenterForObserver(NSNotificationCenter *center ,id obs) { 54 | XXObserverRemover *remover = nil; 55 | static char removerKey; 56 | 57 | @autoreleasepool { 58 | remover = objc_getAssociatedObject(obs, &removerKey); 59 | if (!remover) { 60 | remover = [[XXObserverRemover alloc] initWithObserver:obs]; 61 | objc_setAssociatedObject(obs, &removerKey, remover, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 62 | } 63 | [remover addCenter:center]; 64 | } 65 | } 66 | 67 | XXStaticHookClass(NSNotificationCenter, ProtectNoti, 68 | void, @selector(addObserver:selector:name:object:), (id)obs, (SEL)cmd, (NSString*)name, (id)obj) { 69 | XXHookOrgin(obs, cmd, name, obj); 70 | addCenterForObserver(self, obs); 71 | } 72 | XXStaticHookEnd 73 | -------------------------------------------------------------------------------- /XXShield/Classes/Record/XXRecord.h: -------------------------------------------------------------------------------- 1 | // 2 | // XXRecord.h 3 | // XXShield 4 | // 5 | // Created by nero on 2017/1/18. 6 | // Copyright © 2017年 XXShield. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface XXRecord : NSObject 12 | 13 | /** 14 | 注册汇报中心 15 | 16 | @param record 汇报中心 17 | */ 18 | + (void)registerRecordHandler:(nullable id)record; 19 | 20 | /** 21 | 汇报Crash 22 | 23 | @param reason Sting 原因, maybe nil 24 | */ 25 | + (void)recordFatalWithReason:(nullable NSString *)reason 26 | errorType:(EXXShieldType)type; 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /XXShield/Classes/Record/XXRecord.m: -------------------------------------------------------------------------------- 1 | // 2 | // XXRecord.m 3 | // XXShield 4 | // 5 | // Created by nero on 2017/1/18. 6 | // Copyright © 2017年 XXShield. All rights reserved. 7 | // 8 | 9 | #import "XXRecord.h" 10 | 11 | @implementation XXRecord 12 | 13 | static id __record; 14 | 15 | + (void)registerRecordHandler:(id)record { 16 | __record = record; 17 | } 18 | 19 | + (void)recordFatalWithReason:(nullable NSString *)reason 20 | errorType:(EXXShieldType)type { 21 | 22 | NSDictionary *errorInfo = @{ NSLocalizedDescriptionKey : (reason.length ? reason : @"未标识原因" )}; 23 | 24 | NSError *error = [NSError errorWithDomain:[NSString stringWithFormat:@"com.xxshield.%@", 25 | [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleIdentifier"]] 26 | code:-type 27 | userInfo:errorInfo]; 28 | [__record recordWithReason:error]; 29 | } 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /XXShield/Classes/SmartKit/NSObject+Forward.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+Forward.m 3 | // XXShield 4 | // 5 | // Created by nero on 2017/1/19. 6 | // Copyright © 2017年 XXShield. All rights reserved. 7 | // 8 | #import 9 | #import 10 | #import "XXShieldStubObject.h" 11 | #import "XXRecord.h" 12 | #import "XXShieldSwizzling.h" 13 | 14 | static bool startsWith(const char *pre, const char *str) { 15 | size_t lenpre = strlen(pre), 16 | lenstr = strlen(str); 17 | return lenstr < lenpre ? false : strncmp(pre, str, lenpre) == 0; 18 | } 19 | 20 | 21 | XXStaticHookClass(NSObject, ProtectFW, id, @selector(forwardingTargetForSelector:), (SEL)aSelector) { 22 | static dispatch_once_t onceToken; 23 | static char *app_bundle_path = NULL; 24 | 25 | dispatch_once(&onceToken, ^{ 26 | const char *path = [[[NSBundle mainBundle] bundlePath] UTF8String]; 27 | app_bundle_path = malloc(strlen(path) + 1); 28 | strcpy(app_bundle_path, path); 29 | }); 30 | 31 | struct dl_info self_info = {0}; 32 | dladdr((__bridge void *)[self class], &self_info); 33 | 34 | // ignore system class 35 | if (self_info.dli_fname == NULL || !startsWith(app_bundle_path, self_info.dli_fname)) { 36 | return XXHookOrgin(aSelector); 37 | } 38 | 39 | if ([self isKindOfClass:[NSNumber class]] && [NSString instancesRespondToSelector:aSelector]) { 40 | NSNumber *number = (NSNumber *)self; 41 | NSString *str = [number stringValue]; 42 | return str; 43 | } else if ([self isKindOfClass:[NSString class]] && [NSNumber instancesRespondToSelector:aSelector]) { 44 | NSString *str = (NSString *)self; 45 | NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init]; 46 | NSNumber *number = [formatter numberFromString:str]; 47 | return number; 48 | } 49 | 50 | BOOL aBool = [self respondsToSelector:aSelector]; 51 | NSMethodSignature *signatrue = [self methodSignatureForSelector:aSelector]; 52 | 53 | if (aBool || signatrue) { 54 | return XXHookOrgin(aSelector); 55 | } else { 56 | XXShieldStubObject *stub = [XXShieldStubObject shareInstance]; 57 | [stub addFunc:aSelector]; 58 | 59 | NSString *reason = [NSString stringWithFormat:@"*****Warning***** logic error.target is %@ method is %@, reason : method forword to SmartFunction Object default implement like send message to nil.", 60 | [self class], NSStringFromSelector(aSelector)]; 61 | [XXRecord recordFatalWithReason:reason errorType:EXXShieldTypeUnrecognizedSelector]; 62 | 63 | return stub; 64 | } 65 | } 66 | XXStaticHookEnd 67 | 68 | XXStaticHookMetaClass(NSObject, ProtectFW, id, @selector(forwardingTargetForSelector:), (SEL)aSelector) { 69 | BOOL aBool = [self respondsToSelector:aSelector]; 70 | 71 | NSMethodSignature *signatrue = [self methodSignatureForSelector:aSelector]; 72 | 73 | if (aBool || signatrue) { 74 | return XXHookOrgin(aSelector); 75 | } else { 76 | [XXShieldStubObject addClassFunc:aSelector]; 77 | NSString *reason = [NSString stringWithFormat:@"*****Warning***** logic error.target is %@ method is %@, reason : method forword to SmartFunction Object default implement like send message to nil.", 78 | [self class], NSStringFromSelector(aSelector) ]; 79 | [XXRecord recordFatalWithReason:reason errorType:EXXShieldTypeUnrecognizedSelector]; 80 | 81 | return [XXShieldStubObject class]; 82 | } 83 | } 84 | XXStaticHookEnd 85 | 86 | -------------------------------------------------------------------------------- /XXShield/Classes/SmartKit/XXShieldStubObject.h: -------------------------------------------------------------------------------- 1 | // 2 | // XXShieldStubObject.h 3 | // XXShield 4 | // 5 | // Created by nero on 2017/1/19. 6 | // Copyright © 2017年 XXShield. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface XXShieldStubObject : NSObject 12 | 13 | - (instancetype)init __unavailable; 14 | 15 | + (XXShieldStubObject *)shareInstance; 16 | 17 | - (BOOL)addFunc:(SEL)sel; 18 | 19 | + (BOOL)addClassFunc:(SEL)sel; 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /XXShield/Classes/SmartKit/XXShieldStubObject.m: -------------------------------------------------------------------------------- 1 | // 2 | // XXShieldStubObject.m 3 | // XXShield 4 | // 5 | // Created by nero on 2017/1/19. 6 | // Copyright © 2017年 XXShield. All rights reserved. 7 | // 8 | 9 | #import "XXShieldStubObject.h" 10 | #import 11 | 12 | /** 13 | default Implement 14 | 15 | @param target trarget 16 | @param cmd cmd 17 | @param ... other param 18 | @return default Implement is zero 19 | */ 20 | int smartFunction(id target, SEL cmd, ...) { 21 | return 0; 22 | } 23 | 24 | static BOOL __addMethod(Class clazz, SEL sel) { 25 | NSString *selName = NSStringFromSelector(sel); 26 | 27 | NSMutableString *tmpString = [[NSMutableString alloc] initWithFormat:@"%@", selName]; 28 | 29 | int count = (int)[tmpString replaceOccurrencesOfString:@":" 30 | withString:@"_" 31 | options:NSCaseInsensitiveSearch 32 | range:NSMakeRange(0, selName.length)]; 33 | 34 | NSMutableString *val = [[NSMutableString alloc] initWithString:@"i@:"]; 35 | 36 | for (int i = 0; i < count; i++) { 37 | [val appendString:@"@"]; 38 | } 39 | const char *funcTypeEncoding = [val UTF8String]; 40 | return class_addMethod(clazz, sel, (IMP)smartFunction, funcTypeEncoding); 41 | } 42 | 43 | @implementation XXShieldStubObject 44 | 45 | + (XXShieldStubObject *)shareInstance { 46 | static XXShieldStubObject *singleton; 47 | if (!singleton) { 48 | static dispatch_once_t onceToken; 49 | dispatch_once(&onceToken, ^{ 50 | singleton = [XXShieldStubObject new]; 51 | }); 52 | } 53 | return singleton; 54 | } 55 | 56 | - (BOOL)addFunc:(SEL)sel { 57 | return __addMethod([XXShieldStubObject class], sel); 58 | } 59 | 60 | + (BOOL)addClassFunc:(SEL)sel { 61 | Class metaClass = objc_getMetaClass(class_getName([XXShieldStubObject class])); 62 | return __addMethod(metaClass, sel); 63 | } 64 | 65 | @end 66 | -------------------------------------------------------------------------------- /XXShield/Classes/Swizzle/XXMetamacros.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Macros for metaprogramming 3 | * ExtendedC 4 | * 5 | * Copyright (C) 2012 Justin Spahr-Summers 6 | * Released under the MIT license 7 | */ 8 | 9 | #ifndef EXTC_METAMACROS_H 10 | #define EXTC_METAMACROS_H 11 | 12 | 13 | /** 14 | * Executes one or more expressions (which may have a void type, such as a call 15 | * to a function that returns no value) and always returns true. 16 | */ 17 | #define metamacro_exprify(...) \ 18 | ((__VA_ARGS__), true) 19 | 20 | /** 21 | * Returns a string representation of VALUE after full macro expansion. 22 | */ 23 | #define metamacro_stringify(VALUE) \ 24 | metamacro_stringify_(VALUE) 25 | 26 | /** 27 | * Returns A and B concatenated after full macro expansion. 28 | */ 29 | #define metamacro_concat(A, B) \ 30 | metamacro_concat_(A, B) 31 | 32 | /** 33 | * Returns the Nth variadic argument (starting from zero). At least 34 | * N + 1 variadic arguments must be given. N must be between zero and twenty, 35 | * inclusive. 36 | */ 37 | #define metamacro_at(N, ...) \ 38 | metamacro_concat(metamacro_at, N)(__VA_ARGS__) 39 | 40 | /** 41 | * Returns the number of arguments (up to twenty) provided to the macro. At 42 | * least one argument must be provided. 43 | * 44 | * Inspired by P99: http://p99.gforge.inria.fr 45 | */ 46 | #define metamacro_argcount(...) \ 47 | metamacro_at(20, __VA_ARGS__, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1) 48 | 49 | /** 50 | * Identical to #metamacro_foreach_cxt, except that no CONTEXT argument is 51 | * given. Only the index and current argument will thus be passed to MACRO. 52 | */ 53 | #define metamacro_foreach(MACRO, SEP, ...) \ 54 | metamacro_foreach_cxt(metamacro_foreach_iter, SEP, MACRO, __VA_ARGS__) 55 | 56 | /** 57 | * For each consecutive variadic argument (up to twenty), MACRO is passed the 58 | * zero-based index of the current argument, CONTEXT, and then the argument 59 | * itself. The results of adjoining invocations of MACRO are then separated by 60 | * SEP. 61 | * 62 | * Inspired by P99: http://p99.gforge.inria.fr 63 | */ 64 | #define metamacro_foreach_cxt(MACRO, SEP, CONTEXT, ...) \ 65 | metamacro_concat(metamacro_foreach_cxt, metamacro_argcount(__VA_ARGS__))(MACRO, SEP, CONTEXT, __VA_ARGS__) 66 | 67 | /** 68 | * Identical to #metamacro_foreach_cxt. This can be used when the former would 69 | * fail due to recursive macro expansion. 70 | */ 71 | #define metamacro_foreach_cxt_recursive(MACRO, SEP, CONTEXT, ...) \ 72 | metamacro_concat(metamacro_foreach_cxt_recursive, metamacro_argcount(__VA_ARGS__))(MACRO, SEP, CONTEXT, __VA_ARGS__) 73 | 74 | /** 75 | * In consecutive order, appends each variadic argument (up to twenty) onto 76 | * BASE. The resulting concatenations are then separated by SEP. 77 | * 78 | * This is primarily useful to manipulate a list of macro invocations into instead 79 | * invoking a different, possibly related macro. 80 | */ 81 | #define metamacro_foreach_concat(BASE, SEP, ...) \ 82 | metamacro_foreach_cxt(metamacro_foreach_concat_iter, SEP, BASE, __VA_ARGS__) 83 | 84 | /** 85 | * Iterates COUNT times, each time invoking MACRO with the current index 86 | * (starting at zero) and CONTEXT. The results of adjoining invocations of MACRO 87 | * are then separated by SEP. 88 | * 89 | * COUNT must be an integer between zero and twenty, inclusive. 90 | */ 91 | #define metamacro_for_cxt(COUNT, MACRO, SEP, CONTEXT) \ 92 | metamacro_concat(metamacro_for_cxt, COUNT)(MACRO, SEP, CONTEXT) 93 | 94 | /** 95 | * Returns the first argument given. At least one argument must be provided. 96 | * 97 | * This is useful when implementing a variadic macro, where you may have only 98 | * one variadic argument, but no way to retrieve it (for example, because \c ... 99 | * always needs to match at least one argument). 100 | * 101 | * @code 102 | 103 | #define varmacro(...) \ 104 | metamacro_head(__VA_ARGS__) 105 | 106 | * @endcode 107 | */ 108 | #define metamacro_head(...) \ 109 | metamacro_head_(__VA_ARGS__, 0) 110 | 111 | /** 112 | * Returns every argument except the first. At least two arguments must be 113 | * provided. 114 | */ 115 | #define metamacro_tail(...) \ 116 | metamacro_tail_(__VA_ARGS__) 117 | 118 | /** 119 | * Returns the first N (up to twenty) variadic arguments as a new argument list. 120 | * At least N variadic arguments must be provided. 121 | */ 122 | #define metamacro_take(N, ...) \ 123 | metamacro_concat(metamacro_take, N)(__VA_ARGS__) 124 | 125 | /** 126 | * Removes the first N (up to twenty) variadic arguments from the given argument 127 | * list. At least N variadic arguments must be provided. 128 | */ 129 | #define metamacro_drop(N, ...) \ 130 | metamacro_concat(metamacro_drop, N)(__VA_ARGS__) 131 | 132 | /** 133 | * Decrements VAL, which must be a number between zero and twenty, inclusive. 134 | * 135 | * This is primarily useful when dealing with indexes and counts in 136 | * metaprogramming. 137 | */ 138 | #define metamacro_dec(VAL) \ 139 | metamacro_at(VAL, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19) 140 | 141 | /** 142 | * Increments VAL, which must be a number between zero and twenty, inclusive. 143 | * 144 | * This is primarily useful when dealing with indexes and counts in 145 | * metaprogramming. 146 | */ 147 | #define metamacro_inc(VAL) \ 148 | metamacro_at(VAL, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21) 149 | 150 | /** 151 | * If A is equal to B, the next argument list is expanded; otherwise, the 152 | * argument list after that is expanded. A and B must be numbers between zero 153 | * and twenty, inclusive. Additionally, B must be greater than or equal to A. 154 | * 155 | * @code 156 | 157 | // expands to true 158 | metamacro_if_eq(0, 0)(true)(false) 159 | 160 | // expands to false 161 | metamacro_if_eq(0, 1)(true)(false) 162 | 163 | * @endcode 164 | * 165 | * This is primarily useful when dealing with indexes and counts in 166 | * metaprogramming. 167 | */ 168 | #define metamacro_if_eq(A, B) \ 169 | metamacro_concat(metamacro_if_eq, A)(B) 170 | 171 | /** 172 | * Identical to #metamacro_if_eq. This can be used when the former would fail 173 | * due to recursive macro expansion. 174 | */ 175 | #define metamacro_if_eq_recursive(A, B) \ 176 | metamacro_concat(metamacro_if_eq_recursive, A)(B) 177 | 178 | /** 179 | * Returns 1 if N is an even number, or 0 otherwise. N must be between zero and 180 | * twenty, inclusive. 181 | * 182 | * For the purposes of this test, zero is considered even. 183 | */ 184 | #define metamacro_is_even(N) \ 185 | metamacro_at(N, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1) 186 | 187 | /** 188 | * Returns the logical NOT of B, which must be the number zero or one. 189 | */ 190 | #define metamacro_not(B) \ 191 | metamacro_at(B, 1, 0) 192 | 193 | // IMPLEMENTATION DETAILS FOLLOW! 194 | // Do not write code that depends on anything below this line. 195 | #define metamacro_stringify_(VALUE) # VALUE 196 | #define metamacro_concat_(A, B) A ## B 197 | #define metamacro_foreach_iter(INDEX, MACRO, ARG) MACRO(INDEX, ARG) 198 | #define metamacro_head_(FIRST, ...) FIRST 199 | #define metamacro_tail_(FIRST, ...) __VA_ARGS__ 200 | #define metamacro_consume_(...) 201 | #define metamacro_expand_(...) __VA_ARGS__ 202 | 203 | // implemented from scratch so that metamacro_concat() doesn't end up nesting 204 | #define metamacro_foreach_concat_iter(INDEX, BASE, ARG) metamacro_foreach_concat_iter_(BASE, ARG) 205 | #define metamacro_foreach_concat_iter_(BASE, ARG) BASE ## ARG 206 | 207 | // metamacro_at expansions 208 | #define metamacro_at0(...) metamacro_head(__VA_ARGS__) 209 | #define metamacro_at1(_0, ...) metamacro_head(__VA_ARGS__) 210 | #define metamacro_at2(_0, _1, ...) metamacro_head(__VA_ARGS__) 211 | #define metamacro_at3(_0, _1, _2, ...) metamacro_head(__VA_ARGS__) 212 | #define metamacro_at4(_0, _1, _2, _3, ...) metamacro_head(__VA_ARGS__) 213 | #define metamacro_at5(_0, _1, _2, _3, _4, ...) metamacro_head(__VA_ARGS__) 214 | #define metamacro_at6(_0, _1, _2, _3, _4, _5, ...) metamacro_head(__VA_ARGS__) 215 | #define metamacro_at7(_0, _1, _2, _3, _4, _5, _6, ...) metamacro_head(__VA_ARGS__) 216 | #define metamacro_at8(_0, _1, _2, _3, _4, _5, _6, _7, ...) metamacro_head(__VA_ARGS__) 217 | #define metamacro_at9(_0, _1, _2, _3, _4, _5, _6, _7, _8, ...) metamacro_head(__VA_ARGS__) 218 | #define metamacro_at10(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, ...) metamacro_head(__VA_ARGS__) 219 | #define metamacro_at11(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, ...) metamacro_head(__VA_ARGS__) 220 | #define metamacro_at12(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, ...) metamacro_head(__VA_ARGS__) 221 | #define metamacro_at13(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, ...) metamacro_head(__VA_ARGS__) 222 | #define metamacro_at14(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, ...) metamacro_head(__VA_ARGS__) 223 | #define metamacro_at15(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, ...) metamacro_head(__VA_ARGS__) 224 | #define metamacro_at16(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, ...) metamacro_head(__VA_ARGS__) 225 | #define metamacro_at17(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, ...) metamacro_head(__VA_ARGS__) 226 | #define metamacro_at18(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, ...) metamacro_head(__VA_ARGS__) 227 | #define metamacro_at19(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, ...) metamacro_head(__VA_ARGS__) 228 | #define metamacro_at20(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, ...) metamacro_head(__VA_ARGS__) 229 | 230 | // metamacro_foreach_cxt expansions 231 | #define metamacro_foreach_cxt0(MACRO, SEP, CONTEXT) 232 | #define metamacro_foreach_cxt1(MACRO, SEP, CONTEXT, _0) MACRO(0, CONTEXT, _0) 233 | 234 | #define metamacro_foreach_cxt2(MACRO, SEP, CONTEXT, _0, _1) \ 235 | metamacro_foreach_cxt1(MACRO, SEP, CONTEXT, _0) \ 236 | SEP \ 237 | MACRO(1, CONTEXT, _1) 238 | 239 | #define metamacro_foreach_cxt3(MACRO, SEP, CONTEXT, _0, _1, _2) \ 240 | metamacro_foreach_cxt2(MACRO, SEP, CONTEXT, _0, _1) \ 241 | SEP \ 242 | MACRO(2, CONTEXT, _2) 243 | 244 | #define metamacro_foreach_cxt4(MACRO, SEP, CONTEXT, _0, _1, _2, _3) \ 245 | metamacro_foreach_cxt3(MACRO, SEP, CONTEXT, _0, _1, _2) \ 246 | SEP \ 247 | MACRO(3, CONTEXT, _3) 248 | 249 | #define metamacro_foreach_cxt5(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4) \ 250 | metamacro_foreach_cxt4(MACRO, SEP, CONTEXT, _0, _1, _2, _3) \ 251 | SEP \ 252 | MACRO(4, CONTEXT, _4) 253 | 254 | #define metamacro_foreach_cxt6(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5) \ 255 | metamacro_foreach_cxt5(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4) \ 256 | SEP \ 257 | MACRO(5, CONTEXT, _5) 258 | 259 | #define metamacro_foreach_cxt7(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6) \ 260 | metamacro_foreach_cxt6(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5) \ 261 | SEP \ 262 | MACRO(6, CONTEXT, _6) 263 | 264 | #define metamacro_foreach_cxt8(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7) \ 265 | metamacro_foreach_cxt7(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6) \ 266 | SEP \ 267 | MACRO(7, CONTEXT, _7) 268 | 269 | #define metamacro_foreach_cxt9(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8) \ 270 | metamacro_foreach_cxt8(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7) \ 271 | SEP \ 272 | MACRO(8, CONTEXT, _8) 273 | 274 | #define metamacro_foreach_cxt10(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9) \ 275 | metamacro_foreach_cxt9(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8) \ 276 | SEP \ 277 | MACRO(9, CONTEXT, _9) 278 | 279 | #define metamacro_foreach_cxt11(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) \ 280 | metamacro_foreach_cxt10(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9) \ 281 | SEP \ 282 | MACRO(10, CONTEXT, _10) 283 | 284 | #define metamacro_foreach_cxt12(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) \ 285 | metamacro_foreach_cxt11(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) \ 286 | SEP \ 287 | MACRO(11, CONTEXT, _11) 288 | 289 | #define metamacro_foreach_cxt13(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) \ 290 | metamacro_foreach_cxt12(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) \ 291 | SEP \ 292 | MACRO(12, CONTEXT, _12) 293 | 294 | #define metamacro_foreach_cxt14(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) \ 295 | metamacro_foreach_cxt13(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) \ 296 | SEP \ 297 | MACRO(13, CONTEXT, _13) 298 | 299 | #define metamacro_foreach_cxt15(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) \ 300 | metamacro_foreach_cxt14(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) \ 301 | SEP \ 302 | MACRO(14, CONTEXT, _14) 303 | 304 | #define metamacro_foreach_cxt16(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15) \ 305 | metamacro_foreach_cxt15(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) \ 306 | SEP \ 307 | MACRO(15, CONTEXT, _15) 308 | 309 | #define metamacro_foreach_cxt17(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16) \ 310 | metamacro_foreach_cxt16(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15) \ 311 | SEP \ 312 | MACRO(16, CONTEXT, _16) 313 | 314 | #define metamacro_foreach_cxt18(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17) \ 315 | metamacro_foreach_cxt17(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16) \ 316 | SEP \ 317 | MACRO(17, CONTEXT, _17) 318 | 319 | #define metamacro_foreach_cxt19(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18) \ 320 | metamacro_foreach_cxt18(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17) \ 321 | SEP \ 322 | MACRO(18, CONTEXT, _18) 323 | 324 | #define metamacro_foreach_cxt20(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19) \ 325 | metamacro_foreach_cxt19(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18) \ 326 | SEP \ 327 | MACRO(19, CONTEXT, _19) 328 | 329 | // metamacro_foreach_cxt_recursive expansions 330 | #define metamacro_foreach_cxt_recursive0(MACRO, SEP, CONTEXT) 331 | #define metamacro_foreach_cxt_recursive1(MACRO, SEP, CONTEXT, _0) MACRO(0, CONTEXT, _0) 332 | 333 | #define metamacro_foreach_cxt_recursive2(MACRO, SEP, CONTEXT, _0, _1) \ 334 | metamacro_foreach_cxt_recursive1(MACRO, SEP, CONTEXT, _0) \ 335 | SEP \ 336 | MACRO(1, CONTEXT, _1) 337 | 338 | #define metamacro_foreach_cxt_recursive3(MACRO, SEP, CONTEXT, _0, _1, _2) \ 339 | metamacro_foreach_cxt_recursive2(MACRO, SEP, CONTEXT, _0, _1) \ 340 | SEP \ 341 | MACRO(2, CONTEXT, _2) 342 | 343 | #define metamacro_foreach_cxt_recursive4(MACRO, SEP, CONTEXT, _0, _1, _2, _3) \ 344 | metamacro_foreach_cxt_recursive3(MACRO, SEP, CONTEXT, _0, _1, _2) \ 345 | SEP \ 346 | MACRO(3, CONTEXT, _3) 347 | 348 | #define metamacro_foreach_cxt_recursive5(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4) \ 349 | metamacro_foreach_cxt_recursive4(MACRO, SEP, CONTEXT, _0, _1, _2, _3) \ 350 | SEP \ 351 | MACRO(4, CONTEXT, _4) 352 | 353 | #define metamacro_foreach_cxt_recursive6(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5) \ 354 | metamacro_foreach_cxt_recursive5(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4) \ 355 | SEP \ 356 | MACRO(5, CONTEXT, _5) 357 | 358 | #define metamacro_foreach_cxt_recursive7(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6) \ 359 | metamacro_foreach_cxt_recursive6(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5) \ 360 | SEP \ 361 | MACRO(6, CONTEXT, _6) 362 | 363 | #define metamacro_foreach_cxt_recursive8(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7) \ 364 | metamacro_foreach_cxt_recursive7(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6) \ 365 | SEP \ 366 | MACRO(7, CONTEXT, _7) 367 | 368 | #define metamacro_foreach_cxt_recursive9(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8) \ 369 | metamacro_foreach_cxt_recursive8(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7) \ 370 | SEP \ 371 | MACRO(8, CONTEXT, _8) 372 | 373 | #define metamacro_foreach_cxt_recursive10(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9) \ 374 | metamacro_foreach_cxt_recursive9(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8) \ 375 | SEP \ 376 | MACRO(9, CONTEXT, _9) 377 | 378 | #define metamacro_foreach_cxt_recursive11(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) \ 379 | metamacro_foreach_cxt_recursive10(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9) \ 380 | SEP \ 381 | MACRO(10, CONTEXT, _10) 382 | 383 | #define metamacro_foreach_cxt_recursive12(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) \ 384 | metamacro_foreach_cxt_recursive11(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) \ 385 | SEP \ 386 | MACRO(11, CONTEXT, _11) 387 | 388 | #define metamacro_foreach_cxt_recursive13(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) \ 389 | metamacro_foreach_cxt_recursive12(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11) \ 390 | SEP \ 391 | MACRO(12, CONTEXT, _12) 392 | 393 | #define metamacro_foreach_cxt_recursive14(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) \ 394 | metamacro_foreach_cxt_recursive13(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12) \ 395 | SEP \ 396 | MACRO(13, CONTEXT, _13) 397 | 398 | #define metamacro_foreach_cxt_recursive15(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) \ 399 | metamacro_foreach_cxt_recursive14(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13) \ 400 | SEP \ 401 | MACRO(14, CONTEXT, _14) 402 | 403 | #define metamacro_foreach_cxt_recursive16(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15) \ 404 | metamacro_foreach_cxt_recursive15(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14) \ 405 | SEP \ 406 | MACRO(15, CONTEXT, _15) 407 | 408 | #define metamacro_foreach_cxt_recursive17(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16) \ 409 | metamacro_foreach_cxt_recursive16(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15) \ 410 | SEP \ 411 | MACRO(16, CONTEXT, _16) 412 | 413 | #define metamacro_foreach_cxt_recursive18(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17) \ 414 | metamacro_foreach_cxt_recursive17(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16) \ 415 | SEP \ 416 | MACRO(17, CONTEXT, _17) 417 | 418 | #define metamacro_foreach_cxt_recursive19(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18) \ 419 | metamacro_foreach_cxt_recursive18(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17) \ 420 | SEP \ 421 | MACRO(18, CONTEXT, _18) 422 | 423 | #define metamacro_foreach_cxt_recursive20(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19) \ 424 | metamacro_foreach_cxt_recursive19(MACRO, SEP, CONTEXT, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18) \ 425 | SEP \ 426 | MACRO(19, CONTEXT, _19) 427 | 428 | // metamacro_for_cxt expansions 429 | #define metamacro_for_cxt0(MACRO, SEP, CONTEXT) 430 | #define metamacro_for_cxt1(MACRO, SEP, CONTEXT) MACRO(0, CONTEXT) 431 | 432 | #define metamacro_for_cxt2(MACRO, SEP, CONTEXT) \ 433 | metamacro_for_cxt1(MACRO, SEP, CONTEXT) \ 434 | SEP \ 435 | MACRO(1, CONTEXT) 436 | 437 | #define metamacro_for_cxt3(MACRO, SEP, CONTEXT) \ 438 | metamacro_for_cxt2(MACRO, SEP, CONTEXT) \ 439 | SEP \ 440 | MACRO(2, CONTEXT) 441 | 442 | #define metamacro_for_cxt4(MACRO, SEP, CONTEXT) \ 443 | metamacro_for_cxt3(MACRO, SEP, CONTEXT) \ 444 | SEP \ 445 | MACRO(3, CONTEXT) 446 | 447 | #define metamacro_for_cxt5(MACRO, SEP, CONTEXT) \ 448 | metamacro_for_cxt4(MACRO, SEP, CONTEXT) \ 449 | SEP \ 450 | MACRO(4, CONTEXT) 451 | 452 | #define metamacro_for_cxt6(MACRO, SEP, CONTEXT) \ 453 | metamacro_for_cxt5(MACRO, SEP, CONTEXT) \ 454 | SEP \ 455 | MACRO(5, CONTEXT) 456 | 457 | #define metamacro_for_cxt7(MACRO, SEP, CONTEXT) \ 458 | metamacro_for_cxt6(MACRO, SEP, CONTEXT) \ 459 | SEP \ 460 | MACRO(6, CONTEXT) 461 | 462 | #define metamacro_for_cxt8(MACRO, SEP, CONTEXT) \ 463 | metamacro_for_cxt7(MACRO, SEP, CONTEXT) \ 464 | SEP \ 465 | MACRO(7, CONTEXT) 466 | 467 | #define metamacro_for_cxt9(MACRO, SEP, CONTEXT) \ 468 | metamacro_for_cxt8(MACRO, SEP, CONTEXT) \ 469 | SEP \ 470 | MACRO(8, CONTEXT) 471 | 472 | #define metamacro_for_cxt10(MACRO, SEP, CONTEXT) \ 473 | metamacro_for_cxt9(MACRO, SEP, CONTEXT) \ 474 | SEP \ 475 | MACRO(9, CONTEXT) 476 | 477 | #define metamacro_for_cxt11(MACRO, SEP, CONTEXT) \ 478 | metamacro_for_cxt10(MACRO, SEP, CONTEXT) \ 479 | SEP \ 480 | MACRO(10, CONTEXT) 481 | 482 | #define metamacro_for_cxt12(MACRO, SEP, CONTEXT) \ 483 | metamacro_for_cxt11(MACRO, SEP, CONTEXT) \ 484 | SEP \ 485 | MACRO(11, CONTEXT) 486 | 487 | #define metamacro_for_cxt13(MACRO, SEP, CONTEXT) \ 488 | metamacro_for_cxt12(MACRO, SEP, CONTEXT) \ 489 | SEP \ 490 | MACRO(12, CONTEXT) 491 | 492 | #define metamacro_for_cxt14(MACRO, SEP, CONTEXT) \ 493 | metamacro_for_cxt13(MACRO, SEP, CONTEXT) \ 494 | SEP \ 495 | MACRO(13, CONTEXT) 496 | 497 | #define metamacro_for_cxt15(MACRO, SEP, CONTEXT) \ 498 | metamacro_for_cxt14(MACRO, SEP, CONTEXT) \ 499 | SEP \ 500 | MACRO(14, CONTEXT) 501 | 502 | #define metamacro_for_cxt16(MACRO, SEP, CONTEXT) \ 503 | metamacro_for_cxt15(MACRO, SEP, CONTEXT) \ 504 | SEP \ 505 | MACRO(15, CONTEXT) 506 | 507 | #define metamacro_for_cxt17(MACRO, SEP, CONTEXT) \ 508 | metamacro_for_cxt16(MACRO, SEP, CONTEXT) \ 509 | SEP \ 510 | MACRO(16, CONTEXT) 511 | 512 | #define metamacro_for_cxt18(MACRO, SEP, CONTEXT) \ 513 | metamacro_for_cxt17(MACRO, SEP, CONTEXT) \ 514 | SEP \ 515 | MACRO(17, CONTEXT) 516 | 517 | #define metamacro_for_cxt19(MACRO, SEP, CONTEXT) \ 518 | metamacro_for_cxt18(MACRO, SEP, CONTEXT) \ 519 | SEP \ 520 | MACRO(18, CONTEXT) 521 | 522 | #define metamacro_for_cxt20(MACRO, SEP, CONTEXT) \ 523 | metamacro_for_cxt19(MACRO, SEP, CONTEXT) \ 524 | SEP \ 525 | MACRO(19, CONTEXT) 526 | 527 | // metamacro_if_eq expansions 528 | #define metamacro_if_eq0(VALUE) \ 529 | metamacro_concat(metamacro_if_eq0_, VALUE) 530 | 531 | #define metamacro_if_eq0_0(...) __VA_ARGS__ metamacro_consume_ 532 | #define metamacro_if_eq0_1(...) metamacro_expand_ 533 | #define metamacro_if_eq0_2(...) metamacro_expand_ 534 | #define metamacro_if_eq0_3(...) metamacro_expand_ 535 | #define metamacro_if_eq0_4(...) metamacro_expand_ 536 | #define metamacro_if_eq0_5(...) metamacro_expand_ 537 | #define metamacro_if_eq0_6(...) metamacro_expand_ 538 | #define metamacro_if_eq0_7(...) metamacro_expand_ 539 | #define metamacro_if_eq0_8(...) metamacro_expand_ 540 | #define metamacro_if_eq0_9(...) metamacro_expand_ 541 | #define metamacro_if_eq0_10(...) metamacro_expand_ 542 | #define metamacro_if_eq0_11(...) metamacro_expand_ 543 | #define metamacro_if_eq0_12(...) metamacro_expand_ 544 | #define metamacro_if_eq0_13(...) metamacro_expand_ 545 | #define metamacro_if_eq0_14(...) metamacro_expand_ 546 | #define metamacro_if_eq0_15(...) metamacro_expand_ 547 | #define metamacro_if_eq0_16(...) metamacro_expand_ 548 | #define metamacro_if_eq0_17(...) metamacro_expand_ 549 | #define metamacro_if_eq0_18(...) metamacro_expand_ 550 | #define metamacro_if_eq0_19(...) metamacro_expand_ 551 | #define metamacro_if_eq0_20(...) metamacro_expand_ 552 | 553 | #define metamacro_if_eq1(VALUE) metamacro_if_eq0(metamacro_dec(VALUE)) 554 | #define metamacro_if_eq2(VALUE) metamacro_if_eq1(metamacro_dec(VALUE)) 555 | #define metamacro_if_eq3(VALUE) metamacro_if_eq2(metamacro_dec(VALUE)) 556 | #define metamacro_if_eq4(VALUE) metamacro_if_eq3(metamacro_dec(VALUE)) 557 | #define metamacro_if_eq5(VALUE) metamacro_if_eq4(metamacro_dec(VALUE)) 558 | #define metamacro_if_eq6(VALUE) metamacro_if_eq5(metamacro_dec(VALUE)) 559 | #define metamacro_if_eq7(VALUE) metamacro_if_eq6(metamacro_dec(VALUE)) 560 | #define metamacro_if_eq8(VALUE) metamacro_if_eq7(metamacro_dec(VALUE)) 561 | #define metamacro_if_eq9(VALUE) metamacro_if_eq8(metamacro_dec(VALUE)) 562 | #define metamacro_if_eq10(VALUE) metamacro_if_eq9(metamacro_dec(VALUE)) 563 | #define metamacro_if_eq11(VALUE) metamacro_if_eq10(metamacro_dec(VALUE)) 564 | #define metamacro_if_eq12(VALUE) metamacro_if_eq11(metamacro_dec(VALUE)) 565 | #define metamacro_if_eq13(VALUE) metamacro_if_eq12(metamacro_dec(VALUE)) 566 | #define metamacro_if_eq14(VALUE) metamacro_if_eq13(metamacro_dec(VALUE)) 567 | #define metamacro_if_eq15(VALUE) metamacro_if_eq14(metamacro_dec(VALUE)) 568 | #define metamacro_if_eq16(VALUE) metamacro_if_eq15(metamacro_dec(VALUE)) 569 | #define metamacro_if_eq17(VALUE) metamacro_if_eq16(metamacro_dec(VALUE)) 570 | #define metamacro_if_eq18(VALUE) metamacro_if_eq17(metamacro_dec(VALUE)) 571 | #define metamacro_if_eq19(VALUE) metamacro_if_eq18(metamacro_dec(VALUE)) 572 | #define metamacro_if_eq20(VALUE) metamacro_if_eq19(metamacro_dec(VALUE)) 573 | 574 | // metamacro_if_eq_recursive expansions 575 | #define metamacro_if_eq_recursive0(VALUE) \ 576 | metamacro_concat(metamacro_if_eq_recursive0_, VALUE) 577 | 578 | #define metamacro_if_eq_recursive0_0(...) __VA_ARGS__ metamacro_consume_ 579 | #define metamacro_if_eq_recursive0_1(...) metamacro_expand_ 580 | #define metamacro_if_eq_recursive0_2(...) metamacro_expand_ 581 | #define metamacro_if_eq_recursive0_3(...) metamacro_expand_ 582 | #define metamacro_if_eq_recursive0_4(...) metamacro_expand_ 583 | #define metamacro_if_eq_recursive0_5(...) metamacro_expand_ 584 | #define metamacro_if_eq_recursive0_6(...) metamacro_expand_ 585 | #define metamacro_if_eq_recursive0_7(...) metamacro_expand_ 586 | #define metamacro_if_eq_recursive0_8(...) metamacro_expand_ 587 | #define metamacro_if_eq_recursive0_9(...) metamacro_expand_ 588 | #define metamacro_if_eq_recursive0_10(...) metamacro_expand_ 589 | #define metamacro_if_eq_recursive0_11(...) metamacro_expand_ 590 | #define metamacro_if_eq_recursive0_12(...) metamacro_expand_ 591 | #define metamacro_if_eq_recursive0_13(...) metamacro_expand_ 592 | #define metamacro_if_eq_recursive0_14(...) metamacro_expand_ 593 | #define metamacro_if_eq_recursive0_15(...) metamacro_expand_ 594 | #define metamacro_if_eq_recursive0_16(...) metamacro_expand_ 595 | #define metamacro_if_eq_recursive0_17(...) metamacro_expand_ 596 | #define metamacro_if_eq_recursive0_18(...) metamacro_expand_ 597 | #define metamacro_if_eq_recursive0_19(...) metamacro_expand_ 598 | #define metamacro_if_eq_recursive0_20(...) metamacro_expand_ 599 | 600 | #define metamacro_if_eq_recursive1(VALUE) metamacro_if_eq_recursive0(metamacro_dec(VALUE)) 601 | #define metamacro_if_eq_recursive2(VALUE) metamacro_if_eq_recursive1(metamacro_dec(VALUE)) 602 | #define metamacro_if_eq_recursive3(VALUE) metamacro_if_eq_recursive2(metamacro_dec(VALUE)) 603 | #define metamacro_if_eq_recursive4(VALUE) metamacro_if_eq_recursive3(metamacro_dec(VALUE)) 604 | #define metamacro_if_eq_recursive5(VALUE) metamacro_if_eq_recursive4(metamacro_dec(VALUE)) 605 | #define metamacro_if_eq_recursive6(VALUE) metamacro_if_eq_recursive5(metamacro_dec(VALUE)) 606 | #define metamacro_if_eq_recursive7(VALUE) metamacro_if_eq_recursive6(metamacro_dec(VALUE)) 607 | #define metamacro_if_eq_recursive8(VALUE) metamacro_if_eq_recursive7(metamacro_dec(VALUE)) 608 | #define metamacro_if_eq_recursive9(VALUE) metamacro_if_eq_recursive8(metamacro_dec(VALUE)) 609 | #define metamacro_if_eq_recursive10(VALUE) metamacro_if_eq_recursive9(metamacro_dec(VALUE)) 610 | #define metamacro_if_eq_recursive11(VALUE) metamacro_if_eq_recursive10(metamacro_dec(VALUE)) 611 | #define metamacro_if_eq_recursive12(VALUE) metamacro_if_eq_recursive11(metamacro_dec(VALUE)) 612 | #define metamacro_if_eq_recursive13(VALUE) metamacro_if_eq_recursive12(metamacro_dec(VALUE)) 613 | #define metamacro_if_eq_recursive14(VALUE) metamacro_if_eq_recursive13(metamacro_dec(VALUE)) 614 | #define metamacro_if_eq_recursive15(VALUE) metamacro_if_eq_recursive14(metamacro_dec(VALUE)) 615 | #define metamacro_if_eq_recursive16(VALUE) metamacro_if_eq_recursive15(metamacro_dec(VALUE)) 616 | #define metamacro_if_eq_recursive17(VALUE) metamacro_if_eq_recursive16(metamacro_dec(VALUE)) 617 | #define metamacro_if_eq_recursive18(VALUE) metamacro_if_eq_recursive17(metamacro_dec(VALUE)) 618 | #define metamacro_if_eq_recursive19(VALUE) metamacro_if_eq_recursive18(metamacro_dec(VALUE)) 619 | #define metamacro_if_eq_recursive20(VALUE) metamacro_if_eq_recursive19(metamacro_dec(VALUE)) 620 | 621 | // metamacro_take expansions 622 | #define metamacro_take0(...) 623 | #define metamacro_take1(...) metamacro_head(__VA_ARGS__) 624 | #define metamacro_take2(...) metamacro_head(__VA_ARGS__), metamacro_take1(metamacro_tail(__VA_ARGS__)) 625 | #define metamacro_take3(...) metamacro_head(__VA_ARGS__), metamacro_take2(metamacro_tail(__VA_ARGS__)) 626 | #define metamacro_take4(...) metamacro_head(__VA_ARGS__), metamacro_take3(metamacro_tail(__VA_ARGS__)) 627 | #define metamacro_take5(...) metamacro_head(__VA_ARGS__), metamacro_take4(metamacro_tail(__VA_ARGS__)) 628 | #define metamacro_take6(...) metamacro_head(__VA_ARGS__), metamacro_take5(metamacro_tail(__VA_ARGS__)) 629 | #define metamacro_take7(...) metamacro_head(__VA_ARGS__), metamacro_take6(metamacro_tail(__VA_ARGS__)) 630 | #define metamacro_take8(...) metamacro_head(__VA_ARGS__), metamacro_take7(metamacro_tail(__VA_ARGS__)) 631 | #define metamacro_take9(...) metamacro_head(__VA_ARGS__), metamacro_take8(metamacro_tail(__VA_ARGS__)) 632 | #define metamacro_take10(...) metamacro_head(__VA_ARGS__), metamacro_take9(metamacro_tail(__VA_ARGS__)) 633 | #define metamacro_take11(...) metamacro_head(__VA_ARGS__), metamacro_take10(metamacro_tail(__VA_ARGS__)) 634 | #define metamacro_take12(...) metamacro_head(__VA_ARGS__), metamacro_take11(metamacro_tail(__VA_ARGS__)) 635 | #define metamacro_take13(...) metamacro_head(__VA_ARGS__), metamacro_take12(metamacro_tail(__VA_ARGS__)) 636 | #define metamacro_take14(...) metamacro_head(__VA_ARGS__), metamacro_take13(metamacro_tail(__VA_ARGS__)) 637 | #define metamacro_take15(...) metamacro_head(__VA_ARGS__), metamacro_take14(metamacro_tail(__VA_ARGS__)) 638 | #define metamacro_take16(...) metamacro_head(__VA_ARGS__), metamacro_take15(metamacro_tail(__VA_ARGS__)) 639 | #define metamacro_take17(...) metamacro_head(__VA_ARGS__), metamacro_take16(metamacro_tail(__VA_ARGS__)) 640 | #define metamacro_take18(...) metamacro_head(__VA_ARGS__), metamacro_take17(metamacro_tail(__VA_ARGS__)) 641 | #define metamacro_take19(...) metamacro_head(__VA_ARGS__), metamacro_take18(metamacro_tail(__VA_ARGS__)) 642 | #define metamacro_take20(...) metamacro_head(__VA_ARGS__), metamacro_take19(metamacro_tail(__VA_ARGS__)) 643 | 644 | // metamacro_drop expansions 645 | #define metamacro_drop0(...) __VA_ARGS__ 646 | #define metamacro_drop1(...) metamacro_tail(__VA_ARGS__) 647 | #define metamacro_drop2(...) metamacro_drop1(metamacro_tail(__VA_ARGS__)) 648 | #define metamacro_drop3(...) metamacro_drop2(metamacro_tail(__VA_ARGS__)) 649 | #define metamacro_drop4(...) metamacro_drop3(metamacro_tail(__VA_ARGS__)) 650 | #define metamacro_drop5(...) metamacro_drop4(metamacro_tail(__VA_ARGS__)) 651 | #define metamacro_drop6(...) metamacro_drop5(metamacro_tail(__VA_ARGS__)) 652 | #define metamacro_drop7(...) metamacro_drop6(metamacro_tail(__VA_ARGS__)) 653 | #define metamacro_drop8(...) metamacro_drop7(metamacro_tail(__VA_ARGS__)) 654 | #define metamacro_drop9(...) metamacro_drop8(metamacro_tail(__VA_ARGS__)) 655 | #define metamacro_drop10(...) metamacro_drop9(metamacro_tail(__VA_ARGS__)) 656 | #define metamacro_drop11(...) metamacro_drop10(metamacro_tail(__VA_ARGS__)) 657 | #define metamacro_drop12(...) metamacro_drop11(metamacro_tail(__VA_ARGS__)) 658 | #define metamacro_drop13(...) metamacro_drop12(metamacro_tail(__VA_ARGS__)) 659 | #define metamacro_drop14(...) metamacro_drop13(metamacro_tail(__VA_ARGS__)) 660 | #define metamacro_drop15(...) metamacro_drop14(metamacro_tail(__VA_ARGS__)) 661 | #define metamacro_drop16(...) metamacro_drop15(metamacro_tail(__VA_ARGS__)) 662 | #define metamacro_drop17(...) metamacro_drop16(metamacro_tail(__VA_ARGS__)) 663 | #define metamacro_drop18(...) metamacro_drop17(metamacro_tail(__VA_ARGS__)) 664 | #define metamacro_drop19(...) metamacro_drop18(metamacro_tail(__VA_ARGS__)) 665 | #define metamacro_drop20(...) metamacro_drop19(metamacro_tail(__VA_ARGS__)) 666 | 667 | #endif 668 | -------------------------------------------------------------------------------- /XXShield/Classes/Swizzle/XXShieldSwizzling.h: -------------------------------------------------------------------------------- 1 | // 2 | // XXShieldSwizzling.h 3 | // XXShield 4 | // 5 | // Created by nero on 2017/1/19. 6 | // Copyright © 2017年 XXShield. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import "XXMetamacros.h" 12 | 13 | #define XXForOCString(_) @#_ 14 | 15 | #define XXSEL2Str(_) NSStringFromSelector(_) 16 | 17 | #define FOREACH_ARGS(MACRO, ...) \ 18 | metamacro_if_eq(1,metamacro_argcount(__VA_ARGS__)) \ 19 | () \ 20 | (metamacro_foreach(MACRO , , metamacro_tail(__VA_ARGS__))) \ 21 | 22 | 23 | #define CREATE_ARGS_DELETE_PAREN(VALUE) ,VALUE 24 | 25 | #define CREATE_ARGS(INDEX,VALUE) CREATE_ARGS_DELETE_PAREN VALUE 26 | 27 | #define __CREATE_ARGS_DELETE_PAREN(...) \ 28 | [type appendFormat:@"%s",@encode(metamacro_head(__VA_ARGS__))]; 29 | 30 | #define CRATE_TYPE_CODING_DEL_VAR(TYPE) TYPE , 31 | 32 | #define CRATE_TYPE_CODING(INDEX,VALUE) \ 33 | __CREATE_ARGS_DELETE_PAREN(CRATE_TYPE_CODING_DEL_VAR VALUE) 34 | 35 | #define __XXHookType__void , 36 | 37 | #define __XXHookTypeIsVoidType(...) \ 38 | metamacro_if_eq(metamacro_argcount(__VA_ARGS__),2) 39 | 40 | #define XXHookTypeIsVoidType(TYPE) \ 41 | __XXHookTypeIsVoidType(__XXHookType__ ## TYPE) 42 | 43 | // 调用原始函数 44 | #define XXHookOrgin(...) \ 45 | __xx_hook_orgin_function \ 46 | ?__xx_hook_orgin_function(self,__xxHookSel,##__VA_ARGS__) \ 47 | :((typeof(__xx_hook_orgin_function))(class_getMethodImplementation(__xxHookSuperClass,__xxHookSel)))(self,__xxHookSel,##__VA_ARGS__) 48 | 49 | 50 | // 生成真实调用函数 51 | #define __XXHookClassBegin(theHookClass, \ 52 | notWorkSubClass, \ 53 | addMethod, \ 54 | returnValue, \ 55 | returnType, \ 56 | theSEL, \ 57 | theSelfTypeAndVar, \ 58 | ...) \ 59 | \ 60 | static char associatedKey; \ 61 | __unused Class __xxHookClass = shield_hook_getClassFromObject(theHookClass); \ 62 | __unused Class __xxHookSuperClass = class_getSuperclass(__xxHookClass); \ 63 | __unused SEL __xxHookSel = theSEL; \ 64 | if (nil == __xxHookClass \ 65 | || objc_getAssociatedObject(__xxHookClass, &associatedKey)) \ 66 | { \ 67 | return NO; \ 68 | } \ 69 | metamacro_if_eq(addMethod,1) \ 70 | ( \ 71 | if (!class_respondsToSelector(__xxHookClass,__xxHookSel)) \ 72 | { \ 73 | id _emptyBlock = ^returnType(id self \ 74 | FOREACH_ARGS(CREATE_ARGS,1,##__VA_ARGS__ )) \ 75 | { \ 76 | Method method = class_getInstanceMethod(__xxHookSuperClass,__xxHookSel); \ 77 | if (method) \ 78 | { \ 79 | __unused \ 80 | returnType(*superFunction)(theSelfTypeAndVar, \ 81 | SEL _cmd \ 82 | FOREACH_ARGS(CREATE_ARGS,1,##__VA_ARGS__ )) \ 83 | = (void*)method_getImplementation(method); \ 84 | XXHookTypeIsVoidType(returnType) \ 85 | () \ 86 | (return ) \ 87 | superFunction(self,__xxHookSel,##__VA_ARGS__); \ 88 | } \ 89 | else \ 90 | { \ 91 | XXHookTypeIsVoidType(returnType) \ 92 | (return;) \ 93 | (return returnValue;) \ 94 | } \ 95 | }; \ 96 | NSMutableString *type = [[NSMutableString alloc] init]; \ 97 | [type appendFormat:@"%s@:", @encode(returnType)]; \ 98 | FOREACH_ARGS(CRATE_TYPE_CODING,1,##__VA_ARGS__ ) \ 99 | class_addMethod(__xxHookClass, \ 100 | theSEL, \ 101 | (IMP)imp_implementationWithBlock(_emptyBlock), \ 102 | type.UTF8String); \ 103 | } \ 104 | ) \ 105 | () \ 106 | if (!class_respondsToSelector(__xxHookClass,__xxHookSel)) \ 107 | { \ 108 | return NO; \ 109 | } \ 110 | __block __unused \ 111 | returnType(*__xx_hook_orgin_function)(theSelfTypeAndVar, \ 112 | SEL _cmd \ 113 | FOREACH_ARGS(CREATE_ARGS,1,##__VA_ARGS__ )) \ 114 | = NULL; \ 115 | id newImpBlock = \ 116 | ^returnType(theSelfTypeAndVar FOREACH_ARGS(CREATE_ARGS,1,##__VA_ARGS__ )) { \ 117 | metamacro_if_eq(notWorkSubClass,1) \ 118 | (if (!shield_hook_check_block(object_getClass(self),__xxHookClass,&associatedKey)) \ 119 | { \ 120 | XXHookTypeIsVoidType(returnType) \ 121 | (XXHookOrgin(__VA_ARGS__ ); return;) \ 122 | (return XXHookOrgin(__VA_ARGS__ );) \ 123 | }) \ 124 | () \ 125 | 126 | 127 | #define __xxHookClassEnd \ 128 | }; \ 129 | objc_setAssociatedObject(__xxHookClass, \ 130 | &associatedKey, \ 131 | [newImpBlock copy], \ 132 | OBJC_ASSOCIATION_RETAIN_NONATOMIC); \ 133 | __xx_hook_orgin_function = shield_hook_imp_function(__xxHookClass, \ 134 | __xxHookSel, \ 135 | imp_implementationWithBlock(newImpBlock)); \ 136 | \ 137 | 138 | // 拦截静态类 私有 139 | #define __XXStaticHookClass(theCFunctionName,theHookClassType,selfType,GroupName,returnType,theSEL,... ) \ 140 | static BOOL theCFunctionName (); \ 141 | static void* metamacro_concat(theCFunctionName, pointer) __attribute__ ((used, section ("__DATA,__sh" # GroupName))) = theCFunctionName; \ 142 | static BOOL theCFunctionName () { \ 143 | __XXHookClassBegin(theHookClassType, \ 144 | 0, \ 145 | 0, \ 146 | , \ 147 | returnType, \ 148 | theSEL, \ 149 | selfType self, \ 150 | ##__VA_ARGS__) \ 151 | 152 | // 拦截静态类 153 | #define XXStaticHookPrivateClass(theHookClassType,publicType,GroupName,returnType,theSEL,... ) \ 154 | __XXStaticHookClass(metamacro_concat(__shield_hook_auto_load_function_ , __COUNTER__), \ 155 | NSClassFromString(@#theHookClassType), \ 156 | publicType, \ 157 | GroupName, \ 158 | returnType, \ 159 | theSEL, \ 160 | ## __VA_ARGS__ ) \ 161 | 162 | #define XXStaticHookClass(theHookClassType,GroupName,returnType,theSEL,... ) \ 163 | __XXStaticHookClass(metamacro_concat(__shield_hook_auto_load_function_ , __COUNTER__), \ 164 | [theHookClassType class], \ 165 | theHookClassType*, \ 166 | GroupName, \ 167 | returnType, \ 168 | theSEL, \ 169 | ## __VA_ARGS__ ) \ 170 | 171 | // 拦截静态类 172 | #define XXStaticHookMetaClass(theHookClassType,GroupName,returnType,theSEL,... ) \ 173 | __XXStaticHookClass(metamacro_concat(__shield_hook_auto_load_function_ , __COUNTER__), \ 174 | object_getClass([theHookClassType class]), \ 175 | Class, \ 176 | GroupName, \ 177 | returnType, \ 178 | theSEL, \ 179 | ## __VA_ARGS__ ) \ 180 | 181 | // 拦截静态类 182 | #define XXStaticHookPrivateMetaClass(theHookClassType,publicType,GroupName,returnType,theSEL,... ) \ 183 | __XXStaticHookClass(metamacro_concat(__shield_hook_auto_load_function_ , __COUNTER__), \ 184 | object_getClass(NSClassFromString(@#theHookClassType)), \ 185 | publicType, \ 186 | GroupName, \ 187 | returnType, \ 188 | theSEL, \ 189 | ## __VA_ARGS__ ) \ 190 | 191 | #define XXStaticHookEnd __xxHookClassEnd return YES; \ 192 | } 193 | 194 | #define XXStaticHookEnd_SaveOri(P) __xxHookClassEnd P = __xx_hook_orgin_function; return YES; } \ 195 | 196 | 197 | 198 | #define NSSelectorFromWordsForEach(INDEX,VALUE) \ 199 | metamacro_if_eq(metamacro_is_even(INDEX),1) \ 200 | (@#VALUE) \ 201 | (@"%@") 202 | 203 | #define NSSelectorFromWordsForEach2(INDEX,VALUE) \ 204 | metamacro_if_eq(metamacro_is_even(INDEX),1) \ 205 | () \ 206 | (,@#VALUE) 207 | 208 | #define NSSelectorFromWords(...) \ 209 | NSSelectorFromString( [NSString stringWithFormat:metamacro_foreach(NSSelectorFromWordsForEach,,__VA_ARGS__) \ 210 | metamacro_foreach(NSSelectorFromWordsForEach2,,__VA_ARGS__) ]) 211 | 212 | 213 | 214 | 215 | 216 | // 私有 请不要手动调用 217 | void * shield_hook_imp_function(Class clazz, 218 | SEL sel, 219 | void *newFunction); 220 | BOOL shield_hook_check_block(Class objectClass, Class hookClass,void* associatedKey); 221 | Class shield_hook_getClassFromObject(id object); 222 | 223 | 224 | // 启动 225 | void shield_hook_load_group(NSString* groupName); 226 | BOOL defaultSwizzlingOCMethod(Class self, SEL origSel_, SEL altSel_); 227 | -------------------------------------------------------------------------------- /XXShield/Classes/Swizzle/XXShieldSwizzling.m: -------------------------------------------------------------------------------- 1 | // 2 | // XXShieldSwizzling.m 3 | // XXShield 4 | // 5 | // Created by nero on 2017/1/19. 6 | // Copyright © 2017年 XXShield. All rights reserved. 7 | // 8 | 9 | 10 | #include "XXShieldSwizzling.h" 11 | #import 12 | #import 13 | #import 14 | 15 | BOOL defaultSwizzlingOCMethod(Class self, SEL origSel_, SEL altSel_) { 16 | Method origMethod = class_getInstanceMethod(self, origSel_); 17 | if (!origMethod) { 18 | return NO; 19 | } 20 | 21 | Method altMethod = class_getInstanceMethod(self, altSel_); 22 | if (!altMethod) { 23 | return NO; 24 | } 25 | 26 | class_addMethod(self, 27 | origSel_, 28 | class_getMethodImplementation(self, origSel_), 29 | method_getTypeEncoding(origMethod)); 30 | class_addMethod(self, 31 | altSel_, 32 | class_getMethodImplementation(self, altSel_), 33 | method_getTypeEncoding(altMethod)); 34 | 35 | method_exchangeImplementations(class_getInstanceMethod(self, origSel_), class_getInstanceMethod(self, altSel_)); 36 | return YES; 37 | 38 | } 39 | 40 | void* shield_hook_imp_function(Class clazz, 41 | SEL sel, 42 | void *newFunction) { 43 | Method oldMethod = class_getInstanceMethod(clazz, sel); 44 | BOOL succeed = class_addMethod(clazz, 45 | sel, 46 | (IMP)newFunction, 47 | method_getTypeEncoding(oldMethod)); 48 | if (succeed) { 49 | return nil; 50 | } else { 51 | return method_setImplementation(oldMethod, (IMP)newFunction); 52 | } 53 | } 54 | 55 | BOOL shield_hook_check_block(Class objectClass, Class hookClass,void* associatedKey) { 56 | while (objectClass && objectClass != hookClass) { 57 | if (objc_getAssociatedObject(objectClass, associatedKey)) { 58 | return NO; 59 | } 60 | objectClass = class_getSuperclass(objectClass); 61 | } 62 | return YES; 63 | } 64 | 65 | Class shield_hook_getClassFromObject(id object) { 66 | // 如果不是class 67 | if (!object_isClass(object)) { 68 | return object_getClass(object); 69 | } else { 70 | return object; 71 | } 72 | } 73 | 74 | #ifndef __LP64__ 75 | 76 | #define mach_header_ mach_header 77 | 78 | #else 79 | 80 | #define mach_header_ mach_header_64 81 | 82 | #endif 83 | 84 | void shield_hook_load_group(NSString *groupName) { 85 | uint32_t count = _dyld_image_count(); 86 | for (uint32_t i = 0 ; i < count ; i ++) { 87 | const struct mach_header_* header = (void*)_dyld_get_image_header(i); 88 | NSString *string = [NSString stringWithFormat:@"__sh%@",groupName]; 89 | unsigned long size = 0; 90 | uint8_t *data = getsectiondata(header, "__DATA", [string UTF8String],&size); 91 | if (data && size > 0) { 92 | void **pointers = (void**)data; 93 | uint32_t count = (uint32_t)(size / sizeof(void*)); 94 | for (uint32_t i = 0 ; i < count ; i ++) { 95 | void(*pointer)() = pointers[i]; 96 | pointer(); 97 | } 98 | break; 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /XXShield/Classes/XXShield.h: -------------------------------------------------------------------------------- 1 | // 2 | // XXShield.h 3 | // Pods 4 | // 5 | // Created by nero on 2017/10/31. 6 | // 7 | 8 | #import 9 | 10 | //! Project version number for Expecta. 11 | FOUNDATION_EXPORT double XXShieldVersionNumber; 12 | 13 | //! Project version string for Expecta. 14 | FOUNDATION_EXPORT const unsigned char XXShieldVersionString[]; 15 | 16 | #import 17 | -------------------------------------------------------------------------------- /XXShield/Classes/XXShieldSDK.h: -------------------------------------------------------------------------------- 1 | // 2 | // XXShieldSDK.h 3 | // XXShield 4 | // 5 | // Created by nero on 2017/1/18. 6 | // Copyright © 2017年 XXShield. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | typedef NS_OPTIONS(NSUInteger, EXXShieldType) { 14 | EXXShieldTypeUnrecognizedSelector = 1 << 1, 15 | EXXShieldTypeContainer = 1 << 2, 16 | EXXShieldTypeNSNull = 1 << 3, 17 | EXXShieldTypeKVO = 1 << 4, 18 | EXXShieldTypeNotification = 1 << 5, 19 | EXXShieldTypeTimer = 1 << 6, 20 | EXXShieldTypeDangLingPointer = 1 << 7, 21 | EXXShieldTypeExceptDangLingPointer = (EXXShieldTypeUnrecognizedSelector | EXXShieldTypeContainer | 22 | EXXShieldTypeNSNull| EXXShieldTypeKVO | 23 | EXXShieldTypeNotification | EXXShieldTypeTimer) 24 | }; 25 | 26 | @protocol XXRecordProtocol 27 | 28 | - (void)recordWithReason:(NSError *)reason; 29 | 30 | @end 31 | 32 | @interface XXShieldSDK : NSObject 33 | 34 | /** 35 | 注册汇报中心 36 | 37 | @param record 汇报中心 38 | */ 39 | + (void)registerRecordHandler:(id)record; 40 | 41 | /** 42 | 注册SDK,默认只要开启就打开防Crash,如果需要DEBUG关闭,请在调用处使用条件编译 43 | 本注册方式不包含EXXShieldTypeDangLingPointer类型 44 | */ 45 | + (void)registerStabilitySDK; 46 | 47 | /** 48 | 本注册方式不包含EXXShieldTypeDangLingPointer类型 49 | 50 | @param ability ability 51 | */ 52 | + (void)registerStabilityWithAbility:(EXXShieldType)ability; 53 | 54 | /** 55 | ///注册EXXShieldTypeDangLingPointer需要传入存储类名的array,不支持系统框架类。 56 | 57 | @param ability ability description 58 | @param classNames 野指针类列表 59 | */ 60 | + (void)registerStabilityWithAbility:(EXXShieldType)ability withClassNames:(nonnull NSArray *)classNames; 61 | 62 | @end 63 | 64 | NS_ASSUME_NONNULL_END 65 | 66 | -------------------------------------------------------------------------------- /XXShield/Classes/XXShieldSDK.m: -------------------------------------------------------------------------------- 1 | // 2 | // XXShieldSDK.m 3 | // XXShield 4 | // 5 | // Created by nero on 2017/1/18. 6 | // Copyright © 2017年 XXShield. All rights reserved. 7 | // 8 | 9 | 10 | #import 11 | #import "XXShieldSDK.h" 12 | #import "NSObject+DanglingPointer.h" 13 | #import "XXDanglingPonterService.h" 14 | #import "XXShieldSwizzling.h" 15 | #import "XXRecord.h" 16 | 17 | @implementation XXShieldSDK 18 | 19 | + (void)registerStabilitySDK { 20 | [self registerStabilityWithAbility:(EXXShieldTypeExceptDangLingPointer)]; 21 | } 22 | 23 | + (void)registerStabilityWithAbility:(EXXShieldType)ability { 24 | if (ability & EXXShieldTypeUnrecognizedSelector) { 25 | [self registerUnrecognizedSelector]; 26 | } 27 | if (ability & EXXShieldTypeContainer) { 28 | [self registerContainer]; 29 | } 30 | if (ability & EXXShieldTypeNSNull) { 31 | [self registerNSNull]; 32 | } 33 | if (ability & EXXShieldTypeKVO) { 34 | [self registerKVO]; 35 | } 36 | if (ability & EXXShieldTypeNotification) { 37 | [self registerNotification]; 38 | } 39 | if (ability & EXXShieldTypeTimer) { 40 | [self registerTimer]; 41 | } 42 | } 43 | 44 | + (void)registerStabilityWithAbility:(EXXShieldType)ability withClassNames:(NSArray *)arr { 45 | if ((ability & EXXShieldTypeDangLingPointer) && [arr count]) { 46 | [self registerDanglingPointer:arr]; 47 | } 48 | [self registerStabilityWithAbility:ability]; 49 | } 50 | 51 | + (void)registerNSNull { 52 | static dispatch_once_t onceToken; 53 | dispatch_once(&onceToken, ^{ 54 | shield_hook_load_group(XXForOCString(ProtectNull)); 55 | }); 56 | } 57 | 58 | + (void)registerContainer { 59 | static dispatch_once_t onceToken; 60 | dispatch_once(&onceToken, ^{ 61 | shield_hook_load_group(XXForOCString(ProtectCont)); 62 | }); 63 | } 64 | 65 | + (void)registerUnrecognizedSelector { 66 | static dispatch_once_t onceToken; 67 | dispatch_once(&onceToken, ^{ 68 | shield_hook_load_group(XXForOCString(ProtectFW)); 69 | }); 70 | } 71 | 72 | + (void)registerKVO { 73 | static dispatch_once_t onceToken; 74 | dispatch_once(&onceToken, ^{ 75 | shield_hook_load_group(XXForOCString(ProtectKVO)); 76 | }); 77 | } 78 | 79 | + (void)registerNotification { 80 | static dispatch_once_t onceToken; 81 | dispatch_once(&onceToken, ^{ 82 | BOOL ABOVE_IOS8 = (([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0) ? YES : NO); 83 | if (!ABOVE_IOS8) { 84 | shield_hook_load_group(XXForOCString(ProtectNoti)); 85 | } 86 | }); 87 | } 88 | 89 | + (void)registerTimer { 90 | static dispatch_once_t onceToken; 91 | dispatch_once(&onceToken, ^{ 92 | shield_hook_load_group(XXForOCString(ProtectTimer)); 93 | }); 94 | } 95 | 96 | + (void)registerDanglingPointer:(NSArray *)arr { 97 | NSMutableArray *avaibleList = arr.mutableCopy; 98 | for (NSString *className in arr) { 99 | NSBundle *classBundle = [NSBundle bundleForClass:NSClassFromString(className)]; 100 | if (classBundle != [NSBundle mainBundle]) { 101 | [avaibleList removeObject:className]; 102 | } 103 | } 104 | [XXDanglingPonterService getInstance].classArr = avaibleList; 105 | defaultSwizzlingOCMethod([NSObject class], NSSelectorFromString(@"dealloc"), @selector(xx_danglingPointer_dealloc)); 106 | } 107 | 108 | + (void)registerRecordHandler:(nonnull id)record { 109 | [XXRecord registerRecordHandler:record]; 110 | } 111 | 112 | @end 113 | -------------------------------------------------------------------------------- /XXShield/Classes/template/NSArrayObjectAtIndex.h: -------------------------------------------------------------------------------- 1 | 2 | if (self.count == 0) { 3 | 4 | NSString *reason = [NSString stringWithFormat:@"target is %@ method is %@,reason : index %@ out of count %@ of array ", 5 | [self class], XXSEL2Str(@selector(objectAtIndex:)), @(index), @(self.count)]; 6 | [XXRecord recordFatalWithReason:reason errorType:EXXShieldTypeContainer]; 7 | return nil; 8 | } 9 | 10 | if (index >= self.count) { 11 | NSString *reason = [NSString stringWithFormat:@"target is %@ method is %@,reason : index %@ out of count %@ of array ", 12 | [self class], XXSEL2Str(@selector(objectAtIndex:)), @(index), @(self.count)]; 13 | [XXRecord recordFatalWithReason:reason errorType:EXXShieldTypeContainer]; 14 | return nil; 15 | } 16 | 17 | return XXHookOrgin(index); 18 | -------------------------------------------------------------------------------- /XXShield/Classes/template/NSArrayWithObjects.h: -------------------------------------------------------------------------------- 1 | NSInteger newObjsIndex = 0; 2 | id _Nonnull __unsafe_unretained newObjects[cnt]; 3 | for (int i = 0; i < cnt; i++) { 4 | 5 | id objc = objects[i]; 6 | if (objc == nil) { 7 | NSString *reason = [NSString stringWithFormat:@"target is %@ method is %@,reason : Array constructor appear nil ", 8 | [self class], XXSEL2Str(@selector(arrayWithObjects:count:))]; 9 | 10 | [XXRecord recordFatalWithReason:reason errorType:EXXShieldTypeContainer]; 11 | continue; 12 | } 13 | newObjects[newObjsIndex++] = objc; 14 | 15 | } 16 | return XXHookOrgin(newObjects, newObjsIndex); 17 | -------------------------------------------------------------------------------- /XXShield/XXShield.modulemap: -------------------------------------------------------------------------------- 1 | framework module XXShield { 2 | umbrella header "XXShield.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /_Pods.xcodeproj: -------------------------------------------------------------------------------- 1 | Example/Pods/Pods.xcodeproj --------------------------------------------------------------------------------