Objective-C代码学习大纲(5)
本文为台湾出版的《Objective-C学习大纲》的翻译文档,系统介绍了Objective-C代码,很多名词为台湾同胞特指词汇,在学习时仔细研读才能体会。
记忆体管理
到目前为止我都刻意避开 Objective-C 的记忆体管理议题。你可以唿叫物件上的 dealloc,但是若物件裡包含其他物件的指标的话,要怎么办呢?要释放那些物件所佔据的记忆体也是一个必须关注的问题。当你使用 Foundation framework 建立 classes 时,它如何管理记忆体?这些稍后我们都会解释。
注意:之前所有的範例都有正确的记忆体管理,以免你混淆。
Retain and Release(保留与释放)
Retain 以及 release 是两个继承自 NSObject 的物件都会有的 methods。每个物件都有一个内部计数器,可以用来追踪物件的 reference 个数。如果物件有 3 个 reference 时,不需要 dealloc 自己。但是如果计数器值到达 0 时,物件就得 dealloc 自己。[object retain] 会将计数器值加 1(值从 1 开始),[object release] 则将计数器值减 1。如果唿叫 [object release] 导致计数器到达 0,就会自动 dealloc。
- Fraction.m
- ...
- -(void) dealloc {
- printf( "Deallocing fraction\n" );
- [super dealloc];
- }
- ...
- main.m
- #import "Fraction.h"
- #import
- int main( int argc, const char *argv[] ) {
- Fraction *frac1 = [[Fraction alloc] init];
- Fraction *frac2 = [[Fraction alloc] init];
- // print current counts
- printf( "Fraction 1 retain count: %i\n", [frac1 retainCount] );
- printf( "Fraction 2 retain count: %i\n", [frac2 retainCount] );
- // increment them
- [frac1 retain]; // 2
- [frac1 retain]; // 3
- [frac2 retain]; // 2
- // print current counts
- printf( "Fraction 1 retain count: %i\n", [frac1 retainCount] );
- printf( "Fraction 2 retain count: %i\n", [frac2 retainCount] );
- // decrement
- [frac1 release]; // 2
- [frac2 release]; // 1
- // print current counts
- printf( "Fraction 1 retain count: %i\n", [frac1 retainCount] );
- printf( "Fraction 2 retain count: %i\n", [frac2 retainCount] );
- // release them until they dealloc themselves
- [frac1 release]; // 1
- [frac1 release]; // 0
- [frac2 release]; // 0
- }
output
- Fraction 1 retain count: 1
- Fraction 2 retain count: 1
- Fraction 1 retain count: 3
- Fraction 2 retain count: 2
- Fraction 1 retain count: 2
- Fraction 2 retain count: 1
- Deallocing fraction
- Deallocing fraction
Retain call 增加计数器值,而 release call 减少它。你可以唿叫 [obj retainCount] 来取得计数器的 int 值。 当当 retainCount 到达 0,两个物件都会 dealloc 自己,所以可以看到印出了两个 "Deallocing fraction"。
Dealloc
当你的物件包含其他物件时,就得在 dealloc 自己时释放它们。Objective-C 的一个优点是你可以传递讯息给 nil,所以不需要经过一堆防错测试来释放一个物件。
AddressCard.h
- #import
- #import
- @interface AddressCard: NSObject {
- NSString *first;
- NSString *last;
- NSString *email;
- }
- -(AddressCard*) initWithFirst: (NSString*) f
- last: (NSString*) l
- email: (NSString*) e;
- -(NSString*) first;
- -(NSString*) last;
- -(NSString*) email;
- -(void) setFirst: (NSString*) f;
- -(void) setLast: (NSString*) l;
- -(void) setEmail: (NSString*) e;
- -(void) setFirst: (NSString*) f
- last: (NSString*) l
- email: (NSString*) e;
- -(void) setFirst: (NSString*) f last: (NSString*) l;
- -(void) print;
- @end
- AddressCard.m
- #import "AddressCard.h"
- #import
- @implementation AddressCard
- -(AddressCard*) initWithFirst: (NSString*) f
- last: (NSString*) l
- email: (NSString*) e {
- self = [super init];
- if ( self ) {
- [self setFirst: f last: l email: e];
- }
- return self;
- }
- -(NSString*) first {
- return first;
- }
- -(NSString*) last {
- return last;
- }
- -(NSString*) email {
- return email;
- }
- -(void) setFirst: (NSString*) f {
- [f retain];
- [first release];
- ffirst = f;
- }
- -(void) setLast: (NSString*) l {
- [l retain];
- [last release];
- llast = l;
- }
- -(void) setEmail: (NSString*) e {
- [e retain];
- [email release];
- eemail = e;
- }
- -(void) setFirst: (NSString*) f
- last: (NSString*) l
- email: (NSString*) e {
- [self setFirst: f];
- [self setLast: l];
- [self setEmail: e];
- }
- -(void) setFirst: (NSString*) f last: (NSString*) l {
- [self setFirst: f];
- [self setLast: l];
- }
- -(void) print {
- printf( "%s %s <%S>
- ", [first cString],
- [last cString],
- [email cString] );
- }
- -(void) dealloc {
- [first release];
- [last release];
- [email release];
- [super dealloc];
- }
- @end
- main.m
- #import "AddressCard.h"
- #import
- #import
- int main( int argc, const char *argv[] ) {
- NSString *first =[[NSString alloc] initWithCString: "Tom"];
- NSString *last = [[NSString alloc] initWithCString: "Jones"];
- NSString *email = [[NSString alloc] initWithCString: "tom@jones.com"];
- AddressCard *tom = [[AddressCard alloc] initWithFirst: first
- last: last
- email: email];
- // we're done with the strings, so we must dealloc them
- [first release];
- [last release];
- [email release];
- // print to show the retain count
- printf( "Retain count: %i\n", [[tom first] retainCount] );
- [tom print];
- printf( "\n" );
- // free memory
- [tom release];
- return 0;
- }
output
- Retain count: 1
- Tom Jones
如 AddressCard.m,这个範例不仅展示如何撰写一个 dealloc method,也展示了如何 dealloc 成员变数。
每个 set method 裡的叁个动作的顺序非常重要。假设你把自己当参数传给一个自己的 method(有点怪,不过确实可能发生)。若你先 release,「然后」才 retain,你会把自己给解构(destruct,相对于建构)!这就是为什么应该要 1) retain 2) release 3) 设值 的塬因。
通常我们不会用 C 形式字串来初始化一个变数,因为它不支援 unicode。下一个 NSAutoreleasePool 的例子会用展示正确使用并初始化字串的方式。
这只是处理成员变数记忆体管理的一种方式,另一种方式是在你的 set methods 裡面建立一份拷贝。
Autorelease Pool
当你想用 NSString 或其他 Foundation framework classes 来做更多程式设计工作时,你需要一个更有弹性的系统,也就是使用 Autorelease pools。
当开发 Mac Cocoa 应用程式时,autorelease pool 会自动地帮你设定好。
- main.m
- #import
- #import
- #import
- int main( int argc, const char *argv[] ) {
- NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
- NSString *str1 = @"constant string";
- NSString *str2 = [NSString stringWithString: @"string managed by the pool"];
- NSString *str3 = [[NSString alloc] initWithString: @"self managed string"];
- // print the strings
- printf( "%s retain count: %x\n", [str1 cString], [str1 retainCount] );
- printf( "%s retain count: %x\n", [str2 cString], [str2 retainCount] );
- printf( "%s retain count: %x\n", [str3 cString], [str3 retainCount] );
- // free memory
- [str3 release];
- // free pool
- [pool release];
- return 0;
- }
output
- constant string retain count: ffffffff
- string managed by the pool retain count: 1
- self managed string retain count: 1
如果你执行这个程式,你会发现几件事:第一件事,str1 的 retainCount 为 ffffffff。
另一件事,虽然我只有 release str3,整个程式却还是处于完美的记忆体管理下,塬因是第一个常数字串已经自动被加到 autorelease pool 裡了。还有一件事,字串是由 stringWithString 产生的。这个 method 会产生一个 NSString class 型别的字串,并自动加进 autorelease pool。
千万记得,要有良好的记忆体管理,像 [NSString stringWithString: @"String"] 这种 method 使用了 autorelease pool,而 alloc method 如 [[NSString alloc] initWithString: @"String"] 则没有使用 auto release pool。
在 Objective-C 有两种管理记忆体的方法, 1) retain and release or 2) retain and release/autorelease。
对于每个 retain,一定要对应一个 release 「或」一个 autorelease。
下一个範例会展示我说的这点。
- Fraction.h
- ...
- +(Fraction*) fractionWithNumerator: (int) n denominator: (int) d;
- ...
- Fraction.m
- ...
- +(Fraction*) fractionWithNumerator: (int) n denominator: (int) d {
- Fraction *ret = [[Fraction alloc] initWithNumerator: n denominator: d];
- [ret autorelease];
- return ret;
- }
- ...
- main.m
- #import
- #import "Fraction.h"
- #import
- int main( int argc, const char *argv[] ) {
- NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
- Fraction *frac1 = [Fraction fractionWithNumerator: 2 denominator: 5];
- Fraction *frac2 = [Fraction fractionWithNumerator: 1 denominator: 3];
- // print frac 1
- printf( "Fraction 1: " );
- [frac1 print];
- printf( "\n" );
- // print frac 2
- printf( "Fraction 2: " );
- [frac2 print];
- printf( "\n" );
- // this causes a segmentation fault
- //[frac1 release];
- // release the pool and all objects in it
- [pool release];
- return 0;
- }
output
- Fraction 1: 2/5
- Fraction 2: 1/3
在这个例子裡,此 method 是一个 class level method。在物件建立后,在它上面唿叫 了 autorelease。在 main method 裡面,我从未在此物件上唿叫 release。
这样行得通的塬因是:对任何 retain 而言,一定要唿叫一个 release 或 autorelease。物件的 retainCount 从 1 起跳 ,然后我在上面唿叫 1 次 autorelease,表示 1 - 1 = 0。当 autorelease pool 被释放时,它会计算所有物件上的 autorelease 唿叫次数,并且唿叫相同次数的 [obj release]。
如同註解所说,不把那一行註解掉会造成分段错误(segment fault)。因为物件上已经唿叫过 autorelease,若再唿叫 release,在释放 autorelease pool 时会试图唿叫一个 nil 物件上的 dealloc,但这是不允许的。最后的算式会变为:1 (creation) - 1 (release) - 1 (autorelease) = -1
管理大量暂时物件时,autorelease pool 可以被动态地产生。你需要做的只是建立一个 pool,执行一堆会建立大量动态物件的程式码,然后释放这个 pool。你可能会感到好奇,这表示可能同时有超过一个 autorelease pool 存在。
Objective-C代码学习大纲(5)的更多相关文章
- Objective-C代码学习大纲(3)
Objective-C代码学习大纲(3) 2011-05-11 14:06 佚名 otierney 字号:T | T 本文为台湾出版的<Objective-C学习大纲>的翻译文档,系统介绍 ...
- Objective-C代码学习大纲(6)
2011-05-11 14:06 佚名 otierney 字号:T | T 本文为台湾出版的<Objective-C学习大纲>的翻译文档,系统介绍了Objective-C代码,很多名词为台 ...
- Objective-C代码学习大纲(4)
2011-05-11 14:06 佚名 otierney 字号:T | T 本文为台湾出版的<Objective-C学习大纲>的翻译文档,系统介绍了Objective-C代码,很多名词为台 ...
- Objective-C代码学习大纲(2)
2011-05-11 14:06 佚名 otierney 字号:T | T 本文为台湾出版的<Objective-C学习大纲>的翻译文档,系统介绍了Objective-C代码,很多名词为台 ...
- Objective-C代码学习大纲(1)
2011-05-11 14:06 佚名 otierney 字号:T | T 本文为台湾出版的<Objective-C学习大纲>的翻译文档,系统介绍了Objective-C代码,很多名词为台 ...
- 大数据Python学习大纲
最近公司在写一个课程<大数据运维实训课>,分为4个部分,linux实训课.Python开发.hadoop基础知识和项目实战.这门课程主要针对刚从学校毕业的学生去应聘时不会像一个小白菜一样被 ...
- JVM学习——学习方法论&学习大纲
2020年02月06日22:25:51 完成了Springboot系列的学习和Kafka的学习,接下来进入JVM的学习阶段 深入理解JVM 学习方法论 如何去学习一门课程--方法论 多讨论,从别人身上 ...
- u-boot代码学习内容
前言 u-boot代码庞大,不可能全部细读,只能有选择的读部分代码.在读代码之前,根据韦东山教材,关于代码学习内容和深度做以下预先划定. 一.Makefile.mkconfig.config.mk等 ...
- Linux 系统从入门到精通的学习大纲;
以前没有接触过Linux,生产环境需要,有时候遇到问题,百度一下,问题解决了,在遇到问题,在百度,有时候问题是如何解决的,为什么会解决有点丈二的和尚摸不着头脑, 为此,想用一段时间,系统的学习下Lin ...
随机推荐
- How To run OAI eNB (No S1) with USRP X310(1)
How To run OAI eNB (No S1) with USRP X310 1.Things need to be done 1.1 Install Ubuntu 14.04 1.1.1 In ...
- Atitit. 有限状态机 fsm 状态模式
Atitit. 有限状态机 fsm 状态模式 1. 有限状态机 1 2. "状态表"和"状态轮换表" 1 3. 有限状态机概念(状态(State)事件(Even ...
- Atitit. Dwr 抛出异常error解决方案
Atitit. Dwr 抛出异常error解决方案 1. Dwr3的处理机制..setErrorHandler 1 2. remote Mteh try catch 1 3. 林吧,子好java 处 ...
- 线程相关函数(4)-pthread_mutex_lock(), pthread_mutex_unlock() 互斥锁
互斥锁实例: #include <pthread.h>pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;int pthread_mutex ...
- shell判断文件夹是否存在
#shell判断文件夹是否存在 #如果文件夹不存在,创建文件夹 if [ ! -d "/myfolder" ]; then mkdir /myfolder fi #shell判断文 ...
- js控制伪元素样式
//获取伪元素// CSS代码 #myId:before { content: "hello world!"; display: block; width: 100px; heig ...
- 使用Crypto++库的CBC模式实现加密(二)
前面已经有一篇介绍使用Crypto++库实现的加密的文章了,但是代码中考虑的不完全,所以就重新发了个二 C++封装: #include "zyaes.h" #include < ...
- [转]C艹中的各种const总结
Ps: 难免碰到C家族的代码 ,各种const直接搞晕,搜集各种资料备用.... ----------------------------------------------------------- ...
- 这不是bug,而是语言特性
分析编程语言缺陷的一种方法是把所有的缺陷归于3类:不该做的做了,该做的没做,该做但做得不合适. 在使用switch case时,如果使用缺省的 fall through,请一定在旁边注释,因为97%的 ...
- 本系列love2d示例代码错误集中整理
3.输入和音乐 音乐不是循环播放的,可以在love.audio.play(music) 之前添加music:setLooping(true)