Code Monkey home page Code Monkey logo

xxshield's Introduction

CI Status Version License Platform

前言

正在运行的 APP 突然 Crash,是一件令人不爽的事,会流失用户,影响公司发展,所以 APP 运行时拥有防 Crash 功能能有效降低 Crash 率,提升 APP 稳定性。但是有时候 APP Crash 是应有的表现,我们不让 APPCrash 可能会导致别的逻辑错误,不过我们可以抓取到应用当前的堆栈信息并上传至相关的服务器,分析并修复这些 BUG。

所以本文介绍的 XXShield 库有两个重要的功能:

  1. 防止Crash
  2. 捕获异常状态下的崩溃信息

类似的相关技术分析也有 网易iOS App运行时Crash自动防护实践

目前已经实现的功能

  1. Unrecognized Selector Crash
  2. KVO Crash
  3. Container Crash
  4. NSNotification Crash
  5. NSNull Crash
  6. NSTimer Crash
  7. 野指针 Crash

1 Unrecoginzed Selector Crash

出现原因

由于 Objective-C 是动态语言,所有的消息发送都会放在运行时去解析,有时候我们把一个信息传递给了错误的类型,就会导致这个错误。

解决办法

Objective-C 在出现无法解析的方法时有三部曲来进行消息转发。 详见Objective-C Runtime 运行时之三:方法与消息

  1. 动态方法解析
  2. 备用接收者
  3. 完整转发

1 一般适用与 Dynamic 修饰的 Property 2 一般适用与将方法转发至其他对象 3 一般适用与消息可以转发多个对象,可以实现类似多继承或者转发中心的概念。

这里选择的是方案二,因为三里面用到了 NSInvocation 对象,此对象性能开销较大,而且这种异常如果出现必然频次较高。最适合将消息转发到一个备用者对象上。

这里新建一个智能转发类。此对象将在其他对象无法解析数据时,返回一个 0 来防止 Crash。返回 0 是因为这个通用的智能转发类做的操作接近向 nil 发送一个消息。

代码如下

#import <objc/runtime.h>

/**
 default Implement
 @param target trarget
 @param cmd cmd
 @param ... other param
 @return default Implement is zero
 */
int smartFunction(id target, SEL cmd, ...) {
    return 0;
}

static BOOL __addMethod(Class clazz, SEL sel) {
    NSString *selName = NSStringFromSelector(sel);
    
    NSMutableString *tmpString = [[NSMutableString alloc] initWithFormat:@"%@", selName];
    
    int count = (int)[tmpString replaceOccurrencesOfString:@":"
                                                withString:@"_"
                                                   options:NSCaseInsensitiveSearch
                                                     range:NSMakeRange(0, selName.length)];
    
    NSMutableString *val = [[NSMutableString alloc] initWithString:@"i@:"];
    
    for (int i = 0; i < count; i++) {
        [val appendString:@"@"];
    }
    const char *funcTypeEncoding = [val UTF8String];
    return class_addMethod(clazz, sel, (IMP)smartFunction, funcTypeEncoding);
}

@implementation XXShieldStubObject

+ (XXShieldStubObject *)shareInstance {
    static XXShieldStubObject *singleton;
    if (!singleton) {
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            singleton = [XXShieldStubObject new];
        });
    }
    return singleton;
}

- (BOOL)addFunc:(SEL)sel {
    return __addMethod([XXShieldStubObject class], sel);
}

+ (BOOL)addClassFunc:(SEL)sel {
    Class metaClass = objc_getMetaClass(class_getName([XXShieldStubObject class]));
    return __addMethod(metaClass, sel);
}

@end

我们这里需要 Hook NSObject的 - (id)forwardingTargetForSelector:(SEL)aSelector 方法启动消息转发。 很多人不知道的是如果想要转发类方法,只需要实现一个同名的类方法即可,虽然在头文件中此方法并未声明。

XXStaticHookClass(NSObject, ProtectFW, id, @selector(forwardingTargetForSelector:), (SEL)aSelector) {
    // 1 如果是NSSNumber 和NSString没找到就是类型不对  切换下类型就好了
    if ([self isKindOfClass:[NSNumber class]] && [NSString instancesRespondToSelector:aSelector]) {
        NSNumber *number = (NSNumber *)self;
        NSString *str = [number stringValue];
        return str;
    } else if ([self isKindOfClass:[NSString class]] && [NSNumber instancesRespondToSelector:aSelector]) {
        NSString *str = (NSString *)self;
        NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
        NSNumber *number = [formatter numberFromString:str];
        return number;
    }
    
    BOOL aBool = [self respondsToSelector:aSelector];
    NSMethodSignature *signatrue = [self methodSignatureForSelector:aSelector];
    
    if (aBool || signatrue) {
        return XXHookOrgin(aSelector);
    } else {
        XXShieldStubObject *stub = [XXShieldStubObject shareInstance];
        [stub addFunc:aSelector];
        
        NSString *reason = [NSString stringWithFormat:@"*****Warning***** logic error.target is %@ method is %@, reason : method forword to SmartFunction Object default implement like send message to nil.",
                            [self class], NSStringFromSelector(aSelector)];
        [XXRecord recordFatalWithReason:reason errorType:EXXShieldTypeUnrecognizedSelector];
        
        return stub;
    }
}
XXStaticHookEnd

这里汇报了 Crash 信息,出现消息转发一般是一个 logic 错误,为必须修复的Bug,上报尤为重要。


2 KVO Crash

出现原因

KVOCrash总结下来有以下2大类。

  1. 不匹配的移除和添加关系。
  2. 观察者和被观察者释放的时候没有及时断开观察者关系。

解决办法

尼古拉斯赵四说过 :赵四 对比到程序世界就是,程序世界没有什么难以解决的问题都是不可以通过抽象层次来解决的,如果有,那就两层。 纵观程序的架构设计,计算机网络协议分层设计,操作系统内核设计等等都是如此。

