NSMutableArray初始化崩溃问题
现象:
@property(nonatomic, copy) NSMutableArray *array;
self.array = [NSMutableArray alloc] initWithCapacipty:5];
[self.array addObject:@"hello"];
//大师们为什么上面这样写会报错啊
//如果像下面这样写就不报错了,这是为什么呀
_array = [[NSMutableArray alloc] initWithCapacipty:5];
[_array addObject:@"hello"];
楼主还没弄透@property(我自己也是一知半解),你用self.array = 是通过setter方法来给array赋值,而你又给这个setter方法设置copy,而经过copy生成的array指针指向的是一个不可变数组
(详细请参见苹果的官方文档),这时你在调用add方法,肯定会出问题,而且Xcode会提示:“unrecognized selector sent to instance” 。
至于_array为什么不报错,很简单,因为_array 是一个变量,没有copy修饰,指向的还是你原来生成的可变数组,所以add方法有效。
现象2:
在一个UITableViewController类中声明了一个NSMutableArray *localFiles这样一个数组,作为成员变量。
在viewDidLoad方法中读出应用程序Documents目录下的所有文件名放在这个数组中,初始化时用了这个方法:
localFiles = [NSMutableArray array]; //这是自动释放的对象
然后循环调用了addObject方法,其它就没有什么特殊的操作了,程序能够显示出一个文件列表,但向上或向下滚动一点点时,程序就崩溃,显示的错误信息也相当奇怪:
[__NSArrayI addObject:]: unrecognized selector sent to instance 0x4b1b8b0
我查啊查啊,每次出错的调用栈都显示在执行到cellForRowAtIndexPath这个方法时出错,跟踪到localFiles这个变量时,在调试器上显示out of scope,这个localFiles指针里的内容不知道跑到哪里去了。
从google上查了许多资料,在这个网站的留言中查到下面一段话,顿时茅塞顿开:
It doesn't really matter. [NSMutableArray array] is a nice shortcut, but you have to remember to retain it, so the question really is a matter of [[NSMutableArray array] retain] versus [[NSMutableArray alloc] init]. I usually use the former. The real convenience comes in when you need to statically fill the array; you can do it all in one message. [[NSMutableArray arrayWithObjects:...] retain] is faster than [[NSMutableArray alloc] init] followed by numerous [NSMutableArray addObject:(id)] calls.
原来在调用array方法后得retain!或者改用[[NSMutableArray alloc] init]方法组合,试了2种修改办法果然都好用,程序再也不崩溃了。就这么几个字,害了我几乎一整天。
localFiles = [[NSMutableArray array] retain];
这一天里恶补了一些Objective-C中的内存管理知识,虽然走了一些弯路,但对AutoRelease这个术语算是有了一些认识了。
后来仔细看了《Objective C教程》的第9章“内存管理”,原来第9.3节“Cocoa内存管理规则”(第138页)里作者早就强调三条规则:
(1)当你使用new、alloc或copy方法创建一个对象时,该对象的引用计数器值为1。当不再使用该对象时,你要负责向该对象发送一条release或autorelease消息。这样,该对象将在其使用寿命结束时被销毁。
(2)当你通过任何其它方法获得一个对象时,则假设该对象的引用计数器值为1,而且已经被设置为自动释放,你不需要执行任何操作来确保该对象被清理。如果你打算在一段时间内拥有该对象,则需要保留(retain)它并确保在操作完成时释放它。
(3)如果你保留(retain)了某个对象,你需要(最终)释放或自动释放该对象。必须保持retain方法和release方法的使用次数相等。
我的程序是iPhone程序,为了降低程序的内存空间占用,Cocoa在GUI应用程序中规定了自动释放池的销毁时间,在程序开始处理事件之前创建一个自动释放池,并在事件处理结束后销毁该自动释放池。我的localFiles对象在开始时创建了一个自动释放对象,完成之后进行下一个事件循环,自动释放池已经销毁,localFiles对象也就被释放了,在滚动tableView时进入下一个事件循环,所以localFiles对象的内容就找不到了。