2018-01-28 21:51:07 jhq1990 阅读数 475

iOS问题记录本:iOS8/Swift/WKWebView/address=0x0错误

0、背景描述

前一段时间,公司有个小的新项目,因为考虑到项目本身没有任何技术要求,所以决定采用Swift加上WKWebView的方案进行开发;而且前一段时间团队内部也进行了基础开发库Swift化的工作,这个项目也可以用来检验之前的工作。

当然,吃螃蟹必然是要付出一些代价的,不过因为项目本身的规模很小,所以Swift语言引起的问题并不多,最麻烦的地方在于WKWebView,其中最麻烦的一个是WKWebView的JS交互阻塞AJAX请求的问题,不过这并不是本文叙述的要点,就不详述了。

项目测试快结束的时候,测试人员在进行兼容性测试时,发现在iOS8系统上启动就闪退,我调试发现原因是Swift基础库pod中依赖了Contacts系统framework,而这个framework是iOS9系统之后才有的,所以启动必闪退。
解决了这个问题后,我运行发现应用在iOS8系统上还是闪退,断点停留在AppDelegate上,报错信息为:

EXC_BAD_ACCESS(code=1,address=0x0)

1、问题查找

因为闪退时断点停留在AppDelegate上,且没有更详细的报错信息,所以无法使用常规的方法查找问题所在。

不过类似的问题还是遇到过的,比如EXC_BAD_ACCESS(code=1,address=0x10)这个问题,它是block释放后被执行造成的闪退问题,所以我怀疑现在这个问题也类似,但是在网上无法搜索到类似的信息,StackOverFlow上有人提问,但没有最终结果。

所以最后只能采用笨办法——排除法,把系统启动时执行的代码挨个注释掉查看效果。最终定位到问题所在是为WebView设置UserAgent的代码:

// 获取默认User-Agent
let wkWebView = WKWebView()
wkWebView.evaluateJavaScript("navigator.userAgent") { (result, error) in
    var oldAgent = ""
    if result is String {
        oldAgent = result as! String
        if oldAgent.count > 0 {
            oldAgent += " "
        }
    }
    let newAgent = oldAgent + "app-iOS"
    // 设置global User-Agent
    UserDefaults.standard.register(defaults: ["UserAgent":newAgent])
    UserDefaults.standard.synchronize()
}

2、原因分析

经过测试,即便是在一个空白的Swift工程里进行最简单的调用,如:

WKWebView().evaluateJavaScript("") { (result, error) in }

同样会导致闪退。

而该方法的回调参数不传时WKWebView().evaluateJavaScript("")则不会报错。

我们上一节说过有一个类似的问题,而address=0x10问题的原因在是block的结构体寻址问题:

//__block_imp:  这个是编译器给我们生成的结构体,每一个block都会用到这个结构体
struct __block_impl {
  void *isa;         //类型
  int Flags;          //标识字段
  int Reserved;       //保留字段       
  void *FuncPtr;       //函数指针,这个会指向编译器给我们生成的下面的静态函数__main_block_func_0
};

0x10是十六进制,也就是struct基地址后的第16个字节,其中void *类型占8个字节,int类型占4个字节,所以0x10的地址就是FuncPtr的地址,而address=0x10的问题也正是对值为nil的block强行调用导致的。

那么,举一反三来看,address=0x0的问题,0x0地址就是指向isa函数指针字段的地址,这个错误发生的原因就是调用isa造成的。

WKWebView执行JavaScript时为何调用isa我们暂不去考虑,已经可以确定如果block的值为nil时,调用isa字段,是会造成address=0x0问题的。所以,下面我们需要考虑的就是block为什么变成nil?

在本节开头贴的简单代码中,有几点是需要注意的:

  • WKWebView()会生成一个临时变量;
  • evaluateJavaScript方法执行时是异步的;
  • 异步执行时,尾随闭包可能是直接或者间接被WKWebView的实例持有的;

那么,block的值变为nil的原因应该是WKWebView的临时变量实例被自动释放,所以block变量也随同被释放了。

经过多次测试也发现,该问题并非是100%重现的,我在iPhone6 plus测试机上运行时,有90%左右的重现几率。而这一现象如果用autoReleasePool来解释也是通的。

3、解决方案

这个问题最好的解决方法当然是让WKWebView在执行JavaScript脚本时,对block变为nil的情况进行容错处理;但是很抱歉,这需要苹果爸爸去修改,而且它们可能也无法修复iOS8这种低版本系统的问题。

那么头痛医头脚痛医脚,既然问题的原因可能是block被释放导致的,那就让它不被释放好了。