问题1 : 不成对的添加观察者和移除观察者会导致 Crash,以往我们使用 KVO,观察者和被观察者都是直接交互的。这里的设计方案是我们找一个 Proxy 用来做转发, 真正的观察者是 Proxy,被观察者出现了通知信息,由 Proxy 做分发。所以 Proxy 里面要保存一个数据结构 {keypath : [observer1, observer2,...]} 。

@interface XXKVOProxy : NSObject {
    __unsafe_unretained NSObject *_observed;
}

/**
 {keypath : [ob1,ob2](NSHashTable)}
 */
@property (nonatomic, strong) NSMutableDictionary<NSString *, NSHashTable<NSObject *> *> *kvoInfoMap;

@end

我们需要 Hook NSObject的 �KVO 相关方法。

- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context;

- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath;
  1. 在添加观察者时 addObserver

  2. 在移除观察者时

removeObserver

问题2: 观察者和被观察者释放的时候没有断开观察者关系。 对于观察者, 既然我们是自己用 Proxy 做的分发,我们自己就需要保存观察者,这里我们简单的使用 NSHashTable 指定指针持有策略为 weak 即可。

对于被观察者,我们使用 iOS 界的毒瘤-MethodSwizzling 一文中到的方法。我们在被观察者上绑定一个关联对象,在关联对象的 dealloc 方法中做相关操作即可。

- (void)dealloc {
    @autoreleasepool {
        NSDictionary<NSString *, NSHashTable<NSObject *> *> *kvoinfos =  self.kvoInfoMap.copy;
        for (NSString *keyPath in kvoinfos) {
            // call original  IMP
            __xx_hook_orgin_function_removeObserver(_observed,@selector(removeObserver:forKeyPath:),self, keyPath);
        }
    }
}

3 Container Crash

出现原因

容器在任何编程语言中都尤为重要,容器是数据的载体,很多容器对容器放空值都做了容错处理。不幸的是 Objective-C 并没有,容器插入了 nil 就会导致 Crash,容器还有另外一个最容易 Crash 的原因就是下标越界。

解决办法

常见的容器有 NS(Mutable)Array , NS(Mutable)Dictionary, NSCache 等。我们需要 hook 常见的方法加入检测功能并且捕获堆栈信息上报。

例如

XXStaticHookClass(NSArray, ProtectCont, id, @selector(objectAtIndex:),(NSUInteger)index) {
if (self.count == 0) {
    
    NSString *reason = [NSString stringWithFormat:@"target is %@ method is %@,reason : index %@ out of count %@ of array ",
                        [self class], XXSEL2Str(@selector(objectAtIndex:)), @(index), @(self.count)];
    [XXRecord recordFatalWithReason:reason errorType:EXXShieldTypeContainer];
    return nil;
}

if (index >= self.count) {
    NSString *reason = [NSString stringWithFormat:@"target is %@ method is %@,reason : index %@ out of count %@ of array ",
                        [self class], XXSEL2Str(@selector(objectAtIndex:)), @(index), @(self.count)];
    [XXRecord recordFatalWithReason:reason errorType:EXXShieldTypeContainer];
    return nil;
}

return XXHookOrgin(index);
}
XXStaticHookEnd

但是需要注意的是 NSArray 是一个 Class Cluster 的抽象父类,所以我们需要 Hook 到我们真正的子类。

这里给出一个辅助方法,获取一个类的所有直接子类:

+ (NSArray *)findAllOf:(Class)defaultClass {
    
    int count = objc_getClassList(NULL, 0);
    
    if (count <= 0) {
        
        @throw@"Couldn't retrieve Obj-C class-list";
        
        return @[defaultClass];
    }
    
    NSMutableArray *output = @[].mutableCopy;
    
    Class *classes = (Class *) malloc(sizeof(Class) * count);
    
    objc_getClassList(classes, count);
    
    for (int i = 0; i < count; ++i) {
        
        if (defaultClass == class_getSuperclass(classes[i]))//子类
        {
            [output addObject:classes[i]];
        }
        
    }
    
    free(classes);
    
    return output.copy;
    
}

// 对于NSarray :

//[NSarray array] 和 @[] 的类型是__NSArray0
//只有一个元素的数组类型 __NSSingleObjectArrayI,
// 其他的大部分是//__NSArrayI,



// 对于NSMutableArray :
//[NSMutableDictionary dictionary] 和 @[].mutableCopy__NSArrayM



// 对于NSDictionary: :

//[NSDictionary dictionary];。 @{}; __NSDictionary0
// 其他一般是  __NSDictionaryI

// 对于NSMutableDictionary: :
// 一般用到的是 __NSDictionaryM

4 NSNotification Crash

出现原因

在 iOS8 及以下的操作系统中添加的观察者一般需要在 dealloc 的时候做移除,如果开发者忘记移除,则在发送通知的时候会导致 Crash,而在 iOS9 上即使移忘记除也无所谓,猜想可能是 iOS9 之后系统将通知中心持有对象由 assign 变为了weak

解决办法

所以这里两种解决办法

  1. 类似 KVO 中间加上 Proxy 层,使用 weak 指针来持有对象
  2. 在 dealloc 的时候将未被移除的观察者移除

这里我们使用 iOS 界的毒瘤-MethodSwizzling 一文中到的方法。


5 NSNull Crash

出现原因

虽然 Objecttive-C 不允许开发者将 nil 放进容器内,但是另外一个代表用户态 的类 NSNull 却可以放进容器,但令人不爽的是这个类的实例,并不能响应任何方法。

容器中出现 NSNull 一般是 API 接口返回了含有 null 的 JSON �数据, 调用方通常将其理解为 NSNumber,NSString,NSDictionary 和 NSArray。 这时开发者如果没有做好防御 一旦对 NSNull 这个类型调用任何方法都会出现 unrecongized selector 错误。

解决办法

我们在 NSNull 的转发方法中可以判断�上面的四种类型是否可以解析。如果可以解析直接将其转发给�这几种对象,如果不能则调用父类的默认实现。

