MST

星途 面试题库

面试题:Objective-C中动态创建类与对象的基础概念

在Objective-C里,简述使用`objc_allocateClassPair`和`class_addIvar`等函数动态创建类的基本步骤,并说明动态创建的类与普通定义的类在使用上有哪些区别。
35.2万 热度难度
编程语言Objective-C

知识考点

AI 面试

面试题答案

一键面试

动态创建类的基本步骤

  1. 调用objc_allocateClassPair函数
    • 该函数用于分配一个新的类对象。它接受三个参数,分别是父类(通常可以是NSObject类,如果想继承自其他自定义类也可以传入相应类)、新类的名称(以const char *类型传入)、额外的内存字节数(一般为0,用于为类的实例对象分配额外的内存空间)。例如:
Class newClass = objc_allocateClassPair([NSObject class], "MyDynamicClass", 0);
  1. 添加实例变量(可选步骤,如果需要的话)
    • 使用class_addIvar函数为新类添加实例变量。该函数接受五个参数,依次为要添加实例变量的类、实例变量的名称(const char *类型)、实例变量的大小(以字节为单位,通常通过sizeof获取)、实例变量的对齐方式(一般使用alignof获取对应类型的对齐方式)、实例变量的类型编码(一个描述实例变量类型的字符串,例如@encode(int)表示int类型)。示例如下:
class_addIvar(newClass, "myIvar", sizeof(int), alignof(int), @encode(int));
  1. 添加方法
    • 使用class_addMethod函数为新类添加方法。它接受六个参数,包括要添加方法的类、方法的选择器(SEL类型,通过@selector获取)、实现方法的函数指针(IMP类型)、方法的类型编码(描述方法的参数和返回值类型)。例如,为新类添加一个简单的实例方法:
IMP imp = imp_implementationWithBlock(^(id self, int value) {
    // 这里是方法实现,将传入的值赋给实例变量myIvar
    int *ivar = (int *)((char *)self + object_getInstanceVariableOffset(newClass, "myIvar"));
    *ivar = value;
});
class_addMethod(newClass, @selector(setMyIvar:), imp, "v@i");
  1. 注册类
    • 调用objc_registerClassPair函数将新创建的类注册到运行时系统中,这样才能正常使用这个类。只需要传入之前分配的类对象即可。
objc_registerClassPair(newClass);
  1. 使用动态创建的类
    • 一旦注册完成,就可以像使用普通类一样使用动态创建的类,例如创建实例对象并调用方法:
id instance = [[newClass alloc] init];
[instance performSelector:@selector(setMyIvar:) withObject:@(5)];

动态创建的类与普通定义的类在使用上的区别

  1. 编译时与运行时
    • 普通定义的类:在编译时就已经确定了类的结构和成员,编译器可以对其进行优化,包括类型检查、方法调用的静态解析等。在程序运行前,类的定义已经完全确定。
    • 动态创建的类:在运行时才创建类的结构和成员。这意味着在编译时,编译器对这类类的信息了解有限,无法进行一些静态优化。例如,无法在编译时确定动态创建类的实例变量布局和方法实现,需要在运行时动态解析。
  2. 灵活性与可维护性
    • 普通定义的类:结构相对固定,在程序运行期间无法修改类的定义。如果需要修改类的结构,通常需要修改源代码并重新编译。这使得代码的灵活性较差,但可维护性较好,因为类的定义一目了然,易于理解和修改。
    • 动态创建的类:具有极高的灵活性,可以根据运行时的条件动态创建不同结构的类。例如,根据用户的配置或者网络请求的数据来决定创建什么样的类。然而,这种灵活性也带来了维护上的挑战,因为类的创建逻辑分散在运行时的代码中,调试和理解起来相对困难。
  3. 内存管理
    • 普通定义的类:内存管理相对简单明了,对象的内存分配和释放遵循标准的Objective - C内存管理规则(ARC或MRC)。由于类的结构在编译时确定,编译器可以更好地优化内存使用。
    • 动态创建的类:内存管理稍微复杂一些。除了对象本身的内存管理,还需要注意动态创建类过程中涉及的内存分配和释放。例如,objc_allocateClassPair分配的类对象需要通过objc_registerClassPair正确注册,否则可能导致内存泄漏。同时,动态添加的实例变量和方法的内存管理也需要开发者格外留意。