OC中内存分为五大区域
栈:存储局部变量和指针
堆:创建对象
BSS:存储未初始化的全局变量和静态变量
数据段(常量区):存储已经初始化的全局变量、静态变量、常量数据
代码段:存储程序的代码
类加载
当类被第一次加载的时候,会将类的代码存储到代码段中,这个过程叫类加载。
只有第一次访问的时候,才有类加载。
一旦类被加载到程序中,直到程序结束的时候才会被回收
创建对象
[[NSObject alloc] init];
alloc分配内存 init初始化
NSObject.+alloc
struct obj_layout {
NSUInteger retained;
}
+ (id) alloc {
int size = sizeof(struct obj_layout);
struct obj_layout *p = (struct obj_layout *)calloc(1, size);
return (id)(p + 1);
}
销毁对象
NSObject.release中,首先调用NSDecrementExtraRefCountWasZero使retrain-1。如果retrainCount == 0,调用release方法释放内存
NSObject.dealloc
释放内存
GNUstep
alloc类方法通过 struct obj_layout中对retrained整数来保存引用记数,并将其写入对象内存头部,将该对象内存块全部置0后返回
也就是,一个对象的引用记数,放在这个类地址的最前面。
apple
使用一个散列表保存所有的引用记数。将对象的地址映射到散列表中。
autorelease
autorelease会在超过对象的作用域之后,自动调用对象的release方法(记数-1。如果记数==0,调用dealloc方法)
GNUstep中autorelease的实现
[obj autorelease]方法的原理就是把对象添加到NSAutoreleasePool中
[NSAutoreleasePool drain]就是调用填入的所有对象的release方法
apple的实现
RunLoop和线程
在iOS开发中,会遇到两个线程对象:pthread_t 和 NSThread,NSThread只是对pthread的封装,两者肯定是一一对应的,我们可以通过pthread_main_thread_np()或者 [NSThread mainThread]来获取主线程,也可以通过pthread_self() [NSThread currentThread]来获取当前的线程。
和runloop相关的有5个类,分别是
NSDefaultRunLoopMode:app默认的mode,通常主线程是在这个mode下运行的
UITrackingRunLoopMode:界面跟踪mode。用于scorllview追踪触摸滑动,保证界面滑动时不受影响
UIInitializationRunLoop:刚启动时app进入第一个mode,启动完成就不再使用
GSRunLoopReceiveRunLoopMode:接受系统时间的内部mode,绘图服务,通常用不到
NSRunLoopCommonModes:这是一个占位用的mode,不是一种真的mode
ARC
OC为了处理对象,将变量类型定义为id类型和各种对象类型
对象类型就是指向NSObject这样的OC指针(NSObject)
id类型用于隐藏对象类型的类名。相当于C语言中的void
所有权修饰符
所有权修饰符一种有4种
__strong
__weak
__unsafe_unretained
__autoreleasing
__strong
__strong是id类型和对象类型默认的所有权修饰符
id object = [NSObject new]等价于 id __strong object = [NSObject new];
强引用的变量,在超过变量作用域时,会自动释放。
{
id object = [NSObject new]
}
超过作用于自动释放
__weak
oc使用引用记数,因此会出现循环引用的问题。__weak就是为了解决循环引用的问题。
下面的例子会出现循环引用
id test0 = [Test new];
id test1 = [Test new];
test0.myObj = test1
test1.myObj = test0
持有某对象的若引用时,如果该对象被废弃,此弱引用自动失效,并被置为nil
__unsafe_unretrained
__unsafe_unretrained不会持有对象(类似弱引用),但是指向对象被释放时,不会自动指向nil,还是会指向原来的地址,所以会导致野指针。
一般不使用__unsafe_unretrained
__autoreleasing
使用ARC的时候,不能直接使用autorelease方法,但是可以使用__autoreleaseing权限修饰符
ARC无效的时候的写法
NSAutoreleasePool *pool = [NSAutoreleasePool alloc] init];
id obj = [[NSObject alloc] init];
[obj autorelease]
[pool drain];
ARC有效的时候的写法
@autoreleasepool {
id __autoreleasing obj = [[NSObject alloc] init];
}
编译器会自动检查方法名是否以alloc/new/copy/mutableCopy开始,如果不是则自动将返回值注册到autoreleasepool。
ARC下的内存管理规则
不能使用retain/release/retainCount/autorelease
不能使用NSAllocateObject/NSDeallocateObject
必须遵守内存管理的方法命名
alloc
new
copy
mutableCopy
以上述名称开始的方法在返回对象时,必须返回给调用放应当持有的对象
ARC追加了一条
init方法必须是实例方法。返回的对象应当是id类型、方法声明的对象类型、或者是该类型的父类或子类
不要显式调用dealloc
使用@autoreleasepool代替NSAutoreleasePool
不能使用区域(NSZone)
对象类型变量不能作为C语言结构体成员
void*和id之间转换,必须强制转换
尽量不要使用这种转换方式,ARC不推荐
ARC下面的三种转换方式
id obj = [[NSObject alloc] init];
void *p = (__bridge void*)obj;
id o = (__bridge id)p
这种转换类似于 __unsade_unretained。对象释放不会置为nil,所以很容易出现野指针
id obj = [[NSObject alloc]init];
void *p = (__bridge_retrained void*)obj
对象引用记数+1,相当于retain
相当于
id obj = [[NSObject alloc] init];
void *p = (__bridge_retained void*)obj
id obj = (__bridge_transfer id)p
相当于
id obj = (id)p
[obj retain];
[(id)p release]
属性
属性和所有权修饰符对应
assign __unsade_unretained
copy __strong (赋值的是被复制的对象)
retain __strong
strong __strong
unsafe_unretained __unsafe_unretained
weak __weak
copy不是简单的赋值,他的赋值是通过NSCopying接口的copyWithZone方法生成的对象,然后赋值。
id *类型默认是autorelease,因此可以显式的使用_strong
动态数组(id *)中使用操作符和静态数组(id[])不一样,需要手动释放内存。
因为在静态数组中,编译器能够根据变量的作用域自动插入释放内存的代码。但是编译器不能确定数组的生命周期,所以动态数组需要按照下面的方式释放
for (NSInteger i = 0; i < num; i++) {
array[i] = nil;
}
free(array)
类的成员变量和属性
类的成员变量
@interface ViewController : UIViewController
{
//成员变量
NSArray *array1;
}
类的属性
@property (nonatomic,strong) NSArray *array2;
类的属性会自动创建一个带下划线的成员变量,并且会给这个成员变量创建getter和setter方法。
ARC的实现
__strong的实现
id __strong obj = [[NSObject alloc] init];
编译器会自动插入释放代码,如下
id obj = objc_msgSend(NSObject, @selector(alloc));
objc_msgSend(obj, @selector(init));
objc_release(obj)
__weak的实现
{
id __weak obj1 = obj;
}
等价于
id obj1
objc_initWeak(&obj1, obj);
objc_destoryWeak(&obj1);
等价于
id obj1;
obj1 = 0;
objc_storeWeak(&obj1, obj);
objc_storeWeak(&obj1,0);
oc会维护一张weak表,key是对象的地址,value是带有weak变量的地址。
objc_storeWeak(&obj1, obj)把对象的地址作为key,变量的地址作为value存放到weak表里面。
objc_storeWeak(&obj1,0);把对象对象地址对应的value清0
废弃对象时,会根据该对象的地址,找到weak表里面对应的变量地址,然后给变量赋值nil。
网友评论