iOS编程新手的一些问题(2)
2、Cocoa collections的问题。
NSArray, NSDictionary, NSSet,以及这些类型的Mutable形式类型有一个共同点,就是只能接受对象作为Value。结构体和基本类型是不能保存进collections的。所以诸如
NSMutableArray *array = [[NSMutableArray alloc]init]; int i = 1; NSInteger nsi = 2; CGRect rect = CGRectMake(0, 0, 0, 0); [array addObject:1]; [array addObject:nsi]; [array addObject:rect];
都是不正确的。int是基本类型,CGRect是结构体,所以不能被collections接受。
这里要注意的就是NSInteger声明的变量不是对象,它只是int或者long的别名。NSInteger的定义是这样的:
#if __LP64__ || TARGET_OS_EMBEDDED || TARGET_OS_IPHONE || TARGET_OS_WIN32 || NS_BUILD_32_LIKE_64 typedef long NSInteger; #else typedef int NSInteger; #endif
另外的CGFloat也是一样的。
所以要将这种东西存储到collections里面,需要包装成对象。比如数字类型的东西都包装成NSNumber,CG等类型的结构体包装成NSValue,然后存入collections里面,读取的时候,调用相应的xxValue方法就能还原成基本类型。
3、使用Delegate模式需要小心:
我上次调试别人的程序时,几乎所有的崩溃都是因为使用delegate模式不当造成的。Java、C#使用delegate没有什么困难,而Objective-C里面,在没有ARC的情况下,需要手动管理内存,问题就出现了。
现在有A、B两个对象,A作为B的delegate,即B.delegate = A。假设B需要花很长的时间做完工作,做完工作之后调用A实现的delegate方法,而此时A已经被销毁了。这时B.delegate依旧指向A原来的地址而不是nil,B就会向原来地址的已经被销毁的对象发送消息。这时程序就崩溃了。
所以当B.delegate = A的情况下,在A会被销毁的时候,必须设置B.delegate = nil。这样,即便B要调用delegate方法时,A已经被销毁,B也是像nil发送消息。在Objective -C中,这样是完全合法的,并且会什么都不做。
很多时候,给B设置delegate的工作是在A内部做的,即在A的某个方法里面,有B.delegate = self这样的语句。所以A的dealloc方法里面需要增加
if (B.delegate==self){ B.delegate = nil; }
这样的语句。
4、viewDidUnload需要做事:
很多人在写代码的时候,总是忽略viewDidUnload这个方法。但是在使用xcode生成的ViewController模板里面又一直会看到viewDidUnload这个方法。模板里面明显有两行注释:
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
这是很重要的信息。它的意思是说,如果你在ViewController的对象里面retain了一些subview,就需要在这里将他们释放掉。
如果不写的话,乍看程序不会有什么问题,既不报错也不崩溃。但是在系统内存紧缺时,你的程序被系统kill的概率就非常的高了。
iOS在内存紧缺时发送memory warning。收到这个信号后,你的程序会自动调用现在还活着的ViewController的didReceiveMemoryWarning方法,并且如果你的ViewController并不在当前显示状态(可能在NavigationController栈里面),就会销毁这个ViewController.view。此时,view是retain过它的subview的,而如果viewController也retain过里面某些subview,那么这些subview的retainCount==2。系统销毁view时,对里面所有的subview进行release,此时被ViewController retain过的subview的retainCount==1,系统无法销毁。接下来,系统调用ViewController的viewDidUnload,如果此时你做了self.someSubview = nil这件事,那么对这个subview来说,又进行了release(由于点表达式是setter方法,参加前篇),此时retainCount==0,就可以被销毁了。如果什么都没做,那么这个Subview就不会被销毁,额外增加了内存负担,就提高被系统杀死的风险了。
这里另外要注意的是,写self.someSubview = nil是在someSubview为ViewController的@property(retain)时才能用的,如果不是property,而又在程序里retain了,那么viewDidUnload必须写成:
[someSubview release]; someSubview = nil;
切不可直接写someSubview = nil,否则就内存泄漏了。
本著作係採用創用 CC 姓名標示 2.0 通用版 授權條款授權.
2024年1月16日 19:07
Instantly this web site will irrefutably frequently end up being notable regarding all weblog consumers, due to diligent reviews as well as checks