XXStaticHookClass(NSNull, ProtectNull, id, @selector(forwardingTargetForSelector:), (SEL) aSelector) {
    static NSArray *sTmpOutput = nil;
    if (sTmpOutput == nil) {
        sTmpOutput = @[@"", @0, @[], @{}];
    }
    
    for (id tmpObj in sTmpOutput) {
        if ([tmpObj respondsToSelector:aSelector]) {
            return tmpObj;
        }
    }
    return XXHookOrgin(aSelector);
}
XXStaticHookEnd

6. NSTimer Crash

出现原因

在使用 + (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo 创建定时任务的时候,target� 一般都会持有 timer,timer又会持有 target 对象,在我们没有正确关闭定时器的时候,timer 会一直持有target 导致内存泄漏。

解决办法

同 KVO 一样,既然 timer 和 target 直接交互容易出现问题,我们就再找个代理将 target 和 selctor 等信息保存到 Proxy 里,并且是弱引用 target。
这样避免因为循环引用造成的内存泄漏。然后在触发真正 target 事件的时候如果 target 置为 nil 了这时候手动去关闭定时器。

XXStaticHookMetaClass(NSTimer, ProtectTimer,  NSTimer * ,@selector(scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:),
                      (NSTimeInterval)ti , (id)aTarget, (SEL)aSelector, (id)userInfo, (BOOL)yesOrNo ) {
    if (yesOrNo) {
        NSTimer *timer =  nil ;
        @autoreleasepool {
            XXTimerProxy *proxy = [XXTimerProxy new];
            proxy.target = aTarget;
            proxy.aSelector = aSelector;
            timer.timerProxy = proxy;
            timer = XXHookOrgin(ti, proxy, @selector(trigger:), userInfo, yesOrNo);
            proxy.sourceTimer = timer;
        }
        return  timer;
    }
    return XXHookOrgin(ti, aTarget, aSelector, userInfo, yesOrNo);
}
XXStaticHookEnd
@implementation XXTimerProxy

- (void)trigger:(id)userinfo  {
    id strongTarget = self.target;
    if (strongTarget && ([strongTarget respondsToSelector:self.aSelector])) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
        [strongTarget performSelector:self.aSelector withObject:userinfo];
#pragma clang diagnostic pop
    } else {
        NSTimer *sourceTimer = self.sourceTimer;
        if (sourceTimer) {
            [sourceTimer invalidate];
        }
        NSString *reason = [NSString stringWithFormat:@"*****Warning***** logic error target is %@ method is %@, reason : an object dealloc not invalidate Timer.",
                            [self class], NSStringFromSelector(self.aSelector)];
        
        [XXRecord recordFatalWithReason:reason errorType:(EXXShieldTypeTimer)];
    }
}

@end

7. 野指针 Crash

出现原因

一般在单线程条件下使用 ARC 正确的处理引用关系野指针出现的并不频繁, 但是多线程下则不尽然,通常在一个线程中释放了对象,�另外一个线程还没有更新指针状态 后续访问就可能会造成随机性 bug。

之所以是随机 bug 是因为被回收的内存不一定立马被使用。而且崩溃的位置可能也与原来的逻辑相聚很远,因此收集的堆栈信息也可能是杂乱无章没有什么价值。 具体的分类请看Bugly整理的脑图。 x

更多关于野指针的文章请参考:

  1. 如何定位Obj-C野指针随机Crash(一)
  2. 如何定位Obj-C野指针随机Crash(二)
  3. 如何定位Obj-C野指针随机Crash(三)

解决办法

这里我们可以借用系统的NSZombies对象的设计。 参考buildNSZombie

解决过程

  1. 建立白名单机制,由于系统的类基本不会出现野指针,而且 hook 所有的类开销较大。所以我们只过滤开发者自定义的类。

  2. hook dealloc 方法 这些需要保护的类我们并不让其释放,而是调用objc_desctructInstance 方法释放实例内部所持有属性的引用和关联对象。

  3. 利用 object_setClass(id,Class) 修改 isa 指针将其指向一个Proxy 对象(类比�系统的 KVO 实现),此 Proxy 实现了一个和前面所说的智能转发类一样的 return 0的函数。

  4. 在 Proxy 对象内的 - (void)forwardInvocation:(NSInvocation *)anInvocation 中收集 Crash 信息。

  5. 缓存的对象是有成本的,我们在缓存对象到达一定数量时候将其释放(object_dispose)。

存在问题

  1. 延迟释放内存会造成性能浪费,所以默认缓存会造成野指针的Class实例的对象限制是50,超出之后会释放,如果这时候再此触发了刚好释放掉的野指针,还是会造成Crash的,

  2. 建议使用的时候如果近期没有野指针的Crash可以不必开启,如果野指针类型的Crash突然增多,可以考虑在 hot Patch 中开启野指针防护,待收取异常信息之后,再关闭此开关。


收集信息

由于希望此库没有任何外部依赖,所以并未实现响应的上报逻辑。使用者如果需要上报信息 只需要自行实现 XXRecordProtocol 即可,然后在开启 SDK 之前将其注册进入 SDK。 在实现方法里面会接收到 XXShield 内部定义的错误信息。 开发者无论可以使用诸如 CrashLytics,友盟, bugly等第三库,或者自行 dump堆栈信息都可。

@protocol XXRecordProtocol <NSObject>

- (void)recordWithReason:(NSError * )reason userInfo:(NSDictionary *)userInfo;

@end

使用方法

示例工程

git clone [email protected]:ValiantCat/XXShield.git
cd Example
pod install 
open XXShield.xcworkspace

Install

    
  pod "XXShield"
    

Usage

/**
 注册汇报中心
 
 @param record 汇报中心
 */
+ (void)registerRecordHandler:(id<XXRecordProtocol>)record;

/**
 注册SDK,默认只要开启就打开防Crash,如果需要DEBUG关闭,请在调用处使用条件编译
 本注册方式不包含EXXShieldTypeDangLingPointer类型
 */
+ (void)registerStabilitySDK;

/**
 本注册方式不包含EXXShieldTypeDangLingPointer类型
 
 @param ability ability
 */
+ (void)registerStabilityWithAbility:(EXXShieldType)ability;

/**
 ///注册EXXShieldTypeDangLingPointer需要传入存储类名的array,暂时请不要传入系统框架类
 
 @param ability ability description
 @param classNames 野指针类列表
 */