于是,我让在类里面直接声明并持有闭包变量,然后调用WKWebView方法的时候传递闭包变量进去,可是然并卵!!应用还是闪退了!

然后我就意识到我犯蠢了,作为参数传递的闭包变量,在WKWebView内部一般都要copy一份,所以我直接持有闭包变量并无法影响WKWebView内部闭包变量的释放。

最终还是将WKWebView声明为类的属性,让类持有它,这样它就不会因为是临时变量而被autoReleasePool所释放。

最后测试,效果上OK,闪退问题没有重现。

4.说明

最终的解决方案只是基于对问题原因的猜测,至于问题的原因是否确实如此还没有时间深入探寻,所以如果最终并非如此而对其他人造成误导,还请大家谅解。如果文章中有错误,还请多多指点。

2015-10-08 16:57:28 Ehome_hong 阅读数 701

国庆前我也适配了一下iOS9,由于时间比较匆忙,没来得及做笔记,今天稍微花点时间整理了一下思路,写了这篇适配iOS9的博客,当作是笔记吧

1,NSAppTransportSecurity

iOS9让所有的HTTP默认使用了HTTPS,原来的HTTP协议传输都改成TLS1.2协议进行传输。直接造成的情况就是App发请求的时候弹出网络无法连接。解决办法就是在项目的info.plist 文件里加上如下节点:
NSAppTransportSecurity - NSAllowsArbitraryLoads
这个子节点的意思是:是否允许任性的加载?! 设为YES的话就将禁用了AppTransportSecurity转而使用用户自定义的设置,这个问题就解决了。



2,用Xcode7提交审核遇到 ITMS-90535 错误

删除相关的Info.plist即可,一般是不小心打包进去的。比如ShareSDK里附带的TencentOpenApi_IOS_Bundle.bundle/Info.plist,(特别注意:如果这种情况下删掉了CFBundleExecutable但是app无法安装了,真机上也跑不起来了,这个问题你肯定是删了自己项目的Info plist里面的Key)如下图:





3,提交到itunes Connect时报错---“1 本地化存在错误。”

如图


问题分析:你的app某些必须填的信息没有填,把没有填的补充完整就好了(注意:以前默认是只有一个版本的,当我在升级Xcode7和适配iOS9以后,这里弹出两个版本,而且默认是繁体,于是我又加了个简体版本,忘记一些必须填的信息没有填,所以报这个错误)如图吧:




4,iOS9字体微变后对文字稍微变大,对有些写死的label会出现"..."的情况

iOS8中,字体是Helvetica,中文的字体有点类似于“华文细黑”。只是苹果手机自带渲染,所以看上去可能比普通的华文细黑要美观。iOS9中,中文系统字体变为了专为中国设计的“苹方” 有点类似于一种word字体“幼圆”。字体有轻微的加粗效果,并且最关键的是字体间隙变大了!
所以很多原本写死了width的label可能会出现“...”的情况。

为了在界面显示上不出错,就算是固定长度的文字也还是建议使用sizetofit 或者ios向上取整 ceilf() 或者提前计算


CGSize size = [title sizeWithAttributes:@{NSFontAttributeName: [UIFont systemFontOfSize:14.0f]}];
CGSize adjustedSize = CGSizeMake(ceilf(size.width), ceilf(size.height));

5,Bitcode

bitcode的理解应该是把程序编译成的一种过渡代码,然后苹果再把这个过渡代码编译成可执行的程序。bitcode也允许苹果在后期重新优化我们程序的二进制文件,有类似于App瘦身的思想。

用了xcode7的编译器编译之前没问题的项目可能会出现下列报错。

1
XXXX’ does not contain bitcode. You must rebuild it with bitcode enabled (Xcode setting ENABLE_BITCODE), obtain an updated library from the vendor, or disable bitcode for this target. for architecture arm64

问题的原因是:某些第三方库还不支持bitcode。要不然是等待库的开发者升级了此项功能我们更新库,要不就是把这个bitcode禁用。

禁用的方法就是找到如下配置,选为NO.(iOS中bitcode是默认YES,watchOS中bitcodes是不让改的必须YES。)

如图:



6,iOS9对ShareSDK的影响

在iOS9下,系统默认会拦截对http协议接口的访问,因此无法获取http协议接口的数据。对ShareSDK来说,具体表现可能是,无法授权、分享、获取用户信息等。
还可能造成我们的编辑界面里传http之类的网络图片的时候,我们的编辑界面不会显示图片截图,解决的办法或者全面关闭https,允许使用http请求;或者把图片的域添加;又或者选择使用https的图片

解决办法:

