2

I'm working on an internal tool (something that will never be submitted to the App Store) and I'd like to detect at run time if a class is Objective-C or Swift. Is this possible?

9
  • 1
    What's the goal here? If you find out that a particular class is Objective-C or Swift, what are you planning to do with that information? Commented Dec 1, 2015 at 23:58
  • 2
    This is an XY problem. What are you actually trying to do and why? Commented Dec 2, 2015 at 0:14
  • I'd like to know if it's possible just to satisfy my own curiosity. Commented Dec 2, 2015 at 0:19
  • 1
    It is convention for every class in Objective-C to inherit from NSObject, in Mac and iOS programming. But it is not necessary for a class to inherit from NSObject. Commented Dec 2, 2015 at 2:59
  • 1
    FYI - Not all classes have to inherit from NSObject in Objective-C. Some classes can have a root class of NSProxy (though this isn't very common). Commented Dec 2, 2015 at 4:24

2 Answers 2

9

If, for academic purposes, you absolutely must know if a class was created in swift code or not (and not in a way that can easily be fooled via objc_allocateClassPair), then you can utilize the information that can be found in objc-runtime-new.h, specifically, the flags related to FAST_IS_SWIFT.

To extract these flags without having to make your source code C++ or fight with including a ton of private headers, you can use something similar to the following, but please note: THIS IS SUPER FRAGILE.

This is probably not ABI-enforced anywhere, and any future version can change this without consequence. Bear that in mind, etc., etc.

Without further ado, the crazy hack ensues:

#define FAST_IS_SWIFT         (1UL<<0)
#define FAST_HAS_DEFAULT_RR   (1UL<<1)
#define FAST_DATA_MASK        0xfffffffcUL

uintptr_t getClassBits(Class kls) {
#if __LP64__
    typedef uint32_t mask_t;
#else
    typedef uint16_t mask_t;
#endif

    return ((const struct {
        /* struct objc_class */
        Class isa;
        Class superclass;

        /* struct cache_t */
        void *bucket_t;
        mask_t mask;
        mask_t occupied;

        /* struct class_data_bits_t */
        uintptr_t bits;
    } *) (__bridge const void *) kls)->bits;
}

This is recreating the structure format from objc-runtime-new.h, without all of the additional C++ overhead.

Once you have the bits for a class, simply compare it it with FAST_IS_SWIFT (e.g. bits & FAST_IS_SWIFT), and you should have your answer.

Sign up to request clarification or add additional context in comments.

1 Comment

This did in fact change with the new ABI. The header now defines two flags. #define FAST_IS_SWIFT_LEGACY (1UL<<0) and #define FAST_IS_SWIFT_STABLE (1UL<<1).
2

Swift class names are prefixed with the name of the module. If one uses:

NSStringFromClass(SomeClass.self)

The result will be

MyApp.SomeClass

For my purpose, this works perfectly.

1 Comment

This is not true. Any Swift class annotated with @objc can have a prefix-free name. Also Swift's internal class names can be wax more complex than A.B.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.