+ (void)registerStabilityWithAbility:(EXXShieldType)ability withClassNames:(nonnull NSArray<NSString *> *)classNames;

ChangeLog

ChangeLog

单元测试

相关的单元测试在示例工程的Test Target下,有兴趣的开发者可以自行查看。并且已经接入 TrivisCI保证了�代码质量。

Bug&Feature

如果有相关的 Bug 请提 Issue

如果觉得可以扩充新的防护类型,请提 PR 给我。

作者

ValiantCat, [email protected]

个人博客

南栀倾寒的简书

License

XXShield 使用 Apache-2.0 开源协议.

xxshield's People

Contributors

valiantcat avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

xxshield's Issues

Xcode 11 cocoapods1.8.3 装完编译各种报错

ndefined symbols for architecture x86_64:
"___cxa_allocate_exception", referenced from:
std::__1::__throw_length_error(char const*) in libXXShield.a(NSObject+DanglingPointer.o)
"operator delete(void*)", referenced from:
std::__1::_DeallocateCaller::__do_call(void*) in libXXShield.a(NSObject+DanglingPointer.o)
"vtable for std::length_error", referenced from:
std::length_error::length_error(char const*) in libXXShield.a(NSObject+DanglingPointer.o)
NOTE: a missing vtable usually means the first non-inline virtual member function has no definition.
"std::length_error::~length_error()", referenced from:
std::__1::__throw_length_error(char const*) in libXXShield.a(NSObject+DanglingPointer.o)
"std::logic_error::logic_error(char const*)", referenced from:
std::length_error::length_error(char const*) in libXXShield.a(NSObject+DanglingPointer.o)
"___cxa_begin_catch", referenced from:
___clang_call_terminate in libXXShield.a(NSObject+DanglingPointer.o)
"operator new(unsigned long)", referenced from:
std::__1::__libcpp_allocate(unsigned long, unsigned long) in libXXShield.a(NSObject+DanglingPointer.o)
"typeinfo for std::length_error", referenced from:
std::__1::__throw_length_error(char const*) in libXXShield.a(NSObject+DanglingPointer.o)
"___cxa_throw", referenced from:
std::__1::__throw_length_error(char const*) in libXXShield.a(NSObject+DanglingPointer.o)
"___cxa_free_exception", referenced from:
std::__1::__throw_length_error(char const*) in libXXShield.a(NSObject+DanglingPointer.o)
"std::terminate()", referenced from:
___clang_call_terminate in libXXShield.a(NSObject+DanglingPointer.o)
"___gxx_personality_v0", referenced from:
std::__1::list<objc_object*, std::__1::allocator<objc_object*> >::push_back(objc_object*&&) in libXXShield.a(NSObject+DanglingPointer.o)
std::__1::__list_imp<objc_object*, std::__1::allocator<objc_object*> >::clear() in libXXShield.a(NSObject+DanglingPointer.o)
std::__1::__list_imp<objc_object*, std::__1::allocator<objc_object*> >::__end_as_link() const in libXXShield.a(NSObject+DanglingPointer.o)
std::__1::allocator<std::__1::__list_node<objc_object*, void*> >::deallocate(std::__1::__list_node<objc_object*, void*>, unsigned long) in libXXShield.a(NSObject+DanglingPointer.o)
std::__1::__list_imp<objc_object
, std::__1::allocator<objc_object*> >::__list_imp() in libXXShield.a(NSObject+DanglingPointer.o)
std::__1::__throw_length_error(char const*) in libXXShield.a(NSObject+DanglingPointer.o)
std::__1::unique_ptr<std::__1::__list_node<objc_object*, void*>, std::__1::__allocator_destructor<std::__1::allocator<std::__1::__list_node<objc_object*, void*> > > >::unique_ptr<true, void>(std::__1::__list_node<objc_object*, void*>, std::__1::__dependent_type<std::__1::__unique_ptr_deleter_sfinae<std::__1::__allocator_destructor<std::__1::allocator<std::__1::__list_node<objc_object, void*> > > >, true>::__good_rval_ref_type) in libXXShield.a(NSObject+DanglingPointer.o)
...
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

项目实现第一个功能判断不正确,实现第二个项目会crash

你好,本人小白一枚,了解到SDK的功能强大👍导入了项目,使用过程中遇到以下问题,如有本人使用错误望谅解:
Xcode9.1 iOS设备10.3.3

1.实现第一个功能Unrecoginzed Selector Crash 判断不正常,

我在某一个控制器中的viewWillAppear 写入以下代码 UIButton *btn = [[UIButton alloc] init]; [btn performSelector:@selector(some)]; 运行项目的时候 在这个方法XXStaticHookClass(NSObject, ProtectFW, id, @selector(forwardingTargetForSelector:), (SEL)aSelector) { } 里 直接进入了这个判读然后return了 if (strcmp(app_info.dli_fname, self_info.dli_fname)) { return XXHookOrgin(aSelector); } 我注释了这个判断又可以实现了第一个功能

2.实现第二个功能KVO Crash 项目会crash

具体情况就是我用AFN 3.0进行请求 就会crash 附上日志
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'An instance 0x103c33e10 of class __NSCFLocalDataTask was deallocated while key value observers were still registered with it. Current observation info: <NSKeyValueObservationInfo 0x174034240> (
<NSKeyValueObservance 0x174243510: Observer: 0x170095220, Key path: countOfBytesReceived, Options: <New: YES, Old: NO, Prior: NO> Context: 0x0, Property: 0x174243540>
<NSKeyValueObservance 0x174243840: Observer: 0x170095220, Key path: countOfBytesExpectedToReceive, Options: <New: YES, Old: NO, Prior: NO> Context: 0x0, Property: 0x174243870>
<NSKeyValueObservance 0x174243a50: Observer: 0x170095220, Key path: countOfBytesSent, Options: <New: YES, Old: NO, Prior: NO> Context: 0x0, Property: 0x174243a80>
<NSKeyValueObservance 0x174243c00: Observer: 0x170095220, Key path: countOfBytesExpectedToSend, Options: <New: YES, Old: NO, Prior: NO> Context: 0x0, Property: 0x174243c30>
谢谢

