【问题标题】:Testing for weak-linked symbol in iOS does not work as expected在 iOS 中测试弱链接符号无法按预期工作
【发布时间】:2012-11-02 13:22:21
【问题描述】:

在尝试测试是否存在在较新版本的操作系统中引入的符号时,我遇到了一个奇怪的问题。我按照Apple guidelines 使用弱链接符号,即

检查外部(extern)常量或 通知名称通过显式比较其地址——而不是 符号的裸名——为 NULL 或 nil。

为了重现该问题,我在最新的 Xcode 4.5.2 上使用最新的 iOS 6 SDK,并使用默认编译器(Apple LLVM 编译器 4.1)。我弱链接了社交框架(仅在 iOS 6+ 上可用)。而我在iOS 5.1上运行这段代码(部署目标低于6):

NSLog(@"%p", &SLServiceTypeFacebook);
if (&SLServiceTypeFacebook)
  NSLog(@"Yes1");
if (&SLServiceTypeFacebook != NULL)
  NSLog(@"Yes2");

输出是:

0x0
Yes1
Yes2

换句话说,我们可以在运行时验证表达式 &SLServiceTypeFacebook 的计算结果为 0。然而,测试此表达式的 if 语句将其视为真。


更新:this question,我发现这种解决方法在没有优化的情况下有效,但在优化时无效:

typeof(&SLServiceTypeFacebook) foo = &SLServiceTypeFacebook;
if (foo)
  NSLog(@"Yes3"); // does not get executed on -O0, but does on any optimization

更新: UIKit 符号似乎不存在此问题。在 iOS 4.3 上运行以下命令:

NSLog(@"%p", &UIKeyboardDidChangeFrameNotification);
if (&SLServiceTypeFacebook)
  NSLog(@"Yes1");
if (&SLServiceTypeFacebook != NULL)
  NSLog(@"Yes2");

输出是:

0x0

我假设不同之处在于 UIKit 符号旁边有一个 NS_AVAILABLE_IOS() 宏,因此编译器会以某种方式正确处理它。在 Social 框架符号的情况下,它没有 NS_AVAILABLE_IOS() 宏,因为整个 Social 框架本身仅在 iOS 6 之后才可用(即,该符号从框架版本开始可用,所以我猜不要'不需要这个宏?);但是编译器没有正确处理符号。

【问题讨论】:

  • 第一行不应该是NSLog(@"%p", SLServiceTypeFacebook),其中SLServiceTypeFacebook是一个指针吗?
  • @RoboticCat:不,不可用的弱链接符号的地址为0,尝试访问该符号会导致段错误。
  • 好的 - 最后的想法(然后我没有想法) - NSLog(@"%x", &SLServiceTypeFacebook); 怎么样?
  • @RoboticCat 这不应该有任何区别,因为它只是以不同的格式打印,传递的值将是相同的。
  • @JustSid:它们应该返回不同的值(注意第一条评论中的& 与第二条评论中的&)。无论如何,这只是一个想法。

标签: iphone objective-c ios xcode weak-linking


【解决方案1】:

您确定不想检查 SLRequest 类是否存在而不是检查此常量吗?

无论如何,问题在于编译器正在优化测试(它将这解释为测试一个在编译时为真的常量表达式)。您可以通过将此地址读入本地 volatile 变量来规避此问题。或者您可以在运行时动态搜索符号。

但我会考虑只检查 SLRequest 类。

这里至少有这三个选项:

#include <dlfcn.h>

NSString* const * volatile check = &SLServiceTypeFacebook;
if (check != NULL)
    NSLog(@"SLServiceTypeFacebook is defined");

// Another approach would be to call dlsym() at runtime 
// to search for this symbol:
if (dlsym(RTLD_DEFAULT, "SLServiceTypeFacebook"))
    NSLog(@"SLServiceTypeFacebook found via dlsym");

// But if you really just wanted to know is if SLRequest
// is available, you should really just do this:
if ([SLRequest class])
    NSLog(@"SLRequest class is available");

在 iOS5.1 和 iOS6 中,所有这些都应该像您所期望的那样工作。

希望对您有所帮助。

【讨论】:

  • 1) 我没有开启任何优化,并且 2) 这是测试符号可用性的公认方法,并且用于工作(参见例如stackoverflow.com/questions/3002833/…
  • 在 Xcode 中,您可以检查编译器输出以验证是否正在编译此条件,即使在调试配置文件中也是如此。如果您觉得这是 clang 中的一个错误,那么 radr 当然是解决这个问题的地方。否则,这里有适用于运输代码的选项。我认为,对课程本身进行测试仍然更好。
  • @FirozeLafeer 你确定这是故意优化,还是只是猜测?我对此感到困惑,因为在修复之前如何在编译时知道该值?我正在尝试确定这是否是(或曾经是)错误或其他事情,是否仍然是需要关注的问题?是否在某处对其进行了很好的分析以明确显示发生了什么?
【解决方案2】:

只需检查 NSClassFromString 是否可以获取类。无论如何,objC 都是类:D

【讨论】:

  • 1) Objective-C 并不是所有的类。 2)我在这里检查一个变量的可用性,而不是一个类,所以这完全无关紧要。 3) 你甚至不需要NSClassFromString 来检查课程的可用性。只需检查[TheClass class],或直接将任何其他消息传递给它[TheClass someMessage] 即可。如果类不可用,则类对象将为 nil
  • 我完全知道它不是所有的类。 :P 因此 -> :D |但是很多事情都集中在类上,在检查某个类或方法时可能不需要检查符号。
  • 另一个答案也建议检查类顺便说一句! >|
  • 到 3 -- 那安全吗?您正在使用符号 TheClass 。我看到的文档都使用字符串来避免未引用的符号?如果是这样:酷
猜你喜欢
  • 2019-06-20
  • 2013-05-11
  • 2017-08-25
  • 2010-11-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多