'ios What is the difference between objc_getClass and objc_lookUpClass

Apple’s explanation is

objc_getClass is different from objc_lookUpClass in that if the class is not registered, objc_getClass calls the class handler callback and then checks a second time to see whether the class is registered. objc_lookUpClass does not call the class handler callback.

But I don't really understand "class handler callback". Can you explain in more detail ? and I hope some code to show their difference,Thanks a lot!



Solution 1:[1]

They two methods' explanation is unclear in Apple's documentation, but Objective C runtime code is open source and available at Apple Opensource.

The two methods' implementations are:

Class objc_getClass(const char *aClassName)
{
    if (!aClassName) return Nil;

    // NO unconnected, YES class handler
    return look_up_class(aClassName, NO, YES);
}
    
Class objc_lookUpClass(const char *aClassName)
{
    if (!aClassName) return Nil;
    
    // NO unconnected, NO class handler
    return look_up_class(aClassName, NO, NO);
}

Both of these methods delegate to the look_up_class method, with a different argument to the last parameter includeClassHandler.

But looking at the implementation of look_up_class we can see, the last two parameters aren't actually used at all!

Class 
look_up_class(const char *name, 
              bool includeUnconnected __attribute__((unused)), 
              bool includeClassHandler __attribute__((unused)))
{
    if (!name) return nil;

    Class result;
    bool unrealized;
    {
        rwlock_reader_t lock(runtimeLock);
        result = getClass(name);
        unrealized = result  &&  !result->isRealized();
    }
    if (unrealized) {
        rwlock_writer_t lock(runtimeLock);
        realizeClass(result);
    }
    return result;
}

So, in summary, both objc_getClass and objc_lookUpClass behave identically.

Solution 2:[2]

In addition to what @zhenli wu already said, I'll answer this part:

But I don't really understand "class handler callback"

The objc_getClass hook mechanism

The Objective C runtime lets you register a "hook" (a.k.a. a callback) that it will invoke whenever an attempt is made to call objc_getClass for a class that does not exist.

This callback gives you an opportunity to see the class name (via the parameter), and decide if you would like to dynamically create the class at that point (via objc_allocateClassPair)

This mechanism is especially important for the operation of Swift, where many classes aren't "realized" until they are first looked up.

objc_lookUpClass was intended bypass this mechanism, and only return to you an existing class by its name. If no class existed for the name provided, it would not call the hooks to attempt to create it dynamically, and it would just return nil.

Here's a quick and dirty of example of how you might use it:

import ObjectiveC
import Foundation

// This will store the previous hook, which we'll need to call later
var thePrevHook: objc_hook_getClass? = nil

func myGetClassHook(className: UnsafePointer<Int8>, outClass: AutoreleasingUnsafeMutablePointer<AnyClass?>) -> ObjCBool {
    let name = String(cString: className)
    print("myGetClassHook was called for '\(name)'")
    
    // I don't want to dynamically allocate new classes for all attempts, only those that start with "my"
    let iWantToCreateANewClassForThisName = name.hasPrefix("My")
    if iWantToCreateANewClassForThisName {
        print("Creating a new class!")
        guard let newClass: AnyClass = objc_allocateClassPair(NSObject.self, className, 0) else {
            // If `objc_allocateClassPair` returns `nil`, that's most likely because another class already exists with
            // that name, but that shouldn't happen given that the whole reason this hook was call was because no class
            // was found with the given name.
            //
            // Not even a race condition should cause this, because the getClass hook mechanism is synchronized in the runtime.
            //
            // Let's just do nothing and return false.
            return false
        }
        // Set the ivars, properties, methods, protocols, etc. of the class
        objc_registerClassPair(newClass)
    
        outClass.pointee = newClass // "Return" the new class via this out-parameter
        
        return true // A new class was created, so return true
    } else {
        if let thePrevHook = thePrevHook {
            print("Delegating to the previous hook")
            return thePrevHook(className, outClass)
        } else {
            print("There is no other hook to delegate to. This class does not exist.")
            return false // No class was found or created, so return false
        }
    }
}

// Set our hook, and take note of the previous hook that we should delegate up to.
objc_setHook_getClass(myGetClassHook, &thePrevHook)


let newClass: AnyClass = objc_getClass("MyNewDynamicallyCreatedClass") as! AnyClass
print("Newly created class:", newClass)

The bug in the documentation

The documentation of objc_lookUpClass clearly states:

objc_lookUpClass does not call the class handler callback.

This is no longer the case. Both objc_lookUpClass and objc_getClass invoke the callback, so they both behave identically now.

It was changed somewhere between objc4-274/runtime/objc-runtime.m and objc4-371/runtime/objc-runtime-new.m.auto.html

You can see for yourself:

import ObjectiveC
import Foundation

var thePrevHook: objc_hook_getClass? = nil

func myGetClassHook(className: UnsafePointer<Int8>, outClass: AutoreleasingUnsafeMutablePointer<AnyClass?>) -> ObjCBool {
    fatalError("This should never have been called")
}

objc_setHook_getClass(myGetClassHook, &thePrevHook)

let newClass: AnyClass? = objc_lookUpClass("NoClassExistsWithThisName") // ?
print("This should be nil:", newClass as Any)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Alexander
Solution 2