我了解HIG(非常方便!),但是在编写Objective-C时,尤其是在使用Cocoa(或CocoaTouch)时,会使用哪些编程实践。

评论

看到这篇博客文章,非常好。 ironwolf.dangerousgames.com/blog/archives/913

#1 楼

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

1)随着属性的出现,我不再使用“ _”作为“私有”类变量的前缀。毕竟,如果一个变量可以被其他类访问,那它应该没有属性吗?我一直不喜欢使代码更丑陋的“ _”前缀,现在可以忽略它了。像这样的类扩展:

#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时,通常您可能希望实现委托方法: >
我发现大多数Web调用都非常单一,这是例外,而不是您希望缓存响应的规则,尤其是对于Web服务调用。如所示实现该方法将禁用对响应的缓存。

有趣的是,约瑟夫·马蒂耶洛(Joseph Mattiello)提供了一些iPhone特有的技巧(在iPhone邮件列表中已收到)。还有更多,但是这些是我认为最有用的(请注意,现在已经对原始内容进行了一些编辑,以包括响应中提供的详细信息):4)仅在需要时才使用双精度,例如在使用CoreLocation时。请确保将常量以'f'结尾,以使gcc将其存储为浮点数。不需要混合模式数学运算,因为您将失去存储中“ val”的精度。尽管iPhone的硬件中支持浮点数,但与单精度相反,执行双精度算术仍可能需要更多时间。参考:


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

在旧手机上,计算速度相同,但您可以在寄存器中使用比单精度更多的单精度分量,因此对于许多计算而言,单精度最终会更快。

5)将属性设置为someFloat。它们默认为nonatomic,在综合后,将创建信号量代码以防止出现多线程问题。你们中的99%可能不必担心这一点,并且将代码设置为非原子时,代码的肿程度会大大降低,并且内存效率更高。

6)SQLite可以是一种非常非常快速的方法缓存大数据集。例如,地图应用程序可以将其图块缓存到SQLite文件中。最昂贵的部分是磁盘I / O。通过在大块之间发送atomicBEGIN;避免许多小写操作。例如,我们使用2秒钟的计时器在每次新提交时重置。当它到期时,我们发送COMMIT。 ,这会导致您的所有写操作分成大块。 SQLite将事务数据存储到磁盘,这样做开始/结束包装避免了创建多个事务文件,而是将所有事务分组到一个文件中。

同样,如果SQL在主线程上,它将阻止您的GUI。如果查询时间很长,最好将查询存储为静态对象,然后在单独的线程上运行SQL。确保将所有修改数据库的内容包装在COMMIT;块中。对于简短查询,只需将内容留在主线程上以便于使用,以方便使用。 br />http://web.utk.edu/~jplyon/sqlite/SQLite_optimization_FAQ.html

评论


关于双精度算法的小技巧。

–亚当·恩斯特(Adam Ernst)
08年10月6日在17:05

现在,类扩展是私有方法的首选方式:developer.apple.com/Mac/library/documentation/Cocoa/Conceptual/…

– Casebash
2010-2-3在2:06

您关于iPhone上的双打的建议已过时stackoverflow.com/questions/1622729/…

– Casebash
2010-2-3在2:07

不会过时;完全错误:最初支持的iPhone在硬件上以大约相同的速度浮动并翻倍。 SQLite也不将事务保存在内存中;它们记录在磁盘上。只有长查询会阻塞您的用户界面;在主线程中运行所有内容并使用更快的查询,不会太麻烦。

–tc。
2010-09-23的1:26

@tc:我更正了有关事务的SQL项,请注意,我本人没有写最后四个左右的项。我还澄清了有关将查询移至后台的部分仅适用于非常长的查询(有时您不能使查询更短)。但是由于几点,把整个事情称为“错误”,我感到很极端。同样,上面的回答已经说明:“在较旧的电话上,计算速度应该相同”,但是请注意有关单精度寄存器数量较多的部分,因此它们仍然是可取的。

–肯德尔·赫尔姆斯特·盖尔纳(Kendall Helmstetter Gelner)
2010-09-23在7:50



#2 楼

不要将未知字符串用作格式字符串

当方法或函数采用格式字符串参数时,应确保对格式字符串的内容具有控制权。

例如,在记录字符串时,很容易将字符串变量作为唯一参数传递给NSLog

    NSString *aString = // get a string from somewhere;
    NSLog(aString);


问题是字符串可能包含解释为格式字符串的字符。这可能导致错误的输出,崩溃和安全问题。相反,您应该将字符串变量替换为格式字符串:

    NSLog(@"%@", aString);


