app不询问wlan ios
2018-03-07 11:41:00 weixin_34061042 阅读数 170

问题及解决过程

1、一般情况下,App首次启动都会弹出一个询问用户“是否允许应用访问数据”的弹框;

2、偶尔的一次调用厂家的静态库,库里面需要访问服务器做一些操作,但是App死活就是不弹框提示网络访问请求,并且“设置“里面也没有设置该App访问网络的地方,设置首页、无线局域网和蜂窝移动网络三处都没有显示该App,也就是都不可设。

3、尝试了网上说的各种方法,什么重启手机、Info.plist添加App Transport Security Settings、打开无线局域网助理、修改其他App的网络访问权限等等,都不管用。

4、后来,我用NSURLSession写了一段网络请求代码,发现竟然弹框询问了。


总结:

App里面如果是使用了socket等底层方法进行网络通信,可能不会弹框,这时候就需要采用以下方法:

1、Info.plist,添加App Transport Security Settings,添加Allow Arbitrary Loads并设置为YES;

2、添加代码模拟一次网络请求:

// 模拟网络请求,以弹窗提示是否使用网络数据
NSURL *url = [NSURL URLWithString:@"http://www.baidu.com"];
NSURLRequest *request =[NSURLRequest requestWithURL:url];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *sessionDataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
    
}];
[sessionDataTask resume];

以上是我实验出来的情况,如有疑惑或异议,请留言,谢谢!

2015-12-12 16:21:01 oZuoQi12345678910 阅读数 355
app不满屏:点击打开链接
2018-09-21 18:18:26 ios8988 阅读数 196

Unrecognized Selector Sent to Instance

由于Objective-c是Message机制,而且对象在转换的时候,会有拿到的对象和预期不一致,所以会有方法找不到的情况,在找不到方法时,查找方法将会进入方法Forward流程,系统给了三次补救的机会,所以我们要解决这个问题,在这三次均可以解决这个问题

forward

  • resolveInstanceMethod:(SEL)sel 这是实例化方法没有找到方法,最先执行的函数,首先会流转到这里来,返回值是BOOL,没有找到就是NO,找到就返回YES,如果要解决就需要再当前的实例中加入不存在的Selector,并绑定IMP,示例如下:

static void xxxInstanceName(id self, SEL cmd, id value) {    NSLog(@"resolveInstanceMethod %@", value);}+ (BOOL)resolveInstanceMethod:(SEL)sel{    NSLog(@"resolveInstanceMethod");        NSMethodSignature* sign = [self methodSignatureForSelector:selector];    if (!sign) {        class_addMethod([self class], sel, (IMP)xxxInstanceName, "v@:@");        return YES;    }    return [super resolveInstanceMethod:sel];}
  • forwardingTargetForSelector:(SEL)aSelector

如果resolveInstanceMethod没有处理,将进行到forwardingTargetForSelector这步来,这时候你可以返回nil,你也可以用一个Stub对象来接住,把消息流程流转到了你的Stub那边了,然后在你的Stub里添加不存在的Selector,这样就不会crash了,示例如下:

- (id)forwardingTargetForSelectorSwizzled:(SEL)selector{    NSMethodSignature* sign = [self methodSignatureForSelector:selector];    if (!sign) {        id stub = [[UnrecognizedSelectorHandle new] autorelease];        class_addMethod([stub class], selector, (IMP)unrecognizedSelector, "v@:");        return stub;    }    return [self forwardingTargetForSelectorSwizzled:selector];}
  • methodSignatureForSelector:(SEL)aSelector

  • forwardInvocation:(NSInvocation *)anInvocation

这两个方法一起说,因为他们之间有关联,

  1. 当methodSignatureForSelector返回nil时,会Crash

  2. 如果methodSignatureForSelector返回一个定义好的NSMethodSignature,但是没有实现forwardInvocation,也会闪退,如果实现了forwardInvocation,会先返回到resolveInstanceMethod然后再才会到forwardInvocation

  3. 当流转到forwardInvocation,通过以下方法:

[anInvocation invokeWithTarget:xxxtarget1];[anInvocation invokeWithTarget:xxxtarget2];

