iOS编程新手的一些问题(1)

hikui posted @ 2012年4月15日 00:56 in iOS开发 , 1609 阅读

这几天加班改另外几个已经离职的实习生遗留下来的代码,真是吐血三升啊。不知道这个公司为什么招这么多没有什么经验的iOS实习生,写出来的程序几秒钟就crash一次。

Objective-C因为只有苹果在用,所以接触的机会比较少,大多数人都是从别的语言转过来的,我也是,所以我也犯过很多错误,在错误中渐渐学会了Objective-C。

这里说一下这几天改代码发现的常见错误:

0、retain, release问题:

虽然现在有ARC了,不需要写retain, release,但是由于这个只能在iOS5以上才能实现全部功能,所以现在很多项目依然使用手动内存管理。retain, release的原理很简单,当你创建一个对象之后,什么时候把它销毁呢?答案是在没有人用它的时候就可以销毁了。每个对象有一个计数器叫做retainCount。当对象被创建时,retainCount为1,如果另外有一段代码要用这个对象一段时间,除了获取对象地址之外,还要向对象发送retain消息,这时候retainCount就会+1,此时,即便对象原先的拥有者释放了对象,该对象也不会被销毁。所谓的释放就是向对象发送release消息,retainCount会-1,表明又有一个人不需要用它了,当retainCount==0时,就说明没有人要用它了,便销毁了。

手动管理内存的Objective-C对此必须斤斤计较,否则很容易出现内存泄漏情况。很多Objective-C新手经常忘记release,导致大量的内存泄漏。在手机开发中是很危险的,因为程序会被系统kill掉,看起来就像崩溃一样。

初学者容易犯的错误是:没有retain之后没有release掉。导致大量内存泄漏。

Objective-C的内存管理有个规则:

当你是对象的创造者时(使用alloc, copy方法),在你不用它时,向它发送release。

当你需要保留不是你创造的对象一段时间时,需要向这个对象发送retain,并且用完后向它发送release。如果不需要保留一段时间,则不需要发送retain,也不需要发送release。

当一个函数需要返回一个对象时,需要将这个对象标记为autorelease。

就这三点。

1、getter, setter问题:

用Java的时候写getter setter再容易不过了,事实上只要用IDE生成一下就行了。但是由于Objective-C需要管理内存,所以没那么简单。

setter的写法是这样的:

 

-(void)setSomething:(SomeClass *)anObj
{
    if(anObject != _something){
        [_something release];
        _something = [anObj retain];
    }
}

因为setter和getter都是针对成员变量的,所以要拥有成员变量一段时间,加之你用setter设置成员变量,说明要赋给成员变量的那个anObj不是你创造的,所以需要retain。但是这还没完。当你第二次用setter设置成员变量时,原先的那个对象需要被释放掉,否则原先的对象的retainCount无法清零了。所以首先要release掉原来的,在retain新的。那么第一次使用setter时,something是nil,岂不是变成[nil release]了?没错,这在objective-c里面完全合法,向nil发送任何消息都返回nil且没有错误。

主要问题还不是在这里,现在的Objective C增加了@property和@synthesize指令,说白了就是自动帮你生成setter和getter,并且用了这两个指令之后,可以用点表达式进行getter和setter调用,看起来就像Java一样:

 

anObject.someProperty = newPropertyObject; //相当于调用[anObject setSomeProperty];
tempProperty = anObject.someProperty; //相当于调用[anObject someProperty];

而这个情况放在self的情况下就容易让人疑惑,在Java里面,在对象方法里面访问成员变量可以直接写someProperty,也可以写成this.someProperty,两者几乎完全一样。然而在Objective-C里面则完全不同。

self.someProperty = xxx时,实际上是执行了[self setSomeProperty],而这个方法里面包含了内存管理的方法。如果直接调用someProperty = xxx时,则是直接将someProperty指向xxx的地址,并没有做内存管理,此时就发生内存泄漏了。

初学者往往会被这个迷惑,从而写出

 

[someProperty release];
self.someProperty = nil;

这样的语句(多数在dealloc中)。编码者原来的意图是好的,将这个对象释放掉之后,将指针归到nil。然而这样写是错误的,因为在调用self.someProperty = xxx时,又会发生一次[someProperty release],但是此时someProperty已经被上面一句代码释放了,再释放一次就报错了。另外,

 

self.someProperty = [[SomeClass alloc]init];

这样的写法也是错误的,因为[[SomeClass alloc]init]时,新对象的retainCount==1,self.someProperty = xxx的语句执行了setter,又被retain了一次,此时retainCount==2了。所以之后就总是多1,无法清零了。

正确的写法是:

 

SomeClass *temp = [[SomeClass alloc]init];
self.someProperty = temp;
[temp release];

或者

self.someProperty = [[[SomeClass alloc]init]autorelease];

或者

someProperty = [[SomeClass alloc]init];

实际上,苹果的官方模板在写@synthesize时,特地写成了:

 

@synthesize someProperty = _someProperty;

这样一来,就只能使用self.someProperty和_someProperty了,someProperty是无效的,这样可以避免混淆。

創用 CC 授權條款
本著作係採用創用 CC 姓名標示 2.0 通用版 授權條款授權.
Snowmanzzz 说:
2012年10月23日 22:52

good for dummies like me~


登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter