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