能否以编程的方式在运行时确定用于构建二进制文件的iOS SDK版本?

8
啊,iOS 8 - 很多意外的变化需要考虑!简而言之:有没有一种方法可以在运行时(非预处理宏)编程地确定用于构建应用程序的iOS SDK版本?
我正在为维护的一个库(作为预构建静态库分发)进行一些窗口框架计算方面的努力,因为iOS 8改变了屏幕坐标系统的工作方式。
使用 iOS 7 的代码运行两个初始观察结果而不更改任何内容,如下所示:
1.使用 iOS 7 SDK 构建并在 iOS 8 上运行时,所有内容都像以前一样工作,不需要进行任何更改。 2.使用 iOS 8 SDK 构建并在 iOS 8 上运行时,出现错误:需要对帧计算中的某些更改才能获得正确的定位。
所以,我们更改代码,在 [[UIDevice currentDevice] systemVersion] 上设置条件,以正确地处理新的坐标系统。现在:
1.使用 iOS 7 SDK 构建并在 iOS 7 上运行时,所有内容都正常工作。 2.使用 iOS 8 SDK 构建并在 iOS 8 上运行时,所有内容都正常工作。 3.但是: 使用 iOS 7 SDK 构建,并在 iOS 8 上运行时,计算错误 - 请记住,在使用 iOS 7 SDK 构建时,在进行 iOS 8 特定代码更改之前,所有内容都正常工作。因此,实际上破坏了所做的更改。
通常,我可以通过一些SDK版本的宏条件解决这个问题(#ifdef __IPHONE_8_0 等)。但是,我正在分发使用 iOS 7 SDK 构建的预构建静态库,因此,这些条件中的代码永远不会被包含。以下是为什么会出现问题:
如果使用 iOS 7 SDK 构建静态库,但将其链接到使用 iOS 8 SDK 构建的应用程序中,就像静态库使用 iOS 8 SDK 构建一样(当然,在最终应用程序编译阶段进行链接)。这意味着当使用 iOS 8 SDK 构建应用程序时,需要在其中添加那些 iOS 8 更改-但是我无法使用宏条件来确定是否使用它们,因为C预处理器在 iOS 7 下执行静态库构建时已经完成其操作。
因此,我的问题是:是否有人知道如何能够从预编译的静态库内部在运行时确定应用程序构建是否使用了 iOS 8 SDK?
我尝试检查 iOS 8 特定 SDK 功能(例如-[UIScreen nativeBounds]),但是无法使用该符号-无论 SDK 版本如何,该符号都可用。
有任何想法吗?

你能要求用户在应用的 Info.plist 中输入条目吗? - jscs
有趣的想法,但我认为它会给集成过程增加太多复杂性。不过,也许作为最后的手段? - Michael Tyson
这基本上是UIKit和其他框架在内部使用的链接后检查。我猜你可以通过一些dyld技巧来完成这个操作,我发誓我记得有人曾经构建过这个。让我找找看。 - cbowns
2个回答

5

以下是经验性、未经记录的观察结果:

苹果公司在 Info.plist 文件中记录了您构建 SDK 的信息,其中包括 DTSDKBuildDTSDKName 等键。在这些中,DTSDKName 似乎可以在运行时访问,并以 SDK 版本号结尾。因此,可以获得如下信息:

- (NSString *)buildVersion
{
    // form character set of digits and punctuation
    NSMutableCharacterSet *characterSet = 
        [[NSCharacterSet decimalDigitCharacterSet] mutableCopy];

    [characterSet formUnionWithCharacterSet:
        [NSCharacterSet punctuationCharacterSet]];

    // get only those things in characterSet from the SDK name
    NSString *SDKName = [[NSBundle mainBundle] infoDictionary][@"DTSDKName"];
    NSArray *components =
        [[SDKName componentsSeparatedByCharactersInSet:
              [characterSet invertedSet]]
                  filteredArrayUsingPredicate:
                      [NSPredicate predicateWithFormat:@"length != 0"]];

    if([components count]) return components[0];
    return nil;
}

BOOL wasBuiltWithiOS8SDK = 
    [[[NSBundle mainBundle] infoDictionary][@"DTSDKBuild"] compare:@"11D167"] 
            == NSOrderedDescending;

我必须提醒您,这只是通过经验反向工程得出的结论。因此,这是技术上未记录的API,将来的稳健性无法保证。

你可以直接使用:

BOOL wasBuiltForiOS8 = 
    [[self buildVersion] compare:@"8.0"] != NSOrderedAscending;

(该特性很好,如果找不到版本字符串,它将评估为YES。因此,从技术上讲,如果Apple在未来取消DTSDKBuild,并不重要,重要的是他们不会在7.x中追溯删除它,或者某一天使用字母顺序在8.0之前的版本字符串。)

哦,非常好!它看起来还有一个DTPlatformVersion,其中只有目标SDK版本号。对我很有效!谢谢! - Michael Tyson
在我的测试中,只有在构建设备时才会出现“DTPlatformVersion”属性。当构建模拟器时,该属性不会出现。 - ThomasW

3
这里有一个临时解决方案 - 虽然有点hacky,但最好有更可靠的东西。
- (BOOL)hasiOS8ScreenCoordinateBehaviour {
    if ( [[[UIDevice currentDevice] systemVersion] floatValue] < 8.0 ) return NO;

    CGSize screenSize = [[UIScreen mainScreen] bounds].size;
    if ( UIInterfaceOrientationIsLandscape([[UIApplication sharedApplication] statusBarOrientation]) &&
            screenSize.width < screenSize.height ) {
        return NO;
    }

    return YES;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接