iOS

OC基础知识

2021-03-25  本文已影响0人  小怪兽大作战

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。

上一篇 下一篇

猜你喜欢

热点阅读