[译文]如何才能不崩溃 #6: Properties and Accessors

原文地址:

http://inessential.com/2015/05/27/how_not_to_crash_6_properties_and_acce

这让我毛骨悚然:

- (void)someRandomMethod {
  some stuff…
  _thing = otherThing;
  other stuff…
}

你可以证明这是正确的。你正在使用ARC,它会被适当的保持(retain)、释放(release)和添加(add)。没有人观察 _thing

好吧。这合法并且它工作了。

你意识thing应该是可以被观察的。所以,每一个你设置thing的地方,你可以调用:

[self willChangeValueForKey:kThingKey];
_thing = otherThing;
[self didChangeValueForKey:kThingKey];

依然合法,并且可以工作。

问题是,在未来:今天之后,明天,或在6个月中,你或者其他人写了一个自定义的设置方法 - 也许因为当thing被设置的时候,你需要做类似于这样的事情self.needsDisplay = YES - 并且,现在你发现了一个bug,无论 thing什么时候被改变,视图都不会重绘。

或者,更糟糕的:也许,在未来,每当thing发生改变的时候,自定义的设置方法拆除了一个观察者,然后初始化了一个新的。当你直接设置_thing的时候,观察不会再保持正常,那么,你会遇到崩溃。

答案是一个简单的规则:在获得和设置属性的时候,使用存取器。

换句话说,这么做:

- (void)someRandomMethod {
  some stuff…
  self.thing = otherThing;
  other stuff…
}

不管你是否拥有一个自定义的设置方法,这都会工作。当你设置thing的时候,你不需要关心其他方法。

(这是关于编程的简单的测试规则:如果你遵循它不会出错,但是你不遵循它就会出错,那么你应该遵循它。)

(不用关心存取器的性能问题。我是一个性能狂,我并没有发现这会成为一个问题。如果你的app有性能问题,剖析它,并找出真正的原因。)

例外

你不应该在这四个地方使用存取器:初始化方法、dealloc、自定义获取方法(custom getter)、自定义设置方法(custom setter)。这可以避免副作用。

如果你需要副作用 - 移除观察者,例如,在dealloc中 - 你通常放在设置方法里面,将它分离出来,然后在设置方法和dealloc中调用。(将观察者从初始化方法和dealloc中移出,可能标志着你的代码需要重构。)

自动合成

不要创建实例变量,永远。用属性申明代替。

属性自动合成实例变量。只在Xcode告诉你需要这么做的时候,再使用@synthesize 

使用ARC

如果你有非ARC代码,升级到ARC方式。手动管理内存容易发生错误。甚至有些很多年工作经验的人依然会一次又一次的犯错误,错误导致了崩溃(或者内存泄露,或者内存被遗弃,最多)。

通常,我并不建议修改可以工作正常的代码 - 但是,如果你的代码需要手动管理,你自己或者和你的伙伴们一起将它转换为ARC。(每个人在手动管理内存的时候都会变得更糟。这没有任何一点会让你成为英雄。)

(转换到ARC之后可能会遇到性能问题,特别是你在循环里面处理大量的对象。记得使用自动释放池(autorelease pools)。不要急于下结论:使用性能分析工具。)

(另外,ARC转换器并不一定如你所愿。如果你使用它,要检查修改的内容。记得一次只转一个文件。Targets可以同时包含非ARC和ARC文件。)

不要这么做(Don’t do->this)

这会让我歇斯底里的吼叫:thing->property。不行。

dealloc

如果你不需要dealloc(由于你使用ARC),就不要创建它。不需要在dealloc中设置属性为nil。

一个很大的例外是代理:将代理置为nil。

使用弱引用

弱引用很棒。例如,代理,应该是弱引用。

父类应该持有它们的子类,但是子类对它的父类应该有一个弱引用(如果他们有引用的话)。弱引用可以让你避免这些 打破循环引用的无效方法

不要在任何情况下使用unsafe_unretained。那是一个陷阱。你可能这么做:

#define CRASHING_BUG unsafe_unretained

从字面上说,这是不安全的。

不要使用它。见鬼 - 甚至不要碰它们。它们有一堆问题。

Article by 付军