评论


我以前被这个咬过。

–亚当·恩斯特(Adam Ernst)
08-10-6在17:03

这对于任何编程语言都是很好的建议

–汤姆·佛伯(Tom Fobear)
2011年5月26日20:30

#3 楼

使用标准的可可命名和格式约定以及术语,而不要使用其他环境中常用的内容。有很多Cocoa开发人员,当他们中的另一个开始使用您的代码时,如果外观和感觉与其他Cocoa代码相似,它将变得更加容易上手。做什么和不做什么:


不要在对象的接口中声明id m_something;并将其称为成员变量或字段;请使用something_something作为其名称,并将其称为实例变量。
不要命名getter -getSomething;正确的可可名称仅为-something
不要命名二传手-something:;它应该是-setSomething:

方法名称穿插在参数中并包含冒号;它是-[NSObject performSelector:withObject:]而不是NSObject::performSelector
在方法名称,参数,变量,类名称等中使用大写字母(CamelCase),而不要使用下划线(下划线)。
类名以大写字母开头,变量和方法名称都使用小写字母。

无论做什么,都不要使用Win16 / Win32风格的匈牙利表示法。甚至Microsoft也放弃了向.NET平台的迁移。

评论


我会争辩,根本不使用setSomething:/ something-而是使用属性。在这一点上,真正需要针对Tiger的人很少(不使用属性的唯一原因)

–肯德尔·赫尔姆斯特·盖尔纳(Kendall Helmstetter Gelner)
08-10-1在16:14

属性仍然为您生成访问器方法,并且属性上的getter = / setter =属性使您可以指定方法的名称。另外,您可以使用[foo something]语法代替带有属性的foo.something语法。因此,访问者命名仍然很重要。

–克里斯·汉森(Chris Hanson)
08-10-1 22:44

对于那些来自C ++的人来说,这是一个很好的参考,在C ++中我做了您建议的大多数事情。

–克林顿·布莱克莫尔
09年7月31日在23:17

设置器不应导致将某些内容保存到数据库。原因是Core Data在NSManagedObjectContext上具有-save:方法,而不是让setter产生立即更新的原因。

–克里斯·汉森(Chris Hanson)
09年12月18日在9:28

我怀疑这不是一个选择,但是它可能需要重新访问您的应用程序体系结构。 (要清楚:我不是说“您应该使用Core Data。”我是说“ Setter不应该保存到数据库。”)具有上下文来管理对象图,而不是在其中保存单个对象,几乎总是可能的,并且是更好的解决方案。

–克里斯·汉森(Chris Hanson)
2010年1月12日于20:51

#4 楼

IBOutlets

从历史上看,插座的内存管理很差。
当前的最佳实践是将插座声明为属性:

使用属性可以使内存管理语义清晰明了;如果使用实例变量综合,它还会提供一致的模式。

评论


装入笔尖不会保留两次吗? (一次出现笔尖,其次是分配给属性)。我应该释放那些在dealloc中的吗?

–内核
09年6月17日在9:40

您必须在viewDidUnload(iPhone OS 3.0+)或自定义setView:方法中将出口设置为零,以避免泄漏。显然,您也应该在dealloc中释放。

–弗兰克·斯切尔巴(Frank Szczerba)
09年7月10日在18:15

请记住,并非所有人都同意这种风格:weblog.bignerdranch.com/?p=95

–迈克尔
09年8月17日在7:48

苹果也是这样做的方式。 “ iPhone 3的开始开发”也提到了从以前的版本开始的这一变化。

–ustun
09-10-14在18:04

我在另一条评论中提到了这一点,但应该将其放在此处:一旦iOS应用程序开始进行动态ivar综合(如果/何时?),您会很高兴将IBOutlet放在属性与ivar之间!

