我在 X-Code 中看到的这个警告是什么意思,我该如何阻止它;_;。哇它烦人。
Lazily named class 0x7ffee3d07ba0 wasn't named by lazy name handler
Run Code Online (Sandbox Code Playgroud)
当请求延迟命名的 Objective-C 类的名称并且未设置延迟类命名器挂钩或返回给NULL
定延迟命名类的名称时,会发生此错误。
钩子只是一个指向类型函数的指针objc_hook_lazyClassNamer
:
typedef const char * _Nullable (*objc_hook_lazyClassNamer)(_Nonnull Class);
Run Code Online (Sandbox Code Playgroud)
该函数采用Objective-CClass
并返回它的名称。就如此容易。您可以在以下命令的帮助下设置自己的惰性类名称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);
...
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,第二个参数允许您获取指向原始类名的指针。如果您自己的实现无法提供类的名称(对于您的实现不负责/不知道的类),合同要求您将请求转发给原始命名者。
经验丰富的 Objective-C 开发人员可能熟悉描述 Objective-C 类的结构:
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;
Run Code Online (Sandbox Code Playgroud)
正如您在撰写本文时所看到的,该结构已不再使用,但它很好地说明了 Objective-C 类的组成。粗略地说,成员变量是在请求带有函数、和 name
的类名时使用的。在现代 Objective-C 中,类在实例化时不需要设置此变量,而没有设置变量的类正是所谓的惰性命名 Objective-C 类。class_getName
object_getClassName
NSStringFromClass
name
不幸的是,我不知道拥有此类类背后的动机是什么,但在使用UIKit
或Foundation
框架时你几乎找不到它们,所以这不是你每天必须处理的事情。然而,当您保留在 Objective-C 中定义的 Swift 编程语言的类实例时,往往会出现延迟命名的类。
长话短说——它不能用标准的 Objective-C 运行时函数来完成,而且你通常不想这样做(在objc_allocateClassPair
没有提供参数的情况下无法调用name
)
对于学术研究,您可以模仿 Objective-C 类结构并欺骗 Objective-C 运行时函数,使其认为它是带有桥接转换的 Objective-C 类。
首先定义描述类布局的结构,如下所示:
struct LazyNameClassLayout {
struct LazyNameClassLayout *isa;
struct LazyNameClassLayout *superclass;
void *cachePtr;
uintptr_t zero;
uintptr_t roSection;
};
Run Code Online (Sandbox Code Playgroud)
这roSection
是一个指向变量所在的所谓 RO(只读)数据结构的指针name
。由于我们不需要创建一个完整的类(而是name
不提供变量的类),因此您可以简单地创建一个空的类定义此部分的结构:
struct LazyNameClassLayoutRO {
} LazyNameMetaclassRO;
Run Code Online (Sandbox Code Playgroud)
我们使用LazyNameMetaclassRO
来为我们未来的延迟命名类实例化一个元类。然而,在完成之前,我们需要引入一些来自 Objective-C 运行时的“神奇”定义:
extern struct objc_cache _objc_empty_cache;
extern struct LazyNameClassLayout OBJC_METACLASS_$_NSObject;
extern struct LazyNameClassLayout OBJC_CLASS_$_NSObject;
Run Code Online (Sandbox Code Playgroud)
现在我们准备定义我们自己的 Objective-C 惰性命名类。让我们从元类开始:
struct LazyNameClassLayout LazyNameMetaclass = {
.isa = &OBJC_METACLASS_$_NSObject,
.superclass = &OBJC_METACLASS_$_NSObject,
.cachePtr = &_objc_empty_cache,
.roSection = (uintptr_t)&LazyNameMetaclassRO,
};
Run Code Online (Sandbox Code Playgroud)
以及课程本身:
struct LazyNameClassLayoutRO LazyNameClassRO = {};
struct LazyNameClassLayout LazyNameClass = {
.isa = &LazyNameMetaclass,
.superclass = Nil,
.cachePtr = &_objc_empty_cache,
.roSection = (uintptr_t)&LazyNameClassRO,
};
Run Code Online (Sandbox Code Playgroud)
我们完成了,这样的冒名顶替者足以让命名函数认为它是一个 Objective-C 类。以下代码将打印“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;
}
Run Code Online (Sandbox Code Playgroud)
如果您在任何步骤中遇到困难,请随时参考完整示例列表的要点。
归档时间: |
|
查看次数: |
896 次 |
最近记录: |