1

I am running into a weird issue when trying to test for the existence of a symbol that is introduced in a newer version of the OS. I follow the Apple guidelines on using weak-linked symbols, i.e.

Check the availability of an external (extern) constant or a notification name by explicitly comparing its address—and not the symbol’s bare name—to NULL or nil.

To reproduce the issue, I am using the latest iOS 6 SDK on the latest Xcode 4.5.2, using the default compiler (Apple LLVM compiler 4.1). I weak-linked the Social framework (which is only available on iOS 6+). And I run this code on iOS 5.1 (the deployment target is lower than 6):

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

The output is:

0x0
Yes1
Yes2

In other words, we can verify at runtime that the expression &SLServiceTypeFacebook evaluates to the value 0. Yet, if statements that test on this expression treat it as if it is true.


Update: From this question, I found that this workaround works with no optimization, but not with optimization:

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

Update: It appears that this problem does not exist with UIKit symbols. Running the following on iOS 4.3:

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

The output is:

0x0

I hypothesize that the difference is that the UIKit symbol has a NS_AVAILABLE_IOS() macro next to it, so somehow the compiler handles it correctly. In the case of the Social framework symbol, it doesn't have a NS_AVAILABLE_IOS() macro since the entire Social framework itself is only available since iOS 6 (i.e. the symbol is available since the version of the framework, so I guess the don't need this macro?); but then the compiler does not handle the symbol correctly.

Community
  • 1
  • 1
user102008
  • 30,736
  • 10
  • 83
  • 104
  • Shouldn't the first line be `NSLog(@"%p", SLServiceTypeFacebook)` where `SLServiceTypeFacebook` is a pointer? – Robotic Cat Nov 14 '12 at 03:56
  • @RoboticCat: No, the address of weak-linked symbols that are not available is 0 and trying to access the symbol will cause a segfault. – user102008 Nov 14 '12 at 04:15
  • OK - final thought (and then I'm out of ideas) - what about `NSLog(@"%x", &SLServiceTypeFacebook);`? – Robotic Cat Nov 14 '12 at 04:34
  • @RoboticCat That shouldn't make any difference as it's just prints in a different format, the passed value will be the same. – JustSid Nov 14 '12 at 05:38
  • @JustSid: They should return different values (note no `&` in the first comment versus the `&` in the second comment). Anyway it was just a thought. – Robotic Cat Nov 14 '12 at 18:17

2 Answers2

0

Are you sure you don't want to check that the SLRequest class exists instead of checking for this constant?

In any case, the issue is that the compiler is optimizing the test away (it interprets this as testing a constant expression which is true at compile time). You can circumvent this by reading this address into a local volatile variable. Or you could dynamically search for the symbol at runtime.

But I would consider just checking for the SLRequest class instead.

Here are at least these three options:

#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");

Any of these should work as you were expecting in iOS5.1 versus iOS6.

halfer
  • 19,824
  • 17
  • 99
  • 186
Firoze Lafeer
  • 17,133
  • 4
  • 54
  • 48
  • 1) I have not turned on any optimization, and 2) This is the accepted way of testing for availability of symbols, and used to work (see e.g. http://stackoverflow.com/questions/3002833/weak-linking-on-iphone-refuses-to-work) – user102008 Nov 14 '12 at 09:15
  • In Xcode you can check the compiler output to verify if this conditional is being compiled, even in debug profile. If you feel this is a bug in clang, then radr is of course the venue for that. Otherwise, here are options which work with shipping code. Testing for the class itself is still better, I think. – Firoze Lafeer Nov 14 '12 at 12:09
  • @FirozeLafeer are you sure this is a deliberate optimisation, or is that just a guess? I'm baffled by this because how can the value be known at compile time before it is fixed up? I'm trying to work out if this is (or was) a bug or something else going on, and if it's still an issue to be concerned about? Also is there a good analysis of it somewhere to definitively show what's going on? – jhabbott Oct 08 '14 at 16:24
-2

just check with NSClassFromString if you can get the classes.. objC is all classes anyway :D

Daij-Djan
  • 49,552
  • 17
  • 113
  • 135
  • 1) Objective-C is not all classes. 2) I am checking for the availability of a variable here, not a class, so this is completely irrelevant. 3) And you don't even need to do `NSClassFromString` to check for the availability of a class. Simply checking `[TheClass class]`, or directly passing any other message to it `[TheClass someMessage]` will work fine. The class object will be `nil` if the class is not available. – user102008 Nov 14 '12 at 21:53
  • im fully aware its not ALL classes. :P hence the -> :D | but a good deal is focused around classes and checking for a symbol might be unecessary when checking for a certain class or method. – Daij-Djan Nov 14 '12 at 23:06
  • the other answer also recommends checking the class btw! >| – Daij-Djan Nov 14 '12 at 23:10
  • to 3 -- is that safe? you are using the symbol TheClass . the docs I saw all use strings to avoid the unreferenced symbol? if so : cool – Daij-Djan Nov 14 '12 at 23:11