–乔·安德烈(Joe D'Andrea)
2010年8月30日15:48

#5 楼

使用LLVM / Clang静态分析器

注意:在Xcode 4下,它现在已内置在IDE中。 Mac OS X 10.5上的C和Objective-C代码(尚无C ++)。安装和使用起来很简单:


从此页面下载最新版本。
从命令行cd到您的项目目录。
执行scan-build -k -V xcodebuild

(还有一些其他限制,等等,特别是您应该以“调试”配置分析项目-有关详细信息,请参见http://clang.llvm.org/StaticAnalysisUsage.html然后,分析器为您生成了一组网页,这些网页显示了可能的内存管理和编译器无法检测到的其他基本问题。 。

评论


在按照以下说明进行操作之前,我遇到了一些麻烦:oiledmachine.com/posts/2009/01/06/…

–棕
09年5月11日23:36

在Snow Leopard上的XCode 3.2.1中,它已经内置。您可以使用“运行”->“构建和分析”手动运行它,也可以通过“运行静态分析器”构建设置为所有构建启用它。请注意,该工具目前仅支持C和Objective-C,但不支持C ++ / Objective-C ++。

– oefe
09-10-18在18:03

#6 楼

这是一个微妙的但方便的一。如果要将自己作为委托传递给另一个对象,请在重设对象之前重新设置该对象的委托。将被发送。当您即将进入dealloc并消失在以太坊中时,您想要确保没有任何东西可以意外地向您发送更多消息。记住self.someObject可能被另一个对象(可能是单例对象或在自动释放池中或其他对象)保留,直到您告诉它“停止向我发送消息!”,它才会认为您即将被释放的对象这是公平的游戏。

养成这种习惯可以使您免于调试时很难经历的怪异崩溃。

编辑:

更加防御,更改:

- (void)dealloc
{
self.someObject.delegate = NULL;
self.someObject = NULL;
//
[super dealloc];
}


到: br />
self.someObject.delegate = NULL;


评论


这没有什么微妙之处,文档明确指出您必须执行此操作。摘自《 Cocoa的内存管理编程指南》:Cocoa中弱引用的其他情况包括但不限于表数据源,大纲视图项,通知观察者以及其他目标和委托。在大多数情况下,弱引用对象知道另一个对象对其的弱引用,就像循环引用一样,并负责在解除分配时通知另一个对象。

–约翰
09年7月21日在4:06

最好使用nil而不是NULL,因为NULL不会释放内存。

– Naveen Shan
2011年10月10日,下午5:47

@NaveenShan nil == NULL。它们是完全相同的,除了nil是id和NULL是void *。您的说法不正确。

–user142019
2011-10-25 13:38

@WTP是的,nil == NULL,但是使用nil显然是首选方法,如果您仔细查看苹果示例代码片段,它们在各处都使用nil,并且正如您所说,nil是id,这使其比void更可取* ,即在您发送ID的情况下。

–阿赫蒂
2011年11月23日下午13:15

完全是@Ahti,而Nil(大写)是Class *类型。即使它们都是平等的,使用错误的代码也会引入讨厌的小错误,尤其是在Objective-C ++中。

–user142019
2011-11-23 13:54



#7 楼

@kendell

而不是:

@interface MyClass (private)
- (void) someMethod
- (void) someOtherMethod
@end


使用:

@interface MyClass ()
- (void) someMethod
- (void) someOtherMethod
@end


新在Apple-Objective-C 2.0参考中描述了类扩展。

“类扩展在Apple的Objective-C 2.0参考中进行了描述。而不是在主类@interface块内”。因此它们是实际类的一部分-除了该类之外,它不是(私有)类别。细微但重要的区别。

评论


您可以这样做,但我想将其明确标记为“私有”部分(比功能更多的文档),尽管从.m文件中查找它当然已经很明显了……

–肯德尔·赫尔姆斯特·盖尔纳(Kendall Helmstetter Gelner)
08-10-1在4:03

私有类别和类扩展之间有区别:“类扩展允许您在主类@interface块之外的其他位置为类声明其他必需的API,如以下示例所示:”请参见编辑链接。

– schwa
08-10-1在4:46

我同意,当您尚未实现CE方法时,编译器会向您发出警告,这是有区别的-但是,当所有方法都在同一文件中且全部为私有文件时,我认为这方面不是很重要。我仍然更喜欢将前向参考块标记为私有的可维护性方面

–肯德尔·赫尔姆斯特·盖尔纳(Kendall Helmstetter Gelner)
08-10-1在5:03

我真的不认为(Private)比()更可维护。如果您担心的话,那么大量的评论可能会有所帮助。但显然生活并放任不管。 YMMV等

– schwa
08年10月1日在5:18

使用()而不是(Private)(或某些其他类别名称)有一个相当重要的优点:您可以将属性重新声明为可读写,而向公众公开它们只是只读的。 :)

–帕斯卡
09年12月4日在11:43

#8 楼

避免自动释放

由于通常(1)无法直接控制其生命周期,因此自动释放的对象可以保留相对较长的时间,并且不必要地增加了应用程序的内存占用。尽管在台式机上这可能影响不大,但在更受限的平台上,这可能是一个重要问题。因此,在所有平台上,尤其是在更受限制的平台上,最好的做法是避免使用会导致自动释放对象的方法,而建议您使用alloc / init模式。

因此,而不是:

aVariable = [AClass convenienceMethod];


如果可以,您应该改用:编写自己的返回新创建对象的方法,您可以利用Cocoa的命名约定,通过在方法名称前添加“ new”来标记接收者必须释放该对象。

,而不是:

aVariable = [[AClass alloc] init];
// do things with aVariable
[aVariable release];


您可以这样写:

- (MyClass *)convenienceMethod {
    MyClass *instance = [[[self alloc] init] autorelease];
    // configure instance
    return instance;
}


因为方法名称以“ new”开头,API的使用者知道他们负责释放接收到的对象(例如,参见NSObjectController的newObject方法)。

(1)您可以使用自己的本地自动释放池进行控制。有关更多信息,请参见自动释放池。

评论


我发现不使用自动释放的好处胜过它的成本(即更多的内存泄漏错误)。无论如何,主线程上的代码都应该运行得很短(否则您将冻结UI),并且对于运行时间较长的占用大量内存的后台代码,您始终可以将占用内存的部分包装在本地自动释放池中。

–adib
2010年7月4日在23:27

我不同意。您应该尽可能使用自动释放的对象。如果它们过多增加了内存占用,则应使用另一个NSAutoreleasePool。但是只有在您确认这确实是一个问题之后。过早的优化以及所有这些...

– Sven
2010年9月5日下午13:17

我花了不到40秒。每天键入[someObject release]并在实例化新对象时阅读“额外的代码”,但是我曾经花了17个小时来寻找一个仅在特殊情况下才会出现并且在控制台中未出现连贯错误的自动发布错误。因此,我同意adib的话:“我发现不使用自动发布的好处胜过其花费”。

– RickiG
2010-12-08 10:48

我同意斯文。主要目标应该是代码清晰并减少编码错误,仅在需要的地方进行内存优化。输入[[[Foo alloc] init]自动释放]很快,您就可以立即解决释放此新对象的问题。阅读代码时,您不必四处寻找相应的发行版以确保它不会泄漏。

–麦克·韦勒
2010-12-17 18:42

自动释放对象的生命周期定义明确,可以在足够的水平上确定。

–尼尔
2011年3月29日在17:12

#9 楼

其中一些已经被提及,但这是我想到的:




遵循KVO命名规则。即使您现在不使用KVO,以我的经验,通常它在将来仍然是有益的。而且,如果您使用的是KVO或绑定,则需要知道事情正在按照预期的方式进行。这不仅涵盖访问器方法和实例变量,还包括许多关系,验证,自动通知依赖项等。

将私有方法放入类别。不只是接口,还有实现。在概念上在私有方法与非私有方法之间保持一定距离是很好的。我将所有内容都包含在我的.m文件中。

将后台线程方法放在一个类别中。同上。我发现在考虑主线程上的内容和什么不在线程上时,最好保留明确的概念障碍。

使用#pragma mark [section]。通常,我按自己的方法,每个子类的覆盖以及任何信息或正式协议进行分组。这使跳转到我要寻找的内容变得容易得多。在同一主题上,将相似的方法(如表视图的委托方法)组合在一起,不要只是将它们粘在任何地方。我喜欢它的外观,当我偶然地表示一个属性时,我不太可能使用ivar。因此,我从来没有发生过任何不好的事情,但是如果您更改方法以执行依赖于对象状态的操作,那么我可以看到逻辑。

将IBOutlets放入属性中。我实际上只是在这里读过这篇,但是我将开始做。不管有什么记忆上的好处,从风格上看(至少对我来说)似乎更好。

避免编写您不需要的代码。这确实涵盖了很多内容,例如在#define可以执行操作时制作ivars,或者缓存数组而不是在每次需要数据时对其进行排序。关于这一点,我有很多话要说,但是最重要的是不要编写代码,除非您需要它,否则探查器会告诉您。从长远来看,它使事情变得更容易维护。

完成您的开始。包含大量未完成的错误代码是杀死项目失败的最快方法。如果您需要一个很好的存根方法,只需在其中放入NSLog( @"stub" )即可表明它,或者您想跟踪情况。


评论


我建议您将私有方法放在类的延续中。 (即@interface MyClass()... @end在您的.m中)

–詹森·梅代罗斯(Jason Medeiros)
09年2月7日在22:52

代替#PRAGMA,您可以使用注释// //标记:[Section],它更便于携带并且工作原理相同。

–aleemb
09年6月19日在12:26

除非缺少特殊的语法,否则// Mark:不会在Xcode的功能下拉菜单中添加标签,这实际上是使用它的一半原因。

– Marc Charbonneau
09年6月22日在15:21

您需要使用大写字母“ // MARK:...”才能使其显示在下拉菜单中。

–罗尔特
09年11月21日在8:50

关于完成您开始的事情,您也可以使用// TODO:标记要完成的代码,该代码将显示在下拉列表中。

–我被抢了
2010年7月12日在18:58

#10 楼

编写单元测试。您可以在Cocoa中测试很多在其他框架中可能很难完成的事情。例如,使用UI代码,您通常可以验证事物是否按其应有的方式连接,并相信它们在使用时会起作用。而且,您可以轻松地设置状态并调用委托方法以对其进行测试。

您也没有公开,受保护和私有方法的可见性,从而无法编写内部测试。 br />

评论


您推荐什么测试框架?

– Melfar
09年1月13日在9:06

Xcode包括OCUnit,Objective-C单元测试框架,并支持在构建过程中运行单元测试包。

–克里斯·汉森(Chris Hanson)
09年1月14日下午6:50

#11 楼

黄金法则:如果您alloc,那么您release

更新:除非您使用ARC

评论


同样,如果您复制,mutableCopy,新建或保留。

– Sven
2010年9月5日下午13:21

#12 楼

不要像编写Java / C#/ C ++ / etc那样编写Objective-C。

我曾经看到一个用来编写Java EE Web应用程序的团队试图编写Cocoa桌面应用程序。好像它是一个Java EE Web应用程序。当他们真正需要的只是一个Foo类以及可能的Fooable协议时,有很多AbstractFooFactory和FooFactory以及IFoo和Foo在飞来飞去。语言上的差异。例如,您不需要上面的抽象factory和factory类,因为Objective-C类方法的动态分配与实例方法一样动态,并且可以在子类中进行覆盖。

评论


作为在Objective-C中编写抽象工厂的Java开发人员,我发现这很有趣。您介意解释一下它的工作原理吗?也许举个例子?

–teabot
09年7月13日在12:51

您是否仍然认为自从您发布此答案以来,我们一直不需要抽象工厂类?

–kirk.burleson
10年7月28日在20:35

#13 楼

确保将“调试魔术”页面添加为书签。这是当您试图将可可小虫的源头撞到墙上时,将头撞墙的第一站。稍后会导致崩溃(例如在应用终止期间)。

评论


现在有一个特定于iOS的Debugging Magic页面。

– Jeethu
11年8月27日在9:05

#14 楼

尽量避免我现在决定称之为“新手分类狂”的东西。当Objective-C的新手发现类别时,他们经常会大吃一惊,向现有的每个类别添加有用的小类别(“什么?我可以添加一种将数字转换为罗马数字再转换为NSNumber的方法!”)。

请勿执行此操作。

在几十个基础类的顶部散布数十种小类别方法,您的代码将更易于移植,更易于理解。 br />大多数时候,当您真的认为需要分类方法来简化某些代码时,您将发现自己永远不会再使用该方法。

还有其他危险,除非您重新命名您的类别方法(除了完全疯狂的ddribin之外,还有谁?)Apple,插件或地址空间中运行的其他东西也有可能用相同的名称定义相同的类别方法,但名称略有不同副作用...。

好。现在您已经得到警告,请忽略“请勿执行此部分”。但是要保持克制。

评论


我喜欢您的答案,我的建议是不要使用类别来存储实用程序代码,除非您要在多个地方复制某些代码并且该代码显然属于您要分类的类...

–肯德尔·赫尔姆斯特·盖尔纳(Kendall Helmstetter Gelner)
08年10月1日在6:45

我只想表达自己对命名空间分类方法的支持。这似乎是正确的选择。

–迈克尔·巴克利(Michael Buckley)
08年10月1日在7:43

+1(仅用于罗马数字)。我完全会那样做!

–布莱恩·波斯托(Brian Postow)
2010-3-31在19:58

反对点:在过去的一年半中,我遵循了完全相反的政策:“如果可以在某个类别中实施,请这样做。”因此,与Apple提供的详细示例代码相比,我的代码更加简洁,更具表现力并且更易于阅读。一次名称空间冲突使我总共损失了大约10分钟的时间,而我为自己创造的效率可能使我获得了数月的工作量。对于每个人自己,但是我在了解风险的情况下采取了这项政策,对此我感到非常高兴。

–cduhn
2010年4月6日在8:05

我不同意如果它将成为一个函数并且适用于Foundation对象,并且您可以想到一个好名字,请将其归类。您的代码将更具可读性。我认为这里的重点是:适度地做每件事。

–mxcl
2010年11月4日,0:06

#15 楼

抵抗继承世界。在Cocoa中,很多事情是通过委派和使用基础运行时完成的,而在其他框架中则是通过子类完成的。经常使用您的*Listener子类。在可可中,您什么都不做,而是使用target-action。

评论


否则称为“继承构成”。

–安德鲁·埃布林(Andrew Ebling)
11 Mar 10 '11 at 13:14

#16 楼

按用户需要对字符串进行排序

对要呈现给用户的字符串进行排序时,不应使用简单的compare:方法。相反,您应该始终使用本地化的比较方法,例如localizedCompare:localizedCaseInsensitiveCompare:

有关更多详细信息,请参阅搜索,比较和排序字符串。

#17 楼

声明的属性

您通常应将Objective-C 2.0声明的属性用于所有属性。如果它们不是公共的,则将它们添加到类扩展中。使用声明的属性可以使内存管理语义立即清晰,并使您更容易检查dealloc方法-如果将属性声明组合在一起,则可以快速对其进行扫描,并与dealloc方法的实现进行比较。

在不将属性标记为“非原子”之前,您应该认真考虑。正如《 Objective C编程语言指南》所述,默认情况下,属性是原子的,并且会产生相当大的开销。而且,仅使所有属性原子化并不能使应用程序具有线程安全性。当然,还要注意,如果您不指定'nonatomic'并实现自己的访问器方法(而不是综合它们),则必须以原子方式实现它们。

#18 楼

考虑nil值

正如该问题所指出的那样,发送至nil的消息在Objective-C中有效。尽管这通常是一个优势-导致代码更清晰,更自然-但是,如果您在未预期到的时候获得nil值,则该功能有时会导致特殊且难以跟踪的错误。

评论


我有这个:#define SXRelease(o); o = nil,对于CFRelease和free相同。这简化了一切。

–user142019
2011-10-25 13:45

#19 楼

使用NSAssert和朋友。
我一直都使用nil作为有效对象……尤其是在Obj-C中向nil发送消息是完全有效的。
但是,如果我真的想确定变量的状态,可以使用NSAssert和NSParameterAssert,这有助于轻松地查找问题。

评论


此处的更多信息:developer.apple.com/mac/library/documentation/Cocoa/Reference/…此处:stackoverflow.com/questions/2521275/what-is-nsparameterassert

–戴夫
2010年7月12日在18:40

#20 楼

简单但经常被遗忘的一个。根据规范:


通常,具有相同选择器
(相同名称)的不同
类中的方法也必须共享相同的
返回和参数类型。此
约束是由编译器施加的,以允许动态绑定。具有相同的返回/参数类型。这是一个简单的示例。

@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;
}   


