中心思想
这篇文章也是紧接着上一篇继续归纳和总结,在我们日常的开发工作中,Xcode编译器抛出的各种“无理取闹”的异常和错误。
在每个异常或错误的下方,我会尽可能详细地归纳解决办法,解决方案中涉及网上取材的内容,我会附上网址或者其他大神博客供大家参考。
如果文中有不当或者错误的地方,劳烦您指正,我会及时修改,以免误导他人及一些iOS开发初学者。
Xcode编译器头疼脑热的地方远比我们知道的要多,如果您遇到过文中不曾提及的警告或者错误,并且知道如果解决它,请您以简信或者Email的形式发送给我(网页地址栏有我的邮箱地址),我会一并总结更新,我会附上您的信息(例如博客,Github地址)。在此先谢谢了。
1.Bitcode问题
XXX 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
上面这个问题也是iOS 9SDK发布以后出现在Xcode7中的新问题,Bitcode是Xcode7引入的新特性,旨在为App瘦身,网上对它通俗的解释是在线版安卓ART模式。
下面这段话,是亲身参与2015年WWDC大会的国内一线开发者王巍的总结:Bitcode 是 LLVM 的中间码,在编译器更新时,Apple 可以用你之前提交的 Bitcode 进行优化,这样你就不必在编译器更新后再次提交你的 app,也能享受到编译器改进所带来的好处。Bitcode 支持在新项目中是默认开启的,没有特别理由的话,你也不需要将它特意关掉。
Apple Watch应用必须打开Bitcode,iOS没有强制,但是Xcode7默认打开了Bitcode。
解决上述问题的方法:
1.更新library使包含Bitcode,否则会出现以上问题,陆续都会支持Bitcode。
2.关闭Bitcode,步骤如下。
找到工程的target->build settings->直接搜索bitcode->然后在搜索结果的Build Options选项下找到Enable Bitcode->将YES改为NO,如下动态图所示。
2.升级Xcode7后上传App时报错
解决方案:进入TencentOpenApi_ios_Bundle.bunle 点击显示包内容,把plist文件删除即可
3.故事板与Masonry混合使用时报错
原因:如果用的view是storyboard连线而来的,不是自己在代码里创建的。 很可能运行的时候会报错,说IB已经定义的约束和Masonry的约束冲突。即使你拖进去控件后并没有设置约束。
解决:按以前的方法先用故事版做好约束,然后在每个约束的属性面板设置PlaceHolder : Remove at run time。 这样在运行的时候会自动取消。
Masonry是一个Objective-C版本的第三方Autolayout库,还有一个叫做snapKit的开源库,它是Masonry的Swift版本。在Swift项目中,如果出现上述错误,解决办法同上。
4.升级Xcode7后使用JSONKit第三方库崩溃
出现问题的出处是: void *keyObjectISA = *((void **)keys[idx]);这一段。这个问题我也是从网络搜集的,我没用过JSONKit,一直都是用系统自带的。但是下面的解决办法在我朋友那里得到了验证。如果您有异议,请您指正,我会及时修改。
解决办法:将原项目中JSONKit.m文件替换成下面链接的实现文件即可
https://github.com/jcbertin/JSONKit
5.添加Scheme白名单
这个问题是引起一些在项目中使用到微信,支付宝或者微博SDK相关功能(支付,收藏,分享)出现错误的原因,它会导致Xcode报如下报错:
canOpenURL:failedforURL:“weixin://app/wxdaae92a9cfe5d54c/” - error: “This app is not allowed to query for scheme weixin”
问题描述:在iOS 9下涉及到平台客户端跳转,系统会自动到项目info.plist下检测是否设置平台Scheme。对于需要配置的平台,如果没有配置,就无法正常跳转平台客户端。因此要支持客户端的分享和授权等,需要配置Scheme名单。
解决办法如下:
1)在项目的info.plist中添加一LSApplicationQueriesSchemes,类型为Array。
2)然后给它添加一个需要支持的项目,类型为字符串类型;
下面是一些项目中常用的白名单,你有用到的你就添加到该数组下面,类型全部为string
mqqOpensdkSSoLogin
mqzone
sinaweibo
alipayauth
alipay
safepay
mqq
mqqapi
mqqopensdkapiV3
mqqopensdkapiV2
mqqapiwallet
mqqwpa
mqqbrowser
wtloginmqq2
weixin
6. directory not found for option
问题原因:Xcode7将framworks位置改变了
解决方法:
1)点击项目,选择 Targets->XXXTests
2)选择build setting 选项,找到 Frameworks Search Path 或者 Library Search Paths
3)删除$(SDKROOT)/Developer/Library/Frameworks,
或者使用$(PLATFORM_DIR)/Developer/Library/Frameworks替换
7.iOS 9旧状态栏报错
下图是报错的截图
出错原因:设置app的状态栏样式使用了旧的方式,在info.plist里面设置了View controller-based status bar appearance为NO,默认为YES,一般式iOS6的时候使用这种方式,iOS7,8也兼容,但是到了iOS9就报了警告。
解决办法:
1)删除原先编写的代码
[[UIApplication sharedApplication]setStatusBarStyle:UIStatusBarStyleLightContent]
2)然后在plist文件中添加项View controller-based status bar appearance,类型为Boolean,设置为YES(默认是NO),如下图7-1所示。
3)然后在你自定义的导航控制器里面添加如下代码
-(UIStatusBarStyle)preferredStatusBarStyle { return UIStatusBarStyleLightContent; }
4)在模拟器上删除应用程序并重新运行即可
8.大部分社交平台接口不支持https协议。
这个问题也是上一篇中网络请求出错的一个分支。目前还有大量的应用是通过HTTP协议来访问服务器的,而要让所有的应用都转向支持HTTPS,显然是一件耗时的工作。所以下面的方法可以一试,但只是暂时性的。毕竟苹果帮之前提出了App Transport Security(ATS)的概念,它的提出意味着苹果将准备弃用HTTP,转而支持HTTPS。
上述问题的解决方案就是设置域,可以简单理解成,把不支持https协议的接口设置成http的接口。
具体方法:
1)、在项目的info.plist中添加一个Key:NSAppTransportSecurity,类型为字典类型。
2)、然后给它添加一个NSExceptionDomains,类型为字典类型;
3)、把需要的支持的域添加給NSExceptionDomains。其中域作为Key,类型为字典类型。
4)、每个域下面需要设置3个属性:NSIncludesSubdomains、NSExceptionRequiresForwardSecrecy、NSExceptionAllowsInsecureHTTPLoads。均为Boolean类型,值分别为YES、NO、YES。
以下文章能够帮助你更好的理解ATS的概念
如何使用ATS提高应用的安全性
ATS问题
App Transport Security dictionary primary keys
9.找不到.dylib文件,换成.tbd文件而又无法运行,请尝试下面的方式来解决。
拿添加libsqlite3.dylib为例(你可别照本宣科)
1)在项目Target中的Link Binary With Libraries 手动添加,
首先点击 “+” ;
2)显示搜索添加页面,在这里如果搜索之前的libsqlite3.dylib是搜不出来ios9之前的。所以需要点击 Add Other,出现文件目录页面,正常情况这里也是找不到老的libsqlite3.dylib文件的,因为这个文件是隐藏掉的。所以需要按快捷键 CMD+Shift+G (Go to the folder),输入/usr/lib后,进入隐藏的界面;
3)然后添加你需要的 *.dylib,如libsqlite3.dylib文件.大功告成
今天先告一段落,我会继续更新,keep moving!给广大无聊的程序员推荐一个小游戏,我最近无聊的时候在玩,叫做okay?(你要是在改Bug,当我没说)。
另外给大家推荐一个bug收集工具,叫做Bugtags,我也是偶然在资深开发者唐巧的微信公众号看见的,后来在自己的Demo中体验了一下,感觉小巧实用,之后还接到了一个Bugtags开发团队的使用情况回访电话,就算帮助他们推广一下。
更多有关Bugtags的内容请参考以下链接:
Bugtags官网
Bugtags博客
更新两个使用Swift语言出现的问题
1.新建playground报错Unable to find execution service for selected run destination
这个问题参考一下就行了
解决步骤:
1.先关闭Xcode
2.cd到Xcode所在目录,并打开终端输入下面两行命令
rm-rf~/Library/Developer/CoreSimulator/Devices
killall -9 com.apple.CoreSimulator.CoreSimulatorService
关于Playground
Playground,有人将它称呼为“训练场”,它可以让Swift展示某些脚本语言的特性。实际上,Playground只是提供了一个可实时编译运行并展示的交互式开发环境罢了,类似于著名的REPL。它的这项功能完全借助于LLVM编译器强大的组织能力。
目前,Playground只支持Swift语言,但是一位歪果仁大神开发出了一个可以运行OC版本的Playground,有兴趣可以去看看。点我喽
2.dyld: Library not loaded:@rpath/libswiftCore.dylib
问题描述:真机测试时,出现上述错误
解决步骤:
1.在工程的Targets中找到Build Setting选项
2.搜索Embedded Content Contains Swift Code项,将其设置为YES
第三次更新
更新两个问题,也是在网上搜集的,解决办法比较详细
1. “Unknown class XXViewController in Interface Builder file.” 问题处理
在静态库中写了一个XXViewController类,然后在主工程的xib中,将xib的类指定为XXViewController,程序运行时,报了上述错误:
其实这个问题与Interface Builder无关,最直接的原因还是相关的symbol没有从静态库中加载进来。这种问题的处理就是在Target的”Build Setting”–>“Other Link Flags”中加上”-all_load -ObjC”这两个标识位,这样就OK了。
2.关于Unbalanced calls to begin/end appearance transitions for …问题的处理
当某个业务有这么一个需求,进入一个列表后需要立马又push一个web页面,做一些活动的推广。在iOS 8上,我们的实现是一切OK的;但到了iOS 7上,就发现这个web页面push不出来了,同时控制台给了一条警告消息,即如下:
Unbalanced calls to begin/end appearance transitions for ...
在这种情况下,点击导航栏中的返回按钮时,直接显示一个黑屏。
我们到stackoverflow上查了一下,有这么一段提示:
occurs when you try and display a new viewcontroller before the current view controller is finished displaying.
意思是说在当前视图控制器完成显示之前,又试图去显示一个新的视图控制器。
于是我们去排查代码,果然发现,在viewDidLoad里面去做了次网络请求操作,且请求返回后就去push这个web活动推广页。此时,当前的视图控制器可能并未显示完成(即未完成push操作)。
Basically you are trying to push two view controllers onto the stack at almost the same time.
解释如下:
当几乎同时将两个视图控制器push到当前的导航控制器栈中时,或者同时pop两个不同的视图控制器,就会出现不确定的结果。所以我们应该确保同一时间,对同一个导航控制器栈只有一个操作,即便当前的视图控制器正在动画过程中,也不应该再去push或pop一个新的视图控制器。
所以最后我们把web活动的数据请求放到了viewDidAppear里面,并做了些处理,这样问题就解决了。
原文链接
第四次更新
1.手动performSegueWithIdentifier没有生效的问题
这个问题是因为对视图控制对象的生命周期概念没有深刻理解,比如说当用户打开应用,判断用户是否登陆,如果未登陆,就跳转到登录页面这个情况。
如果你将判断用户是否登陆的语句放到了视图的viewDidLoad中去,那么这个方法是不会生效的。因为在viewDidLoad中就启动segue的话,依然会被后来填充的视图覆盖,要是在视图加载完成后的viewDidAppear中启动segue,就不会出现错误了。