> 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

Reply via email to