XXHookOrgin崩溃

hook方法:
XXStaticHookPrivateClass(__NSCFConstantString, NSString *, ProtectCont, NSString *, @selector(rangeOfString:options:range:), (NSString *)searchString, (NSStringCompareOptions)mask, (NSRange)rangeOfReceiverToSearch) { if (searchString == nil) { NSString *reason = [NSString stringWithFormat:@"[%@ %@]: nil argument", [self class], XXSEL2Str(@selector(rangeOfString:options:range:))]; [XXRecord recordFatalWithReason:reason errorType:EXXShieldTypeContainer]; return nil; } else if (rangeOfReceiverToSearch.location > self.length || rangeOfReceiverToSearch.length > self.length || (rangeOfReceiverToSearch.location + rangeOfReceiverToSearch.length) > self.length { // searchString.length > rangeOfReceiverToSearch.length) { NSString *reason = [NSString stringWithFormat:@"[%@ %@]: Range {%@, %@} out of bounds; string length %@", [self class], XXSEL2Str(@selector(rangeOfString:options:range:)), @(rangeOfReceiverToSearch.location), @(rangeOfReceiverToSearch.length), @(self.length)]; [XXRecord recordFatalWithReason:reason errorType:EXXShieldTypeContainer]; return nil; } return XXHookOrgin(searchString, mask, rangeOfReceiverToSearch);//这一行崩溃 } XXStaticHookEnd

测试崩溃的代码:
NSString *string = @"1213"; NSRange ranage = [string rangeOfString:@"12" options:1 range:NSMakeRange(0, 0)];

然后XXHookOrgin就崩溃了,如果去掉这个hook方法,系统的方法本身并不会崩溃。没有看出来XXHookOrgin为什么会崩溃

无法拦截UnrecognizedSelector

在Demo/XXTimerViewController文件中,注册

[XXShieldSDK registerStabilityWithAbility:EXXShieldTypeUnrecognizedSelector];

在TouchesBegan... 方法中实现

NSArray *temp = [NSArray array];
[temp performSelector:@selector(integerValue)];

仍然会得到 unrecognized selector sent to instance 的crash。

Why?

关于demo中KVO使用的一点疑惑?

感谢寒哥的代码!不过有一点小小的疑惑?

 Student *s = [[Student alloc] init];
 s.name = @"zs";
[s addObserver:self
       forKeyPath:@"name"
             options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld
             context:nil];
    
    // 延时1000ms后改变stu的name属性值
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        s.name = @"Jane";
    });

寒哥,您解释的是因为对象提前释放导致的的crash,我觉得s对象被捕获到block中,并不会提前释放,所以会触发KVO,但是如果不实现- observeValueForKeyPath:ofObject:change:context:才会crash。

 *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 
 <XXKVOViewController: 0x7fc105c3d3a0>: An observeValueForKeyPath:ofObject:change:context: message was received but not handled.
Key path: name
Observed object: <Student: 0x60000001c2a0>
Change: {
    kind = 1;
    new = Jane;
    old = zs;
}

这种情况的上下文是什么 / 1 如果是NSSNumber 和NSString没找到就是类型不对 切换下类型就好了

// 1 如果是NSSNumber 和NSString没找到就是类型不对  切换下类型就好了
if ([self isKindOfClass:[NSNumber class]] && [NSString instancesRespondToSelector:aSelector]) {
    NSNumber *number = (NSNumber *)self;
    NSString *str = [number stringValue];
    return str;
} else if ([self isKindOfClass:[NSString class]] && [NSNumber instancesRespondToSelector:aSelector]) {
    NSString *str = (NSString *)self;
    NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
    NSNumber *number = [formatter numberFromString:str];
    return number;
}

cocoapods1.5.3导入2.2.0运行报错

#7 和这个一样,但是没解决
我使用CocoaPod 1.5.3版本,XXShield 2.2.0版本,直接运行还是会报错。
导入libc++或者注释NSObject+DanglingPointer里面的static std::list undellocedList可以运行,但是拦截不到任何崩溃。
你的demo没有问题。麻烦看下

源码问题

if ([self isKindOfClass:[NSNumber class]] && [NSString instancesRespondToSelector: aSelector]) {
NSNumber * number = (NSNumber *)self;
NSString * str = [number stringValue];
return str;
} else if ([self isKindOfClass:[NSString class]] && [NSNumber instancesRespondToSelector: aSelector]) {
NSString * str = (NSString *)self;
NSNumberFormatter * formatter = [[NSNumberFormatter alloc] init];
NSNumber * number = [formatter numberFromString: str];
return number;
}
在消息转发的时候为什么加上NSString 和NSNumber 的判断直接拦击转发不行吗

Unrecoginzed Selector Crash无效

iPhone7设备,iOS12.2系统版本,试了好几个项目,包括新建test项目,以及github上下载的这个项目,运行时执行[self performSelector:@selector(testAction)];但未实现testAction方法,这样做仍然会崩溃,

void shield_hook_load_group(NSString *groupName) {
    uint32_t count = _dyld_image_count();
    for (uint32_t i = 0 ; i < count ; i ++) {
        const struct mach_header_* header = (void*)_dyld_get_image_header(i);
        NSString *string = [NSString stringWithFormat:@"__sh%@",groupName];
        unsigned long size = 0;
        uint8_t *data = getsectiondata(header, "__DATA", [string UTF8String],&size);
// 打断点运行到这里时,data为NULL,这样是不是相当于啥都没做,崩溃也就无法防护,但是不知道为啥这里data会为NULL,查了很多资料感觉都没搞清楚,希望能帮我解答一下;
        if (data && size > 0) {
            void **pointers = (void**)data;
            uint32_t count = (uint32_t)(size / sizeof(void*));
            for (uint32_t i = 0 ; i < count ; i ++) {
                void(*pointer)() = pointers[i];
                pointer();
            }
            break;
        }
    }
}

关于野指针crash的防治上问题讨论

思路是非常好,但是在实际中,有不少问题,反而导致crash涨了非常多。
1、如果hook系统的dealloc,会调用非常多次xx_danglingPointer_dealloc,导致系统其他异常crash,而且不少;
2、如果根据类,然后进行hook其dealloc方法,不要hook一些UIView的dealloc,会出现DanglingPointStub or one of its superclasses may have overridden -updateViewConstraints without calling super or sending -updateConstraints to the view. Or, something may have dirtied layout constraints in the middle of updating them. Both are programming errors.的crash

与腾讯崩溃收集库Bugly冲突

RT.
目前使用的是Bugly 2.5.0版本, XXShield 2.1.1版本
单独使用都没问题, 放到一块就挂了
希望得到作者的解答, 谢谢

代码如下:

func application(_ app: UIApplication, didFinishLaunchingWithOptions _: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    #if DEBUG
         XXShieldSDK.registerRecordHandler(CrashShieldHandler())
         XXShieldSDK.registerStability(withAbility: EXXShieldType.exceptDangLingPointer)
         XXShieldSDK.registerStabilitySDK()
    #endif

    DispatchQueue.main.async {
        if let buglyAppID = Bundle.main.object(forInfoDictionaryKey: "Bugly_App_ID") as? String {
            Bugly.start(withAppId: buglyAppID)
        }
    }
    return true
}

堆栈信息如下:

2018-04-09 14:56:14.338574+0800 HeyBelt[24028:308049] Could not successfully update network info during initialization.
2018-04-09 14:56:14.597110+0800 HeyBelt[24028:308049] [Info] > HeyBelt Version: 1.4.7-alpha Build: 41 PID: 24028
2018-04-09 14:56:14.597716+0800 HeyBelt[24028:308049] [Info] > XCGLogger Version: 5.0.5 - Level: Debug
2018-04-09 14:56:14.865791+0800 HeyBelt[24028:308211] Reachability Flag Status: -R ------- networkStatusForFlags
2018-04-09 14:56:14.941163+0800 HeyBelt[24028:308049] [MC] System group container for systemgroup.com.apple.configurationprofiles path is /Users/bill/Library/Developer/CoreSimulator/Devices/7C59E547-A8F8-4D19-8D03-624AFB72E5BA/data/Containers/Shared/SystemGroup/systemgroup.com.apple.configurationprofiles
2018-04-09 14:56:14.942379+0800 HeyBelt[24028:308049] [MC] Reading from private effective user settings.
2018-04-09 14:56:14.949321+0800 HeyBelt[24028:308049] [Crashlytics] Version 3.10.1 (129)
2018-04-09 14:56:15.007988+0800 HeyBelt[24028:308203] dynamic_cast error 2: One or more of the following type_info's  has hidden visibility.  They should all have public visibility.   N10__cxxabiv116__shim_type_infoE, id, N10__cxxabiv117__pbase_type_infoE.
2018-04-09 14:56:15.010689+0800 HeyBelt[24028:308203] dynamic_cast error 2: One or more of the following type_info's  has hidden visibility.  They should all have public visibility.   N10__cxxabiv116__shim_type_infoE, id, N10__cxxabiv117__pbase_type_infoE.
2018-04-09 14:56:15.015251+0800 HeyBelt[24028:308253] [CoreBluetooth] XPC connection invalid
2018-04-09 14:56:15.176912+0800 HeyBelt[24028:308203] [Bugly]  Trapped uncaught exception 'NSGenericException', reason: '*** Collection <NSConcreteHashTable: 0x600000131f80> was mutated while being enumerated.' 
(
    0   CoreFoundation                      0x000000010c57a12b __exceptionPreprocess + 171
    1   libobjc.A.dylib                     0x000000010b7c9f41 objc_exception_throw + 48
    2   CoreFoundation                      0x000000010c5edb0c __NSFastEnumerationMutationHandler + 124
    3   Foundation                          0x0000000108f12eec -[NSConcreteHashTable countByEnumeratingWithState:objects:count:] + 59
    4   XXShield                            0x000000010549ea1c -[XXKVOProxy observeValueForKeyPath:ofObject:change:context:] + 668
    5   Foundation                          0x0000000108ee86d1 NSKeyValueNotifyObserver + 349
    6   Foundation                          0x0000000108ee7f8a NSKeyValueDidChange + 483
    7   Foundation                          0x0000000108fc9d05 -[NSObject(NSKeyValueObservingPrivate) _changeValueForKeys:count:maybeOldValuesDict:usingBlock:] + 778
    8   Foundation                          0x0000000108ee8df3 +[__NSOperationInternal _observeValueForKeyPath:ofObject:changeKind:oldValue:newValue:indexes:context:] + 1763
    9   Foundation                          0x0000000108ee7680 -[__NSOperationInternal _start:] + 1084
    10  libdispatch.dylib                   0x000000010e8ce33d _dispatch_client_callout + 8
    11  libdispatch.dylib                   0x000000010e8d39f3 _dispatch_block_invoke_direct + 592
    12  libdispatch.dylib                   0x000000010e8ce33d _dispatch_client_callout + 8
    13  libdispatch.dylib                   0x000000010e8d39f3 _dispatch_block_invoke_direct + 592
    14  libdispatch.dylib                   0x000000010e8d3783 dispatch_block_perform + 109
    15  Foundation                          0x0000000108ee355c __NSOQSchedule_f + 342
    16  libdispatch.dylib                   0x000000010e8ce33d _dispatch_client_callout + 8
    17  libdispatch.dylib                   0x000000010e8d4754 _dispatch_continuation_pop + 967
    18  libdispatch.dylib                   0x000000010e8d2b85 _dispatch_async_redirect_invoke + 780
    19  libdispatch.dylib                   0x000000010e8da102 _dispatch_root_queue_drain + 772
    20  libdispatch.dylib                   0x000000010e8d9da0 _dispatch_worker_thread3 + 132
    21  libsystem_pthread.dylib             0x000000010ed955a2 _pthread_wqthread + 1299
    22  libsystem_pthread.dylib             0x000000010ed9507d start_wqthread + 13
)
2018-04-09 14:56:16.988188+0800 HeyBelt[24028:308243] [Error] [com.apple.CFNetwork.Connection] > 记录崩溃问题: Error Domain=com.xxshield.com.oudmon.hey-belt Code=-4 "target is __NSDictionaryM method is setObject:forKeyedSubscript:, reason : key or value appear nil- key is https://ios.bugly.qq.com/rqd/sync?aid=970FB2E5-7703-43C9-B891-EF401491EF96, obj is (null)" UserInfo={NSLocalizedDescription=target is __NSDictionaryM method is setObject:forKeyedSubscript:, reason : key or value appear nil- key is https://ios.bugly.qq.com/rqd/sync?aid=970FB2E5-7703-43C9-B891-EF401491EF96, obj is (null)}
2018-04-09 14:56:17.371724+0800 HeyBelt[24028:308203] *** Terminating app due to uncaught exception 'NSGenericException', reason: '*** Collection <NSConcreteHashTable: 0x600000131f80> was mutated while being enumerated.'
*** First throw call stack:
(
    0   CoreFoundation                      0x000000010c57a12b __exceptionPreprocess + 171
    1   libobjc.A.dylib                     0x000000010b7c9f41 objc_exception_throw + 48
    2   CoreFoundation                      0x000000010c5edb0c __NSFastEnumerationMutationHandler + 124
    3   Foundation                          0x0000000108f12eec -[NSConcreteHashTable countByEnumeratingWithState:objects:count:] + 59
    4   XXShield                            0x000000010549ea1c -[XXKVOProxy observeValueForKeyPath:ofObject:change:context:] + 668
    5   Foundation                          0x0000000108ee86d1 NSKeyValueNotifyObserver + 349
    6   Foundation                          0x0000000108ee7f8a NSKeyValueDidChange + 483
    7   Foundation                          0x0000000108fc9d05 -[NSObject(NSKeyValueObservingPrivate) _changeValueForKeys:count:maybeOldValuesDict:usingBlock:] + 778
    8   Foundation                          0x0000000108ee8df3 +[__NSOperationInternal _observeValueForKeyPath:ofObject:changeKind:oldValue:newValue:indexes:context:] + 1763
    9   Foundation                          0x0000000108ee7680 -[__NSOperationInternal _start:] + 1084
    10  libdispatch.dylib                   0x000000010e8ce33d _dispatch_client_callout + 8
    11  libdispatch.dylib                   0x000000010e8d39f3 _dispatch_block_invoke_direct + 592
    12  libdispatch.dylib                   0x000000010e8ce33d _dispatch_client_callout + 8
    13  libdispatch.dylib                   0x000000010e8d39f3 _dispatch_block_invoke_direct + 592
    14  libdispatch.dylib                   0x000000010e8d3783 dispatch_block_perform + 109
    15  Foundation                          0x0000000108ee355c __NSOQSchedule_f + 342
    16  libdispatch.dylib                   0x000000010e8ce33d _dispatch_client_callout + 8
    17  libdispatch.dylib                   0x000000010e8d4754 _dispatch_continuation_pop + 967
    18  libdispatch.dylib                   0x000000010e8d2b85 _dispatch_async_redirect_invoke + 780
    19  libdispatch.dylib                   0x000000010e8da102 _dispatch_root_queue_drain + 772
    20  libdispatch.dylib                   0x000000010e8d9da0 _dispatch_worker_thread3 + 132
    21  libsystem_pthread.dylib             0x000000010ed955a2 _pthread_wqthread + 1299
    22  libsystem_pthread.dylib             0x000000010ed9507d start_wqthread + 13
)
libc++abi.dylib: terminating with uncaught exception of type NSException

EXXShieldTypeKVO会崩溃

Collection <NSConcreteHashTable: 0x1c01241a0> was mutated while being enumerated.


0 CoreFoundation	___exceptionPreprocess + 124
1 libobjc.A.dylib	objc_exception_throw + 56
2 CoreFoundation	-[__NSSingleObjectEnumerator initWithObject:]
3 Foundation	-[NSConcreteHashTable countByEnumeratingWithState:objects:count:] + 72
4 XXShield	-[XXKVOProxy observeValueForKeyPath:ofObject:change:context:] + 532
5 Foundation	_NSKeyValueNotifyObserver + 304
6 Foundation	_NSKeyValueDidChange + 404
7 Foundation	-[NSObject(NSKeyValueObservingPrivate) _changeValueForKeys:count:maybeOldValuesDict:usingBlock:] + 668
8 Foundation	+[__NSOperationInternal _observeValueForKeyPath:ofObject:changeKind:oldValue:newValue:indexes:context:] + 1812
9 Foundation	-[__NSOperationInternal _start:] + 1528
10 libdispatch.dylib	_dispatch_client_callout + 16
11 libdispatch.dylib	_dispatch_block_invoke_direct + 268
12 libdispatch.dylib	_dispatch_client_callout + 16
13 libdispatch.dylib	_dispatch_block_invoke_direct + 268
14 libdispatch.dylib	dispatch_block_perform + 104
15 Foundation	___NSOQSchedule_f + 376
16 libdispatch.dylib	_dispatch_client_callout + 16
17 libdispatch.dylib	_dispatch_continuation_pop + 596
18 libdispatch.dylib	_dispatch_async_redirect_invoke + 632
19 libdispatch.dylib	_dispatch_root_queue_drain + 624
20 libdispatch.dylib	_dispatch_worker_thread3 + 136
21 libsystem_pthread.dylib	_pthread_wqthread + 1268
22 libsystem_pthread.dylib	start_wqthread + 4

Need help with installation - global buffer overflow

I have some problem dealing with installation.
Since I've no idea where did I do wrong, please give me some instructions to fix this.

My Project Environment is mainly built by Objective-C. Just few Swift codes written inside.

Injected code as below:

- (void)viewDidLoad {
    static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            [XXShieldSDK registerStabilitySDK];
    });
}