评论


这是一个容易忘记的人。不过很重要

–布鲁克·伍尔夫(Brock Woolf)
10年8月28日在6:06

仅在避免静态键入时才需要关注。如果编译器知道类型,则参数和返回类型可以不同而不会出现问题。从人格上来说,我发现这并不经常成为问题。苹果也有很多名称相同但返回类型不同的方法。最后,有一个编译器标志可以在模棱两可的情况下警告您。

– Nikolai Ruhe
10年8月30日在21:21

如果我们遵循苹果的命名约定准则,这种情况就不会发生:)

–尼尔
2011年3月29日17:17



#21 楼

如果您使用的是Leopard(Mac OS X 10.5)或更高版本,则可以使用Instruments应用程序查找并跟踪内存泄漏。在Xcode中构建程序后,选择“运行”>“从性能工具开始”>“泄漏”。在仪器中,您可以使用ObjectAlloc仪器。在“仪器”文档中选择ObjectAlloc仪器,然后通过选择“视图”>“详细信息”(它旁边应有一个复选标记)来显示该仪器的详细信息(如果尚未显示)。在ObjectAlloc详细信息的“ Allocation Lifespan”下,确保选择了“ Created&Still Living”旁边的单选按钮。

现在,无论何时停止记录应用程序,选择ObjectAlloc工具都会向您展示“#Net”列中对应用程序中每个仍然存在的对象都有很多引用。确保不仅查看自己的类,而且查看NIB文件的顶级对象的类。例如,如果屏幕上没有窗口,并且看到了对仍然有效的NSWindow的引用,则可能尚未在代码中释放它。

