'ios What is the difference between objc_getClass and objc_lookUpClass
Apple’s explanation is
objc_getClass
is different fromobjc_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 |