1.事件传递响应机制
1.UITouch 记录手指在屏幕上触摸时产生的一组信息,包含触摸事件,位置等。
2.UIEvent 单次的交互行为,记录事件事件,类型。触摸类还有一组uitouch 事件。
3.事件传递:事件产生后,从UIApplication往下传递,window,view等。pointinside:withEvent:方法,判断点是否在视图内。事件传递给view之后,会调用hitTest:wITHeVENT:方法。
4.响应者链: UIApplication-->UIWindow-->递归找到最合适处理的控件-->控件调用touches方法-->判断是否实现touches方法-->没有实现默认会将事件传递给上一个响应者-->找到上一个响应者-->找不到方法作废
5.适用情景:点击view,view和父view都响应。
- (
void
)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(@
"red touches begin"
);
[
super
touchesBegan:touches withEvent:event];
}
视图绑定了手势识别器,那么touch事件会优先传递给绑定在视图上的手势识别器,然后手势识别器会对手势进行识别,如果识别出了手势,就会调用创建手势时所绑定的回调方法,并且会取消将touch事件继续传递给其所绑定的视图,如果手势识别器没有识别出对应的手势,那么touch事件会继续向手势识别器所绑定的视图传递。
2.UIView && CALayer
1.定义:UIView : UIResponder; CALayer : NSObject 所以layer不能响应事件,view可以响应事件。
2.frame : layer的frame由position,bounds等决定,view只是返回layer对应的frame。
3.UIView 主要对显示内容的管理,CALayer 主要侧重显示内容的绘制。
4.CALayer 隐式动画。0.25秒,改变属性值时,会平滑的度过。-actionForLayer:forKey: 方法 默认隐含了CABasicAnimation动画实现
3.分类、类别
// http://www.cocoachina.com/ios/20180514/23364.html
实现原理:category_t结构体存放方法、属性、协议等数据,然后将结构体方法列表拷贝到类对象方法列表中。
分类在运行时加载,实例方法、协议属性添加到类上,类方法添加到元类上
1.为什么不能添加成员变量:category_t结构体中 无成员变量结构,而类结构体中有_ivar_list_t。在编译时,内存分布已经确定,成员变量存放在实例对象中,而分类中并没有实现get/set方法。
2.分类中有load方法,并且不会覆盖本类的load方法。先调用类中load,再调用分类中。
extension: 匿名分类,用于声明私有方法、私有属性、私有成员变量。
4.代理、通知、kvo
代理: 协议、代理、委托。设置代理,实际上是用id类型指针弱引用对象。
strong会引起循环引用,assign在对象被释放后,不会将对象置nil,发送消息会产生野指针崩溃,week会置nil,不会崩溃。
通知: 观察者模式跨层传递消息。 NSNotificationCenter为单例,将观察者注册到调度表中,根据标识符name和object识别出观察者,然后调用相应的方法。
addObserver:selector:name:object: 为同步通知,即要等消息处理之后,才继续之后的代码。
使用通知队列实现异步通知:NSNotificationQueue
NSPostingStyle:发送方式,NSPostWhenIdle,空闲发送;NSPostASAP:尽快发送,当前运行循环完成时;NSPostNow:同步发送
NSNotificationCoalescing:合并通知,可以保证相同的通知只被发送一次。
KVO: 键值监听。当观察某个对象a时,kvo会动态创建一个a的子类,重写该属性setter方法。NSKVONotifying_A,被观察对象的sia指针指向新类。
所以使用时必须通过setter方法或则kvc,否则不会触发。
手动触发:常用是自动触发,设置 +(BOOL)automaticallyNotifiesObserversForKey:(NSString *)key 方法;设置no,则不会触发kvo,在setter方法中调用 willChangeValueForKey:
和 didChangevlueForKey:
.手动触发。
5.属性关键字
原子性:atomic和nonatomic用来决定编译器生成的getter和setter是否为原子操作。
atomic:系统生成的 getter/setter 会保证 get、set 操作的完整性,不受其他线程影响
nonatomic:就没有这个保证了,nonatomic返回你的对象可能就不是完整的value。 nonatomic的速度要比atomic的快
retain: 引用计数+1,指针拷贝。
strong: arc下与retain相同,声明block时,strong等同于copy,retain等同于assign
assign: 基本数据类型的简单赋值,不更改引用计数,使用之后不会置nil
week: 弱引用,对象释放后置nil。runtime维护了一个weak哈希表,key是所指对象的地址,value是weak指针的地址数组。代理用weak
copy: 创建一个新对象,内容拷贝。
对不可变对象进行copy操作,只是指针复制,mutablecopy是内容复制;
对可变对象操作,都是内容复制,即深拷贝。
copy返回的都是不可变对象。
block 使用copy,block创建时默认在栈区,作用域只在当前上下文,使用copy时,会讲内存移到堆区,就可以在作用域外使用。
6.内存管理
四个关键字:alloc\new\copy\mutablecopy产生对象就持有对象。
引用计数: 通过一张散列表管理,键 可以理解为 对象的内存地址;值保存的是 引用计数减一。
weak表: 全局表,保存所有week引用,对表内对象delloc操作的时候,将指针值设为nil。表中:键 为对象,值为weak_entry_t,保存该对象的所有weak指针。
获取引用计数: ARC下使用 Runtime _objc_rootRetainCount(id obj)方法获取。
ARC下内存随时符号:
__strong: 默认强引用,当没有任何强引用指向时,才会被释放。
__weak: 弱引用,不影响对象释放,当对象被释放时,所有指向它的弱引用都会被置为nil。当对象被销毁时,会通过他的内存地址,在weak表中查找对应的变量,删除。
声明__weak后,对象会被自动注册到自动释放池,weak不会对引用计数起变化,可能会在运行过程中被释放,所以将其注册到自动释放池中,并在autoreleasePool销毁时释放对象占用的内存。
因此,访问__weak修饰的对象,实际上是访问注册到自动释放池的对象,大量使用weak的话,会影响程序性能。
解决性能:先将其赋给strong变量,再进行访问。
_unsafe_unretain: ios4以前,相当于__weak,纯粹指向对象。
__autoreleasing: 等同于MRC下调用 autorelease方法。
自动释放池:由一系列的 AutoreleasePoolPage 双向链表组成。
参考这里
对象调用 autorelease 方法时,会将对象加入 AutoreleasePoolPage 的栈中,objc_autoreleasePoolPush方法,会调用autoreleaseFast(POOL_SENTINEL);方法。
在每个自动释放池初始化调用 objc_autoreleasePoolPush 的时候,都会把一个 POOL_SENTINEL push 到自动释放池的栈顶,并且返回这个 POOL_SENTINEL 哨兵对象。
autoreleaseFast方法最终会调用 add(objc)方法,将对象添加到自动释放池中。
objc_autoreleasePoolPop 方法:最终会调用 objc_release释放对象。
7.Block
带有自动变量的匿名函数。参考这里
截获自动变量: 在声明block之后,在内部不能修改局部变量的值。类对象可以修改该对象的属性。
原理:__main_block_impl_0结构体,将变量已参数的形式传入到了构造函数中,变量被保存在了结构体中,所以不会有变化。
__block原理: 修饰后变量不再是原类型传入block,而是会变成一个结构体指针__Block_byref_count_0,该结构体保存了变量 和 __forwarding 指针,通过该指针完成对变量的修改。
循环引用:1.__weak typeof(self) __strong 避免中途释放。2.RAC中 @weakify 和 @strongify。宏定义 3.将内部使用的对象,以参数形式传入 4,使用__block,最后将其置nil。
8. GCD
串行:主队列:dispatch_get_main_queue()
并发:全剧队列:dispatch_get_global_queue
1.栅栏方法:dispatch_barrier_async :异步执行两组操作,第一组执行完之后再执行第二组
2.延时执行:dispatch_after。 并不是在指定时间后开始执行,而是在指定时间之后将任务追加到主队列中。时间并不是准确的
3.一次性代码: dispatch_once 程序在运行中只执行1次,保证线程安全。
4.快速迭代: dispatch_apply 按照指定次数将指定的任务追加到队列中。串行队列中 效果等用for循环;并发异步,在多个线程中同时遍历。会等待全部任务执行完毕。
5.对列组: dispatch_group_async将任务放在队列中,等两个耗时操作都执行完毕之后回到主线程执行任务。dispatch_group_notify 回到主线程。dispatch_group_wait暂停当前线程,等group中任务执行完后,继续往下执行。
dispatch_group_enter 追加任务到group,dispatch_group_leave 去除任务
NSOperation :封装gcd,面向对象,代码简单可读性高.
NSOperation 操作,NSOperationQueue 队列。
主队列:添加到主队列的操作,都会在主线程中执行。
自定义队列: 会在子线程中执行,包含串行、并发
9.RunLoop
参考这里
处理事件循环。一条线程对应一个RunLoop 对象。runloop是一个do-while循环,会监测输入源和定时源 接受事件,然后通知线程处理,在无事件时让线程休眠。
source,氛围source0 和source1 。source0 主要是uievent,performSelector,等手动触发事件。source1主要是系统内部端口事件
Timer, NSTimer 定时触发
CFRunLoopModeRef : kCFRunLoopDefaultMode:App的默认运行模式,通常主线程是在这个运行模式下运行
UITrackingRunLoopMode:跟踪用户交互事件(用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他Mode影响)
kCFRunLoopCommonModes 两种模式都被标记
应用: 1.NSTimer 2.imageview 推迟显示,[self.imageView performSelector:@selector(setImage:) withObject:[UIImageimageNamed:@"tupian"] afterDelay:4.0inModes:NSDefaultRunLoopMode];
子线程不会主动创建runloop,在手动开启时 会创建 [[NSRunloop currentRunloop] run]
3.后台常驻线程(下载文件、播放音乐等)
10.Runtime
参考这里
oc 是动态语言,runtime系统使其在运行时执行代码。
objc_msgSend 发送消息
数据结构: typedef struct objc_selector *SEL; SEL 方法选择器,相同名字的方法,即便在不同的类中,选择器相同
typedef struct objc_object *id; 指向类实例的指针
isa指针,指向实例对象所属的类,类对象时 指向元类,元类指向根元类,根元类指向自己。
Method: 方法类型,包括SEL,IMP 等。
IMP: 函数指针,指向方法的具体实现代码。
功能: 1.消息转发、重定向 forwardInvocation方法。
2.method swizzling class_getInstanceMethod 获得方法, method_exchangeImplementations 交换方法,该方法交换了 两个方法的IMP指针。
3.关联对象 objc_setAssociatedObject objc_getAssociatedObject
4.动态方法: @dynamic propertyName; 动态修饰属性 +(BOOL)resolveInstanceMethod,
MARK: 学习博客
1.http://blog.sunnyxx.com sunnyxxx