测试已测试 | ✗ |
语言语言 | Obj-CObjective C |
许可证 | MIT |
发布最新发布 | 2017年3月 |
SwiftSwift 版本 | 3.0 |
由Tomaz Kragelj维护。Tomaz Kragelj链接。
GBCli 是一个命令行界面辅助库。它提供了用于简化命令行 Objective C 基础工具代码的类。该库的设计尽可能灵活,以适应广泛的工具。除了 Foundation.framework 外,库不需要其他外部依赖。该库由 Dave Dribin 的 DDCli 启发。
以下是它的诞生过程(如果只对技术部分感兴趣,可以跳过此部分) - 当我开始重新设计 appledoc 时,我包括的第一组文件之一就是 DDCli 库。但我很快就发现它与 arc 不兼容。加上我想采用的不同工作流程,这促使我深入挖掘 Dave 的代码,看看我能否修改它以更好地满足我的需求。长话短说 - 最终我从头开始编写了整个库,现在解析是手动实现的,而不依赖 getopt_long
。随着我添加更多的功能,我发现,我可以将其作为可重用组件发布在 GitHub 上…
此文件作为一个教程,展示了您可以使用此库做什么。
将 GBCli 集成到项目中的最简单方式是通过 Cocoapods。只需在您的 Podfile 中添加以下行
pod "GBCli"
然后导入所有文件,以便编译器可以看到它们: #import <GBCli/GBCli.h>
。
如果您希望直接包含源文件,只需将 GBCli/src
子文件夹中的所有 .h 和 .m 文件复制到您的 Xcode 项目中,并导入 GBCli.h
头文件(在这种情况下,您可能需要使用 #import "GBCli.h"
)。
库中最基本的构建块是解析命令行参数。这是通过GBCommandLineParser类实现的。要使用它,您需要首先注册所有选项,然后让它解析命令行。基于注册的数据,该类确定命令行是否有效。但仅此还不够有用,因此该类还提供了获取实际选项和参数值的接口。您可以使用回调API在解析过程中通知每一项选项和/或参数(注意:在此,我用options指的是命令行选项,如--verbose
、--help
等,而arguments是跟随选项的各种参数,如源文件路径等)。
除了回调API外,解析器还内部存储了所有识别的选项和参数,因此您可以在解析完成后查询它以获取值。这可以解决存储值的问题。
示例
int main(int argc, char **argv) {
// Create parser and register all options.
GBCommandLineParser *parser = [[GBCommandLineParser alloc] init];
[parser registerOption:@"optiona" shortcut:'a' requirement:GBValueRequired];
[parser registerOption:@"optionb" shortcut:'b' requirement:GBValueOptional];
[parser registerOption:@"optionc" shortcut:'c' requirement:GBValueNone];
// Parse command line
[parser parseOptionsWithArguments:argv count:argc block:^(GBParseFlags flags, NSString *option, id value, BOOL *stop) {
switch (flags) {
case GBParseFlagUnknownOption:
printf("Unknown command line option %s, try --help!\n", [option UTF8String]);
break;
case GBParseFlagMissingValue:
printf("Missing value for command line option %s, try --help!\n", [option UTF8String]);
break;
case GBParseFlagOption:
// do something with 'option' and its 'value'
break;
case GBParseFlagArgument:
// do something with argument 'value'
break;
}
}];
...
// Now that parsing is complete, you can access options and arguments:
id valuea = [parser valueForOption:@"optiona"];
NSArray *arguments = parser.arguments;
...
}
在上例中,我们已注册了3个选项
-a
的--optiona
作为必填值。-b
的--optionb
作为可选值。-c
且无值的--optionc
。您还可以仅注册长选项,没有短格式变体 - 例如,只需为短选项参数传递0
。如您所见,您可以为每个选项指定值为必填、可选或无值。以下是工作方式
--optiona value
、-a value
,或者用另一种方式:--optiona=value
或-a=value
(在后一种情况下,选项名称、等号和值之间必须有空格!)。如果值缺失,解析器将通过GBParseFlagMissingValue标志报告缺失值。请注意,您需要将包含空格的字符串嵌入引号中,例如:--some-option "My whitespaced string"
。--optionb value
、-b value
、--optionb=value
、-b=value
。在这些情况下,给定值被报告为NSString。此外,您也可以完全省略值:--optionb
或-b
。在这种情况下,只要在命令行上提供了该选项,就会报告为@YES
的NSNumber作为块。这些选项也可以使用诸如--no-
(例如:--no-optionc
)之类的否定语法 - 在这种情况下,该选项被认为已禁用,并且报告了[NSNumber numberWithBool:NO]
作为其值。这对于您拥有默认启用(即出厂设置)并希望在命令行上覆盖值的选项特别有用。请注意,无值选项还接受option=value语法,因此--optionc=1
等于--optionc
,而--optionc==0
等于--no-optionc
。请注意,您只能使用option=equal语法,它不与空格一起使用!--optionc
或-c
),则假定它已启用,并在解析块中报告了值为[NSNumber numberWithBool:YES]
。这些选项还可以使用否定语法,如--no-optionc
(例如:)。在这种情况下,该选项假定已禁用,其值为[NSNumber numberWithBool:NO]
。这在您希望在不覆盖默认值的情况下重写命令行上的值时特别有用。请注意,无值选项也接受option=value语法,所以--optionc=1
等于--optionc
,而--optionc==0
等于--no-optionc
。请注意,您只能使用option=equal语法,它不与空格一起使用!在大多数情况下,您会希望将命令行中的值存储到一个简单易用的接口中,然后您可以将其传递给工具的各种组件。GBCli提供了GBSettings类来满足此目的。在其基本形式中,它通过方法如objectForKey:
、setObject:forKey:
、boolForKey:
、setBool:forKey:
等提供了类似NSUserDefaults的接口到设置。以下是一个示例:
int main(int argc, char **argv) {
// Create settings.
GBSettings *settings = [GBSettings settingsWithName:@"CmdLine" parent:nil];
[settings setInteger:50 forKey:@"optiona"];
[settings setInteger:12 forKey:@"optionb"];
// Create parser and register all options.
GBCommandLineParser *parser = [[GBCommandLineParser alloc] init];
[parser registerOption:@"optiona" shortcut:'a' requirement:GBValueRequired];
[parser registerOption:@"optionb" shortcut:'b' requirement:GBValueOptional];
[parser registerOption:@"optionc" shortcut:0 requirement:GBValueNone];
// Parse command line
[parser parseOptionsWithArguments:argv count:argc block:^(GBParseFlags flags, NSString *option, id value, BOOL *stop) {
switch (flags) {
case GBParseFlagUnknownOption:
printf("Unknown command line option %s, try --help!\n", [option UTF8String]);
break;
case GBParseFlagMissingValue:
printf("Missing value for command line option %s, try --help!\n", [option UTF8String]);
break;
case GBParseFlagOption:
[settings setObject:value forKey:option];
break;
case GBParseFlagArgument:
[settings addArgument:value];
break;
}
}];
// From here on, just use settings...
NSInteger a = [settings integerForKey:@"optiona"];
NSInteger b = [settings integerForKey:@"optionb"];
...
}
请注意,我们在创建GBSettings时提供了一些“出厂默认值”。这完全可选,但这是定义应用于可选参数的值的不错方式。
虽然上面的例子可能就是您需要的全部,但GBSettings类允许您轻松管理设置层次结构。例如,出厂默认值和命令行,或者中间的额外层。例如,appledoc也可以从全局或项目文件中读取设置。选项应该优先,所以例如,命令行中提供值将覆盖出厂默认值。GBSettings使这变得非常简单——这里是如何扩展前面例子的:
int main(int argc, char **argv) {
// Create settings stack.
GBSettings *factoryDefaults = [GBSettings settingsWithName:@"Factory" parent:nil];
[factoryDefaults setInteger:50 forKey:@"optiona"];
[factoryDefaults setInteger:12 forKey:@"optionb"];
GBSettings *settings = [GBSettings settingsWithName:@"CmdLine" parent:factoryDefaults];
...the rest is exactly the same as previous example...
}
在此示例中,我们已经设置出厂默认值/命令行层次结构。
您通过创建子GBSettings对象时提供parent设置来建立设置层次结构。通过这样做,子值将优先于父值。然而,如果子项没有特定的值,它会自动回退并要求其父项提供它。依此类推——您可以根据需要嵌套多层。就这么简单——“它就是这样工作的”。注意,GBCommandLineParser不需要了解层次结构,它只需要一个GBSettings对象,将从中复制所有命令行选项。对于应用程序的其余类也是如此:它们只需要知道“最低”级别(或最高优先级——根据您如何看待它)的GBSettings对象。如果需要,它将自动获取其父类的值。如果您想知道:如果给定的值在层次结构中任何地方都没有提供,将返回nil
(或标量)为0
或NO
。
现在,您可能想知道设置层次结构可能有用吗。在下面的章节中会变得更加明显,如果您感兴趣,请继续阅读……
但在我们到达那里之前
我非常赞同您的观点——整个parseOptionsWithArguments:count:block:
感觉像是内置功能过多的开销。实际上,如果您使用GBSettings,确实是这样。在这种情况下,您只需将设置注册到GBCommandLineParser并使用简化的解析方法,如下所示:
int main(int argc, char **argv) {
// Create settings stack.
GBSettings *factoryDefaults = [GBSettings settingsWithName:@"Factory" parent:nil];
[factoryDefaults setInteger:50 forKey:@"optiona"];
[factoryDefaults setInteger:12 forKey:@"optionb"];
GBSettings *settings = [GBSettings settingsWithName:@"CmdLine" parent:factoryDefaults];
// Create parser and register all options.
GBCommandLineParser *parser = [[GBCommandLineParser alloc] init];
[parser registerOption:@"optiona" shortcut:'a' requirement:GBValueRequired];
[parser registerOption:@"optionb" shortcut:'b' requirement:GBValueOptional];
[parser registerOption:@"optionc" shortcut:0 requirement:GBValueNone];
// Register settings and then parse command line
[parser registerSettings:settings];
[parser parseOptionsWithArguments:argv count:argc];
// From here on, just use settings...
NSInteger a = [settings integerForKey:@"optiona"];
NSInteger b = [settings integerForKey:@"optionb"];
...
}
现在这感觉好多了,不是吗?为了使用简化的解析方法,您必须先通过registerSettings:
方法注册设置。如果您正在使用设置层次结构,您只需要注册“最低”层——正如上面的示例所示。
请注意,这些“简化”的解析方法将只需将任何错误打印到标准输出并将选项和参数分配给已注册的设置。
有时,您可能希望允许用户重复选择选项几次。在这种情况下,您很可能希望接收所有值。例如,假设您想要能够将输出复制到几个位置。一种方法是将带有用某些预定义字符分隔的位置列表的--outputs
选项——例如--outputs ~/Dir1;~/Dir2
。这是可能的,但将需要您的某些后处理。
别担心: GBSettings 包含“将键视为数组”特性,它允许您通过重复键来实现这一点。这会使您能够使用像 --output ~/Dir1 --output ~/Dir2
(或使用替代语法 --output=~/Dir1 --output=~/Dir2
)。最好的部分是 - 它将全部为您自动处理(几乎自动) - 您只需将选项注册为数组即可。假设您使用两层 - 工厂默认值和命令行设置,它看起来会是这样
[factorySettings registerArrayForKey:@"output"]
[settings registerArrayForKey:@"output"]
在这种情况下,[settings objectForKey:@"output"]
将返回一个包含所有值的 NSArray
。如果没有值,将返回 nil
,如果有一个单独的值,将返回一个包含单个项目的数组。此外 - 如果您使用设置层次结构 - 结果数组将包含整个设置栈中收集的所有值 - 包括所有父项!
再次强调:请注意,您必须将选项单独注册为数组到每个级别!如果这听起来像是一件重复的工作,请继续阅读:)
虽然可以通过 KVC 界面直接使用 GBSettings 进行基本工具的开发可能足够,但在大多数情况下,将 KVC 集成到针对特定应用程序定制的基于 @property
的界面中会更加有益。
一种方法是通过重写 GBSettings 类,但 Objective-C 为此提供了(在我看来)更好的解决方案 - 即类别。以下是如何在上述示例中看起来
@interface GBSettings (MyAppSettings)
@property (nonatomic, assign) NSInteger optiona;
@property (nonatomic, assign) NSInteger optionb;
@property (nonatomic, assign) BOOL optionc;
@end
现在我们需要以某种方式“映射”与特定键的属性。让我们看看它将如何显示
@implementation GBSettings (MyAppSettings)
- (NSInteger)optiona {
return [self integerForKey:@"optiona"];
}
- (void)setOptiona:(NSInteger)value {
[self setInteger:value forKey:@"optiona"];
}
... repeat same for optionb and optionc
@end
简单明了,但有些啰嗦且麻烦,尤其是在更复杂的工具中。但是,还有一种方法 - 在 GBSettings.h 文件的末尾,有一些便利宏,允许您通过一行代码像这样生成属性
@implementation GBSettings (MyAppSettings)
GB_SYNTHESIZE_INT(optiona, setOptiona, @"optiona")
GB_SYNTHESIZE_INT(optionb, setOptionb, @"optionb")
GB_SYNTHESIZE_BOOL(optionc, setOptionc, @"optionc")
@end
每个支持的值类型都有一个宏 - GB_SYNTHESIZE_BOOL
用于 BOOL
,GB_SYNTHESIZE_INT
用于 NSInteger
,GB_SYNTHESIZE_UINT
用于 NSUInteger
,GB_SYNTHESIZE_FLOAT
用于 CGFloat
,GB_SYNTHESIZE_OBJECT
用于 Objective C 类,以及特例 GB_SYNTHESIZE_COPY
,用于您希望复制值而不是保留它的情况(例如 NSString
)。但是,您可以轻松地为不支持的类型创建自己的宏 - 查看 GBSettings.h 的实现。
您可以将数组键的注册轻松放入您的类别中。我倾向于创建一个新的初始化器方法来做这件事,以保持剩下的代码尽可能简单
@implementation GBSettings (MyAppSettings)
+ (id)mySettingsWithName:(NSString *)name parent:(GBSettings *)parent {
id result = [self settingsWithName:name parent:parent];
if (result) {
[result registerArrayForKey:@"output"];
}
return result;
}
@end
然后您可以使用自定义初始化器来初始化您的设置栈,而不是 settingsWithName:parent:
,如下所示
GBSettings *factoryDefaults = [GBSettings mySettingsWithName:@"Factory" parent:nil];
GBSettings *settings = [GBSettings mySettingsWithName:@"CmdLine" parent:factoryDefaults];
当然,如果您选择继承而不是类别,您可以简单地重写指定的初始化器 initWithName:parent:
并在那里执行初始化!无论哪种情况,这都会将设置设置嵌入到单一位置。
您的类别或子类也是一个嵌入其他行为(如应用工厂默认值)的好地方
@implementation GBSettings (MyAppSettings)
- (void)applyFactoryDefaults {
self.optiona = 44;
self.optionb = 55;
}
@end
然后您可以按如下方式设置您的设置栈
GBSettings *factoryDefaults = [GBSettings mySettingsWithName:@"Factory" parent:nil];
GBSettings *settings = [GBSettings mySettingsWithName:@"CmdLine" parent:factoryDefaults];
[factoryDefaults applyFactoryDefaults];
这将进一步清理外部代码,并将所有相关设置集中在一个地方。
在开发命令行界面时,通常会在多个地方重复大量类似的代码。例如:将命令行选项注册到解析器、创建属性并将其映射到选项名称、实现帮助显示、实现选项值显示等。如果这些都可以自动化,那岂不是更方便?这个想法在我脑海中浮现,幸运的是,答案是肯定的——至少对于上述提到的一些问题来说是这样的。
我选择用GBOptionsHelper类实现其中一些最繁琐的任务。它是一个可选类,如果使用它,就会在GBSettings和GBCommandLineParser的上面工作。使用它,您可以免费获得帮助和版本显示。此外,还有一个附加功能,可以按照设置级别打印所有选项的值。这完全是免费的!
它与之前的方法并没有太大的不同
int main(int argc, char **argv) {
// Create settings stack.
GBSettings *factoryDefaults = [GBSettings settingsWithName:@"Factory" parent:nil];
[factoryDefaults setInteger:50 forKey:@"optiona"];
[factoryDefaults setInteger:12 forKey:@"optionb"];
GBSettings *settings = [GBSettings settingsWithName:@"CmdLine" parent:factoryDefaults];
// Create option shelper and register all options.
GBOptionsHelper *options = [[GBOptionsHelper alloc] init];
[options registerOption:'a' long:@"optiona" description:@"The great option a" flags:GBValueRequired];
[options registerOption:'b' long:@"optionb" description:@"The great option b" flags:GBValueOptional];
[options registerOption:'c' long:@"optionc" description:@"Something else too" flags:GBValueNone];
// Create parser, register options from helper and parser command line.
GBCommandLineParser *parser = [[GBCommandLineParser alloc] init];
[parser registerSettings:settings];
[parser registerOptions:options];
[parser parseOptionsWithArguments:argv count:argc];
// From here on, you can forget about GBOptionsHelper or GBCommandLineParser and only deal with GBSettings
NSInteger a = [settings integerForKey:@"optiona"];
NSInteger b = [settings integerForKey:@"optionb"];
...
}
如您所见,您只需将选项注册到GBOptionsHelper而不是GBCommandLineParser,然后让选项助手将其所有选项注册到命令行解析器,并按之前的步骤解析命令行,将所有遇到的价值复制到设置类。
您可能也注意到了注册需要额外的信息,如描述。在实现帮助输出时这会很方便,但现在请忽略它。
在命令行工具中包括一些形式的帮助是非常有用的。例如,如果用户在命令行输入了--help
、-h
或-?
,或者只是在没有参数的情况下输入命令。这是命令行的预期行为。如果使用GBOptionsHelper,您只需注册帮助选项,并在需要时让该类打印帮助信息。首先让我们看看如何进行注册
int main(int argc, char **argv) {
initialize settings stack as before
...
GBOptionsHelper *options = [[GBOptionsHelper alloc] init]
[options registerSeparator:@"OPTION SET1"];
[options registerOption:'a' long:@"optiona" description:@"The great option a" flags:GBValueRequired];
[options registerOption:'b' long:@"optionb" description:@"The great option b" flags:GBValueOptional];
[options registerSeparator:@"OPTION SET2"];
[options registerOption:'c' long:@"optionc" description:@"Something else too" flags:GBValueNone];
[options registerSeparator:@"MISCELLANEOUS"];
[options registerOption:'?' long:@"help" description:@"Display this help and exit" flags:GBValueNonte];
...
register and parse as above
...
if (argc == 1 || settings.printHelp) {
[options printHelp];
return 0;
}
...
}
上面的示例假设您还扩展了您特定的GBSettings子类或类别,并添加了@property (nonatomic, assign) BOOL printHelp;
和相应的GB_SYNTHESIZE_BOOL(printHelp, setPrintHelp, @"help")
。我还添加了选项注册时的“分隔符”,这是可选的,但可能在更复杂的命令行中非常有用。无论如何,如果您在命令行输入--help
或-?
,或者根本不带任何选项调用它,那么您会看到如下输出:
OPTION SET1
-a --optiona <value> Project name
-b --optionb [<value>] Project version
OPTION SET2
-c --optionc Output path, repeat for multiple paths
MISCELLANEOUS
-? --help Display this help and exit
使用GBOptionsHelper获得的另一个免费特性是打印出当前运行所使用的值。如果这不是调试工具的话,也很有用。假设有一个命令行开关来启用或禁用此功能,以下是实现方法:
int main(int argc, char **argv) {
initialize settings stack as before
GBSettings *factoryDefaults = ...
GBSettings *settings = ...
...
GBOptionsHelper *options = [[GBOptionsHelper alloc] init]
[options registerSeparator:@"OPTION SET1"];
[options registerOption:'a' long:@"optiona" description:@"The great option a" flags:GBValueRequired];
[options registerOption:'b' long:@"optionb" description:@"The great option b" flags:GBValueOptional];
[options registerSeparator:@"OPTION SET2"];
[options registerOption:'c' long:@"optionc" description:@"Something else too" flags:GBValueNone];
[options registerSeparator:@"MISCELLANEOUS"];
[options registerOption:0 long:@"print" description:@"Print values" flags:GBValueNonte];
[options registerOption:'?' long:@"help" description:@"Display this help and exit" flags:GBValueNonte];
...
register and parse as above
...
if (settings.printValues) {
[options printValuesFromSettings:settings];
}
...
}
再次假设您在GBSettings子类或类别中添加了printValues
属性,上面的示例将产生以下输出:
Setting CmdLine Factory
OPTION SET1
optiona 44
optionb 12 55
OPTION SET2
optionc 5
MISCELLANEOUS
print 1
help 0
显示不仅告知在这个运行中将使用哪些值(从上到下为44,12,5,1,0),而且还告知值从何处来——是来自工厂默认设置还是命令行。当然,显示会根据您的设置层次结构进行调整——如果之间存在更多层次,它们也会按照预期打印出来!
有时您只想包含某些选项以提供帮助,而将其他选项用于显示值。例如:虽然 --help
对帮助输出来讲很有用,但它通常包含在值输出中并不合乎逻辑(如果用户调用它,会显示其帮助并退出,因此甚至无法到达值输出)。另一个例子:一些选项可能只能在某些设置级别上使用(例如从设置文件中读取的设置),但不能在命令行上使用(可能是简单地解析所有命令行开关后作为参数的输入路径,但需要作为显式的路径在设置文件中提供)。
默认情况下,所有选项都包含在内,但可以通过向注册方法中添加标志来排除输出中的单个选项。如果您使用 GBOptionNoCmdLine
选项,则选项不会注册到命令行解析器,GBOptionNoPrint
将防止选项出现在打印值输出中,而 GBOptionNoHelp
将出现在帮助输出中。您可以通过或运算将几个标志组合起来,并与值需求组合:GBValueNone|GBOptionNoHelp|GBOptionNoPrint
。
您可以通过在适当的时候调用几个钩子来自定义 printHelp
和 printValues
方法的默认输出。您可以通过这种方式轻松添加标题和页脚,而无需对类进行子类化。可能是最明显的示例之一就是提供应用程序名称和版本信息。虽然从您的这边获取应用程序名称不需要任何努力——该工具将从命令行中获取它,但您必须提供版本和构建信息。以下是您可以这样做的方式
GBOptionsHelper *options = [[GBOptionsHelper alloc] init];
options.applicationName = ^{ return @"mytool"; }; // optional, this is picked up automatically if not given
options.applicationVersion = ^{ return @"1.0"; };
options.applicationBuild = ^{ return @"100"; };
以下是您如何将自己定制的标题添加到帮助输出中
options.printHelpHeader = ^{ return @"%APPNAME: version %APPVERSION (build %APPBUILD)\n"; };
因为只有当需要时才会调用块,所以您可以安全地将计算密集型代码添加其中(尽管在这种类型的信息中可能不是这种情况)。
请注意,“占位符字符串”的使用,它们将方便地替换为相应的值:%APPNAME
、%APPVERSION
、%APPBUILD
。请查看 GBOptionsHelper 类的源代码以获取其他钩子。
GBCli 1.1 及更高版本允许将选项分组到“选项组”(或命令)中。这允许您将选项分为几个组。这使得命令行(和 plist 文件)更具可读性。以下是您可以使用 GBOptions 设置组的方式
GBOptionsHelper *options = [[GBOptionsHelper alloc] init]
[options registerSeparator:@"OPTIONS:"];
[options registerOption:'a' long:@"optiona" description:@"The great option a" flags:GBValueRequired];
[options registerOption:'b' long:@"optionb" description:@"The great option b" flags:GBValueOptional];
[options registerSeparator:@"COMMANDS:"];
[options registerGroup:@"group1" description:@"[GROUP 1 OPTIONS]:" optionsBlock:^(GBOptionsHelper *options) {
[options registerOption:0 long:@"option11" description:@"Group 1 option 1" flags:GBValueNone];
[options registerOption:0 long:@"option12" description:@"Group 1 option 2" flags:GBValueNone];
}];
[options registerGroup:@"group2" description:@"[GROUP 2 OPTIONS]:" optionsBlock:^(GBOptionsHelper *options) {
[options registerOption:0 long:@"option21" description:@"Group 2 option 1" flags:GBValueNone];
[options registerOption:0 long:@"option22" description:@"Group 2 option 2" flags:GBValueNone];
}];
然后您将像往常一样将选项注册到 GBCommandLineParser。请注意,您也可以在组外使用选项,只需按照上面的方式在组外注册选项。在上述代码中,您可以像这样使用命令行
mytool --optiona group1 --option11 --option12 group2 --option21
请注意,组也接受在 plist 文件中使用——简单地设置一个值为字典,然后根据根键的方式准备其中的键和值
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>group1</key>
<dict>
<key>option11</key>
<true/>
<key>option12</key>
<true/>
</dict>
<key>group2</key>
<dict>
<key>option21</key>
<true/>
</dict>
<key>optiona</key>
<true/>
</dict>
</plist>
请注意,目前选项组有一些限制。特别是在 GBSettings 级别上,没有对组的支持,所有设置都存储在相同的级别上。这带来了几个潜在问题
GBCli 1.1 及更高版本为简单的 printf
样式函数增加了支持,这些函数接受 NSString
而不是原始 C 字符串。这些函数包括
gbprint
:将给定的字符串打印到 stdout。示例 gbprint(@"Hello %@", @"World");
。gbprintln
:与 gbprint
相同,但在字符串末尾添加换行符。gbfprint
:与 gbprint
相同,但允许您指定文件。示例 gbfprint(stderr, @"Hello %@", @"World");
gbfprintln
:与gbfprint
相同,但在字符串末尾添加换行符。好了,这个教程结束了,请查看包含在Xcode项目中的示例应用程序,以便在更实际的情况中查看。
想要为项目做出贡献吗?我们热烈欢迎 - 在GitHub上叉项目并通过创建带有您的更改的pull request进行操作!尽管我可能会通过电子邮件接受更改,但pull request可以使操作更加方便和简单!它们还能保留作者的身份。哦,请确保您对源代码的更改与编码风格相符,以保持可读性。
以下代码根据MIT许可提供
Copyright (C) 2012 by Tomaz Kragelj
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.