设置域。可以简单理解成,把不支持https协议的接口设置成http的接口。

1)、在项目的info.plist中添加一个Key:NSAppTransportSecurity,类型为字典类型。
2)、然后给它添加一个NSExceptionDomains,类型为字典类型;
3)、把需要的支持的域添加給NSExceptionDomains。其中域作为Key,类型为字典类型。
4)、每个域下面需要设置3个属性:NSIncludesSubdomains、NSExceptionRequiresForwardSecrecy、NSExceptionAllowsInsecureHTTPLoads。
均为Boolean类型,值分别为YES、NO、YES。

如图:



各平台设置域

新浪微博

  • weibo.com
  • weibo.cn
  • sina.com.cn

腾讯微博

  • qq.com

微信

  • qq.com

QQ/QZone

  • qq.com

易信

  • 无需配置

豆瓣

  • 无需配置

人人

  • renren.com

开心网

  • kaixin001.com
  • kaixin001.com.cn

有道

  • youdao.com

Facebook

facebook.com

fbcdn.net

akamaihd.net

Twitter

  • twitter.com

Google+

  • 无需配置

Instagram

  • instagram.com
  • instagramstatic-a.akamaihd.net

Tumblr

  • tumblr.com

Instapaper

  • instapaper.com

Pinterest

  • 无需配置

明道

  • 无需配置

Evernote印象笔记

  • 无需配置

VK

  • 无需配置

pocket

  • 无需配置

LinkedIn

  • 无需配置

Dropbox

  • 无需配置

Flickr

  • 无需配置


添加Scheme白名单。
问题描述:在iOS 9下涉及到平台客户端跳转,系统会自动到项目info.plist下检测是否设置平台Scheme。对于需要配置的平台,如果没有配置,就无法正常跳转平台客户端。因此要支持客户端的分享和授权等,需要配置Scheme名单。


具体方法:

1)、在项目的info.plist中添加一LSApplicationQueriesSchemes,类型为Array。
2)、然后给它添加一个需要支持的项目,类型为字符串类型;



各平台OpenURL白名单说明

新浪微博

  • sinaweibo,
  • sinaweibohd,
  • sinaweibosso,
  • sinaweibohdsso,
  • weibosdk,
  • weibosdk2.5
  • [后两个若导入新浪SDK则需要]

腾讯微博

  • TencentWeibo,
  • tencentweiboSdkv2
  • [控制台会提示这两个,但是腾讯微博SDK已经弃用,可以忽略不配置]

豆瓣

  • 无需配置

开心网

  • 无需配置

微信

  • wechat,
  • weixin

易信

  • yixin,
  • yixinopenapi

支付宝

  • alipay,
  • alipayshare

QQ

  • mqqOpensdkSSoLogin, 
  • mqqopensdkapiV2,
  • mqqopensdkapiV3,
  • wtloginmqq2,
  • mqq,
  • mqqapi

QZONE

  • mqzoneopensdk, 
  • mqzoneopensdkapi,
  • mqzoneopensdkapi19,
  • mqzoneopensdkapiV2,
  • mqqOpensdkSSoLogin,
  • mqqopensdkapiV2,
  • mqqopensdkapiV3,
  • wtloginmqq2,
  • mqqapi,
  • mqqwpa
  • mqzone,
  • mqq

[:若同时使用QQQZONE,则直接添加本格即可]

Google+

  • googlechrome, 
  • googlechrome-x-callback,
  • hasgplus4,
  • com.google.gppconsent,
  • com.google.gppconsent.2.2.0,
  • com.google.gppconsent.2.3.0,
  • com.google.gppconsent.2.4.0,
  • com.google.gppconsent.2.4.1

人人网

  • renrenapi,
  • renrenios,
  • renreniphone,
  • renren,
  • 以及在使用人人SDK时所需配置的URL Scheme,例如:rm226427com.mob.demoShareSDK

Facebook

  • fbauth2

Twitter

  • 无需配置

Pocket

  • pocket-oauth-v1

Pinterest

  • pinit

Instagram

  • instagram

WhatsApp

  • whatsapp

Line

  • line

KakaoTalk

  • kakaolink

KaokaoStory

  • storylink

LinkedIn

  • 无需配置

Tumblr

  • 无需配置

非平台类,如短信,复制,邮件等

  • 无需配置

大部分社交平台SDK不支持bitcode。
问题描述:iOS 9新建项目默认需要支持bitcode,而不支持bitcode的SDK会导致无法编译运行。
解决方案:
1)、暂时关闭对bitcode的支持(建议),方法如下图
(2)、移除不支持bitcode的平台SDK。




