从字符串初始化一个单元
Initializing a Unit from String
我正在寻找一种 Unit(symbol: String)
类型的初始化程序,它将 识别 ,而不是定义符号。
这是一个代码片段,通过提供一个有限的解决方案来说明我的问题。
虽然下面的解决方案有效,但明确列出 Foundation 框架 中预定义的所有单元是乏味的,并且如果 Apple 在即将发布的新单元中发布新单元,则不具有隐式的向前兼容性 iOS 更新。
func matchedUnit<S: Sequence>(symbol: String, valid sequence: S) -> S.Iterator.Element? where S.Iterator.Element: Unit {
let symbols = sequence.map { [=10=].symbol };
let candidate = zip(sequence, symbols).first {
return [=10=].1 == symbol;
}?.0
return candidate;
}
let knownUnitsOfLength = [UnitLength.meters, UnitLength.centimeters, UnitLength.kilometers, UnitLength.decimeters];
if let aUnit = matchedUnit(symbol: "dm", valid: knownUnitsOfLength) {
let measurement = Measurement(value: 1, unit: aUnit);
}
也许我在 SDK 中遗漏了一个明显的难题 - 一个识别符号而不是定义符号的初始化器?
更新:
已确认没有 Apple 提供 API 来执行此操作。我最终使用 Objective-C runtime 找到所有继承自 NSDimension
的 classes 并遍历它们的所有 class 属性他们的名字以 NSUnit
开头。我映射了所有符号,class 和 属性 名称,并且可以在运行时根据用户提供的符号创建 NSUnit
实例。
诚然,在语言支持方面,它与 NSMeasurementFormatter
不在同一级别。 但是 我会自动获得对 Apple 在未来版本中引入的任何新单位和符号的访问权限。
下面是一个示例客户端代码:
NSUnit *kg = [NSUnit unitRecognizingSymbol:@"kg"];
NSMeasurement *mkg = [[NSMeasurement alloc] initWithDoubleValue:3.0 unit:kg];
NSUnit *g = [NSUnit unitRecognizingSymbol:@"g"];
NSMeasurement *mg = [[NSMeasurement alloc] initWithDoubleValue:200 unit:g];
NSMeasurement *total = [mkg measurementByAddingMeasurement:mg];
NSMeasurementFormatter *formatter = [[NSMeasurementFormatter alloc] init];
[formatter setUnitOptions:NSMeasurementFormatterUnitOptionsProvidedUnit];
NSLog(@"%@", [formatter stringFromMeasurement:total]);
// 3.2 kg
下面是实施方面的核心部分。
// Count receives its value from objc_copyClassList().
unsigned int classCount;
Class *classes = objc_copyClassList(&classCount);
// Inspect all available classes.
for (int classIterator = 0; classIterator < classCount; classIterator++) {
char const *className = class_getName(classes[classIterator]);
// Classes we are looking for are derived from NSDimension or NSUnit.
Class superClass = class_getSuperclass(classes[classIterator]);
char const *superClassName = class_getName(superClass);
// Single out NSDimension exception because NSDimension itself inherits from NSUnit.
if ((strcmp(superClassName, "NSDimension") == 0 || strcmp(superClassName, "NSUnit") == 0) && strcmp(className, "NSDimension") != 0) {
// To access available class properties, gain access to meta class.
Class meta = objc_getMetaClass(class_getName(classes[classIterator]));
// Count receives its value from class_copyPropertyList()
unsigned int propertyCount = 0;
objc_property_t *properties = class_copyPropertyList(meta, &propertyCount);
// Inspect all available class properties.
for (int propertyIterator = 0; propertyIterator < propertyCount; propertyIterator++) {
char const *propertyName = property_getName(properties[propertyIterator]);
// ...
}
// ...
}
// ...
没有比您正在做的更好的方法了,因为 NSMeasurementFormatter 只能以一种方式运行 — 它根据测量值生成字符串,而不是相反。您的方法可能存在很大缺陷,因为您没有考虑区域设置,而测量格式化程序知道如何做(对于它前进的一个方向)。如果您希望看到内置功能,您可以向 Apple 提出增强请求。
我正在寻找一种 Unit(symbol: String)
类型的初始化程序,它将 识别 ,而不是定义符号。
这是一个代码片段,通过提供一个有限的解决方案来说明我的问题。
虽然下面的解决方案有效,但明确列出 Foundation 框架 中预定义的所有单元是乏味的,并且如果 Apple 在即将发布的新单元中发布新单元,则不具有隐式的向前兼容性 iOS 更新。
func matchedUnit<S: Sequence>(symbol: String, valid sequence: S) -> S.Iterator.Element? where S.Iterator.Element: Unit {
let symbols = sequence.map { [=10=].symbol };
let candidate = zip(sequence, symbols).first {
return [=10=].1 == symbol;
}?.0
return candidate;
}
let knownUnitsOfLength = [UnitLength.meters, UnitLength.centimeters, UnitLength.kilometers, UnitLength.decimeters];
if let aUnit = matchedUnit(symbol: "dm", valid: knownUnitsOfLength) {
let measurement = Measurement(value: 1, unit: aUnit);
}
也许我在 SDK 中遗漏了一个明显的难题 - 一个识别符号而不是定义符号的初始化器?
更新:
已确认没有 Apple 提供 API 来执行此操作。我最终使用 Objective-C runtime 找到所有继承自 NSDimension
的 classes 并遍历它们的所有 class 属性他们的名字以 NSUnit
开头。我映射了所有符号,class 和 属性 名称,并且可以在运行时根据用户提供的符号创建 NSUnit
实例。
诚然,在语言支持方面,它与 NSMeasurementFormatter
不在同一级别。 但是 我会自动获得对 Apple 在未来版本中引入的任何新单位和符号的访问权限。
下面是一个示例客户端代码:
NSUnit *kg = [NSUnit unitRecognizingSymbol:@"kg"];
NSMeasurement *mkg = [[NSMeasurement alloc] initWithDoubleValue:3.0 unit:kg];
NSUnit *g = [NSUnit unitRecognizingSymbol:@"g"];
NSMeasurement *mg = [[NSMeasurement alloc] initWithDoubleValue:200 unit:g];
NSMeasurement *total = [mkg measurementByAddingMeasurement:mg];
NSMeasurementFormatter *formatter = [[NSMeasurementFormatter alloc] init];
[formatter setUnitOptions:NSMeasurementFormatterUnitOptionsProvidedUnit];
NSLog(@"%@", [formatter stringFromMeasurement:total]);
// 3.2 kg
下面是实施方面的核心部分。
// Count receives its value from objc_copyClassList().
unsigned int classCount;
Class *classes = objc_copyClassList(&classCount);
// Inspect all available classes.
for (int classIterator = 0; classIterator < classCount; classIterator++) {
char const *className = class_getName(classes[classIterator]);
// Classes we are looking for are derived from NSDimension or NSUnit.
Class superClass = class_getSuperclass(classes[classIterator]);
char const *superClassName = class_getName(superClass);
// Single out NSDimension exception because NSDimension itself inherits from NSUnit.
if ((strcmp(superClassName, "NSDimension") == 0 || strcmp(superClassName, "NSUnit") == 0) && strcmp(className, "NSDimension") != 0) {
// To access available class properties, gain access to meta class.
Class meta = objc_getMetaClass(class_getName(classes[classIterator]));
// Count receives its value from class_copyPropertyList()
unsigned int propertyCount = 0;
objc_property_t *properties = class_copyPropertyList(meta, &propertyCount);
// Inspect all available class properties.
for (int propertyIterator = 0; propertyIterator < propertyCount; propertyIterator++) {
char const *propertyName = property_getName(properties[propertyIterator]);
// ...
}
// ...
}
// ...
没有比您正在做的更好的方法了,因为 NSMeasurementFormatter 只能以一种方式运行 — 它根据测量值生成字符串,而不是相反。您的方法可能存在很大缺陷,因为您没有考虑区域设置,而测量格式化程序知道如何做(对于它前进的一个方向)。如果您希望看到内置功能,您可以向 Apple 提出增强请求。