iOS面试资料搜集iOS基础篇

深入浅出 Runtime(六):相关面试题

2020-02-26  本文已影响0人  师大小海腾

Runtime 系列文章

深入浅出 Runtime(一):初识
深入浅出 Runtime(二):数据结构
深入浅出 Runtime(三):消息机制
深入浅出 Runtime(四):super 的本质
深入浅出 Runtime(五):具体应用
深入浅出 Runtime(六):相关面试题

网络配图

Q:你了解 isa 指针吗?

Q:类对象与元类对象的区别和联系。

Q:为什么要设计 meta-class ?

目的是将实例和类的相关方法列表以及构建信息区分开来,方便各司其职,符合单一职责设计原则。

Q:Runtime 的消息机制,objc_msgSend 方法调用流程。

传送门:深入浅出 Runtime(三):消息机制
OC中的方法调用,其实都是转换为objc_msgSend()函数的调用(不包括[super message])。objc_msgSend()的执行流程可以分为 3 大阶段:消息发送、动态方法解析、消息转发。

Q:调用以下 init 方法的打印结果是什么?(super)

@interface HTPerson : NSObject
@end

@interface HTStudent : HTPerson
@end

@implementation HTStudent
- (instancetype)init
{
    if (self = [super init]) {
        
        NSLog(@"[self class] = %@",[self class]);
        NSLog(@"[super class] = %@",[super class]);
        NSLog(@"[self superclass] = %@",[self superclass]);
        NSLog(@"[super superclass] = %@",[super superclass]);
        
    }
    return self;
}
@end

[self class] = HTStudent
[super class] = HTStudent
[self superclass] = HTPerson
[super superclass] = HTPerson

classsuperclass方法的实现在 NSObject 类中,可以看到它们的返回值取决于receiver

+ (Class)class {
    return self;
}
- (Class)class {
    return object_getClass(self);
}
+ (Class)superclass {
    return self->superclass;
}
- (Class)superclass {
    return [self class]->superclass;
}

[self class]是从receiverClass开始查找方法的实现,如果没有重写的情况,则会一直找到基类 NSObject,然后调用。
[super class]是从receiverClass->superclass开始查找方法的实现,如果没有重写的情况,则会一直找到基类 NSObject,然后调用。
由于receiver相同,所以它们的返回值是一样的。

Q:如何防止“调用无法识别的方法导致应用程序崩溃”?

实现doseNotRecognizeSelector方法。

Q:@synthesize 和 @dynamic

Q:能否向编译后的类增加实例变量?能否向运行时动态创建的类增加实例变量?

    // 动态创建一对类和元类(参数:父类,类名,额外的内存空间)
    Class newClass = objc_allocateClassPair([NSObject class], "Person", 0);
    // 动态添加成员变量
    class_addIvar(newClass, "_age", 4, 1, @encode(int));
    class_addIvar(newClass, "_name", sizeof(NSString *), log2(sizeof(NSString *)), @encode(NSString *));
    // 注册一对类和元类(要在类注册之前添加成员变量)
    objc_registerClassPair(newClass);
    // 创建实例
    id person = [[newClass alloc] init];
    [person setValue:@"Lucy" forKey:@"name"];
    [person setValue:@"20" forKey:@"age"];  
    NSLog(@"name:%@, age:%@", [person valueForKey:@"name"], [person valueForKey:@"age"]);    
    // 当类和它的子类的实例存在时,不能调用 objc_disposeClassPair(),否则会 Crash:Attempt to use unknown class 0x1005af5c0.
    person = nil;    
    // 销毁一对类和元类
    objc_disposeClassPair(newClass);

    // name:Lucy, age:20

Q:你是否有使用过 performSelector: 方法?

使用场景:一个类在编译时没有这个方法,在运行的时候才产生了这个方法,这个时候要调用这个方法就要用到performSelector:方法。
关于动态添加方法的实现可以查看:传送门:深入浅出 Runtime(三):消息机制

Q:以下打印结果是什么?(isKindOfClass & isMemberOfClass)

@interface Person : NSObject
@end
......
    BOOL res1 = [[NSObject class] isKindOfClass:[NSObject class]];
    BOOL res2 = [[NSObject class] isMemberOfClass:[NSObject class]];
    BOOL res3 = [[Person class] isKindOfClass:[Person class]];
    BOOL res4 = [[Person class] isMemberOfClass:[Person class]];

    NSLog(@"%d,%d,%d,%d", res1, res2, res3, res4);
......

打印结果:1,0,0,0
以下是isMemberOfClassisKindOfClass方法以及object_getClass()函数的实现。

+ (BOOL)isMemberOfClass:(Class)cls {
    return object_getClass((id)self) == cls;
}

- (BOOL)isMemberOfClass:(Class)cls {
    return [self class] == cls;
}

+ (BOOL)isKindOfClass:(Class)cls {
    for (Class tcls = object_getClass((id)self); tcls; tcls = tcls->superclass) {
        if (tcls == cls) return YES;
    }
    return NO;
}

- (BOOL)isKindOfClass:(Class)cls {
    for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
        if (tcls == cls) return YES;
    }
    return NO;
}

Class object_getClass(id obj)
{
    if (obj) return obj->getIsa();
    else return Nil;
}

显然isKindOfClass的范围更大。如果方法调用着是instance对象,传参就应该是class对象。如果方法调用着是class对象,传参就应该是meta-class对象。所以res2-res4都为 0。那为什么res1为 1呢?
因为 NSObject 的class的对象的isa指向它的meta-class对象,而它的meta-classsuperclass指向它的class对象,所以它满足isKindOfClass方法的判断条件。
总之,[instance/class isKindOfClass:[NSObject class]];都返回 1。

上一篇下一篇

猜你喜欢

热点阅读