注:本文有些资料参考自这里这里


2017-07-19 19:57:50 NLYNN 阅读数 1109

iOS已知时间加N天(减N天)后的日期

- (void)addDay {
    
    NSInteger distance = 3; // 加的天数
    
    NSDate *nowDate = [NSDate date];
    
    NSDate *otherDate;
    
    NSTimeInterval  oneDay = 24 * 60 * 60 * 1;  //1天的长度
    
    // 加N天
    otherDate = [nowDate initWithTimeIntervalSinceNow: +oneDay* distance ];
    
    // 减去
    //theDate = [nowDate initWithTimeIntervalSinceNow: -oneDay*dis ];
    
    
    // 方法2
    NSDate *newDate = [nowDate dateByAddingTimeInterval:24 * 60 * 60  * distance];
    
    NSLog(@"\n现在的时间%@\nN天后: %@\n方法2N天后newDate=%@", nowDate, otherDate, newDate);

}

现在的时间 2017-07-19 11:55:04 +0000

N天后:      2017-07-22 11:55:04 +0000

方法2N天后newDate=2017-07-22 11:55:04 +0000



2013-06-26 20:07:33 bl1988530 阅读数 2118

这个问题其实,遇到几次了,事情比较多,就没去仔细研究,解决方法也是很暴力的直接加上8小时。

这次遇到了,调试了下,发现其实不是那么回事。

NSDate * nowDate = [NSDate date];//这里的时间为CST时间,直接在变量查看窗口看变量值是正确的。
但如果你用NSLog(@"nowDate:%@",nowDate)把它print出来,就会发现少了8个小时;

我又试了下用DateFormater来进行时间转字符串和字符串转成时间,查看时间变量值,都是对得上的,也就是说只有NSLog(@"nowDate:%@",nowDate)这种直接打印时间对象的情况,显示的时间会少8小时。

应该是NSLog里,对时间的格式化是按GMT时间来转的,这样一转化就少了8小时,解决方式也很简单,不要这样打印使用就行了,代码里内部计算,该怎么用就怎么用,但一旦涉及到需要显示到界面或需要用打印来赋值的一些操作,要输出时间时,先用DateFormater把它格式化为所需的字符串,然后就可以放心使用了

2018-01-18 11:43:40 autom_lishun 阅读数 734

/**

 *  ** 在当前日期时间加上 某个时间段(传负数即返回当前时间之前x月x日的时间)

 *

 *  @param year   当前时间若干年后 (传负数为当前时间若干年前)

 *  @param month  当前时间若干月后  (传0即与当前时间一样)

 *  @param day    当前时间若干天后

 *  @param hour   当前时间若干小时后

 *  @param minute 当前时间若干分钟后

 *  @param second 当前时间若干秒后

 *

 *  @return 处理后的时间字符串

 */

- (NSArray *)dateStringAfterlocalDateForYear:(NSInteger)year Month:(NSInteger)month Day:(NSInteger)day Hour:(NSInteger)hour Minute:(NSInteger)minute Second:(NSInteger)second;




 (NSArray *)dateStringAfterlocalDateForYear:(NSInteger)year Month:(NSInteger)month Day:(NSInteger)day Hour:(NSInteger)hour Minute:(NSInteger)minute Second:(NSInteger)second

{

    NSDate *localDate = [NSDate date];

    NSDateComponents *comps = [[NSDateComponents alloc] init];

    [comps setYear:year];

    [comps setMonth:month];

    [comps setDay:day];

    [comps setHour:hour];

    [comps setMinute:minute];

    [comps setSecond:second];

    NSCalendar *calender = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];

    

    NSDate *minDate = [calender dateByAddingComponents:comps toDate:localDate options:0];

    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];

    [formatter setDateStyle:NSDateFormatterMediumStyle];

    [formatter setTimeStyle:NSDateFormatterShortStyle];

    [formatter setDateFormat:@"YYYY-MM-dd HH"];

        

    NSDateComponents *components = [calender components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay|NSCalendarUnitHour fromDate:minDate];

    NSInteger thisYear=[components year];

    NSInteger thisMonth=[components month];

    NSInteger thisDay=[components day];

    NSInteger thisHour=[components hour];

    NSString *DateTime = [NSString stringWithFormat:@"%ld-%ld-%ld-%ld",(long)thisYear,(long)thisMonth,(long)thisDay,(long)thisHour];

    NSArray  *array = [DateTime componentsSeparatedByString:@"-"];

    return array;

}


ios touch事件

阅读数 11

iOS 七巧板动画

阅读数 1191

iOS开发中的HexString

阅读数 3733

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