一面复盘!!

152 阅读5分钟

这次面试问了很多问题,但其实有很多都是自己学过或者了解过的,但是没有及时复习,导致很多知识遗忘了,这次最大的经验教训就是不管是什么点,都要反复反复的去看,温故而知新!不着手 = 不复习 = 遗忘 = 掌握情况下降,还有就是计算机基础有些也没有去再多看多学,例如数据库的很多东西自己概念也不清晰了

但是这次面试让我发现了自己很多的问题!虽然存在应该把握却没有把握好的地方,可塞翁失马焉知非福,找到自己的漏洞和缺陷可以更好的进步!

对于还没有学习的点例如网络编程、项目开发等,可以在后续的学习中继续填补

Runtime

其实消息调用和转发就是要找到响应者和对应的方法实现,消息的调用过程就是在类中找,如果没有找到就到消息转发阶段

消息转发的步骤就是(通俗的说):

先自己动态添加方法(找不到方法我就自己添加一个方法实现) --> 将消息发送给其他对象(我转发给其他人看看别人能不能实现) --> 最后一次消息分发了,可以发给很多不同的对象或者直接自己“消化” --> 真的没有办法了,我只能报错了

问题:消息重定向这里没有描述清楚 (其实自己当时学的时候就不太清楚这里到底是什么目的)

记得关键字:函数签名 --> -methodSignatureForSelector

  1. 利用 -methodSignatureForSelector获取函数签名(函数的参数和返回值类型),系统会创建一个 NSInvocation对象,并通过 forwardInvocation: 消息通知该对象,给予此次消息发送最后一次寻找IMP的机会
  2. 如果 -methodSignatureForSelector返回了一个nil(没有获取函数签名),系统发出 -doesNotRecognizeSelector: 消息,程序崩溃
// 获取函数的参数和返回值类型,返回签名
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector;

// 消息重定向
- (void)forwardInvocation:(NSInvocation *)anInvocation;

对于 forwardInvocation:,我们可以在该方法中对不能处理的消息做一些处理,也可以将消息转发给其他对象处理,而不抛出错误

这个方法只有一个参数 NSInvocation对象,该对象其实就是我们说的函数签名,里面封装了函数的参数和返回类型

那么我们的 NSInvocation对象是从哪里哪里来的呢?

其实就是在调用 forwardInvocation:方法之前,Runtime系统调用 -methodSignatureForSelector,取得返回的函数签名用于生成

NSInvocation对象

所以我们重写 forwardInvocation:方法的同时也需要重写

-methodSignatureForSelector方法,否则会抛出异常

其实 forwardInvocation:就是一个不能识别消息的分发中心,将这些不能识别的消息转发给不同的消息对象,或者转发给同一个对象,又或者是简单的“吃掉”某些消息,因此没有响应也不会报错,例如我们可以为了避免直接闪退,可以当消息没发处理的时候在这个方法中给用户一个提示,也不失为一种友好的体验

Runloop

其实自己之前也整理过了,但是你不复习就是会遗忘的很快!

发现用思维导图的方式很不错

自己再看看梳理一下自己的点吧

Runloop.png

修饰符默认情况

属性默认修饰符:

  • 原子性:atomic
  • 内存管理:ARC默认是strong
  • 读写性: readwrite

基本数据类型默认修饰符:

  • 原子性:atomic
  • 内存管理:assign
  • 读写性:readwrite

copy和strong使用情景

  • strong

    • 对应的是weak,weak一般用于防止强引用导致的循环引用
  • copy

    • 对于NSString, NSArray, NSDictionary类型(不可变)的属性,它们有对应的可变子类NSMutableString, NSMutableArray, NSMutableDictionary这些子类对象也可以用来给属性赋值

    • 就以NSString为例,传递给setter方法的新字符串可能是一个NSMutableString类的实例,这是内容可变的字符串,如果不在setter方法中拷贝一个副本,那么设置完属性后,这个可变字符串可能会在某些时候被无意间进行修改,导致出错

    copy经常用于对象的修改、移动和保护。如果上述的几种应用都不需要,持有原始对象的引用就足够了,并不需要copy

详细的去看我整理的这个吧

举个例子

@interface ViewController ()
@property(nonatomic,copy)NSMutableString *mutableString; //copy修饰的可变字符串
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    NSMutableString *str = [NSMutableString stringWithFormat:@"1234"]; //可变字符串1234
    self.mutableString = str; //将str赋给mutableString
    NSLog(@"string:%@ --- mutableString:%@",str,self.mutableString);
    [self.mutableString appendString:@"5678"];
    NSLog(@"string:%@ --- mutableString:%@",str,self.mutableString);
}

@end

string:1234 --- mutableString:1234
报错原因
unrecognized selector sent to instance

在这里其实出错的关键点在于 self.mutableString = str;[self.mutableString appendString:@"5678"];

  • self.mutableString = str; 这行代码其实就是 [self.mutableString setMutableString:str];,具体实现如下
-(void)setMutableString:(NSMutableString *)mutableString{
    if (_mutableString != mutableString) {
        [_mutableString release];
        _mutableString = [mutableString copy];
    }
}

主要的代码_mutableString = [mutableString copy];

  • 其中的mutableString是传入进来的参数str
  • 使用copy方法是因为属性由copy修饰
  • 因为str本身是可变字符串,因此可变字符串copy得到的值是不可变对象,即[mutableString copy]得到的是不可变对象
  • mutableString本身虽然一开始定义的是可变字符串,但是不可变对象赋值给了_mutableString,因此其也还是一个不可变字符串了

而下面又对如今不可变字符串mutableString进行了[self.mutableString appendString:@"5678"];

  • 不可变对象是没有appendString方法的,所以报错unrecognized selector sent to instance