Where compiler stuck:

void shield_hook_load_group(NSString *groupName) {
    uint32_t count = _dyld_image_count();
    for (uint32_t i = 0 ; i < count ; i ++) {
        const struct mach_header_* header = (void*)_dyld_get_image_header(i);
        NSString *string = [NSString stringWithFormat:@"__sh%@",groupName];
        unsigned long size = 0;
        uint8_t *data = getsectiondata(header, "__DATA", [string UTF8String],&size);
        if (data && size > 0) {
            void **pointers = (void**)data;
            uint32_t count = (uint32_t)(size / sizeof(void*));
            for (uint32_t i = 0 ; i < count ; i ++) {
                void(*pointer)() = pointers[i]; // Compiler stuck at here with error : global-buffer-overflow
                pointer();
            }
            break;
        }
    }
}

Error warning:

SUMMARY: AddressSanitizer: global-buffer-overflow (/private/var/containers/Bundle/Application/A8A42A5A-7B0F-4165-9F7D-3304C7B70406/MyProject.app/Frameworks/XXShield.framework/XXShield:arm64+0x37c9f) in shield_hook_load_group
 Shadow bytes around the buggy address:
   0x0001318b4a60: f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9
   0x0001318b4a70: f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9
   0x0001318b4a80: f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9
   0x0001318b4a90: f9 f9 f9 f9 00 00 00 00 00 00 00 00 00 00 00 00
   0x0001318b4aa0: 00 00 00 00 00 00 00 00 00 f9 f9 f9 f9 f9 f9 f9
 =>0x0001318b4ab0: 00 f9 f9 f9 f9 f9 f9 f9 00[f9]f9 f9 f9 f9 f9 f9
   0x0001318b4ac0: 00 f9 f9 f9 f9 f9 f9 f9 00 f9 f9 f9 f9 f9 f9 f9
   0x0001318b4ad0: 00 f9 f9 f9 f9 f9 f9 f9 00 f9 f9 f9 f9 f9 f9 f9
   0x0001318b4ae0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
   0x0001318b4af0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
   0x0001318b4b00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

 Shadow byte legend (one shadow byte represents 8 application bytes):
   Addressable:           00
   Partially addressable: 01 02 03 04 05 06 07
   Heap left redzone:       fa
   Freed heap region:       fd
   Stack left redzone:      f1
   Stack mid redzone:       f2
   Stack right redzone:     f3
   Stack after return:      f5
   Stack use after scope:   f8
   Global redzone:          f9
   Global init order:       f6
   Poisoned by user:        f7
   Container overflow:      fc
   Array cookie:            ac
   Intra object redzone:    bb
   ASan internal:           fe
   Left alloca redzone:     ca
   Right alloca redzone:    cb