#22 楼

用dealloc清理。

这是最容易忘记的事情之一-尤其是。当以150mph的速度编码时。总是,总是,总是在dealloc中清理您的属性/成员变量。通常很简单:在dealloc中再次访问该成员变量-很少见,但可能会发生。)

在10.5中打开GC,就不需要太多了-但您可能仍需要清理其他资源创建时,您可以改为使用finalize方法。

评论


通常,不应在dealloc(或init)中使用访问器方法。

–mmalc
08-10-3在15:38

除了性能原因(访问器比直接访问慢一点)之外,为什么我不应该在dealloc或init中使用访问器?

– schwa
08-10-10在19:11

(a)性能原因本身就足够了(尤其是如果访问器是原子的)。 (b)您应该避免访问器可能产生的任何副作用。如果您的类可能是子类的,则后者尤其成问题。

–mmalc
08-10-11在4:28

我会注意到,如果您在现代运行时上运行具有合成ivars,则必须在dealloc中使用访问器。许多现代的运行时代码是GC,但并非全部。

–路易·格巴(Louis Gerbarg)
08年10月10日在20:34

有关在-init和-dealloc方法中是否使用访问器方法/属性的更详细的信息,可以在这里找到:mikeash.com/?page=pyblog / ...

–约翰·库尔(Johan Kool)
09年11月28日在7:49

