赞
踩
本文是阅读《Objective-C高级编程》
过程中的一些心得。
OC中根据自动计数来进行内存空间的管理,针对一个对象的操作经历以下几个阶段:
alloc、new、copy、mutableCopy
等方法,持有对象才能对该对象做出操作;retain
,持有会使该对象的引用计数加一,可调用retainCount
查看引用计数大小;release
,释放会使该对象的引用计数减一,前提是已经持有该对象;dealloc
,对象的引用计数为0时会清理对象的存储空间。还有一种操作autorelease
,使用NSRunloop
的NSAutoreleasePool
管理引用持有的对象,直到其运行生命周期结束才会释放所有持有的对象,相当于执行了对象的release
方法,有点类似局部变量。
==注意:以上方法在ARC
开启时不可用。
_ _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
代表弱引用,不持有对象,当引用的对象被释放时自动指向nil
。前面的例子只要把Test
中的obj
改为弱引用即可。_ _unsafe_unretained
与_ _weak
不同的是当引用的对象释放时使用该修饰符修饰的变量时程序会崩溃。
相当于代替调用autorelease
方法,一般不显示附加该关键词,取而代之的是@autoreleasepool{ }
。在代码块中,编译器会检查方法名是否以alloc、new、copy、mutableCopy
开始,如果不是则将返回值的对象注册到autoreleasepool
。常用在循环中,可以减少内存占用率。
将对象注册到autoreleasepool
还有几种隐示的方法(不加_ _autorelease
关键字):
+ (id) array {return [[NSMutableArray alloc] init]};
生成的对象超出函数范围应该被自动释放,但该对象同时又是函数的返回值,会自动被注册到autoreleasepool
;_ _weak
修饰的变量时,实际上必定访问注册到autoreleasepool
的对象,因为访问过程中对象可能被废弃,如果注册的话保证在@autoreleasepool
块结束之前对象存在;_ _autoreleasing
修饰符。属性声明的属性与所有权修饰符的对应关系如下:
对于copy
关键词,一般用于不可变类型,如NSString、NSArray
等。相当于在setter
方法中做了一次深拷贝,防止浅拷贝导致该属性随着所赋值的改变而改变。而strong
相当于在setter
方法中进行浅拷贝,与所赋值的指针共享对象。
至于NSObject
的copy
和mutableCopy
,只有类似于NSString
的copy
方法是浅拷贝。
block可以理解为匿名函数,写法为^ 返回值类型 (参数列表) {表达式}
,参数列表和返回值都可省略,即便有返回值,也可以通过return
推测出来。当block作为变量时可以这样写:返回值类型 (^变量名称) (参数列表)
,跟一般的变量不同的是block变量被复制后,可以用变量名称(参数列表)
,相当于函数调用。比较常见的方式是利用typedef
声明block变量,例如typedef int (^blk_t) (int);
,之后就可以利用blk_t blk
之类的方式声明变量,非常方便。
block可截获自动变量值,比如在block之前定义了一个变量,block里面要利用此变量的值,之后修改了该变量的值,再次调用block的话此变量的值仍是最初捕获的值。当在block中修改变量的值时会产生编译错误,此时可在变量前加上_ _block
修饰符。
当对象中含有block属性,且block属性中代码块中又利用到self
或其它属性的值,一般用id _ _wak tmp = self;
来代替block中用到的self
。
将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中 |
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);
});
diapactch_group_create()
创建dispatch_group_t
对象dispatch_group_async(group, queue, block)
添加block到queuedispatch_group_notify(group, queue, block)
监视block的执行情况,全部执行完毕会调用刚方法中的block类似的还有dispatch_apply
,均可实现等待所有block执行完毕:
dispatch_apply(counts, queue, ^(size_t index){ })
,将counts个block加入到queue中,全部执行完毕后再进行其它处理。
利用信号量对数据的更新进行控制:
API | 作用 |
---|---|
dispatch_semaphore_create(<n>) | 获取dispatch_semaphore_t 对象,计数为n |
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER) | 一直等待,直到计数值>=1 |
dispatch_semaphore_signal(semaphore) | 计数值+1 |
单例模式:dispatch_once(static修饰的dispatch_once_t对象, block进行数据初始化)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。