iOS高级开发知识点总结(一)
一、分类与扩展的区别:
-
分类一般是用于给类添加方法,使得类可以根据不同的功能来划分,更有利于维护,如果添加属性, 类中不会生成
"_成员变量"
需要使用属性关联来实现;类扩展里面可以添加属性和方法,添加的属性默认是私有的, 会生成"_成员变量"
。 -
分类中添加的方法在编译时存在分类中的
classMethods
和instanceMethods
中,而且是在APP冷启动时添加到类的方法列表(二维数组)中的,如果有多个分类有相同方法的实现,在最终调用的是最后一个编译的那个分类的中的方法实现;扩展是在编译阶段添加到类中的。
struct category_t {
const char *name;
classref_t cls;
struct method_list_t *instanceMethods;
struct method_list_t *classMethods;
struct protocol_list_t *protocols;
struct property_list_t *instanceProperties;
};
二、automic的实现机制
-
automic是声明属性时默认的关键字,添加属性时使用automic声明,那么系统会自动在setter和getter方法中添加锁来使得读写操作原子化,保证线程安全。但对于属性是NSMutableArray,
-
通过源码查看可知automic的原理是使用iOS 10之前是
OSSPinLock
, iOS10之后是os_unfair_lock
来实现的,OSSPinLock
自旋锁会出现优先级反转导致死锁的问题,所以不推荐用automic,如果涉及到线程安全问题可以自己加锁来解决:
setter和getter方法的安全可以加锁@synchronized (), 对于容器类的属性如Array、dictionary、set类如果涉及到多线程需要在使用时自己加锁,下面是SDWebImage框架加的锁:设置dispatch_semaphore信号量为1当做一把锁,性能最高。
#define SD_LOCK(lock) dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
SD_LOCK(self.runningOperationsLock);
[self.runningOperations addObject:operation];
SD_UNLOCK(self.runningOperationsLock);
三、被weak修饰的对象在被释放的时候做了什么?
- 对象在被释放前会调用dealloc方法,通过源码查看可以知道,dealloc内部会调用C语言函数,进一步追踪可以知道,系统会将对象的关联属性清除并且会从系统的SideTables中查找对象的weak指针, 逐个置为nil。 weak指针是如何实现自动设置为nil的
四、KVO的底层实现?
KVO原理总结
数组添加/移除元素被KVO观察到:
self.arrToObserve = @[].mutableCopy;
[self addObserver:self forKeyPath:@"arrToObserve" options:NSKeyValueObservingOptionNew context:nil]
[[self mutableArrayValueForKey:@"arrToObserve"] addObject:@"fdsa"];
五、说说Autoreleasepool
-
系统通过一个一个的Autoreleasepool来实现对象的autoRelease。对于调用了autoRelease的对象,系统会在Autoreleasepool释放时统一调用一次release方法,把引用计数器减1,当引用计数器为0时,系统会释放对象。
-
Autoreleasepool是通过一个或多个
AutoreleasePoolPage
双向链表来实现的,每一个AutoreleasePoolPage
有着固定的大小的栈结构(先进后出的结构),当一个page不够的时候会创建下一个。 -
在Autoreleasepool创建之初,会push一个
哨兵作为记号
到AutoreleasePoolPage
中,在Autoreleasepool销毁时会将AutoreleasePoolPage
中当前位置到哨兵
位置的对象调用一个release。 -
那么什么时候会有Autoreleasepool的push/pop操作?
<1> 手动创建的@autoreleasePool{}, 在大括号开始时push和结束前pop。<2> 在主线程的runloop中, 系统有添加对runloop状态的两个监听,第一个是对
kCFRunLoopEntry
(runloop开启)的监听,进入runloop时会创建Autoreleasepool;第二个是对kCFRunLoopBeforeWaiting | kCFRunLoopExit
的监听,在kCFRunLoopBeforeWaiting
(runloop休眠)时先释放掉之前Autoreleasepool(即pop操作),再创建一个新的Autoreleasepool,在kCFRunLoopExit
(runloop退出)时销毁掉Autoreleasepool。<3>可以在主线程中打印
[NSRunLoop mainRunLoop]
,查看observers可以证明是对runloop进行了监听。
六、说一下copy和mutableCopy
- copy生成的是不可变对象,mutableCopy生成的是可变的对象。
- 对于不可变对象如:NSString、NSArry、NSDictionary: 调用copy是不生成新对象,只拷贝对象的指针(引用计数会+1);调用mutableCopy方法生成的是一个新对象,而且是可变的:NSMutableString、NSMutbleArry、NSMutableDictionary。
- 对于可变对象如:NSMutableString、NSMutbleArry、NSMutableDictionary, 调用copy生成的是新对象,新对象是不可变的NSString、NSArry、NSDictionary; 调用mutableCopy生成的是新对象,而且是可变的NSMutableString、NSMutbleArry、NSMutableDictionary。
七、OC属性默认关键字是哪几个?
有3个,atomic + readWrite +
如果属性是基本数据类型:assign;
如果属性是对象类型:strong。
八、数组中元素如何去重?
// 方法一:去重后需要保留原来顺序
NSArray *array = @[@"aaa", @"bbb", @"aaa", @"ccc"];
NSMutableArray *resultArr = [NSMutableArray array];
for (unsigned i = 0; i < array.count; i++){
if (![resultArr containsObject: array[i]]){
[resultArr addObject:array[i]];
}
}
// 方法二:去重后不保留原来顺序
NSSet *set = [NSSet setWithArray:array];
NSArray *resultArr = [set allObjects];
// 方法三:去重后不保留原来顺序
NSArray *resultArr = [array valueForKeyPath:@"@distinctUnionOfObjects.self"];
NSLog(@"%@", resultArr);