面试点

1、一个NSObject对象占用多少内存?

系统会分配16个字节(通过malloc_size函数获得),但实际只占用8个字节(通过class_getInstanceSize获得)

补充1:结构体的内存大小,会根据内存对齐来定(是结构体最大成员的大小的倍数);对象的大小是以分配内存块的方式来决定的,内存块的大小都是16字节的倍数。
补充2:sizeof是一个运算符,传入一个类型,返回类型所占的内存大小,它在编译过程中会当做一个常量;class_getInstanceSize是一个函数,传入的参数是类对象。

2、对象的isa指针指向哪里?
  • 实例对象(instance)的isa指向类对象(class)
  • 类对象(class)的isa指向元类对象(meta-class)
  • 元类对象(meta-class)的isa指向元类的基类
  • 基类meta-class的isa指向自己
3、OC的类信息存放在哪里?
  • 成员变量的值存放在实例对象中
  • 实例方法、成员变量、属性、协议存放在类对象
  • 类方法存放在元类对象
4、KVO的本质是什么?

当添加监听方法以后,被监听对象的isa指针会指向一个由Runtime动态生成的类:NSKVONotifying_ClassName,它是被监听类的子类;当被监听属性发生变化时,新生成子类的属性setter方法中会调用Foundation框架里的函数_NSSet*ValueAndNotify,在该函数中会依次调用-willChangeValueForKey:,父类的属性setter方法,-didChangeValueForKey:,在-didChangeValueForKey:中会调用-observeValueForKeyPath:ofObject:change:context:,完成对属性的监听。

5、如何手动触发KVO?

手动调用-willChangeValueForKey:-didChangeValueForKey:

6、直接修改成员变量会触发KVO吗?

不会。因为不会调用setter方法。

7、通过KVC修改属性会触发KVO吗?

会。因为KVC修改属性会主动调用-willChangeValueForKey:-didChangeValueForKey:

8、KVC的赋值和取值过程是怎样的?原理是什么?
  • setValue:forKey原理
  • valueForKey:原理
9、category的实现原理?
  • category在编译之后的底层结构是struct category_t,里边存储着分类的对象方法、类方法、属性、协议信息
  • 在程序运行的时候,runtime会将category的数据,合并到类信息中(类对象、元类对象)
10、category和Class Extension的区别?
  • Class Extension在编译的时候,它的数据就已经包含在类信息中了
  • category是在运行时,才会将数据合并到类信息中
11、category中有load方法吗?load方法什么时候调用?load方法可以继承吗?
  • 在runtime加载类、分类的时候调用
  • 可以继承。一般情况下不会主动去调用load方法,都是让系统自动调用
12、load、initialize方法的区别是什么?
  • 调用方式
    • load是函数指针的方式;initialize是objc_msgSend方式
  • 调用时机
    • load是runtime加载类和分类数据时调用;initialize是类在第一次接受到消息时调用
  • 调用顺序
    • load是先调用类,再调用分类。调用类时,按照编译的先后顺序进行调用,如果有父类,则先调用父类load。调用分类时,也是按照编译的先后顺序进行调用
    • initialize在调用时,如果子类没有实现,则会调用父类方法,因为是objc_msgSend调用方式,分类的initialize会覆盖类本身的initialize方法
13、category能否添加成员变量?

因为category底层结构的限制,不能直接添加成员变量。
可以通过关联对象的方式间接实现添加成员变量的效果。

14、block的本质是什么?

封装了函数调用以及函数调用环境的OC对象

15、__block的作用是什么?有什么注意点?

它修饰的变量可以在block内部进行修改,只能修饰auto变量,不能修饰static和全局变量
注意点:要合理使用,因为他会将修饰的变量包装到一个对象中

16、block的属性修饰词为什么是copy?使用block有哪些注意点?

不进行copy操作,它就不会在堆上
注意循环引用问题

17、block在修改NSMutableArray,需不需要使用__block?

不需要。因为它不是修改array指针,是使用它,类似对它进行打印输出

18、讲一下OC的消息发送机制?
  • OC中的方法调用其实都是转成了objc_msgSend函数的调用,给receiver(方法调用者)发送了一条消息(selector方法名)
  • objc_msgSend底层有三大阶段
    • 消息发送(当前类,父类中查找)、动态方法解析、消息转发
19、讲一下消息转发机制的流程?

当消息发送的前两个阶段(消息发送和动态方法解析)都没有实现的时候,就会来到消息转发阶段。首先会调用forwardingTargetForSelector,如果该方法返回值不为空,则转发流程结束,重新调用objc_msgSend(返回值,SEL);如果返回nil,则会进入消息签名方法(methodSignatureForSelector),如果返回值为nil,整个消息发送流程结束,抛出异常;如果返回值不为空,则进入forwardInvocation方法,在这里可以写任何方法实现代码。

20、说出下列代码的打印值?
void test()
{
    // 方法调用者的isa指向的对象,或者指向对象的父类,如果有等于传入参数的类,返回YES
    BOOL res1 = [[NSObject class] isKindOfClass:[NSObject class]];
    // 方法调用者的isa指向的对象,如果等于传入参数的类,返回YES
    BOOL res2 = [[NSObject class] isMemberOfClass:[NSObject class]];
    
    BOOL res3 = [[MJPerson class] isKindOfClass:[MJPerson class]];
    // 这句代码的调用者不管是哪个类(只要是NSObject体系下的类),都会返回YES 
    BOOL res4 = [[MJPerson class] isKindOfClass:[NSObject class]]; // 基类的元类对象的父类指向基类的类对象
    BOOL res5 = [[MJPerson class] isMemberOfClass:[MJPerson class]];
    NSLog(@"res1 - %d,res2 - %d,res3 - %d,res4 - %d,res5 - %d",res1,res2,res3,res4,res5);
    // res1 - 1,res2 - 0,res3 - 0,res4 - 1,res5 - 0
}
  • 相关源码
+ (id)self {
    return (id)self;
}

- (id)self {
    return self;
}

+ (Class)class {
    return self;
}

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

+ (Class)superclass {
    return self->superclass;
}

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

+ (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;
}

+ (BOOL)isSubclassOfClass:(Class)cls {
    for (Class tcls = self; tcls; tcls = tcls->superclass) {
        if (tcls == cls) return YES;
    }
    return NO;
}
21、以下代码能不能执行成功?如果可以,执行结果是什么?

可以执行。涉及的知识点:Class结构、消息发送机制、栈内存布局、super调用等。

22、什么是runtime?项目中有哪些用处?
  • OC是一门动态性比较强的编程语言,允许很多操作推迟到程序运行时再进行
  • OC的动态性就是由runtime来支撑和实现,runtime是一套C语言的API,封装了很多动态性相关的函数
  • 平时编写的OC代码,底层都是转换成了runtime API进行调用
  • 具体应用
    • 使用关联对象给分类添加属性
    • 遍历类的所有成员变量(修改textfield的占位文字颜色、字典转模型、自动解档归档等)
    • 交换方法实现(一般是交换系统的方法)
    • 利用消息转发机制解决方法找不到的异常问题
    • ......

--Edited from Rpc

转载于:https://my.oschina.net/u/4112912/blog/3036699

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值