Objective-C:
一、block
1. 没有用__block修饰的外部变量,在定义block时会作为参数传递给block,block内部获取的值都是定义的时候传进来的值,所有变量不可修改。
__block修饰的变量被包装成对象,然后把变量封装到结构体里面(包含__forwarding指针和变量),存入block结构体中,block通过变量结构体拿到__forwarding指针,通过__forwarding指针拿到结构体中的变量并修改其值。
__forwarding指针指向的是结构体自己。当使用变量的时候,通过结构体找到__forwarding指针,在通过__forwarding指针找到相应的变量
2. 在 ARC 中,捕获外部了变量的 block 的类会是 __NSMallocBlock__ 或者 __NSStackBlock__,如果 block 被赋值给了某个变量在这个过程中会执行 _Block_copy 将原有的 __NSStackBlock__ 变成 __NSMallocBlock__;但是如果 block 没有被赋值给某个变量,那它的类型就是 __NSStackBlock__;没有捕获外部变量的 block 的类会是 __NSGlobalBlock__ 即不在堆上,也不在栈上,它类似 C 语言函数一样会在代码段中。
在非 ARC 中,捕获了外部变量的 block 的类会是 __NSStackBlock__,放置在栈上,没有捕获外部变量的 block 时与 ARC 环境下情况相同。
二、runtime
1. Objective-C类是由Class类型来表示的,它实际上是一个指向objc_class结构体的指针,objc_class结构体有指向其类的isa指针,通过isa指针找到对象所属的类,类中存放着实例方法列表,方法列表中SEL作为key,IMP作为value。IMP其实就是函数指针,指向了最终的函数实现。runtime的核心就是objc_msgSend函数,通过给类发送SEL以传递消息,找到匹配的IMP获取最终实现
找不到匹配的IMP就进行消息转发,resolveInstanceMethod:或者resolveClassMethod:方法返回NO,会重新启动一次消息发送过程。
(1)动态解析: 向接收者所属的类请求能否动态添加方法,如果能就动态添加执行,如不能执行(2),
(2)备援接受者:runtime会询问当前接收者是否有其他对象可以处理这个selector(执行forwardingTargetForSelector:方法),
(3)消息重定向:methodSignatureForSelector:方法返回nil,调用doesNotRecognizeSelector:方法抛出异常,返回methodSignature,runtime会将未知消息封装为NSInvocation对象,然后调用forwardInvocation:(NSInvocation*)invocation方法,如果不能处理就调用父类的相关方法,一直到NSObject的这个方法,如果NSObject无法处理就调用doesNotRecognizeSelector:方法抛出异常
消息转发流程2.在 Objective-C 中向 nil 发送消息是完全有效的——只是在运行时不会有任何作用:
o 如果一个方法返回值是一个对象,那么发送给nil的消息将返回0(nil)。
o 如果方法返回值为指针类型,其指针大小为小于或者等于sizeof(void*),float,double,long double 或者 long long 的整型标量,发送给 nil 的消息将返回0。
o 如果方法返回值为结构体,发送给 nil 的消息将返回0。结构体中各个字段的值将都是0。
o 如果方法的返回值不是上述提到的几种情况,那么发送给 nil 的消息的返回值将是未定义的
3. runtime应用场景:
o 关联对象给分类增加属性
o 方法添加和替换(Method Swizzling),KVO实现
o 消息转发(热更新)
o 实现的自动归档和自动解档
o 实现字典和模型的自动转换
Swizzling应该只在+load/+initialize中完成,应只在dispatch_once中完成,在不同线程中也能确保代码只执行一次
4. Meta Class:
o 每个Class都有一个isa指针指向一个唯一的Meta Class元类 (Meta Class存储了类的类方法)
o 每一个Meta Class的isa指针都指向最上层的Meta Class,即NSObject的Meta Class,而最上层的Meta Class的isa指针又指向自己
MetaClassSwift:
一、协议与泛型
~ 泛型:根据自定义的需求,编写出适用于任意类型,灵活可重用的函数或类型
~ Swift 通过为泛型代码引入一层间接的中间层来解决这些问题。当编译器遇到一个泛型类型的 值时,它会将其包装到一个容器中。这个容器有固定的大小,并存储这个泛型值。如果这个值 超过容器的尺寸,Swift 将在堆上申请内存,并将指向堆上该值的引用存储到容器中去。
对于每个泛型类型的参数,编译器还维护了一系列一个或者多个所谓的目击表 (witness table): 其中包含一个值目击表,以及类型上每个协议约束一个的协议目击表。这些目击表 (也被叫做 vtable) 将被用来将运行时的函数调用动态派发到正确的实现去
~ 协议目击表提供了一组映射关系,通过这组映射,我们可以知道泛型类型满足的协议 (编译器通 过泛型约束可以静态地知道这个信息) 和某个具体类型对于协议功能的具体实现 (这只在运行时 才能知道) 的对应关系。实际上,只有通过目击表我们才能查询或者操作某个值。我们无法在不 加约束地定义一个 参数的同时,还期望它能对任意实现了 < 的类型工作。如果没有满足 Comparable 的保证,编译器就不会让我们使用 < 操作,这是因为没有目击表可以让我们找到 正确的 < 的实现。这就是我们说泛型和协议是紧密着联系的原因,除了像是 Array 或者 Optional 这样的容器类型,脱离了使用协议来约束泛型,泛型所能做的事情也 就非常有限了
~ 通过协议扩展进行代码共享与继承相比,有以下几个优势:
o 不需要强制使用某个父类
o 可以让已经存在的类型遵循协议
o 协议既可以用于类,也可以用于结构体和枚举
o 通过协议,不需要处理super方法的调用问题
二、变量及初始化
~ 当你不需要 weak 的时候,还是建议使用 unowned。一个 weak 变量总是需要被定义为 var,而 unowned 变量可以使用 let 来定义。不过,只有在你确定你的引用将一直有效时,才应该使用unowned。
Use an unowned reference only when you are sure that the reference always refers to an instance that has not been deallocated. If you try to access the value of an unowned reference after that instance has been deallocated, you’ll get a runtime error.
An unowned reference is expected to always have a value. As a result, ARC never sets an unowned reference’s value to nil, which means that unowned references are defined using nonoptional types
~ 懒加载只会在第一次调用时执行创建对象,后面如果对象被释放了,则不会再次创建。而oc中会再次创建
~ 当我们在子类定义了指定初始化器(包括自定义和重写父类指定初始化器),那么必须显示实现required init?(coder aDecoder: NSCoder),而其他情况下则会隐式继承,我们可以不用理会。而 init(coder aDecoder: NSCoder) 方法是来自父类的指定构造器, 因为这个构造器是 required, 必须要实现. 但是因为我们已经重载了 init(), 定义了一个指定构造器, 所以这个方法不会被继承, 要手动覆写
~ 访问级别
1,private
private访问级别所修饰的属性或者方法只能在当前类里访问。
2,fileprivate
fileprivate访问级别所修饰的属性或者方法在当前的Swift源文件里可以访问。(比如上门样例把private改成fileprivate就不会报错了)
3,internal(默认访问级别,internal修饰符可写可不写)
internal访问级别所修饰的属性或方法在源代码所在的整个模块都可以访问。
如果是框架或者库代码,则在整个框架内部都可以访问,框架由外部代码所引用时,则不可以访问。
如果是App代码,也是在整个App代码,也是在整个App内部可以访问。
4,public
可以被任何人访问。但其他module中不可以被override和继承,而在module内可以被override和继承。
5,open
可以被任何人使用,包括override和继承。
三、派发机制
~ 动态派发的作用就是,在执行时,会先去寻找具体类型中对这个方法的现实,如果有会调用这个类型的实现,如果没有,再调用协议扩展中的实现。而静态派发,就是调用者声明的类型是什么,就去类型中找这个方法的实现,如果类型声明为协议,即使这个类型中有这个方法的现实,也只会调用协议扩展中的实现
~ 在 swift 3 中除了手动添加 @objc 声明函数支持 OC 调用还有另外一种方式:继承 NSObject。class 继承了 NSObject 后,编译器就会默认给这个类中的所有函数都标记为 @objc
~ 苹果修改了自动添加 @objc 的逻辑:一个继承 NSObject 的 swift 类不再默认给所有函数添加 @objc。只在实现 OC 接口和重写 OC 方法时才自动给函数添加 @objc 标识
网友评论