==36156==ABORTING

程序启动闪退 (NSArrayObjectAtIndex.h:17)

Fatal Exception: NSInvalidArgumentException
*** -[NSArray objectAtIndex:]: method only defined for abstract class. Define -[__NSSingleObjectArrayI objectAtIndex:]!

Fatal Exception: NSInvalidArgumentException
0 CoreFoundation 0x182c4efe0 __exceptionPreprocess
1 libobjc.A.dylib 0x1816b0538 objc_exception_throw
2 CoreFoundation 0x182c5690c __CFAllocateObject
3 CoreFoundation 0x182c40888 -[NSArray objectAtIndex:]
4 MintLive 0x1003707dc ____shield_hook_auto_load_function_1_block_invoke (NSArrayObjectAtIndex.h:17)
5 MintLive 0x100370fa8 ____shield_hook_auto_load_function_2_block_invoke (NSArrayObjectAtIndex.h:17)
6 CoreFoundation 0x182ba798c CFStringCreateByCombiningStrings
7 CFNetwork 0x183446d0c HTTPHeaderDict::copyStringValue(__CFAllocator const*, HTTPHeaderKeyMixedValue const&) const
8 CFNetwork 0x18334c570 HTTPMessage::copyHeaderFieldValue(CFNetworkCanonicalStringConstant)
9 CFNetwork 0x18338bda4 HTTPProtocol::_requestsAreCacheEquivalent(_CFURLRequest const*, _CFURLRequest const*, NSURLSession const*, void const*)
10 CFNetwork 0x183323c34 +[_NSCFURLProtocol requestIsCacheEquivalent:toRequest:]
11 CFNetwork 0x1832f9994 __CFURLCache::calloutForAsyncDiskCache(__CFString const*, _CFURLRequest const*, NSURLSession const*, void (_CFCachedURLResponse const*) block_pointer)
12 CFNetwork 0x1832f5ae0 __CFURLCache::CopyResponseForRequestWithCompletionHandler(_CFURLRequest const*, bool, NSURLSession const*, void (_CFCachedURLResponse const*) block_pointer)
13 CFNetwork 0x183462b18 CFXURLCache::getResponseForTask(NSURLSessionTask const*, unsigned char, void (_CFCachedURLResponse const*) block_pointer) const
14 CFNetwork 0x1833e1718 URLConnectionLoader::_loaderEvent_StartLoad(NSURLSessionTask const*)
15 CFNetwork 0x18332753c -[__NSCFURLLocalSessionConnection withLoaderOnQueue:]
16 CFNetwork 0x183327b3c -[__NSCFURLLocalSessionConnection _tick_initialize]
17 CFNetwork 0x1833270f0 __71-[__NSCFURLLocalSessionConnection initWithTask:delegate:delegateQueue:]_block_invoke
18 libdispatch.dylib 0x181b069e0 _dispatch_call_block_and_release
19 libdispatch.dylib 0x181b069a0 _dispatch_client_callout
20 libdispatch.dylib 0x181b14ad4 _dispatch_queue_serial_drain
21 libdispatch.dylib 0x181b0a2cc _dispatch_queue_invoke
22 libdispatch.dylib 0x181b14fa8 _dispatch_queue_override_invoke
23 libdispatch.dylib 0x181b16a50 _dispatch_root_queue_drain
24 libdispatch.dylib 0x181b167d0 _dispatch_worker_thread3
25 libsystem_pthread.dylib 0x181d0f100 _pthread_wqthread
26 libsystem_pthread.dylib 0x181d0ecac start_wqthread

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.