一、基础知识篇
iOS基本数据结构
(1)swift中,字典的定义:1
struct Dictionary<Key, Value> where Key : Hashable(Key要遵守Hashable协议)
内存管理
@autoreleasepool{}1
2
3
4
5struct __AtAutoreleasePool {
__AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}
~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);}
void * atautoreleasepoolobj;
};
自动释放池是由 AutoreleasePoolPage 以双向链表的方式实现的
在没有手加Autorelease Pool的情况下,Autorelease对象是在当前的runloop迭代结束时释放的,而它能够释放的原因是系统在每个runloop迭代中都加入了自动释放池Push和Pop
当对象调用 autorelease 方法时,会将对象加入 AutoreleasePoolPage 的栈中
调用 AutoreleasePoolPage::pop 方法会向栈中的对象发送 release 消息
@synthesize和@dynamic
@property定义属性后,会自动合成setter和getter方法,和带下划线的实例变量。
@synthesize的作用:
(1)让编译器自动生成setter和getter方法
(2)指定与属性对应的实例变量
@dynamic的作用:
(1)告诉编译器,属性的setter和getter方法自己实现
(2)如果没有提供对应的setter和getter方法,编译不会错,运行时才会报错属性关键字
weak,strong,copy,assignruntime
SEL: 是方法的表示形式
IMP: 是一个函数指针,指向方法的实现
Cache: 一个存储Method的链表,主要是为了优化方法调用性能- OC消息传递
首先,需要知道OC的方法调用,通常是这种形式;Son *son = [[Son alloc] init] son->isa == Son objc_msgSend(id self, SEL cmd, ...) id returnValue = [someObject messageName:parameter];
实例对象调用一个方法,会在父类对象的方法列表里面找。一直到顶端的父类。如果没有找到会抛出“unrecognized selector sent to XXX”异常!!!
不过在此,提供三次拯救机会:(1)Method resolution:
· 你可以通过实现 +resolveInstanceMethod: 以及 +resolveClassMethod: 方法以动态的实现一个指定实例的实例方法或者类方法。
· 一般说来,消息转发机制 和 动态加载机制 是正交的。在转发机制介入之前,一个类有机会动态的加载一个方法。
(2)Fast Forwarding
如果对象实现 -forwardingTargetForSelector:,runtime就会调用这个方法,给把这个消息转发给其他对象的机会。
(3)Normal forwarding - swift消息派发
KVC和KVO
KVC:键值编码(NSKeyValueCoding),通过key值,来获取对象的属性进行操作,而不是通过我们明确的存取方式来获取,是一个非正式的Protocol。KVO就是基于KVC来实现的。KVC使用原则:在调用 setValue: forKey: 的时,程序优先调用 setName: 方法,如果没有找到 setName: 方法 KVC会检查这个类的 + (BOOL)accessInstanceVariablesDirectly 类方法看是否返回YES(默认YES),返回YES则会继续查找该类有没有名为_name的成员变量,如果还是没有找到则会继续查找_isName成员变量,还是没有则依次查找name,isName。上述的成员变量都没找到则执行setValue:forUndefinedKey: 抛出异常,如果不想程序崩溃应该重写该方法。假如这个类重写了+ (BOOL)accessInstanceVariablesDirectly 返回的是NO,则程序没有找到setName:方法之后,会直接执行setValue:forUndefinedKey: 抛出异常。
在调用valueForKey:的时,会依次按照getName,name,isName的顺序进行调用。如果这3个方法没有找到,那么KVC会按照countOfName,objectInNameAtIndex来查找。如果查找到这两个方法就会返回一个数组。如果还没找到则调用+ (BOOL)accessInstanceVariablesDirectly 看是否返回YES,返回YES则依次按照_name,_isName,name,isName顺序查找成员变量名,还是没找到就调用valueForUndefinedKey:;返回NO直接调用valueForUndefinedKey:KVO原理(Key-Value Observing)
KVO的底层是通过isa-swizzling实现的。替换类的isa指针指向NSKVONotifying_ClaaName,是他的子类。然后将被观察的类对象的isa指针指向这个子类。再重写了setter方法。并在当中添加了willChangeValueForKey:和didChangeValueForKey:。
移除观察就是将isa指针指向原来的类对象中。- Notifaction原理
- RunLoop
A RunLoop object processes input for sources such as mouse and keyboard events from the window system, Port objects, and NSConnection objects. A RunLoop object also processes Timer events.
NSRunLoop就是一个对象,不是线程安全的。
是什么:一个Runloop就是一个事件处理的循环,用来不停的调度工作及处理输入事件。
Mode:一个Runloop有几个Mode,但同一时刻只能跑在一个mode下,处理Source,Timer和Observe。
Runloop5个mode | 备注 |
---|---|
NSDefaultRunLoopMode | 默认的mode,正常情况下都是在这个mode |
NSConnectionReplyMode | |
NSModalPanelRunLoopMode | |
NSEventTrackingRunLoopMode | 使用这个Mode去跟踪来自用户交互的事件(比如UITableView上下滑动) |
NSRunLoopCommonModes | 所有mode的集合 |
iOS 中公开暴露出来的只有 NSDefaultRunLoopMode 和 NSRunLoopCommonModes 。 NSRunLoopCommonModes 实际上是一个 Mode 的集合,默认包括 NSDefaultRunLoopMode 和 NSEventTrackingRunLoopMode
- iOS响应链和事件传递
首先我们需要知道UIResponder对象。只有继承UIResponder,才能处理事件。UIApplication,UIView,UIViewController。CALayer不是UIResponder的子类,无法处理事件。
事件传递:1.当iOS程序中发生触摸事件后,系统会将事件加入到UIApplication管理的一个任务队列中
2.UIApplication将处于任务队列最前端的事件向下分发。即UIWindow。
3.UIWindow将事件向下分发,即UIView。
4.UIView首先看自己是否能处理事件,触摸点是否在自己身上。如果能,那么继续寻找子视图。
5.遍历子控件,重复以上两步。
6.如果没有找到,那么自己就是事件处理者。如果
7.如果自己不能处理,那么不做任何处理。
其中 UIView不接受事件处理的情况主要有以下三种
1)alpha <0.01
2)userInteractionEnabled = NO
3.hidden = YES.响应者链:从最合适的View开始传递给下一任响应者,如果所有的响应者都不处理事件,则事件将会被丢弃。UIResponder的nextResponder方法。
- block捕获变量
ARC下, block种类_NSConcreteStackBlock 只用到外部局部变量、成员属性变量,且没有强指针引用的block都是StackBlock。
StackBlock的生命周期由系统控制的,一旦返回之后,就被系统销毁了。
_NSConcreteMallocBlock: 有强指针引用或copy修饰的成员属性引用的block会被复制一份到堆中成为MallocBlock,没有强指针引用即销毁,生命周期由程序员控制 。
_NSConcreteGlobalBlock: 没有用到外界变量或只用到全局变量、静态变量的block为_NSConcreteGlobalBlock,生命周期从创建到应用程序结束。
没有用到外部变量肯定是_NSConcreteGlobalBlock,这点很好理解。不过只用到全局变量、静态变量的block也是_NSConcreteGlobalBlock
block捕获变量的方式有两种:1.传递内存地址指针到Block中;2.改变存储区方式(__block).
- 多线程、死锁
GCD:同步、异步、串行、并发
NS- realm数据库
- fastlane 自动化打包