#23 楼

所有这些评论都很棒,但是我真的很惊讶没有人提到前不久发布的Google的《 Objective-C风格指南》。我认为他们做得非常透彻。

评论


嗯,第一个例子已经是胡扯了。切勿记录语言习语。如果我能在头文件中找到这些注释,就不会再继续阅读了。

–斯蒂芬·埃格蒙特(Stephan Eggermont)
2010年8月15日下午14:15

哦,我的眼睛!我简直不敢相信我所看到的。

–尼尔
2011年3月29日17:19



#24 楼

此外,半相关的主题(还有更多的答复余地!):

您希望两年前知道的那些Xcode小技巧是什么?。

#25 楼

不要忘记NSWindowController和NSViewController会释放它们管理的NIB文件的顶级对象。您已经完成了他们。

#26 楼

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

Xcode实际上将解析该部分并根据方括号,循环等对其进行缩进。这比敲击每行的空格键或Tab键要有效得多。

评论


您甚至可以将Tab设置为缩进,然后执行Cmd-A和Tab。

–羽化器
2010年7月15日在6:31

#27 楼

我知道我第一次进入Cocoa编程时就忽略了这一点。

确保您了解有关NIB文件的内存管理职责。您有责任在加载的任何NIB文件中释放顶级对象。阅读有关该主题的Apple文档。

