我知道HIG(这非常方便!),但是在编写Objective-C时,更具体地说,在使用Cocoa(或CocoaTouch)时,您使用了什么编程实践?


当前回答

我已经开始做的一些事情,我认为不是标准的:

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实际上可能是double类型时,这是非常重要的,你不需要混合模式数学,因为你在存储上失去了'val'的精度。虽然iphone的硬件支持浮点数,但与单精度相比,执行双精度算术可能仍需要更多时间。引用:

iPhone上的Double vs float iPhone/iPad双精度数学

在老式手机上,计算的速度应该是相同的,但你可以在寄存器中使用更多的单精度组件,所以对于许多计算来说,单精度最终会更快。

5)设置你的属性为非原子的。默认情况下,它们是原子的,在合成时,将创建信号量代码以防止多线程问题。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.

此外,如果GUI在主线程上,SQL将阻塞它。如果你有一个很长的查询,最好将查询存储为静态对象,并在单独的线程上运行SQL。确保将修改数据库查询字符串的任何内容包装在@synchronize(){}块中。对于简短的查询,为了更方便,只需将内容留在主线程中。

更多的SQLite优化技巧在这里,虽然文档看起来过时了,但许多要点可能仍然是好的;

http://web.utk.edu/~jplyon/sqlite/SQLite_optimization_FAQ.html

其他回答

变量和属性

1/保持头文件整洁,隐藏实现 不要在头文件中包含实例变量。作为属性放入类延续中的私有变量。公共变量在头文件中声明为公共属性。 如果它应该是只读的,将其声明为readonly,并在类延续中将其重写为readwrite。 基本上我根本不用变量,只用属性。

2/给你的属性一个非默认变量名,例如:


@synthesize property = property_;

原因1:在分配属性时,您将捕获由于忘记“self.”而引起的错误。 原因2:从我的实验中,仪器中的泄漏分析仪在检测默认名称的泄漏属性时存在问题。

3/不要在属性上直接使用retain或release(或仅在非常特殊的情况下)。在dealloc中,给它们分配一个nil。保留属性意味着自己处理保留/释放。例如,您永远不知道setter是否没有添加或删除观察器。您应该只在它的setter和getter中直接使用变量。

的观点

1/如果可以的话,将每个视图定义放入xib中(例外通常是动态内容和层设置)。它节省时间(比写代码简单),易于更改,并保持代码整洁。

2/不要试图通过减少视图数量来优化视图。不要在代码中创建UIImageView而不是xib,因为你想在其中添加子视图。使用UIImageView作为背景。视图框架可以毫无问题地处理数百个视图。

3/ iboutlet不需要总是被保留(或强大)。注意,大多数iboutlet都是视图层次结构的一部分,因此隐式保留。

4/释放viewDidUnload中的所有iboutlet

5/从dealloc方法调用viewDidUnload。它不是隐式调用的。

内存

1/创建对象时自动释放对象。许多错误是由于将释放调用移动到一个if-else分支或在返回语句之后引起的。释放而不是自动释放应该只在特殊情况下使用——例如,当你在等待一个运行循环时,你不想让你的对象过早被自动释放。

2/即使你在使用自动引用计数,你也必须完全理解保留-释放方法是如何工作的。手动使用保留-释放并不比ARC更复杂,在这两种情况下,您都必须考虑泄漏和保留周期。 考虑在大型项目或复杂的对象层次结构上手动使用保留释放。

评论

1/让你的代码自动文档化。 每个变量名和方法名都应该说明它在做什么。如果代码编写正确(这方面需要大量实践),则不需要任何代码注释(与文档注释不同)。算法可能很复杂,但代码应该总是简单的。

2/有时候,你需要别人的评论。通常用来描述一种不明显的代码行为或黑客行为。如果您觉得必须写注释,首先尝试重写代码,使其更简单,不需要注释。

缩进

1/不要增加太多缩进。 大多数方法代码应该在方法级别上缩进。嵌套块(if, for等)降低了可读性。如果您有三个嵌套块,您应该尝试将内部块放入一个单独的方法中。四个或更多嵌套的块永远不应该使用。 如果大部分方法代码都在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编码标准,谷歌Obj-C/ c++标准和我们自己的添加。记录你的代码,在正确的地方使用标准的缩进,空格和空行等等。

对于初学者来说,一个很明显的方法是:利用Xcode的自动缩进功能。即使是从其他源复制/粘贴代码,粘贴完代码后,也可以选择整个代码块,右键单击它,然后选择选项重新缩进该代码块中的所有内容。

Xcode会解析这个section并根据括号,循环等缩进它。这比每一行都按空格键或tab键要有效率得多。

在dealloc中清理。

这是最容易忘记的事情之一——尤其是在以150英里/小时的速度编码时。总是,总是,总是清理dealloc中的属性/成员变量。

我喜欢使用Objc 2属性-新的点表示法-所以这使得清理变得无痛。通常很简单:

- (void)dealloc
{
    self.someAttribute = NULL;
    [super dealloc];
}

这将为您处理发布,并将属性设置为NULL(我认为这是防御性编程——以防dealloc中的另一个方法再次访问成员变量——很少,但也可能发生)。

在10.5中打开GC后,就不再需要这么多了——但是你可能仍然需要清理你创建的其他资源,你可以在finalize方法中做这件事。

只释放dealloc方法中的属性。如果你想释放属性所持有的内存,只需将其设置为nil:

self.<property> = nil;

我已经开始做的一些事情,我认为不是标准的:

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实际上可能是double类型时,这是非常重要的,你不需要混合模式数学,因为你在存储上失去了'val'的精度。虽然iphone的硬件支持浮点数,但与单精度相比,执行双精度算术可能仍需要更多时间。引用:

iPhone上的Double vs float iPhone/iPad双精度数学

在老式手机上,计算的速度应该是相同的,但你可以在寄存器中使用更多的单精度组件,所以对于许多计算来说,单精度最终会更快。

5)设置你的属性为非原子的。默认情况下,它们是原子的,在合成时,将创建信号量代码以防止多线程问题。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.

此外,如果GUI在主线程上,SQL将阻塞它。如果你有一个很长的查询,最好将查询存储为静态对象,并在单独的线程上运行SQL。确保将修改数据库查询字符串的任何内容包装在@synchronize(){}块中。对于简短的查询,为了更方便,只需将内容留在主线程中。

更多的SQLite优化技巧在这里,虽然文档看起来过时了,但许多要点可能仍然是好的;

http://web.utk.edu/~jplyon/sqlite/SQLite_optimization_FAQ.html