{"id":24896750,"url":"https://github.com/zackhyz/zhsafetool","last_synced_at":"2025-07-11T07:41:14.561Z","repository":{"id":56930556,"uuid":"141947844","full_name":"ZackHyz/ZHSafeTool","owner":"ZackHyz","description":null,"archived":false,"fork":false,"pushed_at":"2019-08-30T06:30:14.000Z","size":84,"stargazers_count":3,"open_issues_count":0,"forks_count":2,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-02-01T20:01:23.712Z","etag":null,"topics":["crash","guard","ios","objective-c","safe"],"latest_commit_sha":null,"homepage":null,"language":"Objective-C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ZackHyz.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-07-23T01:50:31.000Z","updated_at":"2024-05-22T13:35:14.000Z","dependencies_parsed_at":"2022-08-21T05:50:23.714Z","dependency_job_id":null,"html_url":"https://github.com/ZackHyz/ZHSafeTool","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ZackHyz%2FZHSafeTool","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ZackHyz%2FZHSafeTool/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ZackHyz%2FZHSafeTool/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ZackHyz%2FZHSafeTool/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ZackHyz","download_url":"https://codeload.github.com/ZackHyz/ZHSafeTool/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":236713492,"owners_count":19193079,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["crash","guard","ios","objective-c","safe"],"created_at":"2025-02-01T20:15:06.984Z","updated_at":"2025-02-01T20:15:07.801Z","avatar_url":"https://github.com/ZackHyz.png","language":"Objective-C","funding_links":[],"categories":[],"sub_categories":[],"readme":"# 不写一行代码，大幅降低崩溃率\n\n\n# 背景：\n我们在项目中，经常会遇到各种莫名其妙的崩溃，但是常见的就那么几种而且出现率非常高，如果我们每次出现了就去修复，那么以后同样的崩溃出现的时候，还是又得做一次修复，这样效率非常低下，现在我们可以基于Runtime来做统一的处理，只要遇到类似的崩溃情况都能hold住，大幅降低崩溃率，而且不入侵业务代码。自从用了这套解决方案之后，我们App的崩溃率从千分之五降到了万分之二。\n\n# 接入方法\n\n```\npod 'ZHSafeTool'\n```\n\n# 解决常见崩溃的代码段\n1.数组溢出\n\n```\n@implementation NSMutableArray (Exception)\n+ (void)load{\n    static dispatch_once_t onceToken;\n    dispatch_once(\u0026onceToken, ^{\n        @autoreleasepool {\n            [objc_getClass(\"__NSArrayM\") swizzleMethod:@selector(objectAtIndex:) swizzledSelector:@selector(replace_objectAtIndex:)];\n            [objc_getClass(\"__NSArrayM\") swizzleMethod:@selector(insertObject:atIndex:) swizzledSelector:@selector(replace_insertObject:atIndex:)];\n            [objc_getClass(\"__NSArrayM\") swizzleMethod:@selector(setObject:atIndexedSubscript:) swizzledSelector:@selector(replace_setObject:atIndexedSubscript:)];\n            [objc_getClass(\"__NSArrayM\") swizzleMethod:@selector(replaceObjectAtIndex:withObject:) swizzledSelector:@selector(replace_replaceObjectAtIndex:withObject:)];\n            [objc_getClass(\"__NSArrayM\") swizzleMethod:@selector(removeObjectAtIndex:) swizzledSelector:@selector(replace_removeObjectAtIndex:)];\n            [objc_getClass(\"__NSArrayM\") swizzleMethod:@selector(removeObjectsInRange:) swizzledSelector:@selector(replace_removeObjectsInRange:)];\n            [objc_getClass(\"__NSArrayM\") swizzleMethod:@selector(removeObject:inRange:) swizzledSelector:@selector(replace_removeObject:inRange:)];\n            [objc_getClass(\"__NSArrayM\") swizzleMethod:@selector(objectAtIndexedSubscript:) swizzledSelector:@selector(replace_objectAtIndexedSubscript:)];\n        }\n    });\n}\n\n- (id)replace_objectAtIndex:(NSInteger)index{\n    if (index \u003e= self.count || index \u003c 0) {\n        return nil;\n    }\n    return [self replace_objectAtIndex:index];\n}\n\n- (void)replace_insertObject:(id)object atIndex:(NSInteger)index{\n    if (index \u003e self.count || index \u003c 0) {\n        return ;\n    }\n    if (!object) {\n        return;\n    }\n    return [self replace_insertObject:object atIndex:index];\n}\n\n- (void)replace_setObject:(id)object atIndexedSubscript:(NSInteger)index{\n    if (index \u003e self.count || index \u003c 0) {\n        return ;\n    }\n    if (!object) {\n        return;\n    }\n    return [self replace_setObject:object atIndexedSubscript:index];\n}\n\n- (void)replace_replaceObjectAtIndex:(NSInteger)index withObject:(id)anObject{\n    if (index \u003e= self.count || index \u003c 0) {\n        return ;\n    }\n    if (!anObject) {\n        return;\n    }\n    return [self replace_replaceObjectAtIndex:index withObject:anObject];\n}\n\n\n- (void)replace_removeObjectAtIndex:(NSInteger)index{\n    if (index \u003e= self.count || index \u003c 0) {\n        return ;\n    }\n    return [self replace_removeObjectAtIndex:index];\n}\n\n- (void)replace_removeObjectsInRange:(NSRange)range {\n    if (range.location \u003e self.count) {\n        return;\n    }\n    if (range.length \u003e self.count) {\n        return;\n    }\n    if ((range.location + range.length) \u003e self.count) {\n        return;\n    }\n    return [self replace_removeObjectsInRange:range];\n}\n\n- (void)replace_removeObject:(id)anObject inRange:(NSRange)range{\n    if (range.location \u003e self.count) {\n        return;\n    }\n    if (range.length \u003e self.count) {\n        return;\n    }\n    if ((range.location + range.length) \u003e self.count) {\n        return;\n    }\n    if (!anObject){\n        return;\n    }\n    return [self replace_removeObject:anObject inRange:range];\n}\n\n- (id)replace_objectAtIndexedSubscript:(NSInteger)index{\n    if (index \u003e= self.count || index \u003c 0) {    \n        return nil;\n    }\n    return [self replace_objectAtIndexedSubscript:index];\n}\n\n```\n\n2.set nil object\n```\n@implementation NSDictionary (Exception)\n\n+ (void)load {\n    static dispatch_once_t onceToken;\n    dispatch_once(\u0026onceToken, ^{\n        @autoreleasepool {\n            [objc_getClass(\"__NSPlaceholderDictionary\") swizzleMethod:@selector(initWithObjects:forKeys:count:) swizzledSelector:@selector(replace_initWithObjects:forKeys:count:)];\n            [objc_getClass(\"__NSDictionaryM\") swizzleMethod:@selector(setObject:forKey:) swizzledSelector:@selector(replace_setObject:forKey:)];\n            [objc_getClass(\"__NSDictionaryM\") swizzleMethod:@selector(removeObjectForKey:) swizzledSelector:@selector(replace_removeObjectForKey:)];\n        }\n    });\n}\n\n- (instancetype)replace_initWithObjects:(id *)objects forKeys:(id\u003cNSCopying\u003e *)keys count:(NSUInteger)count{\n    NSUInteger rightCount = 0;\n    for (NSUInteger i = 0; i \u003c count; i++) {\n        if (!(keys[i] \u0026\u0026 objects[i])) {\n            break;\n        }else{\n            rightCount++;\n        }\n    }\n    return [self replace_initWithObjects:objects forKeys:keys count:rightCount];\n}\n\n- (void)replace_setObject:(id)object forKey:(id\u003cNSCopying\u003e)key{\n    if ((!object) || (!key)) {\n      return;\n    }\n    return [self replace_setObject:object forKey:key];\n}\n\n- (void)replace_removeObjectForKey:(id)key{\n    if (!key) {\n        return;\n    }\n    return [self replace_removeObjectForKey:key];\n}\n```\n\n3.no selector\n\n\n```\n@interface FakeForwardTargetObject : NSObject\n\n- (instancetype)initWithSelector:(SEL)aSelector;\n\n@end\n\nid fakeIMP(id sender,SEL sel,...){\n    return nil;\n}\n\n@implementation FakeForwardTargetObject\n\n- (instancetype)initWithSelector:(SEL)aSelector\n{\n    if (self = [super init]) {\n        if(class_addMethod([self class], aSelector, (IMP)fakeIMP, NULL)) {\n            NSLog(@\"add Fake Selector:[instance %@]\", NSStringFromSelector(aSelector));\n        }\n    }\n    return self;\n}\n\n@end\n\n\n\n\n@implementation NSObject (Exception)\n\n\n+ (void)load {\n    static dispatch_once_t onceToken;\n    dispatch_once(\u0026onceToken, ^{\n        @autoreleasepool {\n           [objc_getClass(\"NSObject\") swizzleMethod:@selector(forwardingTargetForSelector:) swizzledSelector:@selector(replace_forwardingTargetForSelector:)];\n            \n        }\n    });\n}\n\n- (id)replace_forwardingTargetForSelector:(SEL)aSelector\n{\n    NSMethodSignature *signature = [self methodSignatureForSelector:aSelector];\n    if ([self respondsToSelector:aSelector] || signature) {\n        return [self replace_forwardingTargetForSelector:aSelector];\n    }\n    \n    return [NSObject createFakeForwardTargetObject:self selector:aSelector];\n\n}\n\n+ (id)createFakeForwardTargetObject:(id)aTarget selector:(SEL)aSelector\n{\n    if ([[NSString string] respondsToSelector:aSelector]) {\n        NSString *szTarget = nil;\n        if ([aTarget isKindOfClass:[NSNumber class]]) {\n            szTarget = [NSString stringWithFormat:@\"%@\", aTarget];\n        }\n        \n        if (szTarget) {\n            return szTarget;\n        }\n    }\n    \n    FakeForwardTargetObject *fakeTaget = [[FakeForwardTargetObject alloc] initWithSelector:aSelector];\n    return fakeTaget;\n}\n```\n4.webView的webGL随机崩溃（在后台的时候关闭这个webGL可以非常有效的降低崩溃率，这个需要在业务代码里面加一点点代码，详情可以看下面说的Demo）\n\n```\n@implementation UIWebView (Exception)\n\ntypedef void (*CallFuc)(id, SEL, BOOL);\ntypedef BOOL (*GetFuc)(id, SEL);\n\n//打开/关闭 webview 的GL \n-(BOOL)enableGL:(BOOL)bEnable\n{\n    UIWebView *view = self;\n    BOOL bRet = NO;\n    do\n    {\n        Ivar internalVar = class_getInstanceVariable([view class], \"_internal\");\n        if (!internalVar)\n        {\n            NSLog(@\"enable GL _internal invalid!\");\n            break;\n        }\n        \n        UIWebViewInternal* internalObj = object_getIvar(view, internalVar);\n        Ivar browserVar = class_getInstanceVariable(object_getClass(internalObj), \"browserView\");\n        if (!browserVar)\n        {\n            NSLog(@\"enable GL browserView invalid!\");\n            break;\n        }\n        \n        id webbrowser = object_getIvar(internalObj, browserVar);\n        Ivar webViewVar = class_getInstanceVariable(object_getClass(webbrowser), \"_webView\");\n        if (!webViewVar)\n        {\n            NSLog(@\"enable GL _webView invalid!\");\n            break;\n        }\n        \n        id webView = object_getIvar(webbrowser, webViewVar);\n        if (!webView)\n        {\n            NSLog(@\"enable GL webView obj nil!\");\n        }\n        \n        if(object_getClass(webView) != NSClassFromString(@\"WebView\"))\n        {\n            NSLog(@\"enable GL webView not WebView!\");\n            break;\n        }\n        \n        SEL selector = NSSelectorFromString(@\"_setWebGLEnabled:\");\n        IMP impSet = [webView methodForSelector:selector];\n        CallFuc func = (CallFuc)impSet;\n        func(webView, selector, bEnable);\n        \n        SEL selectorGet = NSSelectorFromString(@\"_webGLEnabled\");\n        IMP impGet = [webView methodForSelector:selectorGet];\n        GetFuc funcGet = (GetFuc)impGet;\n        BOOL val = funcGet(webView, selector);\n        \n        bRet = (val == bEnable);\n        \n    }while(NO);\n    \n    return bRet;\n}\n```\n\n# Demo\n\nhttps://github.com/ZackHyz/ZHSafeTool\n\n\n\n-------------------------------------------------------\n# 8月29日更新1.1.0\n\n1.增加异常frame设置时候的防崩溃。\n\n2.对UITableView reloadSections 和 reloadRowsAtIndexPaths做输入参数校验。\n  对UICollectionView reloadSections 和 reloadItemsAtIndexPaths做输入参数校验。\n\n-------------------------------------------------------\n# 10月10日 \n10月的崩溃率重新稳定到了万分之二，因为中途加入新人挖了不少坑， 从1月开始崩溃率从千分之五的峰值降到了万分之二。\n\n-------------------------------------------------------\n# 2019年4月25日 \n解决iOS 8\u00269弹出键盘按Home键崩溃的问题。\n\n\n-------------------------------------------------------\n\n# 2019年8月29日 \n1. 增加Debug宏（DISABLE_SAFETOOL）禁用防崩库。\n2. 增加catch崩溃时候的代码片段回调。\n3. 移除 UIWebView+Exception 防止审核不通过。\n\n![](https://user-gold-cdn.xitu.io/2018/10/10/1665c85387ebee1e?w=2254\u0026h=818\u0026f=png\u0026s=219149)\n\n# 掘金原文\n\nhttps://juejin.im/post/5b5e82ade51d4513ee6ded6b\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzackhyz%2Fzhsafetool","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fzackhyz%2Fzhsafetool","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzackhyz%2Fzhsafetool/lists"}