评论


这不是真的。是否负责发布顶级对象取决于您从哪个类继承以及所使用的平台。参见developer.apple.com/documentation/Cocoa/Conceptual/MemoryMgmt/…。

–mmalc
08-10-3在16:10

#28 楼

打开所有GCC警告,然后关闭由Apple标头引起的常规警告,以减少噪音。

还经常运行Clang静态分析;您可以通过“运行静态分析器”构建设置为所有构建启用它。

编写单元测试并在每个构建中运行它们。

评论


并且,如果可以,请打开“将警告视为错误”。不允许存在任何警告。

– Peter Hosey
09-10-18在23:14

可在此处使用方便的脚本来设置带有建议警告的项目:rentzsch.tumblr.com/post/237349423/hoseyifyxcodewarnings-scpt

–约翰·库尔(Johan Kool)
09年11月28日在7:52

#29 楼

变量和属性

1 /保持标题干净,隐藏实现
不要在标题中包含实例变量。私有变量作为属性放入类的延续中。公共变量在标头中声明为公共属性。
如果应仅将其读取,则将其声明为readonly并在类延续中将其覆盖为readwrite。
基本上我根本不使用变量,而只使用属性。

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


@synthesize property = property_;


原因1:您会发现由于忘记了“自。”分配属性时。
原因2:从我的实验来看,Instruments中的Leak Analyzer在检测默认名称泄漏的属性时遇到问题。

3 /切勿直接对属性使用保留或释放(或仅在非常特殊的情况下)。在您的dealloc中,只需将它们分配为零即可。保留属性旨在自行处理保留/释放。您永远不会知道设置器是否不是例如添加或删除观察者。您应该只在变量的setter和getter内部直接使用该变量。

Views

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

2 /不要尝试通过减少视图数量来优化视图。不要仅仅因为要向其中添加子视图而在代码中而不是xib中创建UIImageView。请改用UIImageView作为背景。视图框架可以处理数百个视图,而不会出现问题。

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

