XCode启动参数和环境变量

Posted 黄文臣

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了XCode启动参数和环境变量相关的知识,希望对你有一定的参考价值。

前言

最近在写《ios代码调试》系列的博客,估计会有十篇以上的内容,等到都写完了会在Github以一个仓库的形式开源出来,欢迎关注我的Github:

这一部分介绍XCode中Argument/Options模块,通过这两个模块,我们可以在启动App的时候传递一些额外的参数进去,覆盖系统的默认值,从而实现特定场景的调试。

Options

Options这里的内容相对简单,但是却容易被忽略。

  • Core Location用来模拟App的位置
  • Application Data 可以用于测试CoreData的Scheme迁移
  • Routing App Coverage File 一个GeoJSON文件,对于导航类应用指明App支持的区域
  • Background fetch 表示启动由backgroud fetch触发
  • Show non-localized strings 显示没有本地话的字符串
  • Application Language & Application Region 系统的语言和区域

Argument Passed On Launch

启动参数用来覆盖NSUserDefaults中的默认值。

注意:启动参数只有在通过XCode启动App的时候才会起作用,直接点击图标启动是没用的。

语言

AppleLanguages可以用来设置启动的语言。

更改语言最直接的方式就是:设置 -> 通用 -> 语言 -> 修改语言,然后重启模拟器,接着重启App,这个过程是很繁琐的。

利用启动参数,这个过程变得非常的直接,比如,设置App在简体中文下启动

-AppleLanguages (zh-Hans)

一些常见的语言列表如下:

English (U.S.)              en
English (UK)                en-GB
English (Australian)        en-AU
English (Indian)            en-IN
French                      fr
Spanish                     es
Portuguese                  pt
German                      de
Italian                     it
Chinese (Simplified)        zh-Hans
Chinese (Traditional)       zh-Hant
Japanese                    ja
Korean                      ko
Russian                     ru

当然,也可以通过Options中的图形化界面来设置语言:

本地化

当你的App需要同时支持多语言的时候,本地化变得很重要。同样的文字,可能在某一中语言中会显示的很长,这时候你可以先通过NSDoubleLocalizedStrings来看看你的UI在双倍显示当前字符串的时候的样子:

-NSDoubleLocalizedStrings YES

对比下开启前后的效果

当你完成了本地化,你想知道那些字符串没有被本地化

-NSShowNonLocalizedStrings YES

开启这个参数,运行项目,对于没有本地话的字符串,会打印出log,并且在英文环境下,没有被本地话的字符串会变成全部是大写的:

2018-06-09 17:16:23.899756+0800 LaunchArgumentDemo[1592:42786] [strings] ERROR: FlZ-Ch-fUI.text not found in table Main of bundle CFBundle 0x7ffd464002e0 …/Bundle/Application/43466A60-F706-4CCE-A3D5-064C05CD04C6/LaunchArgumentDemo.app> (executable, loaded)

这里有个小技巧,如果是xib/storyboard中的视图,需要通过源代码查看的方式去找到问题: 右键Storyboard -> Open As -> Source Code。接着查找log中提到的id:

<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Leo'Name" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="FlZ-Ch-fUI">
    <rect key="frame" x="278" y="626.5" width="77" height="20.5"/>
    <fontDescription key="fontDescription" type="system" pointSize="17"/>
    <nil key="textColor"/>
    <nil key="highlightedColor"/>
</label>

Core Data

当你使用Core Data作为本地持久化存储的技术栈时,你会发现很难对程序进行跟踪,这时候可以使用启动参数

log等级分为1到3,越高越详细

-com.apple.CoreData.SQLDebug 3

Log如下

CoreData: sql: pragma cache_size=1000
CoreData: sql: SELECT ...

还有Core Data迁移调试

-com.apple.CoreData.MigrationDebug

Environment Variable

对比启动参数,环境变量的作用域更广一些,它更像是App的全局变量,在应用内任何地方都可以访问到。

可以通过以下方式在代码里获取环境变量

[[NSProcessInfo processInfo] environment]

dyld

优化过App启动时间的同学都知道,启动时间分为main前和main后,XCode可以通过环境变量来打印main函数前的几个过程

常用的有两个环境变量

DYLD_PRINT_STATISTICS
DYLD_PRINT_STATISTICS_DETAILS

比如,开启DYLD_PRINT_STATISTICS,

再运行应用,会发现log打印,然后你就知道哪里拖慢了你的应用启动:

