iOS Objective-C 常见的面试题详解

133 阅读1分钟
  1. 以下代码运行会存在什么问题:
    @autoreleasepool {
        for (int i = 0; i < INT_MAX; i++) {
            SFPerson *person = [[SFPerson alloc] init];
            [person autorelease];
        }
    }

在 MRC 环境中,调用方法 autorelease 虽然会让 person 实例引用计数减 1,但不是立即减 1。其本质上只是把对象放到离它最近的自动释放池里,当自动释放池销毁了,才会向自动释放池里的每一个对象发送 release 消息。这道题问题就出在 autorelease 上,因为 INT_MAX 是一个很大的整型数,而 autorelease 又不能使引用计数立即减 1,所以在循环结束前会造成内存溢出的问题。

解决方案也很简单,做如下更改,就能保证在每创建一个对象,就会得到及时释放。

@autoreleasepool  {

    for (int i = 0; i < INT_MAX; i++) {

        @autoreleasepool  {
            SFPerson *person = [[SFPerson alloc] init];
            [person autorelease];
        }
    }
}
  1. 以下代码运行会存在什么问题:
     @autoreleasepool  {

        NSString *string = [[NSString alloc] init]; // 1

        [string retain]; // 2
        [string retain]; // 3

        string = @"aaa";

        [string release];
        [string release];
        [string release];
    }

指针变量 string 原本指向一块开辟的堆区空间,但经过重新给赋值后,string 的指向发生了变化,指向常量区。常量区的变量是自动管理内存的,不需要我们手动释放,这样一来,就导致原来的堆区内存没有被释放,造成内存泄露。

  1. 请写出 NSLog 方法打印的内容,并作出解释:
// .h 文件
#import "SFPerson.h"

@interface SFStudent : SFPerson

@end

// .m 文件
#import "SFStudent.h"

@implementation SFStudent

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

输出结果:都输出 SFStudent

解释:self 是类的隐藏参数,指向当前调用方法的这个类的实例,而 super 关键字只是一个编译器标志符,和self 是指向同一个消息接收者的。上述例子中,不管调用那个,接收消息的对象都是 SFStudent *xx 这个对象。而不同的是,super 是告诉编译器,调用 class 这个方法时要去父类的方法开始查找,而不是从本类开始查找。self 则会从当前类的方法列表中开始查找,如果没找到,再去父类中查找;而当使用 super 时,则从父类的方法开始查找,然后调用父类的这个方法。

其中方法 class 默认实现如下,具体可以去苹果开源的源码中加以验证:

- Class)class {
    return object_getClass(self);
}

我们梳理一下调用流程来加深理解:

  • 调用 [self class] 时,系统先调用 objc_msgSend 函数,第一个参数是 SFStudent 当前这个实例,然后在 SFStudent 这个类中查找 (Class)class 这个方法,发现自身没有实现,再去父类 SFPerson 里面去找,也没有找到具体实现,最后在基类 NSObject 中发现这个方法。而这个方法的实现就是返回 self 的类别,因此输出 SFStudent;
  • 调用 [super class] 时,系统会去调用 objc_msgSendSuper 函数。第一步先构造 objc_super 结构体,结构体的第一个成员是 self,第二个成员是 (id)class_getSuperclass(objc_getClass(@"SFPerson"));实际结果会输出 SFPerson。第二步是去 SFPerson 这个类中去找 (Class)class 这个方法,结果没有,然后去基类 NSObject找到了。最后内部是使用objc_msgSend(objc_super->receiver,@selector(class))去调用,此时已经和[self class]调用相同了,最后的输出结果是相同的,都是SFStudent。