Answers:
我已经开始做一些我认为不标准的事情:
1)随着属性的出现,我不再使用“ _”作为“私有”类变量的前缀。毕竟,如果一个变量可以被其他类访问,那它应该没有属性吗?我总是不喜欢使代码更丑陋的“ _”前缀,现在可以将其省略。
2)说到私有事物,我更喜欢将私有方法定义放在.m文件中的类扩展中,如下所示:
#import "MyClass.h"
@interface MyClass ()
- (void) someMethod;
- (void) someOtherMethod;
@end
@implementation MyClass
为什么要使.h文件杂乱无章,这是外部人员不关心的事情?empty()适用于.m文件中的私有类别,如果您未实现声明的方法,则会发出编译警告。
3)我已经将dealloc放在.m文件的顶部,就在@synthesize指令的下面。您取消分配的内容不应该放在您要在类中考虑的事情列表的顶部吗?在iPhone之类的环境中尤其如此。
3.5)在表格单元格中,使每个元素(包括单元格本身)不透明以提高性能。这意味着在所有内容中设置适当的背景色。
3.6)使用NSURLConnection时,通常您可能希望实现委托方法:
- (NSCachedURLResponse *)connection:(NSURLConnection *)connection
willCacheResponse:(NSCachedURLResponse *)cachedResponse
{
return nil;
}
我发现大多数Web调用都非常单一,这是例外,而不是您希望缓存响应的规则,尤其是对于Web服务调用。如图所示实施该方法将禁用响应缓存。
有趣的是,约瑟夫·马蒂耶洛(Joseph Mattiello)提供了一些iPhone特有的技巧(在iPhone邮件列表中已收到)。还有更多,但是这些是我认为最有用的(请注意,现在已经对原始内容进行了一些编辑,以包括响应中提供的详细信息):
4)仅在需要时才使用双精度,例如在使用CoreLocation时。确保以“ f”结尾常量,以使gcc将其存储为浮点数。
float val = someFloat * 2.2f;
当someFloat
实际上可能是双精度数,并且不需要混合模式数学运算时,这非常重要,因为这样会丢失存储中“ val”的精度。尽管iPhone的硬件中支持浮点数,但与单精度相反,执行双精度算术可能仍需要更多时间。参考文献:
在较旧的电话上,计算速度可能相同,但是寄存器中的单精度分量要比双精度多,因此对于许多计算,单精度最终会更快。
5)将属性设置为nonatomic
。它们是atomic
默认设置,在综合后,将创建信号量代码以防止多线程问题。你们中的99%可能不需要担心这一点,并且将代码设置为非原子时,代码的肿程度会大大降低,内存效率更高。
6)SQLite可以是非常快的缓存大型数据集的方法。例如,地图应用程序可以将其图块缓存到SQLite文件中。最昂贵的部分是磁盘I / O。通过在大块之间发送BEGIN;
和避免许多小写操作COMMIT;
。例如,我们使用2秒计时器来重置每个新提交。到期时,我们发送COMMIT;,这会导致您的所有写入工作都集中在一起。SQLite将事务数据存储到磁盘,并且此操作的开始/结束包装避免了创建多个事务文件,而是将所有事务分组到一个文件中。
另外,如果SQL在主线程上,它将阻止您的GUI。如果查询时间很长,最好将查询存储为静态对象,然后在单独的线程上运行SQL。确保包装所有修改数据库的内容,以@synchronize() {}
块形式存储查询字符串。对于短查询,只需将内容留在主线程上即可,以方便使用。
这里有更多的SQLite优化技巧,尽管该文档看起来过时了,但许多观点可能仍然不错。
http://web.utk.edu/~jplyon/sqlite/SQLite_optimization_FAQ.html
当方法或函数采用格式字符串参数时,应确保对格式字符串的内容具有控制权。
例如,在记录字符串时,很容易将字符串变量作为唯一参数传递给NSLog
:
NSString *aString = // get a string from somewhere;
NSLog(aString);
问题在于字符串可能包含被解释为格式字符串的字符。这可能导致错误的输出,崩溃和安全问题。相反,您应该将字符串变量替换为格式字符串:
NSLog(@"%@", aString);
使用标准的可可命名和格式约定以及术语,而不要使用其他环境中常用的内容。目前有很多的Cocoa开发在那里,当他们中的一个又一个开始你的代码的工作,如果它的外观和感觉类似于其他可可代码它会更平易近人。
做什么和不做什么的示例:
id m_something;
在对象的接口中声明并将其称为成员变量或字段;使用something
或_something
为其名称,并称其为实例变量。-getSomething
; 可可的正确名字是-something
。-something:
; 它应该是-setSomething:
-[NSObject performSelector:withObject:]
,不是NSObject::performSelector
。无论您做什么,都不要使用Win16 / Win32风格的匈牙利符号。甚至微软也放弃了向.NET平台的迁移。
从历史上看,网点的内存管理很差。当前的最佳做法是将网点声明为属性:
@interface MyClass :NSObject {
NSTextField *textField;
}
@property (nonatomic, retain) IBOutlet NSTextField *textField;
@end
使用属性可以使内存管理语义清晰明了;如果使用实例变量综合,它还提供了一致的模式。
注意:在Xcode 4下,它现在内置在IDE中。
毫不奇怪,您可以使用Clang静态分析器在Mac OS X 10.5上分析您的C和Objective-C代码(尚无C ++)。安装和使用很简单:
cd
到您的项目目录。scan-build -k -V xcodebuild
。(还有一些其他限制,等等,特别是您应该在“调试”配置下分析项目- 有关详细信息,请参见http://clang.llvm.org/StaticAnalysisUsage.html-但这或多或少归结为
然后,分析器为您生成一组网页,这些网页显示了可能的内存管理和编译器无法检测到的其他基本问题。
这是一个微妙的但方便的一。如果要将自己作为委托传递给另一个对象,请先重置该对象的委托dealloc
。
- (void)dealloc
{
self.someObject.delegate = NULL;
self.someObject = NULL;
//
[super dealloc];
}
这样可以确保不再发送委托方法。当您即将dealloc
消失在以太坊中时,您想要确保没有任何东西可以意外地向您发送更多消息。记住self.someObject可能被另一个对象保留(可能是单例对象,也可能在自动释放池中,或其他任何对象),直到您告诉它“停止向我发送消息!”,它才认为您即将被释放的对象是公平的游戏。
养成这种习惯将使您免于调试时很痛苦的怪异崩溃。
相同的原则适用于键值观察和NSNotifications。
编辑:
更具有防御性的变化:
self.someObject.delegate = NULL;
变成:
if (self.someObject.delegate == self)
self.someObject.delegate = NULL;
Memory Management Programming Guide for Cocoa
: Additional cases of weak references in Cocoa include, but are not restricted to, table data sources, outline view items, notification observers, and miscellaneous targets and delegates. In most cases, the weak-referenced object is aware of the other object’s weak reference to it, as is the case for circular references, and is responsible for notifying the other object when it deallocates.
@肯德尔
代替:
@interface MyClass (private)
- (void) someMethod
- (void) someOtherMethod
@end
采用:
@interface MyClass ()
- (void) someMethod
- (void) someOtherMethod
@end
Objective-C 2.0的新功能。
在Apple的Objective-C 2.0参考中描述了类扩展。
“类扩展允许您在主类@interface块之外的其他位置为类声明其他必需的API”
因此,它们是实际类的一部分-除了该类之外,不是(私有)类别。细微但重要的区别。
()
代替(Private)
(或其他一些类别名称):您可以将属性重新声明为可读写,而对公众来说它们只是只读的。:)
由于您通常(1)无法对其生存期进行直接控制,因此自动释放的对象可以保留相当长的时间,并且不必要地增加了应用程序的内存占用。虽然在台式机上这可能影响不大,但在更受限的平台上,这可能是一个重要问题。因此,在所有平台上,尤其是在更受限制的平台上,最好的做法是避免使用会导致自动释放对象的方法,而应鼓励您使用alloc / init模式。
因此,而不是:
aVariable = [AClass convenienceMethod];
如果可以,您应该改用:
aVariable = [[AClass alloc] init];
// do things with aVariable
[aVariable release];
当您编写自己的方法以返回一个新创建的对象时,可以利用Cocoa的命名约定将必须释放的方法标记为接收者,方法名称前应加上“ new”。
因此,代替:
- (MyClass *)convenienceMethod {
MyClass *instance = [[[self alloc] init] autorelease];
// configure instance
return instance;
}
你可以这样写:
- (MyClass *)newInstance {
MyClass *instance = [[self alloc] init];
// configure instance
return instance;
}
由于方法名称以“ new”开头,因此API的使用者知道他们负责释放接收到的对象(例如,参见NSObjectController的newObject
method)。
(1)您可以使用自己的本地自动释放池进行控制。有关更多信息,请参见自动释放池。
NSAutoreleasePool
。但是只有在您确认这确实是一个问题之后。过早的优化以及所有这些……
其中一些已经被提及,但是这是我想到的:
#pragma mark [section]
。通常,我按自己的方法,每个子类的覆盖以及任何信息或正式协议进行分组。这使跳转到我正在寻找的内容变得容易得多。在同一主题上,将相似的方法(如表视图的委托方法)组合在一起,不要只是将它们粘在任何地方。#define
will会做时制作ivars ,或者缓存数组而不是在每次需要数据时对其进行排序。关于这一点,我有很多话要说,但是最重要的是不要编写代码,除非您需要它,否则探查器会告诉您。从长远来看,它使事情变得更容易维护。NSLog( @"stub" )
内部即可,或者您想跟踪情况。Finish what you start
您还可以使用// TODO:
标记代码完成,该代码将显示在下拉列表中。
不要像编写Java / C#/ C ++ / etc那样编写Objective-C。
我曾经见过一个用来编写Java EE Web应用程序的团队尝试编写Cocoa桌面应用程序。好像它是一个Java EE Web应用程序。当他们真正需要的只是一个Foo类以及可能的Fooable协议时,有很多AbstractFooFactory和FooFactory以及IFoo和Foo到处飞。
确保您不这样做的一部分,是真正了解语言的差异。例如,您不需要上面的抽象工厂类和工厂类,因为Objective-C类方法的调度与实例方法一样动态,并且可以在子类中重写。
确保将“ 调试魔术”页面添加为书签。这是您在试图寻找可可小虫的源头时将头撞在墙上的第一站。
例如,它将告诉您如何在首先分配内存的位置找到该方法,该内存后来导致崩溃(例如在应用程序终止期间)。
尽量避免我现在决定称之为“新手分类狂”的东西。当Objective-C的新手发现类别时,他们常常会大吃一惊,向现有的每个类别添加有用的小类别(“什么?我可以添加一种方法,将数字转换为罗马数字,然后再转换为NSNumber!”)。
不要这样
在几十个基础类的基础上增加了数十种小类别方法,您的代码将更易于移植,更易于理解。
大多数时候,当您真的认为您需要一个类别方法来帮助简化某些代码时,您将发现您永远都不会重用该方法。
还有其他的危险,除非您为类别方法命名(除了完全疯狂的ddribin之外,还有谁?)Apple或插件或在地址空间中运行的其他东西也有可能定义相同的类别具有相同名称的方法,但副作用略有不同。
好。现在,您已经得到警告,请忽略“请勿执行此部分”。但是要保持克制。
抵抗继承世界。在Cocoa中,很多事情是通过委派和使用底层运行时完成的,而在其他框架中则是通过子类化完成的。
例如,在Java中,您经常使用匿名*Listener
子类的实例,而在.NET中,您EventArgs
经常使用子类的实例。在可可中,您都不执行任何操作,而是使用target-action。
对要呈现给用户的字符串进行排序时,不应使用简单的compare:
方法。相反,您应该始终使用本地化的比较方法,例如localizedCompare:
或localizedCaseInsensitiveCompare:
。
有关更多详细信息,请参见搜索,比较和排序字符串。
通常,您应该为所有属性使用“ Objective-C 2.0声明的属性”功能。如果它们不是公开的,则将它们添加到类扩展中。使用声明的属性可以使内存管理语义立即清晰,并使您更容易检查dealloc方法-如果将属性声明组合在一起,则可以快速对其进行扫描,并与dealloc方法的实现进行比较。
在不将属性标记为“非原子”之前,您应该认真考虑。正如《 Objective C编程语言指南》所述,默认情况下,属性是原子的,并且会产生相当大的开销。而且,仅使所有属性原子化并不能使应用程序具有线程安全性。当然,还请注意,如果您未指定“ nonatomic”并实现自己的访问器方法(而不是综合它们),则必须以原子方式实现它们。
使用NSAssert和朋友。我一直将nil用作有效对象...尤其是在Obj-C中向nil发送消息是完全有效的。但是,如果我真的想确定变量的状态,可以使用NSAssert和NSParameterAssert,这有助于轻松地查找问题。
简单但经常被遗忘的一个。根据规格:
通常,具有相同选择器(相同名称)的不同类中的方法还必须共享相同的返回和参数类型。该约束由编译器强加以允许动态绑定。
在这种情况下,即使是在不同的类中,所有相同的命名选择器也将被视为具有相同的返回/参数类型。这是一个简单的例子。
@interface FooInt:NSObject{}
-(int) print;
@end
@implementation FooInt
-(int) print{
return 5;
}
@end
@interface FooFloat:NSObject{}
-(float) print;
@end
@implementation FooFloat
-(float) print{
return 3.3;
}
@end
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
id f1=[[FooFloat alloc]init];
//prints 0, runtime considers [f1 print] to return int, as f1's type is "id" and FooInt precedes FooBar
NSLog(@"%f",[f1 print]);
FooFloat* f2=[[FooFloat alloc]init];
//prints 3.3 expectedly as the static type is FooFloat
NSLog(@"%f",[f2 print]);
[f1 release];
[f2 release]
[pool drain];
return 0;
}
如果您使用的是Leopard(Mac OS X 10.5)或更高版本,则可以使用Instruments应用程序查找并跟踪内存泄漏。在Xcode中构建程序后,选择“运行”>“从性能工具开始”>“泄漏”。
即使您的应用程序未显示任何泄漏,您也可能将对象放置的时间过长。在仪器中,您可以使用ObjectAlloc仪器。在“仪器”文档中选择“ ObjectAlloc”仪器,然后通过选择“视图”>“详细信息”(旁边应有一个复选标记)来调出该仪器的详细信息(如果尚未显示)。在ObjectAlloc详细信息的“分配寿命”下,确保选择“已创建并仍然存在”旁边的单选按钮。
现在,无论何时停止记录应用程序,选择ObjectAlloc工具都将在“#Net”列中为您的应用程序中的每个仍然存在的对象显示多少引用。确保您不仅查看自己的类,而且查看NIB文件的顶级对象的类。例如,如果屏幕上没有窗口,并且看到了对仍然有效的NSWindow的引用,则可能尚未在代码中释放它。
在dealloc中清理。
这是最容易忘记的事情之一-尤其是。当以150mph的速度编码时。总是,总是,总是在dealloc中清理您的属性/成员变量。
我喜欢使用Objc 2属性(带有新的点符号),因此使清理工作变得轻松自如。通常很简单:
- (void)dealloc
{
self.someAttribute = NULL;
[super dealloc];
}
这将为您解决发行问题,并将属性设置为NULL(我认为这是防御性编程,以防在dealloc之后出现的另一种方法再次访问成员变量,这种情况很少见,但有可能发生)。
在10.5中启用GC后,就不需要太多了-但是您可能仍需要清理创建的其他资源,可以改为使用finalize方法。
所有这些评论都很棒,但是我真的很惊讶没有人提到Google不久前发布的《 Objective-C风格指南》。我认为他们做得非常透彻。
另外,半相关的主题(还有更多的回响余地!):
对于初学者来说,一个相当明显的用途是:对代码使用Xcode的自动缩进功能。即使您是从其他来源复制/粘贴,粘贴代码后,也可以选择整个代码块,右键单击它,然后选择重新缩进该代码块中所有内容的选项。
Xcode实际上将解析该部分并根据方括号,循环等对其进行缩进。这比敲击每行的空格键或Tab键要高效得多。
我知道我第一次进入Cocoa编程时就忽略了这一点。
确保您了解有关NIB文件的内存管理职责。您有责任在所加载的任何NIB文件中释放顶级对象。阅读有关该主题的Apple文档。
打开所有GCC警告,然后关闭由Apple标头引起的常规警告,以减少噪音。
也要经常运行Clang静态分析;您可以通过“运行静态分析器”构建设置为所有构建启用它。
编写单元测试,并在每次构建时运行它们。
变量和属性
1 /保持标题干净,隐藏实现
不要在标题中包含实例变量。私有变量作为属性放入类的延续中。公共变量在标头中声明为公共属性。如果应该只读取它,则在类延续中将其声明为readonly并将其覆盖为readwrite。基本上我根本不使用变量,仅使用属性。
2 /给您的属性一个非默认变量名,例如:
@synthesize property = property_;
原因1:您会发现由于忘记“自我”而导致的错误。分配属性时。原因2:根据我的实验,Instruments中的Leak Analyzer难以检测具有默认名称的泄漏属性。
3 /切勿直接在属性上使用保留或释放(或仅在非常特殊的情况下)。在您的dealloc中,只需将它们分配为零即可。保留属性旨在自行处理保留/释放。您永远不会知道设置器是否不是例如添加或删除观察者。您应该仅在其setter和getter内部直接使用该变量。
观看次数
1 /如果可以的话,将每个视图定义放入xib(通常是动态内容和图层设置)。它可以节省时间(比编写代码容易),易于更改并且可以保持代码干净。
2 /不要尝试通过减少视图数量来优化视图。不要仅在您的代码中创建UIImageView而是因为要向其中添加子视图。请改用UIImageView作为背景。视图框架可以处理数百个视图而不会出现问题。
3 / IBOutlets不必总是保留(或保留)。请注意,大多数IBOutlets是视图层次结构的一部分,因此被隐式保留。
4 /在viewDidUnload中释放所有IBOutlets
5 /从您的dealloc方法中调用viewDidUnload。它不是隐式调用的。
记忆
1 /创建对象时自动释放它们。许多错误是由于将发布调用移至一个if-else分支或return语句之后引起的。仅在特殊情况下才应使用释放而不是自动释放,例如,在等待运行循环并且您不希望对象过早地自动释放时。
2 /即使您正在使用自动参考计数,您也必须完全理解保留释放方法的工作方式。手动使用保留释放并不比ARC复杂,在两种情况下,您都必须考虑泄漏和保留周期。考虑在大型项目或复杂的对象层次结构上手动使用保留释放。
注释
1 /使您的代码自动记录在案。每个变量名和方法名都应说明其作用。如果代码编写正确(您需要进行大量练习),则无需任何代码注释(与文档注释不同)。算法可能很复杂,但是代码应该总是很简单。
2 /有时,您需要发表评论。通常用来描述不明显的代码行为或黑客行为。如果您觉得必须编写注释,请首先尝试重写代码,以使其更简单并且不需要注释。
缩进
1 /不要增加太多的缩进。您的大多数方法代码应在方法级别缩进。嵌套块(如果是等)会降低可读性。如果您有三个嵌套块,则应尝试将内部块放入单独的方法中。永远不要使用四个或更多的嵌套块。如果大多数方法代码都在if中,则取反if条件,例如:
if (self) {
//... long initialization code ...
}
return self;
if (!self) {
return nil;
}
//... long initialization code ...
return self;
了解C代码,主要是C结构
请注意,Obj-C只是C语言之上的轻型OOP层。您应该了解C语言中的基本代码结构如何工作(枚举,结构,数组,指针等)。例:
view.frame = CGRectMake(view.frame.origin.x, view.frame.origin.y, view.frame.size.width, view.frame.size.height + 20);
是相同的:
CGRect frame = view.frame;
frame.size.height += 20;
view.frame = frame;
还有很多
维护自己的编码标准文档并经常进行更新。尝试从您的错误中学习。了解为什么会创建错误,并尝试使用编码标准来避免它。
目前,我们的编码标准大约有20页,混合了Java编码标准,Google Obj-C / C ++标准和我们自己的新增内容。记录您的代码,在正确的地方使用标准的标准缩进,空格和空白行等。
更具功能性。
Objective-C是一种面向对象的语言,但是Cocoa框架支持功能样式,并且在许多情况下被设计为功能样式。
变异性是分离的。使用不可变类作为主要对象,使用可变对象作为辅助对象。例如,主要使用NSArray,仅在需要时使用NSMutableArray。
有纯函数。并不是很多,购买的框架API的设计就像纯函数一样。查看诸如CGRectMake()
或的函数CGAffineTransformMake()
。显然指针形式看起来更有效。但是,带有指针的间接参数不能提供无副作用的功能。尽可能纯粹地设计结构。分离偶数状态对象。在将值传递给其他对象时使用-copy
代替-retain
。因为共享状态可以静默地影响其他对象中价值的变异。因此不能无副作用。如果您有来自object的external值,请复制它。因此,将共享状态设计得尽可能小也很重要。
但是,也不要害怕使用不纯函数。
懒惰的评价。看到像-[UIViewController view]
财产。创建对象时将不会创建视图。呼叫者view
首次读取属性时将创建它。UIImage
在实际绘制之前不会加载。有许多类似这种设计的实现。这种设计对资源管理非常有帮助,但是如果您不了解惰性评估的概念,那么理解它们的行为就不容易了。
有关闭。尽可能使用C块。这将大大简化您的生活。但是在使用它之前,请再次阅读有关块内存管理的信息。
有半自动GC。NSAutoreleasePool。使用-autorelease
主要的。-retain/-release
实际需要时,请使用手动辅助。(例如:内存优化,显式资源删除)
autorelease
通常会保留更长的retain/release
内存,在这种情况下,手动可以减少内存消耗。但是,它应该作为特殊情况优化的指南(即使您总是感觉不到!),也不是将过早优化归纳为实践的原因。实际上,您的建议与我并不相反。我提到它是