1.NSArray和NSSet的区别
>.NSArray内存中存储地址连续,而NSSet不连续
>.NSSet效率高,内部使用hash查找,NSArray查找需要遍历
>.NSSet通过anyObject访问元素,NSArray通过下标访问
2.NSHashTable与NSMapTable?
>.NSHashTable是NSSet的通用版本,对元素弱引用,可变类型;可以在访问成员时copy
>.NSMapTable是NSDictionary的通用版本,对元素弱引用,可变类型;可以在访问成员时copy
(注:NSHashTable与NSSet的区别:NSHashTable可以通过option设置元素弱引用/copyin,只有可变类型。但是添加对象的时候NSHashTable耗费时间是NSSet的两倍。NSMapTable与NSDictionary的区别:同上)
3.属性关键字assign、retain、weak、copy
assign:用于基本数据类型和结构体。如果修饰对象的话,当销毁时,属性值不会自动置nil,可能造成野指针。
weak:对象引用计数为0时,属性值也会自动置nil
retain:强引用类型,ARC下相当于strong,但block不能用retain修饰,因为等同于assign不安全。
strong:强引用类型,修饰block时相当于copy。
4、Block的循环引用、内部修改外部变量、三种block
>block强引用self,self强引用block
>内部修改外部变量:block不允许修改外部变量的值,这里的外部变量指的是栈中指针的内存地址。__block的作用是只要观察到变量被block使用,就将外部变量在栈中的内存地址放到堆中。
>三种block:NSGlobalBlack(全局)、NSStackBlock(栈block)、NSMallocBlock(堆block)
5.KVO底层实现原理?手动触发KVO?swift如何实现KVO?
>KVO原理:当观察一个对象时,runtime会动态创建继承自该对象的类,并重写被观察对象的setter方法,重写的setter方法会负责在调用原setter方法前后通知所有观察对象值得更改,最后会把该对象的isa指针指向这个创建的子类,对象就变成子类的实例。
>如何手动触发KVO:在setter方法里,手动实现NSObject两个方法:willChangeValueForKey、didChangeValueForKey
>swift的kvo:继承自NSObject的类,或者直接willset/didset实现。
6.category和extennsion有什么区别?category是如何加载的?category的方法覆盖是怎么处理的?
extension在编译期决定,他就是类的一部分,在编译期和头文件里的@interface以及实现文件的@implement一起形成一个完整的类,它伴随类的产生而产生,亦随之一起消亡。
extension一般用来隐藏类的私有信息,你必须有一个类的圆满才能为一个类添加extension,所以你无法为系统的类比如NSString添加extension
但是category则完全不一样,它是在运行期决定的,就category和extension的区别来看,我们可以推导出一个明显的事实,extension可以添加实例变量,而category是无法添加实例变量的(因为运行期,对象的内存布局已经确定,如果添加实例变量就会破坏类的内部布局,这对变异语言来说是灾难性的)
category的加载是发生在运行时,加载category的过程
1.把category的实例方法,协议以及属性添加到类上
2.把category的类方法和协议添加到类的metaclass上
其中需要注意的是:>1.category 的方法没有「完全替换掉」原来类已经有的方法,也就是说如果 category 和原来类都有 methodA,那么 category 附加完成之后,类的方法列表里会有两个 methodA。
>2.category 的方法被放到了新方法列表的前面,而原来类的方法被放到了新方法列表的后面,这也就是我们平常所说的category 的方法会「覆盖」掉原来类的同名方法,这是因为运行时在查找方法的时候是顺着方法列表的顺序查找的,它只要一找到对应名字的方法,就会返回,不会管后面可能还有一样名字的方法。
多个分类重写原有类方法,排序也是固定的。分类中的方法都在原来类方法的前面,而分类中相同的方法,会根据你Xcode中 Build Phases ->Complie Source 里文件的顺序来排序,在最下面的分类文件中的方法会被调用。而load方法的调用方式跟其他方法不一样,在程序启动加载类,完成分类的合成之后,RunTime会主动调用类和分类的load方法。而调用方式是找到load方法的方法地址,通过地址直接调用,不会通过objc_msgSend方法去调用,就不会去方法列表里面去查找,也就不会有“假覆盖”情况的出现
@property 的本质是,@property = ivar + getter +getter;
实例变量 + get方法 + set方法,也就是说使用@property系统会自动生成setter和getter方法
7.atomic和nonatomic
->1.atomic和nonatomic用来决定编译器生成的getter和setter是否为原子操作
->2.atomic:系统生成的 getter/setter 会保证 get、set 操作的完整性,不受其他线程影响。getter 还是能得到一个完好无损的对象(可以保证数据的完整性),但这个对象在多线程的情况下是不能确定的
如果线程
A 调了 getter,与此同时线程 B 、线程 C 都调了 setter——那最后线程 A get 到的值,有3种 可能:可能是B、C set 之前原始的值,也可能是 B set 的值,也可能是 C set 的值。同时,最终这个属性 的值,可能是 B set的值,也有可能是 C set 的值。所以atomic可并不能保证对象的线程安全
->也就是说:如果有多个线程同时调用setter的话,不会出现某一个线程执行完setter全部语句之前,另一个线程开始执行setter情况,相当于函数头尾加了锁一样,每次只能有一个线程调用对象的setter方法,所以可以保证数据的完整性
->atomic所说的线程安全只是保证了getter和setter存取方法的线程安全,并不能保证整个对象是线程安全的
->3.nonatomic:就没有这个保证了,nonatomic返回你的对象可能就不是完整的value。因此,在多线程的环境下原子操作是非常必要的,否则有可能会引起错误的结果。但仅仅使用atomic并不会使得对象线程安全,我们还要为对象线程添加lock来确保线程的安全
->4.nonatomic的速度要比atomic的快。atomic是Objc使用的一种线程保护技术,这种机制是耗费系统资源的,所以在iPhone这种小型设备上,我们基本上都是使用nonatomic,而对象的线程安全问题则由程序员代码控制
->5.atomic与nonatomic的本质区别其实也就是在setter方法上的操作不同
8.assign
->1.这个修饰词是直接赋值的意思 , 整型/浮点型等数据类型都用这个词修饰
->2.如果没有使用 weak strong retain copy 修饰 , 那么默认就是使用 assign 了
->3.当然其实对象也可以用 assign 修饰 , 只是对象的计数器不会+1 . ( 与 strong 的区别 )
->4.如果用来修饰对象属性 , 那么当对象被销毁后指针是不会指向 nil 的 . 所以会出现野指针错误 . ( 与weak的区别 )
网友评论