Swift 3.0 - 损坏的函数值(对于组件:Calendar.Component)-> Int?
Swift 3.0 - Broken func value(for component: Calendar.Component) -> Int?
是否有 value(对于组件:Calendar.Component)似乎被破坏的解决方法?或者调用 属性 版本的动态方式?
func dateComponentsValueShouldBeNil() {
let dateComponents = DateComponents(month: 3)
debugPrint("Month", dateComponents.month) // "Month" Optional(3)
debugPrint("Property", dateComponents.hour) // "Property" nil
debugPrint("Enumeration", dateComponents.value(for: .hour)) // "Enumeration" Optional(9223372036854775807)
}
DateComponents
实例中的非定义日期组件(那里的值为 nil
)在包装的 NSDateComponents
实例中由全局 int
属性 NSDateComponentUndefined
这不是 bug/broken 方法,而是从 DateComponents
与 NSDateComponents
紧密相连的事实得出的(它派生自 NSObject
:不使用可选项nil
来指定没有值的对象)。具体来说,前者的 value(...)
方法主要是后者 value(forComponent:)
方法的包装器 (API Reference: Foundation -> NSDateComponents)
value(forComponent:)
Returns the value for a given NSCalendarUnit value.
Declaration
func value(forComponent unit: NSCalendar.Unit) -> Int
此方法不能 return nil
,但表示 global int
variable NSDateComponentUndefined
.
没有值的日期组件
let dateComponents = DateComponents(month: 3)
debugPrint("Enumeration", dateComponents.value(for: .hour))
// "Enumeration" Optional(9223372036854775807)
debugPrint("NSDateComponentUndefined", NSDateComponentUndefined)
// "NSDateComponentUndefined" 9223372036854775807
从API Reference: Foundation -> NSDateComponents我们读到:
...
An NSDateComponents
object is not required to define all the component
fields. When a new instance of NSDateComponents
is created the date
components are set to NSDateComponentUndefined
.
因此,尝试为 DateComponents
的 Calendar.Component
成员调用 value(...)
的 return 还没有被初始化不是乱码,而是默认的未定义值NSDateComponentUndefined
(全局 int
属性 恰好 return/be 设置为 Int64.max = 9223372036854775807
)。
要深入了解这方面的细节,我们可以访问 DateComponents
(swift-corelibs-foundation/Foundation/DateComponents.swift)
的来源
/// Set the value of one of the properties, using an enumeration value instead of a property name.
///
/// The calendar and timeZone and isLeapMonth properties cannot be set by this method.
public mutating func setValue(_ value: Int?, for component: Calendar.Component) {
_applyMutation {
[=13=].setValue(_setter(value), forComponent: Calendar._toCalendarUnit([component]))
}
}
/// Returns the value of one of the properties, using an enumeration value instead of a property name.
///
/// The calendar and timeZone and isLeapMonth property values cannot be retrieved by this method.
public func value(for component: Calendar.Component) -> Int? {
return _handle.map {
[=13=].value(forComponent: Calendar._toCalendarUnit([component]))
}
}
属性 _handle
上面换行 NSDateComponents
internal var _handle: _MutableHandle<NSDateComponents>
在 _MutableHandle
(swift-corelibs-foundation/Foundation/Boxing.swift)
internal final class _MutableHandle<MutableType : NSObject> where MutableType : NSCopying {
fileprivate var _pointer : MutableType
// ...
/// Apply a closure to the reference type.
func map<ReturnType>(_ whatToDo : (MutableType) throws -> ReturnType) rethrows -> ReturnType {
return try whatToDo(_pointer)
}
// ...
}
从上面value(...)
的签名推导出ReturnType
为Int?
,_toCalendarValue
定义为(swift-corelibs-foundation/Foundation/Calendar.swift)
internal static func _toCalendarUnit(_ units: Set<Component>) -> NSCalendar.Unit {
let unitMap: [Component : NSCalendar.Unit] =
[.era: .era,
.year: .year,
.month: .month,
.day: .day,
.hour: .hour,
.minute: .minute,
.second: .second,
.weekday: .weekday,
.weekdayOrdinal: .weekdayOrdinal,
.quarter: .quarter,
.weekOfMonth: .weekOfMonth,
.weekOfYear: .weekOfYear,
.yearForWeekOfYear: .yearForWeekOfYear,
.nanosecond: .nanosecond,
.calendar: .calendar,
.timeZone: .timeZone]
var result = NSCalendar.Unit()
for u in units {
let _ = result.insert(unitMap[u]!)
}
return result
}
因此,value(...)
的 return 正文中的 whatToDo
可以破译为等同于对
的调用
NSDateComponents.value(forComponent: NSCalendar.Unit)
并且如该答案顶部所述,此调用永远不会 return nil
(return 类型 Int
)。
如果我们最后 return 到 DateComponents
的来源,我们会看到下面的私有 getter 和 setter 处理 "bridging" 之间NSDateComponentUndefined
和 nil
.
/// Translate from the NSDateComponentUndefined value into a proper Swift optional
private func _getter(_ x : Int) -> Int? { return x == NSDateComponentUndefined ? nil : x }
/// Translate from the proper Swift optional value into an NSDateComponentUndefined
private func _setter(_ x : Int?) -> Int { if let xx = x { return xx } else { return NSDateComponentUndefined } }
是否有 value(对于组件:Calendar.Component)似乎被破坏的解决方法?或者调用 属性 版本的动态方式?
func dateComponentsValueShouldBeNil() {
let dateComponents = DateComponents(month: 3)
debugPrint("Month", dateComponents.month) // "Month" Optional(3)
debugPrint("Property", dateComponents.hour) // "Property" nil
debugPrint("Enumeration", dateComponents.value(for: .hour)) // "Enumeration" Optional(9223372036854775807)
}
DateComponents
实例中的非定义日期组件(那里的值为 nil
)在包装的 NSDateComponents
实例中由全局 int
属性 NSDateComponentUndefined
这不是 bug/broken 方法,而是从 DateComponents
与 NSDateComponents
紧密相连的事实得出的(它派生自 NSObject
:不使用可选项nil
来指定没有值的对象)。具体来说,前者的 value(...)
方法主要是后者 value(forComponent:)
方法的包装器 (API Reference: Foundation -> NSDateComponents)
value(forComponent:)
Returns the value for a given NSCalendarUnit value.
Declaration
func value(forComponent unit: NSCalendar.Unit) -> Int
此方法不能 return nil
,但表示 global int
variable NSDateComponentUndefined
.
let dateComponents = DateComponents(month: 3)
debugPrint("Enumeration", dateComponents.value(for: .hour))
// "Enumeration" Optional(9223372036854775807)
debugPrint("NSDateComponentUndefined", NSDateComponentUndefined)
// "NSDateComponentUndefined" 9223372036854775807
从API Reference: Foundation -> NSDateComponents我们读到:
...
An
NSDateComponents
object is not required to define all the component fields. When a new instance ofNSDateComponents
is created the date components are set toNSDateComponentUndefined
.
因此,尝试为 DateComponents
的 Calendar.Component
成员调用 value(...)
的 return 还没有被初始化不是乱码,而是默认的未定义值NSDateComponentUndefined
(全局 int
属性 恰好 return/be 设置为 Int64.max = 9223372036854775807
)。
要深入了解这方面的细节,我们可以访问 DateComponents
(swift-corelibs-foundation/Foundation/DateComponents.swift)
/// Set the value of one of the properties, using an enumeration value instead of a property name.
///
/// The calendar and timeZone and isLeapMonth properties cannot be set by this method.
public mutating func setValue(_ value: Int?, for component: Calendar.Component) {
_applyMutation {
[=13=].setValue(_setter(value), forComponent: Calendar._toCalendarUnit([component]))
}
}
/// Returns the value of one of the properties, using an enumeration value instead of a property name.
///
/// The calendar and timeZone and isLeapMonth property values cannot be retrieved by this method.
public func value(for component: Calendar.Component) -> Int? {
return _handle.map {
[=13=].value(forComponent: Calendar._toCalendarUnit([component]))
}
}
属性 _handle
上面换行 NSDateComponents
internal var _handle: _MutableHandle<NSDateComponents>
在 _MutableHandle
(swift-corelibs-foundation/Foundation/Boxing.swift)
internal final class _MutableHandle<MutableType : NSObject> where MutableType : NSCopying {
fileprivate var _pointer : MutableType
// ...
/// Apply a closure to the reference type.
func map<ReturnType>(_ whatToDo : (MutableType) throws -> ReturnType) rethrows -> ReturnType {
return try whatToDo(_pointer)
}
// ...
}
从上面value(...)
的签名推导出ReturnType
为Int?
,_toCalendarValue
定义为(swift-corelibs-foundation/Foundation/Calendar.swift)
internal static func _toCalendarUnit(_ units: Set<Component>) -> NSCalendar.Unit {
let unitMap: [Component : NSCalendar.Unit] =
[.era: .era,
.year: .year,
.month: .month,
.day: .day,
.hour: .hour,
.minute: .minute,
.second: .second,
.weekday: .weekday,
.weekdayOrdinal: .weekdayOrdinal,
.quarter: .quarter,
.weekOfMonth: .weekOfMonth,
.weekOfYear: .weekOfYear,
.yearForWeekOfYear: .yearForWeekOfYear,
.nanosecond: .nanosecond,
.calendar: .calendar,
.timeZone: .timeZone]
var result = NSCalendar.Unit()
for u in units {
let _ = result.insert(unitMap[u]!)
}
return result
}
因此,value(...)
的 return 正文中的 whatToDo
可以破译为等同于对
NSDateComponents.value(forComponent: NSCalendar.Unit)
并且如该答案顶部所述,此调用永远不会 return nil
(return 类型 Int
)。
如果我们最后 return 到 DateComponents
的来源,我们会看到下面的私有 getter 和 setter 处理 "bridging" 之间NSDateComponentUndefined
和 nil
.
/// Translate from the NSDateComponentUndefined value into a proper Swift optional
private func _getter(_ x : Int) -> Int? { return x == NSDateComponentUndefined ? nil : x }
/// Translate from the proper Swift optional value into an NSDateComponentUndefined
private func _setter(_ x : Int?) -> Int { if let xx = x { return xx } else { return NSDateComponentUndefined } }