Objective-C 内存管理和多线程

本文是阅读《Objective-C高级编程》过程中的一些心得。

1、ARC

1、内存管理

OC中根据自动计数来进行内存空间的管理,针对一个对象的操作经历以下几个阶段:

  • 生成并持有对象:alloc、new、copy、mutableCopy等方法,持有对象才能对该对象做出操作;
  • 持有对象:retain,持有会使该对象的引用计数加一,可调用retainCount查看引用计数大小;
  • 释放对象:release,释放会使该对象的引用计数减一,前提是已经持有该对象;
  • 废弃对象:dealloc,对象的引用计数为0时会清理对象的存储空间。

还有一种操作autorelease,使用NSRunloopNSAutoreleasePool管理引用持有的对象,直到其运行生命周期结束才会释放所有持有的对象,相当于执行了对象的release方法,有点类似局部变量。

==注意:以上方法在ARC开启时不可用。

2、引用修饰符

_ _strong

_ _strong修饰符是id类型和对象类型的默认所有权修饰符,表示对对象的强引用,持有对象,该变量在超出其作用域时被废弃,其引用的对象也会随之释放。

强引用带来的问题是循环引用,造成内存泄漏,看下面这个例子:

@interface Test:NSObject
{
	id _strong obj;
}
- (void)setObject:(id _strong)obj;
@end
//循环引用
{
id test0 = [Test new];	//对象A
id test1 = [Test new];	//对象B
[test0 setObject:test1];
[test1 setObject:test0];
}

当test0要释放对象A时,发现test1的变量对A有强引用,要test1先释放B,同理要test0先释放A。谁也无法释放,导致内存泄漏。

_ _weak

_ _weak代表弱引用,不持有对象,当引用的对象被释放时自动指向nil。前面的例子只要把Test中的obj改为弱引用即可。_ _unsafe_unretained_ _weak不同的是当引用的对象释放时使用该修饰符修饰的变量时程序会崩溃。

_ _autoreleasing

相当于代替调用autorelease方法,一般不显示附加该关键词,取而代之的是@autoreleasepool{ }。在代码块中,编译器会检查方法名是否以alloc、new、copy、mutableCopy开始,如果不是则将返回值的对象注册到autoreleasepool。常用在循环中,可以减少内存占用率。

将对象注册到autoreleasepool还有几种隐示的方法(不加_ _autorelease关键字):

  • + (id) array {return [[NSMutableArray alloc] init]};生成的对象超出函数范围应该被自动释放,但该对象同时又是函数的返回值,会自动被注册到autoreleasepool
  • 访问_ _weak修饰的变量时,实际上必定访问注册到autoreleasepool的对象,因为访问过程中对象可能被废弃,如果注册的话保证在@autoreleasepool块结束之前对象存在;
  • id和对象的指针会默认加上_ _autoreleasing修饰符。

3、属性

属性声明的属性与所有权修饰符的对应关系如下:

在这里插入图片描述
对于copy关键词,一般用于不可变类型,如NSString、NSArray等。相当于在setter方法中做了一次深拷贝,防止浅拷贝导致该属性随着所赋值的改变而改变。而strong相当于在setter方法中进行浅拷贝,与所赋值的指针共享对象。

至于NSObjectcopymutableCopy,只有类似于NSStringcopy方法是浅拷贝。

2、BLOCK

1、语法

block可以理解为匿名函数,写法为^ 返回值类型 (参数列表) {表达式},参数列表和返回值都可省略,即便有返回值,也可以通过return推测出来。当block作为变量时可以这样写:返回值类型 (^变量名称) (参数列表),跟一般的变量不同的是block变量被复制后,可以用变量名称(参数列表),相当于函数调用。比较常见的方式是利用typedef声明block变量,例如typedef int (^blk_t) (int);,之后就可以利用blk_t blk之类的方式声明变量,非常方便。

block可截获自动变量值,比如在block之前定义了一个变量,block里面要利用此变量的值,之后修改了该变量的值,再次调用block的话此变量的值仍是最初捕获的值。当在block中修改变量的值时会产生编译错误,此时可在变量前加上_ _block修饰符。

2、BLOCK循环引用

当对象中含有block属性,且block属性中代码块中又利用到self或其它属性的值,一般用id _ _wak tmp = self;来代替block中用到的self

3、多线程

1、Dispatch Queue

将block加入不同的队列执行顺序也不一样:

  • Serial:按添加顺序先后执行block,只有一个线程
  • Concurrent:有多个线程,不同线程并行,同一个线程串行

通过dispatch_queue_create("<reverse DNS id>", DISPATCH_QUEUE_SERIAL/DISPATCH_QUEUE_CONCURRENT)创建一个dispatch_queue_t对象,队列中的每个blcok都持有该对象,除非执行完毕,否则不释放。

API作用
dispatch_get_main_queue()获取主线程中执行的队列,在RunLoop中执行,属于Serial
dispatch_get_gloabl_queue(<priority>, 0)获取全局的Concurrent队列,优先级可以设定
dispatch_time(DISPATCH_TIME_NOW, <n>ull * NSEC_PER_SEC)获取n秒之后的dispatch_time_t对象
dispatch_after(<dispatch_time_t>, <queue>, block)n秒后将block加入到queue中

2、dispatch_async和dispatch_sync

  • dispatch_async表示异步,将block加入queue的队尾,串/并执行取决于queue的类型,并马上返回
  • dispatch_sync表示同步,将block加入queue的队尾,串/并执行取决于queue的类型,但是会阻塞调用该函数的queue,直到block执行完毕才会返回,使用时需要避免死锁的情况:
//主线程执行下面这条语句会出现死锁
dispatch_async(dispatch_get_main_queue(), ^{
	dispatch_sync(dispatch_get_main_queue(), block);
});

3、Dispatch Group

  • diapactch_group_create()创建dispatch_group_t对象
  • dispatch_group_async(group, queue, block)添加block到queue
  • dispatch_group_notify(group, queue, block)监视block的执行情况,全部执行完毕会调用刚方法中的block

类似的还有dispatch_apply,均可实现等待所有block执行完毕:
dispatch_apply(counts, queue, ^(size_t index){ }),将counts个block加入到queue中,全部执行完毕后再进行其它处理。

4、Dispatch Semaphore

利用信号量对数据的更新进行控制:

API作用
dispatch_semaphore_create(<n>)获取dispatch_semaphore_t对象,计数为n
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)一直等待,直到计数值>=1
dispatch_semaphore_signal(semaphore)计数值+1

5、dispatch_once

单例模式:dispatch_once(static修饰的dispatch_once_t对象, block进行数据初始化)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值