The warning below gets occasionally reported in the Xcode console. What does this warning mean and how do I stop it from happening?
objc[4082]: Lazily named class 0x7ffee3d07ba0 wasn't named by lazy name handler
This error happens when the name of a lazily named Objective-C class is requested and either lazy class namer hook is not set or it returns NULL
name for the given lazily named class.
The hook is just a pointer to a function of type objc_hook_lazyClassNamer
:
typedef const char * _Nullable (*objc_hook_lazyClassNamer)(_Nonnull Class);
The function takes an Objective-C Class
and returns a name for it. As simple as that. You can set your own lazy class namer with help of objc_setHook_lazyClassNamer
:
static objc_hook_lazyClassNamer OrigNamer;
static const char *ClassNamer(Class cls) {
const char *name = OrigNamer(cls);
printf("A lazily named class: %s!\n", name);
return name;
}
int main(int argc, const char * argv[]) {
objc_setHook_lazyClassNamer(ClassNamer, &OrigNamer);
...
As you can see, the second argument allows you to take a pointer to the original class namer. The contract requires you to forward request to the original namer if your own implementation failed to provide the name for the class (for classes your implementation is not responsible for/not aware of).
Sophisticated Objective-C developers are probably familiar with a structure which describes an Objective-C class:
struct objc_class {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class _Nullable super_class OBJC2_UNAVAILABLE;
const char * _Nonnull name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list * _Nullable ivars OBJC2_UNAVAILABLE;
struct objc_method_list * _Nullable * _Nullable methodLists OBJC2_UNAVAILABLE;
struct objc_cache * _Nonnull cache OBJC2_UNAVAILABLE;
struct objc_protocol_list * _Nullable protocols OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;
As you can see at the time of writing the structure is no longer used, however it gives quite a good idea of what an Objective-C class consists of. Roughly saying, the name
member variable is used when requesting a class name with functions class_getName
, object_getClassName
and NSStringFromClass
. In modern Objective-C a class is not required to have this variable set when being instantiated, and the classes which don't have the name
variable set are exactly what is called a lazily named Objective-C class.
Unfortunately I don't know what is the motivation behind having such classes, but you will barely find them when working with UIKit
or Foundation
frameworks, so it's not something you may have to deal with on daily basis. The lazily named classes tend to appear, however, when you retain instances of classes defined Swift programming language in Objective-C.
Long story short - it can't be done with standard Objective-C runtime functions, and you usually don't want to do that (objc_allocateClassPair
cannot be called without name
argument provided)
For academic research, you can mimic an Objective-C class structure and trick the Objective-C runtime functions into thinking it's an Objective-C class with a bridge cast.
First define the structure which describes a class layout like this:
struct LazyNameClassLayout {
struct LazyNameClassLayout *isa;
struct LazyNameClassLayout *superclass;
void *cachePtr;
uintptr_t zero;
uintptr_t roSection;
};
The roSection
is a pointer to so-called RO (read-only) data structure where the name
variable is located in. As we are not required to make a complete class (but rather where the name
variable is not provided), you can simply make an empty structure to define this section:
struct LazyNameClassLayoutRO {
} LazyNameMetaclassRO;
We use the LazyNameMetaclassRO
to instantiate a metaclass for our future lazily named class. Before it can be done, however, we need to introduce a couple of "magic" definitions which comes out of Objective-C runtime:
extern struct objc_cache _objc_empty_cache;
extern struct LazyNameClassLayout OBJC_METACLASS_$_NSObject;
extern struct LazyNameClassLayout OBJC_CLASS_$_NSObject;
Now we are ready to define our own Objective-C lazily named classes. Let's start with metaclass:
struct LazyNameClassLayout LazyNameMetaclass = {
.isa = &OBJC_METACLASS_$_NSObject,
.superclass = &OBJC_METACLASS_$_NSObject,
.cachePtr = &_objc_empty_cache,
.roSection = (uintptr_t)&LazyNameMetaclassRO,
};
And the class itself:
struct LazyNameClassLayoutRO LazyNameClassRO = {};
struct LazyNameClassLayout LazyNameClass = {
.isa = &LazyNameMetaclass,
.superclass = Nil,
.cachePtr = &_objc_empty_cache,
.roSection = (uintptr_t)&LazyNameClassRO,
};
And we are done, such impostor is enough to make a naming functions think it's an Objective-C class. The following code will print "Lazy Name":
static objc_hook_lazyClassNamer OrigNamer;
static const char *ClassNamer(Class cls) {
if (cls == (__bridge Class)(&LazyNameClass)) {
return "Lazy Name";
}
return OrigNamer(cls);
}
int main(int argc, const char * argv[]) {
objc_setHook_lazyClassNamer(ClassNamer, &OrigNamer);
printf("%s\n", class_getName([(__bridge Class)&LazyNameClass class]));
return 0;
}
Feel free to refer to the gist with the complete example listing if you struggle at any step.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With