编译器给结构体开辟空间
首先找到结构体中最宽的基本数据类型,然后寻找内存地址能是该基本数据类型的整倍的位置,作为结构体的首地址。将这个最宽的基本数据类型的大小作为对齐模数。为结构体的一个成员开辟空间之前,编译器首先检查预开辟空间的首地址相对于结构体首地址的偏移是否是本成员的整数倍,若是,则存放本成员,反之,则在本成员和上一个成员之间填充一定的字节,以达到整数倍的要求,也就是将预开辟空间的首地址后移几个字节。
内存对齐为两个原则:
原则 1. 前面的地址必须是后面的地址正数倍,不是就补齐。
原则 2. 整个Struct的地址必须是最大字节的整数倍。
对象的isa指针指向哪里?
答:instance对象的isa指针指向class对象,class对象的isa指针指向meta-class对象,meta-class对象的isa指针指向基类的meta-class对象,基类自己的isa指针也指向自己。
OC的类信息存放在哪里?
成员变量的具体值存放在instance对象。对象方法,协议,属性,成员变量信息存放在class对象。类方法信息存放在meta-class对象。
KVO的内部实现原理
Person对象的 isa 指针在经过KVO监听之后已经指向了 NSKVONotifyin_Person 类对象,NSKVONotifyin_Person 其实是 Person 的子类,那么也就是说其 superclass 指针是指向 Person 类对象的,NSKVONotifyin_Person 是 runtime 在运行时生成的。那么p1对象在调用 setage 方法的时候,肯定会根据p1的 isa 找到 NSKVONotifyin_Person,在 NSKVONotifyin_Person 中找 setage 的方法及实现。
NSKVONotifyin_Person 中的 setage 方法中其实调用了 Fundation框架中C语言函数 _NSsetIntValueAndNotify,_NSsetIntValueAndNotify内部做的操作相当于,首先调用willChangeValueForKey 将要改变方法,之后调用父类的 setage 方法对成员变量赋值,最后调用didChangeValueForKey 已经改变方法。didChangeValueForKey 中会调用监听器的监听方法,最终来到监听者的 observeValueForKeyPath 方法中。
RunLoop基本作用
1、保持程序持续运行,程序一启动就会开一个主线程,主线程一开起来就会跑一个主线程对应的RunLoop,RunLoop保证主线程不会被销毁,也就保证了程序的持续运行
2、处理App中的各种事件(比如:触摸事件,定时器事件,Selector事件等)
3、节省CPU资源,提高程序性能,程序运行起来时,当什么操作都没有做的时候,RunLoop就告诉CPU,现在没有事情做,我要去休息,这时CPU就会将其资源释放出来去做其他的事情,当有事情做的时候RunLoop就会立马起来去做事情
RunLoop和线程间的关系
1、每条线程都有唯一的一个与之对应的RunLoop对象
2、RunLoop保存在一个全局的Dictionary里,线程作为key,RunLoop作为value
3、主线程的RunLoop已经自动创建好了,子线程的RunLoop需要主动创建
4、RunLoop在第一次获取时创建,在线程结束时销毁
RunLoop 中 Source1/Source0/Timers/Observer 分别代表什么?
Source1: 基于Port的线程间通信Source0: 触摸事件,PerformSelectorsTimers: 定时器,NSTimerObserver: 监听器,用于监听RunLoop的状态
Core Foundation中关于RunLoop的5个类
CFRunLoopRef- 获得当前RunLoop和主RunLoop;
CFRunLoopModeRef- RunLoop 运行模式,只能选择一种,在不同模式中做不同的操作;
CFRunLoopSourceRef- 事件源,输入源;
CFRunLoopTimerRef- 定时器时间;
CFRunLoopObserverRef- 观察者;
block的本质
block本质上也是一个oc对象,他内部也有一个isa指针。block是封装了函数调用以及函数调用环境的OC对象。
block的变量捕获
image.png
总结:局部变量都会被block捕获,自动变量是值捕获,静态变量为地址捕获。全局变量则不会被block捕获。局部变量因为跨函数访问所以需要捕获,全局变量在哪里都可以访问 ,所以不用捕获。
block的3种类型
image.png
1、
__NSGlobalBlock__( _NSConcreteGlobalBlock )
2、__NSStackBlock__( _NSConcreteStackBlock )
3、__NSMallocBlock__( _NSConcreteMallocBlock )
block在内存中的存储
image.png
1、
__NSGlobalBlock__直到程序结束才会被回收,不过我们很少使用到__NSGlobalBlock__类型的block,因为这样使用block并没有什么意义。
2、__NSStackBlock__类型的block存放在栈中,我们知道栈中的内存由系统自动分配和释放,作用域执行完毕之后就会被立即释放,而在相同的作用域中定义block并且调用block似乎也多此一举。
3、__NSMallocBlock__是在平时编码过程中最常使用到的。存放在堆中需要我们自己进行内存管理。
什么情况下ARC会自动将block进行一次copy操作?
1、block作为函数返回值时
2、将block赋值给__strong指针时
3、block作为Cocoa API中方法名含有usingBlock的方法参数时
4、block作为GCD API的方法参数时
MRC环境下,如何解决循环引用?
1、使用 __unsafe_unretained 解决
2、使用 __block 也能解决循环引用的问题。因为上文 __block 内存管理中提到过,MRC 环境下,尽管调用了 copy 操作,__block 结构体不会对 person 产生强引用,依然是弱引用。因此同样可以解决循环引用的问题。
分类中写@property
添加属性,但是不会自动生成私有属性,也不会生成set,get方法的实现,只会生成set,get的声明,需要我们自己去实现。









网友评论