知识体系自提

212 阅读7分钟

1.KVC与KVO原理?

KVO原理

1.KVO是基于runtime实现的,当给A类添加KVO时,runtime动态生成了一派生类NSKVONotifying_A,让A类的isa指针指向NSKVONotifying_A类,重写class方法,隐藏对象的真实信息。

2.重写监听属性的setter方法,在setter方法内部调用了Foundation的_NSSetobjectValueAndNotify函数

3._NSSetObjectValueAndNotify函数内部

a.首先会调用willChangeValueForKey

b.然后给属性赋值

c.最后高用didchangeValueForKey

d.最后调用observer的observeValueForyKeyPath去告诉监听属性值发生了改变。

4.重写dealloc做一些KVO内存释放

KVC原理

KVC,键值编码,使用字符串直接访问对象的属性。 KVC主要对三种类型进行操作,基础数据类型及常量、对象类型、集合类型

当一个对象调用setValue方法时,方法内部会做以下操作:

1.查找set:或_set命名的setter,按照这个顺序,如果找到的话,调用这个方法并将值传进去(根据需要进行对象转换)。

2.如果没有发现一个简单的setter,但是accessInstanceVariablesDirectly类属性返回YES,则查找一个命名规则为_、_is、、is的实例变量。根据这个顺序,如果发现则将value赋值给实例变量。

3.如果没有发现setter或实例变量,则调用setValue:forUndefinedKey:方法,并默认提出一个异常,但是一个NSObject的子类可以提出合适的行为。

取值流程

这是valueForKey:的默认实现,给定一个key当做输入参数,开始下面的步骤,在这个接收valueForKey:方法调用的类内部进行操作。

1.通过getter方法搜索实例,例如get, , is, _的拼接方案。按照这个顺序,如果发现符合的方法,就调用对应的方法并拿着结果跳转到第五步。否则,就继续到下一步。

2.如果没有找到简单的getter方法,则搜索其匹配模式的方法countOf、objectInAtIndex:、AtIndexes:。

如果找到其中的第一个和其他两个中的一个,则创建一个集合代理对象,该对象响应所有NSArray的方法并返回该对象。否则,继续到第三步。

代理对象随后将NSArray接收到的countOf、objectInAtIndex:、AtIndexes:的消息给符合KVC规则的调用方。

当代理对象和KVC调用方通过上面方法一起工作时,就会允许其行为类似于NSArray一样。

3.如果没有找到NSArray简单存取方法,或者NSArray存取方法组。则查找有没有countOf、enumeratorOf、memberOf:命名的方法。

如果找到三个方法,则创建一个集合代理对象,该对象响应所有NSSet方法并返回。否则,继续执行第四步。

此代理对象随后转换countOf、enumeratorOf、memberOf:方法调用到创建它的对象上。实际上,这个代理对象和NSSet一起工作,使得其表象上看起来是NSSet。

4.如果没有发现简单getter方法,或集合存取方法组,以及接收类方法accessInstanceVariablesDirectly是返回YES的。搜索一个名为_、_is、、is的实例,根据他们的顺序。 如果发现对应的实例,则立刻获得实例可用的值并跳转到第五步,否则,跳转到第六步。

5.如果取回的是一个对象指针,则直接返回这个结果。 如果取回的是一个基础数据类型,但是这个基础数据类型是被NSNumber支持的,则存储为NSNumber并返回。 如果取回的是一个不支持NSNumber的基础数据类型,则通过NSValue进行存储并返回。

6.如果所有情况都失败,则调用valueForUndefinedKey:方法并抛出异常,这是默认行为。但是子类可以重写此方法。

category中category都有哪些东西

1.类扩展与分类区别

  1. 类别如果调用成员变量,程序会报错。备注(一直都知道分类里面添加属性会报黄色警告,使用的时候会crash)
  2. 类扩展不仅可以增加方法,还可以增加实例变量,只是该变量默认为@private类型(所以作用范围只能在自身类,而不是子类或是其他地方)
  3. 类扩展中声明方法没被实现,编绎器会报警,这是因为类扩展在编绎阶段被添加到类中,而分类是运行时。
  4. 类扩展不能像类别哪样拥有独产的实现部分,和本类共享一个实现,也就是说,类扩展所声明的方法必须依托对应宿主类的实现部分来实现。

2.load与initialize区别

调用方式

1.load是根据函数地址直接调用, initialize是通过objc_msgSend调用

调用时刻

1.load是runtime加载类、分类的时候调用(只会调用一次)

2.initialize是类第一次接收到消息的进候调用,
每个类只会initialize一次(父类的initialize)方法可能会被调用多少

调用顺序

load 
一个程序中若是全部的类、分类都实现了load方法,那么全部的load方法都会被调用
先编译先执行

一个类的load方法不用写明[super load],父类就会收到调用,并且在子类之前。

执行类的load方法时,是按照参与编译的顺序,先编译的类先执行,可是若是某个类是继承自另外     一个类,那么会先执行父类的load方法个再执行本身的load方法。

执行分类的load方法时,是按照分类参与编译的顺序,先编译的分类先执行
子类的+load方法会在它所有父类的+load方法之后执行,

而分类的+load方法会在它的主类的+load方法之后执行。

但不同的类之间的+load方法的调用顺序是不确定的,所以不要在此方法中用另一个类

+load方法调用顺序是:SuperClass -->SubClass --> CategaryClass。

initialize 优先级 分类>子类>父类

** 使用注意事项**

虽然使用initialize要比使用load安全(由于在调用initialize时全部类已经被加载进内存了),但咱们仍是要尽可能少用initialize这个方法个,尤为要谨慎在分类中实现initialize方法,由于若是在分类中实现了,本类实现的initialize方法将不会被调用。实际开发中initialize方法通常用于初始化全局变量或静态变量。

什么情况下会调用layoutSubviews

1、调用setNeedsLayout  layoutIfNeed,直接调用setLayoutSubviews  

2、addsubview时触发layoutSubviews  

3、改变一个view的frame会触发layoutSubviews  

4、改变view的size会触发父view的layoutSubviews 

5、滚动会触发layoutSubviews  

6、旋转Screen会触发父UIView上的layoutSubviews事件

二、什么情况会调用draw rect方法

1、controller的loadView、viewdidLoad方法调用之后,view即将出现在屏幕之前系统调用drawRect。

2、sizeToFit方法调用之后。 

3、设置contetMode为UIViewCOntentModelRedraw,之后每次更改frame的时候调用redraw方法。  

4、调用setNeedsDisplay方法。

block相关

1.block的原理是怎样的?本质是什么?

block本质上也是一个OC对象,它内部也有个isa指针 封装了函数调用以及调用环境的OC对象

2.__block的作用是什么?

__block说明符类似static、auto、register一样,只要观察到该变量被 block 所持有,就将“外部变量”在栈中的内存地址放到了堆中。 进而在block内部也可以修改外部变量的值。

__block可以用于解决block内部无法修改auto变量值的问题

__block不能修饰全局变量、静态变量(static)

编译器会将__block变量包装成一个对象

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

block一旦没有进行copy操作,就不会在堆上 使用注意:循环引用问题

4.block在修改NSMutableArray,需不需要添加__block?

不需要 当变量是一个指针的时候,block里只是复制了一份这个指针,两个指针指向同一个地址。所以,在block里面对指针指向内容做的修改,在block外面也一样生效。