参考:Clang Diagnostics
谈谈Objective-C的警告
iOS警告收录及科学快速的消除方法
一、什么是Clang Diagnostics?
诊断是结合了逻辑与分析来得出的一个结论。
在医学界,诊断是通过实验室样本做后盾的本能来判断。而对于工业制造,则是通过在统计和方向都等同应用来诊断产品故障。
对于我们开发者来说,我们通过代码通知后续代码的生产,创建了一个在过去半个世纪里呈几何倍数发展的技术的正反馈循环。尤其对于我们的OC的开发者来说,最有效的诊断来自Clang。
二、OC中常用的代码诊断
1、当你在 XCode 中运行 "Build & Analyze" (⌘⇧B) 后得到的惊人结果,就是Clang的代码诊断
这个命令是简单的逻辑诊断,会诊断出简单的逻辑错误,比如下图,就诊断出imageArray初始化之后没有用。经常用这个命令可以避免很多基本的代码逻辑错误。
2、如果你想在编程的过程中开启困难模式,你只要设置设置 -Weverything 标志,并在你的编译设置中勾选上 "Treat Warnings as Errors"。
一个有节操的程序员会在乎自己的代码的警告,就像在乎饭碗边上有只死蟑螂那样。 ——@onevcat
编译器给出的警告对开发者开说是很有用的信息。警告不会阻止继续编译和链接,也不会导致程序不能运行,但是很多时候,编译器会先你一步发现问题所在,对于OC来更是如此。Clang不仅对于明显的错误能够提出警告(比如某个方法或者接口未实现),也能对很多潜在的可能的问题作出提示(比如说方法已经废弃,或者有问题的转换),而这些在很多时候都可能成为潜在的致命错误,必须加以重视。
像Ruby或者PHP这样的动态语言没有所谓的编译警告,而C#或者Java这类语言的警告很多都是不得不照顾的废弃方法什么的,很多开发者已经习惯于忽略警告进行开发。OC由于现在由苹果负责维护,Clang的LLVM也同时是苹果在做,可以说从语言到编译器到SDK全局都在掌握之中,因此做OC开发时的警告往往比其他语言的警告更有参考价值。打开尽可能多的警告提示,并且在程序开发中尽量避免生成警告,对于构建一个健壮高效的程序来说,是必须的。
当然在UI里一个一个点击激活警告虽然简单,但每次都这样来一回是一种一点也不有趣的做法,特别是在你已经了解它们的内容并决定打开它们的时候。在编译选项中加入合适的flag能够打开或者关闭警告:在
Build Setting
中的Other C Flags
里添加形似-W...
的编译标识。你可以在其中填写任意多的-W...
以开关某些警告,比如,填写为-Wall -Wno-unused-variable
即可打开“全部”警告(其实并不是全部,只是一大部分严重警告而已),但是不启用“未使用变量”的警告。使用-W...`的形式,而不是在UI上勾选的一大好处是,在编译器版本更新时,新加入的警告如果包含在-Wall中的话,不需要对工程做任何修改,新的警告即可以生效。这样立即可以察觉到同一个工程由于编译器版本更新时可能带来的隐患。另外一个更重要的原因是..Xcode的UI并没有提供所有的警告 =_=
可以看见,使用了这个flag之后,我的警告数从234,迅速飙升到999+。。。
刚才提到的,需要注意的是,-Wall
的名字虽然是all,但是这真的只是一个迷惑人的词语,实际上-Wall
涵盖的仅只是所有警告中的一个子集。在StackExchange上有一个在Google工作的Clang开发者进行的回答,其中解释了有一些重要的警告组:
Wall
并不是所有警告。这一个警告组开启的是编译器开发者对于“你所写的代码中有问题”这一命题有着很高的自信的那些警告。要是在这一组设定下你的代码出现了警告,那基本上就是你的代码真的存在严重问题了。但是同时,并不是说打开Wall
就万事大吉了,因为Wall
所针对的仅仅只是经典代码库中的为数不多的问题,因此有一些致命的警告并不能被其捕捉到。但是不论如何,因为Wall的警告提供的都是可信度和优先级很高的警告,所以为所有项目(至少是所有新项目)打开这组警告,应该成为一种良好的习惯。Wextra
如其所名,-Wextra
组提供“额外的”警告。这个组和-Wall
组几乎一样有用,但是有些情况下对于代码相对过于严苛。一个很常见的例子是,-Wextra
中包含了-Wsign-compare
,这个警告标识会开启比较时候对signed和unsigned的类型检查,当比较符两边一边是signed一边是unsigned时,产生警告。其实很多代码并没有特别在意这样的比较,而且绝大多数时候,比较signed和unsigned也是没有太大问题的(当然不排除会有致命错误出现的情况)。需要注意,-Wextra
和-Wall
是相互独立的两个警告组,虽然里面打开的警告标识有个别是重复的,但是两组并没有包含的关系。想要同时使用的话必须在Other C Flags中都加上Weverything
这个是真正的所有警告。但是一般开发者不会选择使用这个标识,因为它包含了那些还正在开发中的可能尚存bug的警告提示。这个标识一般是编译器开发者用来调试时使用的,如果你想在自己的项目里开启的话,警告一定会爆棚导致你想开始撞墙..
3、控制警告,局部加入或关闭
Clang提供了我们自己加入警告或者暂时关闭警告的方法。
//Generate a warning
#pragma message "Warning 1"
//Another way to generate a warning
#warning "Warning 2"
两种强制警告的方法在视觉效果上是一样的,但是警告的类型略有不同,一个是-W#pragma-messages
,另一个是-W#warnings
。对于第二种写法,把warning换成error,可以强制使编译失败。比如发布一些需要API Key之类的类库,可以使用这个方法来提示别的开发者别忘了输入必要的信息。
//Generate an error to fail the build.
#error "Something wrong"
对于关闭警告,如果要全局关闭的话,直接在Other C Flags里写 -Wno-...
就行了,比如-Wextra -Wno-sign-compare
就是一个常见的组合。如果想对某几个文件开启或禁用警告,在Build Phases
的Compile Source
相应的文件中加入对应的编译标识即可。如果只是想在某几行关闭某个警告的话,可以通过临时改变诊断编译标记来抑制指定类型的警告,具体如下:
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-variable"
int a;
#pragma clang diagnostic pop
如果a之后没有被使用,也不会出现未使用变量的警告了。对于想要抑制的警告类型的标识名,可以在build产生的警告后的build log
中看到。
三、关于Clang diagnostics的思考
我应该开启哪些警告提示
个人喜好(代码洁癖)不同,会有不同的需求。我的建议是对于所有项目,特别是新开的项目,首先开启
-Wall
和-Wextra
,然后在此基础上构建项目并且避免一切警告。如果在开发过程中遇到了某些确实无法解决或者确信自己的做法是正确的话(其实这种情况,你的做法一般即使不是错误的,也会是不那么正确的),可以有选择性地关闭某些警告。一般来说,关闭的警告项目不应该超过一只手能数出来的数字,否则一定哪儿出问题了..
是否要让警告等于错误
一种很常见的做法和代码洁癖是将警告标识为错误,从而中断编译过程。这让开发者不得不去修复这些警告,从而保持代码干净整洁。在Xcode中,可以通过勾选相应的Treat Warnings as Errors
来开启,或者加入-Werror
标识。我个人来说不喜欢使用这个设定,因为它总是打断开发流程。很多时候并不可能把代码全写完再编译调试,相反地,我更喜欢写一点就编译运行一下看看结果,这样在中间debug编译的时候会出现警告也不足为奇。另外,如果做TDD开发时,也可能会有大量正常的警告出现,如果有警告就不让编译的话,开发效率可能会打折扣。一个比较好的做法是只在Release Build时将警告视为错误,因为Xcode中是可以为Debug和Release分别指定标识的,所以这很容易做到。
另外也可以只把某些警告当作错误,-Werror=...
即可,同样地,也可以在-Werror
被激活时使用-Wno-error=...
来使某些警告不成为错误。结合使用这些编译标识可以达到很好的控制。
ps:全面的 Clang 警告综合列表,可以在这里找到:F***ingClangWarnings.com
在GCC的手册中有一个参考。虽然苹果现在用的都是LLVM了,但是这部分内容应该是继承了GCC的设定。
不显示pod的警告
添加到你的Podfile:
platform :ios, '8.0'
# ignore all warnings from all pods
inhibit_all_warnings!
# ignore warnings from a specific pod
pod 'Facebook-iOS-SDK', :inhibit_warnings => true
这样依赖,所有通过cocoaPods安装的第三方库的警告就没有了。
也可以,如下图所示,单独忽略某一个第三方的所有警告