> On 15 Feb 2017, at 20:40, Greg Parker <gpar...@apple.com> wrote: > >> On Feb 15, 2017, at 11:11 AM, Ole Begemann via swift-users >> <swift-users@swift.org> wrote: >> >> In macOS 10.12 and iOS 10.0, class properties were introduced to Objective-C >> [1]. I noticed that the Objective-C runtime treats class properties >> differently based on the deployment target. Example: >> >> // MyClass is an Objective-C class that has a class property >> let metaclass = object_getClass(MyClass.self) >> var count: UInt32 = 0 >> let properties = class_copyPropertyList(metaclass, &count) >> // Work with properties >> // Deallocate properties >> >> When the deployment target is macOS 10.12, passing a metaclass to >> copyPropertyList() returns the class properties, but it returns an empty >> list on a lower deployment target. > > That's right. The Objective-C runtime was written with the expectation that > class properties would be added someday, but that code was buggy. On macOS > 10.10 and iOS 8.x and older if libobjc sees a class property then it may > crash. The fix was for clang and swiftc to leave class properties out of the > ObjC metadata when compiling for those deployment targets. > > macOS 10.11 and iOS 9.x deployment targets are both safe. (Class properties > still did not exist then. Instead the bug that nobody had seen yet was > unknowingly fixed when the code was rewritten for an unrelated optimization.)
Thank you, Greg. I only tested with 10.12 and 10.10, so I hadn't noticed that it would already work with 10.11. > Note that class property visibility depends on the deployment target used to > compile that class's implementation. It is possible to see class properties > from some classes but not others if the classes come from different > executables with different deployment targets. > >> I'd like to perform a check (either at runtime or compile time) which of >> these behaviors I'll get. >> >> I didn't find a way to perform a compile-time check in Swift against the >> deployment target; if #available() checks against the SDK the code is linked >> against. Likewise, the usual runtime check using >> ProcessInfo().isOperatingSystemAtLeast() doesn't check against the >> deployment target. >> >> The best I came up with is this: >> >> /** >> Returns `true` if the current deployment target is at least the specified >> version that corresponds to the current platform. >> >> The arguments must be passed in the form of the `__MAC_XX_X`, >> `__IPHONE_XX_X`, etc. constants defined in Availability.h. >> */ >> public func isDeploymentTargetAtLeast( >> macOS macOSVersion: Int32, iOS iOSVersion: Int32, >> tvOS tvOSVersion: Int32, watchOS watchOSVersion: Int32) -> Bool { >> >> #if os(macOS) >> return __MAC_OS_X_VERSION_MIN_REQUIRED >= macOSVersion >> #elseif os(iOS) >> return __IPHONE_OS_VERSION_MIN_REQUIRED >= iOSVersion >> #elseif os(tvOS) >> return __TV_OS_VERSION_MIN_REQUIRED >= tvOSVersion >> #elseif os(watchOS) >> return __WATCH_OS_VERSION_MIN_REQUIRED >= watchOSVersion >> #else >> return false >> #endif >> } >> >> For example, to test if the current deployment target is at least macOS >> 10.12, iOS 10.0, tvOS 10.0, or watchOS 3.0: >> >> guard isDeploymentTargetAtLeast( >> macOS: __MAC_10_12, iOS: __IPHONE_10_0, >> tvOS: __TVOS_10_0, watchOS: __WATCHOS_3_0) else { >> >> // Deployment target is lower >> ... >> } >> >> Is this correct? Is there a better way to do this? > > That code ought to work, assuming that the implementation of > isDeploymentTargetAtLeast() and the implementations of the interrogated > classes are in the same executable, or are all in executables compiled with > the same deployment target.
_______________________________________________ swift-users mailing list swift-users@swift.org https://lists.swift.org/mailman/listinfo/swift-users