还可以流转到多个对象,[anInvocation invokeWithTarget:xxxtarget2]是为了让不存在的方法有着陆点

  • doesNotRecognizeSelector:(SEL)aSelector 执行到这里的时候,两种情况:

  1. 当methodSignatureForSelector返回一种任意的方法签名的时候,也会进入doesNotRecognizeSelector,但是不会闪退

  2. 当methodSignatureForSelector返回nil时,进入doesNotRecognizeSelector就会闪退

根据以上流程,最终还是选择流程2,原因如下:

  1. resolveInstanceMethod虽然可以解决问题,给不存在的方法增加到示例中去,会污染当前示例

  2. forwardInvocation在三步中式最后一步,会导致流转的周期变长,而且会产生NSInvocation,性能不是最好的选择

如何监听实例化对象什么时候释放

先说下这个知识点,因为在接下来的好几个地方都会用到,会有一些异常的情况,所以需要一种知道当前创建者啥时候释放,首先会想到dealloc,这样会Hook的NSObject,在一定程度会影响性能,后面发现一种比较优雅的方法,原理来自于Runtime源码:

/************************************************************************ objc_destructInstance* Destroys an instance without freeing memory. * Calls C++ destructors.* Calls ARR ivar cleanup.* Removes associative references.* Returns `obj`. Does nothing if `obj` is nil.* Be warned that GC DOES NOT CALL THIS. If you edit this, also edit finalize.* CoreFoundation and other clients do call this under GC.**********************************************************************/void *objc_destructInstance(id obj) {    if (obj) {        // Read all of the flags at once for performance.        bool cxx = obj->hasCxxDtor();        bool assoc = !UseGC && obj->hasAssociatedObjects();        bool dealloc = !UseGC;        // This order is important.        if (cxx) object_cxxDestruct(obj);        if (assoc) _object_remove_assocations(obj);        if (dealloc) obj->clearDeallocating();    }    return obj;}

_object_remove_assocations会释放所有的用AssociatedObject数据。

objc_setAssociatedObject给当前对象添加一个中间对象,当前对象释放时,会清理AssociatedObject数据,AssociatedObject的中间对象将被清理释放,中间对象的dealloc方法将被执行。

最终清理被遗漏的监听者,会用在KVO和NSNotification清理没用的监听者,不过这种方式有以下问题需要注意:

  • 清理的时候线程安全问题

  • 清理时机偏晚,是否适合你当前的情况

NSArray,NSMutableArray,NSDictonary,NSMutableDictionary

  • 类族(Class Cluster)

NSDictonary,NSArray,NSString等,都使用了类族,这种模式最大的好处就是,可以隐藏抽象基类背后的复杂细节,使用者只需调用基类简单的方法就可以返回不同的子类实例

  • Swizzle Hook

这里就不赘述Swizzle概念了,Google到处都是讲解的,这里给一个典型的例子:

swizzleInstanceMethod(NSClassFromString(@"__NSArrayI"), @selector(objectAtIndex:), @selector(hookObjectAtIndex:));- (id) hookObjectAtIndex:(NSUInteger)index {    if (index < self.count) {        return [self hookObjectAtIndex:index];    }    handleCrashException(@"HookObjectAtIndex invalid index");    return nil;}

Zombie Pointer

让野指针不闪退是模仿了XCode debug的Zombie Object,也参考了网易和美团的做法,主要是以下步骤:

  1. Hook住dealloc方法

  2. 如果当前示例在黑名单里,就把当年前示例加入集合,并把当前对象objc_destructInstance清理引用关系,并未真正释放内存,并将object_setClass设置成自己的中间对象

  3. Hook中间对象的方法,收到的消息都由中间对象来处理

  4. 维护的野指针集合,要么根据个数来维护,要么根据总大小来维护,当满了,就需要真正释放对象内存free(obj)

存在的问题:

  1. 需要单独的内存那些问题对象

  2. 最后释放内存后,再访问时会闪退,这个方法只是一定程度延迟了闪退时间

  3. 需要后台维护黑名单机制,来指定那些问题对象

KVO

KVO在以下情况会导致闪退:

  • 添加监听后没有清除会导致闪退

  • 清除不存在的key也会闪退

  • 添加重复的key导致闪退

需要Hook以下方法:

  • addObserver:forKeyPath:options:context:

  • removeObserver:forKeyPath:

主要解决以下问题:

  • 在注册监听后,中间对象需要维护注册的数据集合,当宿主释放时,清除还在集合中的监听者

  • 保护key不存在的情况

  • 保护重复添加的情况

NSTimer

NSTimer存在以下问题:

  • Target是强引用,内存泄漏

  • 在宿主不存在的时候,清理NSTimer

Hook以下方法:

  • scheduledTimerWithTimeInterval:target:selector:userInfo:repeats

解决方法: 1.当repeats为NO时,走原始方法 2.当repeats为YES时,新建一个对象,声明一个target属性为weak类型,指向参数的target,当中间对象的target为空时,清理NSTimer

NSNotification

NSNotification的主要问题是:

  • 添加通知后,没有移除导致Crash的问题(不过在iOS9以后没有这个问题,我在真机8.3测试也没有这个问题,不知道iOS8是否有这个问题)

Hook以下方法:

  • addObserver:selector:name:object:

原因和解决办法: 问题就在在于和assign和weak问题,野指针问题,要么置空指针或者删除空指针的集合

MRC

这里单独说下,为什么工程选择了MRC,因为在Hook集合类型的时候,启动的时候就闪退了,Crash的地方在系统类里,Stack里显示在CF这层,这里只能猜测系统底层对ARC的支持不好导致的,后续改成MRC就没有问题,所以这个需要继续研究和追踪,如果有知道的同学记得告知我下

性能

本来是没有打算注意性能这个问题的,因为从Hook原理的角度来说,只是交换IMP的指向,时间复杂度来说,只是在系统级别上增加了几条逻辑判断指令,所以这个影响是极小的,基本可以忽略,我经过测试,循环1000000次,没有HOOK和HOOK相差0.0x秒的,所以减少Crash,来增加这么点时间复杂度来说,是值得的。

不过最后说一点,就是dealloc确实需要注意,因为这里存在集合的操作,所以要注意时间复杂度,dealloc执行的很频繁的,而且主线程和子线程都会涉及到,尤其是主线程一定注意,否则会影响到UI的体验。

参考资料

2018-09-21 09:48:00 weixin_34185364 阅读数 18

转自 Cocoa开发者社区 微信公众号
https://mp.weixin.qq.com/s/uVRT-rTWZQ0tdYo6CfkLhg

Unrecognized Selector Sent to Instance

由于Objective-c是Message机制,而且对象在转换的时候,会有拿到的对象和预期不一致,所以会有方法找不到的情况,在找不到方法时,查找方法将会进入方法Forward流程,系统给了三次补救的机会,所以我们要解决这个问题,在这三次均可以解决这个问题。

  • resolveInstanceMethod:(SEL)sel 这是实例化方法没有找到方法,最先执行的函数,首先会流转到这里来,返回值是BOOL,没有找到就是NO,找到就返回YES,如果要解决就需要再当前的实例中加入不存在的Selector,并绑定IMP,示例如下:
static void xxxInstanceName(id self, SEL cmd, id value) {    
  NSLog(@"resolveInstanceMethod %@", value);
}
+ (BOOL)resolveInstanceMethod:(SEL)sel{    
  NSLog(@"resolveInstanceMethod");        
  NSMethodSignature* sign = [self methodSignatureForSelector:selector];    
  if (!sign) {        
    class_addMethod([self class], sel, (IMP)xxxInstanceName, "v@:@");        return YES;    
}    
return [super resolveInstanceMethod:sel];
  • forwardingTargetForSelector:(SEL)aSelector

如果resolveInstanceMethod没有处理,将进行到forwardingTargetForSelector这步来,这时候你可以返回nil,你也可以用一个Stub对象来接住,把消息流程流转到了你的Stub那边了,然后在你的Stub里添加不存在的Selector,这样就不会crash了,示例如下:

- (id)forwardingTargetForSelectorSwizzled:(SEL)selector{      
  NSMethodSignature* sign = [self methodSignatureForSelector:selector];    
    if (!sign) {        
      id stub = [[UnrecognizedSelectorHandle new] autorelease];        
      class_addMethod([stub class], selector, (IMP)unrecognizedSelector, "v@:");        
      return stub;    
    }    
    return [self forwardingTargetForSelectorSwizzled:selector];
}
  • methodSignatureForSelector:(SEL)aSelector

  • forwardInvocation:(NSInvocation *)anInvocation

这两个方法一起说,因为他们之间有关联,

1.当methodSignatureForSelector返回nil时,会Crash

2.如果methodSignatureForSelector返回一个定义好的NSMethodSignature,但是没有实现forwardInvocation,也会闪退,如果实现了forwardInvocation,会先返回到resolveInstanceMethod然后再才会到forwardInvocation

3.当流转到forwardInvocation,通过以下方法:

[anInvocation invokeWithTarget:xxxtarget1];
[anInvocation invokeWithTarget:xxxtarget2];

还可以流转到多个对象,[anInvocation invokeWithTarget:xxxtarget2]是为了让不存在的方法有着陆点

  • doesNotRecognizeSelector:(SEL)aSelector 执行到这里的时候,两种情况:

1.当methodSignatureForSelector返回一种任意的方法签名的时候,也会进入doesNotRecognizeSelector,但是不会闪退

2.当methodSignatureForSelector返回nil时,进入doesNotRecognizeSelector就会闪退

根据以上流程,最终还是选择流程2,原因如下:

1.resolveInstanceMethod虽然可以解决问题,给不存在的方法增加到示例中去,会污染当前示例

2.forwardInvocation在三步中式最后一步,会导致流转的周期变长,而且会产生NSInvocation,性能不是最好的选择

如何监听实例化对象什么时候释放

先说下这个知识点,因为在接下来的好几个地方都会用到,会有一些异常的情况,所以需要一种知道当前创建者啥时候释放,首先会想到dealloc,这样会Hook的NSObject,在一定程度会影响性能,后面发现一种比较优雅的方法,原理来自于Runtime源码:

/************************************************************************ 
objc_destructInstance* Destroys an instance without freeing memory. * Calls C++ destructors.* Calls ARR ivar cleanup.* Removes associative references.* Returns `obj`. Does nothing if `obj` is nil.* Be warned that GC DOES NOT CALL THIS. If you edit this, also edit finalize.* CoreFoundation and other clients do call this under GC.
**********************************************************************/
void *objc_destructInstance(id obj) {    
  if (obj) {        
    // Read all of the flags at once for performance.        
    bool cxx = obj->hasCxxDtor();        
    bool assoc = !UseGC && obj->hasAssociatedObjects();        
    bool dealloc = !UseGC;        
    // This order is important.        
    if (cxx) object_cxxDestruct(obj);        
    if (assoc) _object_remove_assocations(obj);        
    if (dealloc) obj->clearDeallocating();    
    }    
  return obj;
}

_object_remove_assocations会释放所有的用AssociatedObject数据。

objc_setAssociatedObject给当前对象添加一个中间对象,当前对象释放时,会清理AssociatedObject数据,AssociatedObject的中间对象将被清理释放,中间对象的dealloc方法将被执行。

最终清理被遗漏的监听者,会用在KVO和NSNotification清理没用的监听者,不过这种方式有以下问题需要注意:

  • 清理的时候线程安全问题

  • 清理时机偏晚,是否适合你当前的情况

NSArray,NSMutableArray,NSDictonary,NSMutableDictionary

  • 类族(Class Cluster)

NSDictonary,NSArray,NSString等,都使用了类族,这种模式最大的好处就是,可以隐藏抽象基类背后的复杂细节,使用者只需调用基类简单的方法就可以返回不同的子类实例

  • Swizzle Hook

这里就不赘述Swizzle概念了,Google到处都是讲解的,这里给一个典型的例子:

swizzleInstanceMethod(NSClassFromString(@"__NSArrayI"), 
@selector(objectAtIndex:), 
@selector(hookObjectAtIndex:));
- (id) hookObjectAtIndex:(NSUInteger)index {    
  if (index < self.count) {        
  return [self hookObjectAtIndex:index];    
  }    
  handleCrashException(@"HookObjectAtIndex invalid index");    
  return nil;
}

Zombie Pointer

让野指针不闪退是模仿了XCode debug的Zombie Object,也参考了网易和美团的做法,主要是以下步骤:

1.Hook住dealloc方法

2.如果当前示例在黑名单里,就把当年前示例加入集合,并把当前对象objc_destructInstance清理引用关系,并未真正释放内存,并将object_setClass设置成自己的中间对象

3.Hook中间对象的方法,收到的消息都由中间对象来处理

4.维护的野指针集合,要么根据个数来维护,要么根据总大小来维护,当满了,就需要真正释放对象内存free(obj)

存在的问题:

1.需要单独的内存那些问题对象

2.最后释放内存后,再访问时会闪退,这个方法只是一定程度延迟了闪退时间

3.需要后台维护黑名单机制,来指定那些问题对象

KVO

KVO在以下情况会导致闪退:

  • 添加监听后没有清除会导致闪退

  • 清除不存在的key也会闪退

  • 添加重复的key导致闪退

需要Hook以下方法:

  • addObserver:forKeyPath:options:context:

  • removeObserver:forKeyPath:

主要解决以下问题:

  • 在注册监听后,中间对象需要维护注册的数据集合,当宿主释放时,清除还在集合中的监听者

  • 保护key不存在的情况

  • 保护重复添加的情况

NSTimer

NSTimer存在以下问题:

  • Target是强引用,内存泄漏

  • 在宿主不存在的时候,清理NSTimer

Hook以下方法:

  • scheduledTimerWithTimeInterval:target:selector:userInfo:repeats

解决方法: 1.当repeats为NO时,走原始方法 2.当repeats为YES时,新建一个对象,声明一个target属性为weak类型,指向参数的target,当中间对象的target为空时,清理NSTimer

NSNotification

NSNotification的主要问题是:

  • 添加通知后,没有移除导致Crash的问题(不过在iOS9以后没有这个问题,我在真机8.3测试也没有这个问题,不知道iOS8是否有这个问题)

Hook以下方法:

  • addObserver:selector:name:object:

原因和解决办法: 问题就在在于和assign和weak问题,野指针问题,要么置空指针或者删除空指针的集合

MRC

这里单独说下,为什么工程选择了MRC,因为在Hook集合类型的时候,启动的时候就闪退了,Crash的地方在系统类里,Stack里显示在CF这层,这里只能猜测系统底层对ARC的支持不好导致的,后续改成MRC就没有问题,所以这个需要继续研究和追踪,如果有知道的同学记得告知我下.

性能

本来是没有打算注意性能这个问题的,因为从Hook原理的角度来说,只是交换IMP的指向,时间复杂度来说,只是在系统级别上增加了几条逻辑判断指令,所以这个影响是极小的,基本可以忽略,我经过测试,循环1000000次,没有HOOK和HOOK相差0.0x秒的,所以减少Crash,来增加这么点时间复杂度来说,是值得的。

不过最后说一点,就是dealloc确实需要注意,因为这里存在集合的操作,所以要注意时间复杂度,dealloc执行的很频繁的,而且主线程和子线程都会涉及到,尤其是主线程一定注意,否则会影响到UI的体验。

参考资料

https://github.com/opensource-apple/objc4/blob/master/runtime/objc-runtime-new.mm

大白健康系统

最后给出开源代码:https://github.com/jezzmemo/JJException

2017-10-24 16:48:00 weixin_34066347 阅读数 29

iOS 11 对用户的隐私更加保护了,一些需要使用用户位置信息的App 可能在iOS 11 中不会询问用户“是否允许使用位置信息”,导致无法获取到用户的位置。

解决办法

2188748-be8aa072c27a1aed.png
info.plist

在info.plist 文件中写入这两行,一般就可以解决了。

特殊情况

有时候写入上面的两行还是不会询问,这时检查代码,在获取位置的代码中是否缺少这样一句:

[self.locationManager requestWhenInUseAuthorization];

添加之后就可以了。


以上方法,仅供参考,亲测有效,欢迎指正。

iOS:保护App不闪退iOS:保护App不闪退

博文 来自: weixin_33817333

iOS:保护App不闪退

阅读数 176

没有更多推荐了,返回首页