Total pre-main time: 823.29 milliseconds (100.0%)
         dylib loading time: 226.83 milliseconds (27.5%)
        rebase/binding time: 391.41 milliseconds (47.5%)
            ObjC setup time:  72.95 milliseconds (8.8%)
           initializer time: 131.81 milliseconds (16.0%)
           slowest intializers :
               libSystem.dylib :  12.17 milliseconds (1.4%)
                    Foundation :  45.39 milliseconds (5.5%)
    libMainThreadChecker.dylib :  74.21 milliseconds (9.0%)

除此之外,dyld还有很多可以用来调试的环境变量

DYLD_FRAMEWORK_PATH
DYLD_FALLBACK_FRAMEWORK_PATH
DYLD_VERSIONED_FRAMEWORK_PATH
DYLD_LIBRARY_PATH
DYLD_FALLBACK_LIBRARY_PATH
DYLD_VERSIONED_LIBRARY_PATH
DYLD_PRINT_TO_FILE
DYLD_SHARED_REGION
DYLD_INSERT_LIBRARIES
DYLD_FORCE_FLAT_NAMESPACE
DYLD_IMAGE_SUFFIX
DYLD_PRINT_OPTS
DYLD_PRINT_ENV
DYLD_PRINT_LIBRARIES
DYLD_BIND_AT_LAUNCH
DYLD_DISABLE_DOFS
DYLD_PRINT_APIS
DYLD_PRINT_BINDINGS
DYLD_PRINT_INITIALIZERS
DYLD_PRINT_REBASINGS
DYLD_PRINT_SEGMENTS
DYLD_PRINT_STATISTICS
DYLD_PRINT_DOFS
DYLD_PRINT_RPATHS
DYLD_SHARED_CACHE_DIR
DYLD_SHARED_CACHE_DONT_VALIDATE

Zombie

开启Zombie,当对象被释放后,他们仍然在内存里,只不过视图访问僵尸对象会报错,可以用于调试EXC_BAD_ACCESS

可以通过环境变量NSZombieEnabled来开启

NSZombieEnabled YES

也可以选择NSDeallocateZombies,这样僵尸对象的内存会被释放调。

MallocDebug

内存相关的bug是很难调试的,幸运的是XCode为我们提供了一系列工具,这组工具就是malloc debug。

环境变量对应的功能如下:

MallocStackLogging

记录下来内存分配的调用栈,配合memory debugging等其他可以获取到对象内存地址的debug技巧,可以很容易的查看到一个对象是如何被创建的

  • MallocStackLoggingNoCompact的log粒度比MallocStackLogging更细一些,功能上类似

MallocScribble

对于释放的内存,每个Byte填充成0x55,能够提高野指针的crash率。

原理:以OC对象为例,对象被释放后,内存被标记为回收,但是在第二次写入前,内存还是之前的OC对象;这就导致了即使对象被释放了,只有内存被覆盖后的野指针访问才会crash。

对于开发者来说:野指针的crash很有可能是在对象被释放一段时间后,给调试带来了难度,而MallocScribble会在内存释放后,强制覆盖内存,提高野指针的crash率。

MallocGuardEdges

在分配大内存的时候,在内存前后添加额外的页,进行内存保护。

这个环境变量用处比较少,起码我写了这么久的代码,还没有遇到过MallocGuardEdges帮我找到的内存问题。

MallocGuard

开启Malloc Guard后,在调试的时候会使用libgmalloc替换malloc,从而在当内存出现错误的时候,及时crash你的App。

可以通过以下环境变量来开启MallocGuard

DYLD_INSERT_LIBRARIES /usr/lib/libgmalloc.dylib

举例:

int * array = malloc(sizeof(int) * 10);
for (int index = 0; index < 20; index ++) 
    array[index] = index;

在不开启malloc guard的时候,会crash在main函数,并不会提供有用的信息,在开启malloc guard后:

自定义

除了系统的启动参数和环境变量之外,也支持自定义参数。

我们都知道NSUserDefaults可以用来存储用户的配置信息,比如有一个配置信息是AllowCellularNetwork,即是否允许蜂窝移动网络下访问网络,这时候就可以测试在用户不同设置的情况下启动:

当开发一个framework的时候,可以利用环境变量来开启一些debug功能,这样能保证线上环境不受影响。

然后,在代码里读取

NSDictionary * environments = [[NSProcessInfo processInfo] environment];
BOOL logOn = [[environments objectForKey:@"Network_Log_Enabled"] isEqualToString:@"YES"];

资料

以上是关于XCode启动参数和环境变量的主要内容,如果未能解决你的问题,请参考以下文章

Xcode 7 UI 测试目标语言环境和区域设置

Xcode Playground 中的 ViewController - 帧报告双倍大小 (viewDidLoad)

idea指定启动参数环境变量

Java中的双倍与双倍[重复]

如何在 Xcode 4 中指定命令行参数?

jupyter 启动时的问题