4 /在viewDidUnload

5 /中释放所有IBOutlet。5/从dealloc方法调用viewDidUnload。它不是隐式调用的。

内存

1 /创建对象时自动释放它们。许多错误是由于将发布调用移至一个if-else分支或return语句之后引起的。仅在特殊情况下才应使用释放而不是自动释放-例如当您等待运行循环并且不希望对象过早地自动释放时。

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

注释

1 /使您的代码自动记录。
每个变量名和方法名都应说明其作用。如果代码编写正确(在此需要大量练习),则不需要任何代码注释(与文档注释不同)。算法可能很复杂,但是代码应该总是很简单。

2 /有时,您需要添加注释。通常描述一个不明显的代码行为或黑客行为。如果您觉得必须编写注释,请首先尝试重写代码,以使其更简单并且不需要注释。

缩进

1 /不要增加太多缩进。
您的大多数方法代码应在方法级别缩进。嵌套块(如果是等)会降低可读性。如果您有三个嵌套块,则应尝试将内部块放入单独的方法中。绝对不要使用四个或更多的嵌套块。
如果大多数方法代码都在if中,则否定if条件,例如:

if (self) {
   //... long initialization code ...
}

return self;



了解C代码,主要是C结构

请注意,Obj-C只是C语言上的一个轻型OOP层。您应该了解C语言中的基本代码结构如何工作(枚举,结构,数组,指针等)。
示例:


if (!self) {
   return nil;
}

//... long initialization code ...

return self;



与以下内容相同:


view.frame = CGRectMake(view.frame.origin.x, view.frame.origin.y, view.frame.size.width, view.frame.size.height + 20);


还有更多

维护自己的编码标准文档并经常进行更新。尝试从您的错误中学习。了解为什么会创建错误,并尝试使用编码标准来避免它。 。记录您的代码,在正确的地方使用标准的标准缩进,空格和空白行等。

#30 楼

具有更多功能。变异性是分离的。使用不可变类作为主要对象,使用可变对象作为辅助对象。例如,主要使用NSArray,仅在需要时使用NSMutableArray。
有纯函数。并不是很多,购买的框架API的设计就像纯函数一样。查看诸如CGRectMake()CGAffineTransformMake()之类的函数。显然指针形式看起来更有效。但是,带有指针的间接参数不能提供无副作用的功能。尽可能纯粹地设计结构。
分离均匀的状态对象。将值传递给其他对象时,请使用-copy而不是-retain。因为共享状态可以静默地影响其他对象中价值的变异。因此不能无副作用。如果您有来自object的external值,请复制它。因此,将共享状态设计得尽可能小也很重要。

但是也不要害怕使用不纯函数。


评估很懒。看到类似-[UIViewController view]属性。创建对象时将不会创建视图。它将在调用者首次读取view属性时创建。在实际绘制之前,不会加载UIImage。有很多这样的实现方式。这种设计对资源管理非常有帮助,但是如果您不了解惰性评估的概念,那么理解它们的行为就不容易了。
有关闭。尽可能使用C块。这将大大简化您的生活。但是在使用之前,请再次阅读有关块内存管理的信息。
有半自动GC。 NSAutoreleasePool。使用-autorelease primary。实际需要时,请使用手册-retain/-release。 (例如:内存优化,显式资源删除)


评论


关于3)我将提出相反的方法:尽可能使用手动保留/释放!谁知道此代码将如何使用-如果将其用于紧密循环,可能会不必要地消耗您的内存。

– Eiko
2011年5月18日在22:58

@Eiko这只是一个过早的优化,不能作为一般指导。

–尼尔
2011年5月19日下午2:34

我认为这是更多的设计工作,尤其是在处理模型类时。我认为增加内存是一个副作用,而这并不是我想要经常出现的东西。更糟糕的是,另一个使用我的代码的开发人员没有机会将昂贵的调用包装到自动释放池中(如果可能的话-我的对象可能会发送到其他一些库代码)。这些问题以后很难诊断,但是首先要避免。如果复制/自动释放传入的对象,则如果它们比预期的要大得多,则可能会丢失。不过,我对GUI代码更加放松。

– Eiko
2011年5月19日在8:49

@Eiko我同意自动释放​​通常会保留更长的内存,在这种情况下,手动保留/释放可以减少内存消耗。但是,它应该作为特殊情况优化的指导(即使您总是感觉不到!),也不是将过早优化作为实践概括的原因。实际上,您的建议与我并不相反。我提到它是真正需要的情况:)

–尼尔
2011年5月19日在9:48