ios 您在编写 Objective-C 和 Cocoa 时使用的最佳实践是什么?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/155964/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What are best practices that you use when writing Objective-C and Cocoa?
提问by pixel
回答by Kendall Helmstetter Gelner
There are a few things I have started to do that I do not think are standard:
我已经开始做一些我认为不标准的事情:
1) With the advent of properties, I no longer use "_" to prefix "private" class variables. After all, if a variable can be accessed by other classes shouldn't there be a property for it? I always disliked the "_" prefix for making code uglier, and now I can leave it out.
1)随着属性的出现,我不再使用“_”作为“私有”类变量的前缀。毕竟,如果一个变量可以被其他类访问,不应该有它的属性吗?我总是不喜欢“_”前缀使代码更丑,现在我可以省略它。
2) Speaking of private things, I prefer to place private method definitions within the .m file in a class extension like so:
2)说到私有的东西,我更喜欢将私有方法定义放在 .m 文件中的类扩展名中,如下所示:
#import "MyClass.h"
@interface MyClass ()
- (void) someMethod;
- (void) someOtherMethod;
@end
@implementation MyClass
Why clutter up the .h file with things outsiders should not care about? The empty () works for private categories in the .m file, and issues compile warnings if you do not implement the methods declared.
为什么将 .h 文件与外人不应该关心的东西混在一起?空 () 适用于 .m 文件中的私有类别,如果您没有实现声明的方法,则会发出编译警告。
3) I have taken to putting dealloc at the top of the .m file, just below the @synthesize directives. Shouldn't what you dealloc be at the top of the list of things you want to think about in a class? That is especially true in an environment like the iPhone.
3)我已经把dealloc放在.m文件的顶部,就在@synthesize指令的下面。你在课堂上想考虑的事情列表的顶部不应该是你dealloc的内容吗?在像 iPhone 这样的环境中尤其如此。
3.5) In table cells, make every element (including the cell itself) opaque for performance. That means setting the appropriate background color in everything.
3.5)在表格单元格中,使每个元素(包括单元格本身)不透明以提高性能。这意味着在所有内容中设置适当的背景颜色。
3.6) When using an NSURLConnection, as a rule you may well want to implement the delegate method:
3.6) 当使用 NSURLConnection 时,通常你可能想要实现委托方法:
- (NSCachedURLResponse *)connection:(NSURLConnection *)connection
willCacheResponse:(NSCachedURLResponse *)cachedResponse
{
return nil;
}
I find most web calls are very singular and it's more the exception than the rule you'll be wanting responses cached, especially for web service calls. Implementing the method as shown disables caching of responses.
我发现大多数 Web 调用都非常单一,这比您希望缓存响应的规则更例外,尤其是对于 Web 服务调用。实现如图所示的方法会禁用响应缓存。
Also of interest, are some good iPhone specific tips from Joseph Mattiello (received in an iPhone mailing list). There are more, but these were the most generally useful I thought (note that a few bits have now been slightly edited from the original to include details offered in responses):
同样有趣的是,约瑟夫·马蒂洛 (Joseph Mattiello) 提供了一些很好的 iPhone 特定技巧(在 iPhone 邮件列表中收到)。还有更多,但这些是我认为最有用的(请注意,现在对原始内容略有编辑,以包括回复中提供的详细信息):
4) Only use double precision if you have to, such as when working with CoreLocation. Make sure you end your constants in 'f' to make gcc store them as floats.
4) 仅在必要时使用双精度,例如在使用 CoreLocation 时。确保以 'f' 结束常量以使 gcc 将它们存储为浮点数。
float val = someFloat * 2.2f;
This is mostly important when someFloat
may actually be a double, you don't need the mixed-mode math, since you're losing precision in 'val' on storage. While floating-point numbers are supported in hardware on iPhones, it may still take more time to do double-precision arithmetic as opposed to single precision. References:
这在someFloat
实际上可能是双精度数时非常重要,您不需要混合模式数学,因为您在存储中失去了 'val' 的精度。虽然 iPhone 上的硬件支持浮点数,但与单精度相比,执行双精度算术可能仍需要更多时间。参考:
On the older phones supposedly calculations operate at the same speed but you can have more single precision components in registers than doubles, so for many calculations single precision will end up being faster.
在较旧的手机上,据说计算以相同的速度运行,但寄存器中的单精度组件可以多于双精度,因此对于许多计算,单精度最终会更快。
5) Set your properties as nonatomic
. They're atomic
by default and upon synthesis, semaphore code will be created to prevent multi-threading problems. 99% of you probably don't need to worry about this and the code is much less bloated and more memory-efficient when set to nonatomic.
5) 将您的属性设置为nonatomic
. 他们是atomic
在默认情况下并在合成信号代码将会被创建,以防止多线程问题。99% 的人可能不需要担心这个,当设置为 nonatomic 时,代码不会那么臃肿并且内存效率更高。
6) SQLite can be a very, very fast way to cache large data sets. A map application for instance can cache its tiles into SQLite files. The most expensive part is disk I/O. Avoid many small writes by sending BEGIN;
and COMMIT;
between large blocks. We use a 2 second timer for instance that resets on each new submit. When it expires, we send COMMIT; , which causes all your writes to go in one large chunk. SQLite stores transaction data to disk and doing this Begin/End wrapping avoids creation of many transaction files, grouping all of the transactions into one file.
6) SQLite 可以是一种非常非常快的缓存大型数据集的方法。例如,地图应用程序可以将其切片缓存到 SQLite 文件中。最昂贵的部分是磁盘 I/O。通过在大块之间发送BEGIN;
和避免许多小写COMMIT;
。例如,我们使用一个 2 秒的计时器,它会在每次新提交时重置。当它到期时,我们发送 COMMIT;,这会导致您的所有写入都集中在一个大块中。SQLite 将事务数据存储到磁盘,并且执行此开始/结束包装可避免创建许多事务文件,将所有事务分组到一个文件中。
Also, SQL will block your GUI if it's on your main thread. If you have a very long query, It's a good idea to store your queries as static objects, and run your SQL on a separate thread. Make sure to wrap anything that modifies the database for query strings in @synchronize() {}
blocks. For short queries just leave things on the main thread for easier convenience.
此外,如果 SQL 在您的主线程上,它会阻塞您的 GUI。如果您有一个很长的查询,最好将您的查询存储为静态对象,并在单独的线程上运行您的 SQL。确保在@synchronize() {}
块中包装任何修改数据库的查询字符串。对于简短的查询,只需将事情留在主线程上即可更方便。
More SQLite optimization tips are here, though the document appears out of date many of the points are probably still good;
更多 SQLite 优化技巧在这里,虽然文档看起来已经过时了,但很多点可能仍然很好;
http://web.utk.edu/~jplyon/sqlite/SQLite_optimization_FAQ.html
http://web.utk.edu/~jplyon/sqlite/SQLite_optimization_FAQ.html
回答by mmalc
Don't use unknown strings as format strings
不要使用未知字符串作为格式字符串
When methods or functions take a format string argument, you should make sure that you have control over the content of the format string.
当方法或函数采用格式字符串参数时,您应该确保您可以控制格式字符串的内容。
For example, when logging strings, it is tempting to pass the string variable as the sole argument to NSLog
:
例如,在记录字符串时,很容易将字符串变量作为唯一参数传递给NSLog
:
NSString *aString = // get a string from somewhere;
NSLog(aString);
The problem with this is that the string may contain characters that are interpreted as format strings. This can lead to erroneous output, crashes, and security problems. Instead, you should substitute the string variable into a format string:
问题在于该字符串可能包含被解释为格式字符串的字符。这可能会导致错误输出、崩溃和安全问题。相反,您应该将字符串变量替换为格式字符串:
NSLog(@"%@", aString);
回答by Chris Hanson
Use standard Cocoa naming and formatting conventions and terminology rather than whatever you're used to from another environment. There arelots of Cocoa developers out there, and when another one of them starts working with your code, it'll be much more approachable if it looks and feels similar to other Cocoa code.
使用标准的 Cocoa 命名和格式约定和术语,而不是您在其他环境中习惯使用的任何内容。那里有很多 Cocoa 开发人员,当他们中的另一个人开始使用您的代码时,如果它的外观和感觉与其他 Cocoa 代码相似,它将更加平易近人。
Examples of what to do and what not to do:
做什么和不做什么的例子:
- Don't declare
id m_something;
in an object's interface and call it a member variableor field; usesomething
or_something
for its name and call it an instance variable. - Don't name a getter
-getSomething
; the proper Cocoa name is just-something
. - Don't name a setter
-something:
; it should be-setSomething:
- The method name is interspersed with the arguments and includes colons; it's
-[NSObject performSelector:withObject:]
, notNSObject::performSelector
. - Use inter-caps (CamelCase) in method names, parameters, variables, class names, etc. rather than underbars (underscores).
- Class names start with an upper-case letter, variable and method names with lower-case.
- 不要
id m_something;
在对象的接口中声明并将其称为成员变量或字段;使用something
或_something
作为其名称并将其称为实例变量。 - 不要命名吸气剂
-getSomething
;正确的可可名称是-something
. - 不要命名一个 setter
-something:
;它应该是-setSomething:
- 方法名称中穿插着参数并包含冒号;是
-[NSObject performSelector:withObject:]
,不是NSObject::performSelector
。 - 在方法名称、参数、变量、类名称等中使用大写字母 (CamelCase) 而不是下划线(下划线)。
- 类名以大写字母开头,变量名和方法名以小写字母开头。
Whatever else you do, don'tuse Win16/Win32-style Hungarian notation. Even Microsoft gave up on that with the move to the .NET platform.
无论您做什么,都不要使用 Win16/Win32 风格的匈牙利表示法。甚至微软也放弃了这一点,转向了 .NET 平台。
回答by mmalc
IBOutlets
IB奥特莱斯
Historically, memory management of outlets has been poor. Current best practice is to declare outlets as properties:
从历史上看,插座的内存管理一直很差。当前的最佳实践是将网点声明为属性:
@interface MyClass :NSObject {
NSTextField *textField;
}
@property (nonatomic, retain) IBOutlet NSTextField *textField;
@end
Using properties makes the memory management semantics clear; it also provides a consistent pattern if you use instance variable synthesis.
使用属性使内存管理语义清晰;如果您使用实例变量合成,它还提供了一致的模式。
回答by mmalc
Use the LLVM/Clang Static Analyzer
使用 LLVM/Clang 静态分析器
NOTE: Under Xcode 4 this is now built into the IDE.
注意:在 Xcode 4 下,这现在已内置到 IDE 中。
You use the Clang Static Analyzerto -- unsurprisingly -- analyse your C and Objective-C code (no C++ yet) on Mac OS X 10.5. It's trivial to install and use:
不出所料,您可以使用Clang 静态分析器在 Mac OS X 10.5 上分析 C 和 Objective-C 代码(目前还没有 C++)。安装和使用很简单:
- Download the latest version from this page.
- From the command-line,
cd
to your project directory. - Execute
scan-build -k -V xcodebuild
.
- 从此页面下载最新版本。
- 从命令行,
cd
到您的项目目录。 - 执行
scan-build -k -V xcodebuild
。
(There are some additional constraints etc., in particular you should analyze a project in its "Debug" configuration -- see http://clang.llvm.org/StaticAnalysisUsage.htmlfor details -- the but that's more-or-less what it boils down to.)
(还有一些额外的约束等,特别是你应该在它的“调试”配置中分析一个项目——有关详细信息,请参见http://clang.llvm.org/StaticAnalysisUsage.html——但这或多或少它归结为什么。)
The analyser then produces a set of web pages for you that shows likely memory management and other basic problems that the compiler is unable to detect.
然后,分析器为您生成一组网页,显示可能的内存管理和编译器无法检测到的其他基本问题。
回答by schwa
This is subtle one but handy one. If you're passing yourself as a delegate to another object, reset that object's delegate before you dealloc
.
这是一种微妙但方便的方法。如果您将自己作为委托传递给另一个对象,请在您之前重置该对象的委托dealloc
。
- (void)dealloc
{
self.someObject.delegate = NULL;
self.someObject = NULL;
//
[super dealloc];
}
By doing this you're ensuring that no more delegate methods will get sent. As you're about to dealloc
and disappear into the ether you want to make sure that nothing can send you any more messages by accident. Remember self.someObject could be retained by another object (it could be a singleton or on the autorelease pool or whatever) and until you tell it "stop sending me messages!", it thinks your just-about-to-be-dealloced object is fair game.
通过这样做,您可以确保不再发送委托方法。当您即将dealloc
消失在以太中时,您要确保没有任何东西可以意外地向您发送更多消息。记住 self.someObject 可以被另一个对象保留(它可以是单例或自动释放池或其他任何东西),直到你告诉它“停止向我发送消息!”,它认为你即将被释放的对象是公平的游戏。
Getting into this habit will save you from lots of weird crashes that are a pain to debug.
养成这种习惯将使您免于发生许多难以调试的奇怪崩溃。
The same principal applies to Key Value Observation, and NSNotifications too.
同样的原则也适用于 Key Value Observation 和 NSNotifications。
Edit:
编辑:
Even more defensive, change:
更具防御性,更改:
self.someObject.delegate = NULL;
into:
进入:
if (self.someObject.delegate == self)
self.someObject.delegate = NULL;
回答by schwa
@kendell
@肯德尔
Instead of:
代替:
@interface MyClass (private)
- (void) someMethod
- (void) someOtherMethod
@end
Use:
用:
@interface MyClass ()
- (void) someMethod
- (void) someOtherMethod
@end
New in Objective-C 2.0.
Objective-C 2.0 中的新功能。
Class extensions are described in Apple's Objective-C 2.0 Reference.
Apple 的 Objective-C 2.0 参考中描述了类扩展。
"Class extensions allow you to declare additional required API for a class in locations other than within the primary class @interface block"
“类扩展允许您在主类 @interface 块以外的位置为类声明额外的必需 API”
So they're part of the actual class - and NOT a (private) category in addition to the class. Subtle but important difference.
所以它们是实际课程的一部分 - 而不是课程之外的(私人)类别。细微但重要的区别。
回答by mmalc
Avoid autorelease
避免自动释放
Since you typically(1) don't have direct control over their lifetime, autoreleased objects can persist for a comparatively long time and unnecessarily increase the memory footprint of your application. Whilst on the desktop this may be of little consequence, on more constrained platforms this can be a significant issue. On all platforms, therefore, and especially on more constrained platforms, it is considered best practice to avoid using methods that would lead to autoreleased objects and instead you are encouraged to use the alloc/init pattern.
由于您通常 (1) 无法直接控制它们的生命周期,因此自动释放的对象可能会持续较长时间并不必要地增加应用程序的内存占用。虽然在桌面上这可能无关紧要,但在更受限制的平台上这可能是一个重大问题。因此,在所有平台上,尤其是在更受限制的平台上,避免使用会导致自动释放对象的方法被认为是最佳实践,而是鼓励您使用 alloc/init 模式。
Thus, rather than:
因此,而不是:
aVariable = [AClass convenienceMethod];
where able, you should instead use:
在可能的情况下,您应该改为使用:
aVariable = [[AClass alloc] init];
// do things with aVariable
[aVariable release];
When you're writing your own methods that return a newly-created object, you can take advantage of Cocoa's naming conventionto flag to the receiver that it must be released by prepending the method name with "new".
当您编写自己的返回新创建对象的方法时,您可以利用Cocoa 的命名约定通过在方法名称前加上“new”来向接收者标记它必须被释放。
Thus, instead of:
因此,而不是:
- (MyClass *)convenienceMethod {
MyClass *instance = [[[self alloc] init] autorelease];
// configure instance
return instance;
}
you could write:
你可以写:
- (MyClass *)newInstance {
MyClass *instance = [[self alloc] init];
// configure instance
return instance;
}
Since the method name begins with "new", consumers of your API know that they're responsible for releasing the received object (see, for example, NSObjectController's newObject
method).
由于方法名称以“new”开头,API 的使用者知道他们负责释放接收到的对象(例如,参见NSObjectController 的newObject
方法)。
(1) You can take control by using your own local autorelease pools. For more on this, see Autorelease Pools.
(1) 您可以使用自己的本地自动释放池来控制。有关更多信息,请参阅自动释放池。
回答by Marc Charbonneau
Some of these have already been mentioned, but here's what I can think of off the top of my head:
其中一些已经提到过,但这是我能想到的:
- Follow KVO naming rules.Even if you don't use KVO now, in my experience often times it's still beneficial in the future. And if you are using KVO or bindings, you need to know things are going work the way they are supposed to. This covers not just accessor methods and instance variables, but to-many relationships, validation, auto-notifying dependent keys, and so on.
- Put private methods in a category.Not just the interface, but the implementation as well. It's good to have some distance conceptually between private and non-private methods. I include everything in my .m file.
- Put background thread methods in a category.Same as above. I've found it's good to keep a clear conceptual barrier when you're thinking about what's on the main thread and what's not.
- Use
#pragma mark [section]
.Usually I group by my own methods, each subclass's overrides, and any information or formal protocols. This makes it a lot easier to jump to exactly what I'm looking for. On the same topic, group similar methods (like a table view's delegate methods) together, don't just stick them anywhere. - Prefix private methods & ivars with _.I like the way it looks, and I'm less likely to use an ivar when I mean a property by accident.
- Don't use mutator methods / properties in init & dealloc.I've never had anything bad happen because of it, but I can see the logic if you change the method to do something that depends on the state of your object.
- Put IBOutlets in properties.I actually just read this one here, but I'm going to start doing it. Regardless of any memory benefits, it seems better stylistically (at least to me).
- Avoid writing code you don't absolutely need.This really covers a lot of things, like making ivars when a
#define
will do, or caching an array instead of sorting it each time the data is needed. There's a lot I could say about this, but the bottom line is don't write code until you need it, or the profiler tells you to. It makes things a lot easier to maintain in the long run. - Finish what you start.Having a lot of half-finished, buggy code is the fastest way to kill a project dead. If you need a stub method that's fine, just indicate it by putting
NSLog( @"stub" )
inside, or however you want to keep track of things.
- 遵循 KVO 命名规则。即使你现在不使用 KVO,根据我的经验,很多时候它在未来仍然是有益的。如果您正在使用 KVO 或绑定,您需要知道事情按预期进行。这不仅包括访问器方法和实例变量,还包括对多关系、验证、自动通知依赖键等。
- 将私有方法放在一个类别中。不仅仅是接口,还有实现。在私有方法和非私有方法之间在概念上保持一定距离是很好的。我将所有内容都包含在我的 .m 文件中。
- 将后台线程方法放在一个类别中。和上面一样。我发现当您考虑主线程上的内容和不包含的内容时,最好保持清晰的概念障碍。
- 使用
#pragma mark [section]
. 通常我按我自己的方法、每个子类的覆盖以及任何信息或正式协议进行分组。这使得跳转到我正在寻找的内容变得更加容易。在同一主题上,将相似的方法(如表视图的委托方法)组合在一起,不要只是将它们粘贴在任何地方。 - 用 _ 前缀私有方法和变量。我喜欢它的外观,当我无意中指的是财产时,我不太可能使用 ivar。
- 不要在 init 和 dealloc 中使用 mutator 方法/属性。我从来没有因为它发生过任何不好的事情,但是如果你改变方法来做一些取决于你的对象状态的事情,我可以看到逻辑。
- 将 IBOutlets 放在属性中。我实际上只是在这里阅读了这个,但我将开始这样做。不管有什么记忆好处,它在风格上似乎更好(至少对我而言)。
- 避免编写您绝对不需要的代码。这确实涵盖了很多事情,例如在需要时创建 ivars
#define
,或者缓存数组而不是每次需要数据时对其进行排序。关于这个我可以说很多,但最重要的是不要在需要之前编写代码,或者分析器告诉你。从长远来看,它使事情更容易维护。 - 完成你开始的事情。拥有大量半成品、有缺陷的代码是杀死项目的最快方法。如果你需要一个很好的存根方法,只需通过放入
NSLog( @"stub" )
里面来指示它,或者你想跟踪事情。
回答by Chris Hanson
Write unit tests. You can test a lotof things in Cocoa that might be harder in other frameworks. For example, with UI code, you can generally verify that things are connected as they should be and trust that they'll work when used. And you can set up state & invoke delegate methods easily to test them.
编写单元测试。您可以在 Cocoa 中测试很多在其他框架中可能更难的东西。例如,使用 UI 代码,您通常可以验证事物是否按应有的方式连接,并相信它们在使用时会起作用。您可以轻松设置状态并调用委托方法来测试它们。
You also don't have public vs. protected vs. private method visibility getting in the way of writing tests for your internals.
您也没有公共与受保护与私有方法的可见性妨碍为您的内部编写测试。