内存管理
- 引用计数:引用计数表维护对象的引用数
- autorelease:添加到最内层的AutoReleasePool对象列表,在销毁AutoReleasePool时释放列表内对象
- 非alloc/new/copy/mutableCopy开始的方法返回的对象,自动注册到autoreleasepool,但是如果返回值被强应用,则编译器会优化为不需要注册到autoreleasepool。
- 对象指针的指针,默认指向的对象为__autorealease,因为原则上非自己创建的对象不应由自己持有。如NSError **等价于 NSError * __autorelease *
- ARC使用规则
- 不能使用retain/release/retainCount/autorelease
- 不能使用NSAllocateObject/NSDeallocateObject
- 必须遵守内存管理方法的命名规则
alloc/new/copy/mutableCopy开头的方法必须返回对象
init开头的方法要求更严格,必须是实例方法,返回的对象必须是id、所在的类、该类的超类或子类。返回的对象不会注册到autoreleasepool。
- 不要显示调用dealloc
- 使用@autoreleasepool代替NSReleasePool
MRC也应该用@autoreleasepool
- 不能使用NSZone
MRC也不要用,其实系统会忽略
- 对象变量不能作为C语言结构体成员
C语言没法管理结构体上的成员的生命周期
- 需要显示转换id和void *
__bridge转换,不发生所有权的转换
__bridge_retained,转换后的变量也持有对象
__bridge_transfer,被转换的变量所持有的对象赋值给目标变量后释放
对应Core Foundation的转换CFBridgingRetain/CFBridgingRelease
void *p = (__bridge_retained void *) obj;
id obj = (__bridge_transfer id) p;
- 属性@property
assign/copy/retain/strong/unsafe_unretained/weak
在生命类成员变量时,和属性声明的不一样时会有编译错误,当然现在一般也不需要手动写生成类成员变量,一般都是直接用@property自动生成。
- ARC实现
1 . strong,在作用域结束时release,对于非alloc/init/copy方法的对象,本来是通过objc_autoreleaseReturnValue(obj)注册到autoreleasepool,但是在被strong引用时,会通过调用objc_retainAutorealseReturnValue(obj)来引用对象,这时编译器就判断为不需要添加到autoreleasepool。
- weak,引用对象时记录到weak表中,在对象释放时,吧weak表中指向对象的变量置空。使用weak变量时会自动生成临时变量,对象被添加到autorealeasepool。
id __weak o = obj;
NSLog(@"1 %@", obj); // 添加一次
NSLog(@"2 %@", obj); // 再添加一次
// 改为下面可以避免添加到autoreleasepool
id __weak o = obj;
id tmp = o;
NSLog(@"1 %@", tmp);
NSLog(@"2 %@", tmp);
3、autorealease,相当于MRC的手动调用autorealease方法
Blocks
Blocks是C语言的扩展功能:带有自动变量的匿名函数。
格式:
^int (int count) {return count + 1;} // 完整格式
^(int count) {printf("%d", count)} // 无返回值
^{printf("test")} // 无返回值
// block类型定义
typedef int (^blk_t) (int);
- 截获自动变量
普通变量的值未截获时的值,不能修改
__block变量可以被block修改值
不支持截获数组,需要的话转为指针
- block的实现
block实质上是会生成一个Objective-C的对象,截获的自动变量会保存在对象中,__block变量会生成__Block_byrefer_val结构体,存放到block中,通过其中的__forwarding间接访问实际变量,当block被复制到堆时,如果栈上的__block变量也会被复制到堆。
- 有三种区域的block
全局Block:在全局变量处定义或者在Block不需要截获自动变量时
栈Block:除全局Block之外,定义时默认都是栈Block
堆Block:Block作为函数返回值时会被复制到堆,使用copy方法会复制到堆,将Block赋值给strong类型的变量,或者赋值给Block类型的成员变量时会复制到堆。
向方法传递Block时不会自动复制到堆,除了下面两种方法:
- Cocoa框架中代用usingBlock的方法
- GCD的API
对全局Block进行copy没有任何作用
- block循环应用
- 通过__weak解决
- MRC时通过__block解决,但是需要在Block执行完后置空,在置空之后才会释放。这是因为__block指向的对象复制到堆时不会被retain。
GCD
- 两种队列:Serial DispatchQueue和Concurrent Dispatch Queue
一个Serial Dispatch Queue生成并使用时会创建一个新线程,需要防止过多线程
Dispatch Queue必须自己release,并且对于非create方法返回的queue要retain
- dispatch_set_target_queue
- 变更队列的执行优先级
- 目标队列可以成为原队列的执行阶层(多个queue被设置到某个target时)
- dispatch_after
并不是在指定的时间后执行,二是在指定的时间后添加到queue
- Dispatch Group
在希望多个处理全部执行之后,再执行其他处理时使用
- dispatch_barrier_async
等待所有位于barrier函数之前的操作执行完毕后执行,并且在barrier函数执行之后,barrier函数之后的操作才会得到执行
- dispatch_async
在处理执行结束之前,函数不会返回。注意死锁问题,如下:
dispatch_queue_t queue dispatch_get_main_queue();
dispatch_async(queue, ^{
dispatch_sync(queue, ^{});
})
- dispatch_apply
按指定次数讲block追加到指定的queue中并等待处理完毕。
- dispatch_suspend / dispatch_resume
暂停或恢复queue对执行的处理,对于已处理或正在处理的没有影响
- Dispatch Semaphore
信号量,wait方法计数未0时等待,计数>=1时不等待而减去1。signal方法加1
dispatch_semaphore_t semaphore = dispatch_semaphore_create(2); // 创建数量为2的信号量
dispatch_semaphore_wait(semaphore, time); // 信号量-1
dispatch_semaphore_signal(semaphore); // 信号量+1
- dispatch_once
保证应用程序执行过程中只执行一次处理
网友评论