Как мы все знаем, CGFloat
(который повсеместно в CoreGraphics, UIKit и т.д.) может быть 32-битное или 64-битное число с плавающей запятой, в зависимости от архитектуры процессора ,Преобразование между CGFloat и NSNumber без ненужного продвижения Удвоить
В C, CGFloat
это typealias к float
или double
, в Свифта он определен как struct CGFloat
с native
собственности (который является Float
или Double
).
Было замечено неоднократно, что NSNumber
может быть создан из и преобразован в Float
и Double
, но существует не подобных преобразований от и до CGFloat
. Общие рекомендации (например, в Convert CGFloat to NSNumber in Swift) является обращенного через Double
CGFloat <--> Double <--> NSNumber
Пример:
let c1 = CGFloat(12.3)
let num = NSNumber(double: Double(c1))
let c2 = CGFloat(num.doubleValue)
и что является простым и правильным, нет точного не теряется. Также большинство платформ сегодня 64-бит, а затем преобразование CGFloat/Double
тривиально и, вероятно, оптимизировано компилятором.
Однако это вызвало мое любопытство, если преобразование может быть сделано без продвижения CGFloat
к Double
на 32-битных платформах.
Можно было бы использовать заявление в сборки конфигурации (как, например, в Should conditional compilation be used to cope with difference in CGFloat on different architectures?):
extension NSNumber {
convenience init(cgFloatValue value : CGFloat) {
#if arch(x86_64) || arch(arm64)
self.init(double: value.native)
#else
self.init(float: value.native)
#endif
}
}
Но что, если Swift портирован на другие архитектуры, которые не являются Intel или ARM? Это не выглядит очень перспективным.
Можно также использовать константу CGFLOAT_IS_DOUBLE
(как, например, в NSNumber from CGFloat):
if CGFLOAT_IS_DOUBLE != 0 {
// ...
} else {
// ...
}
Недостатком здесь является то, что компилятор всегда будет излучать «никогда не будет выполнена» предупреждение на одном из случаев.
Итак, чтобы сделать длинную историю короткой:
- Как мы можем конвертировать между
CGFloat
иNSNumber
безопасным способом, без предупреждений компилятора, и без ненужного продвижения кDouble
?
Обратите внимание, что это означает «академическую» проблему. Как упоминалось выше, (и в других Q & A) можно просто преобразовать через Double
практически.
Я размещаю здесь «self-answer» в духе share your knowledge, Q&A-style. Конечно, другие ответы приветствуются!
Следует отметить, что в апреле 2017 года, с Swift 3.1 выпущен, бросок между NSNumber и CGFloat может произойти сбой во время выполнения и станет проверенным актером в Swift 4, поэтому более запутанным решением может стать путь. –