PrefaceI don’t normally post highly technical stuff to my blog, but here’s an exception that I hope will benefit the Mac and iOS (iPhone & iPad) developer community. So if you’re not part of that, feel free to skip it.IntroductionThis article is a cumulative list of the most commonly-violated best practices for Objective-C coders that I’ve witnessed in my many years of experience with the language. I call them “commandments” because there are many good reasons to observe them and few, if any valid reasons not to. However, when I show these practices to other developers, they frequently raise one big objection…The Big Objection: Won’t That Hurt Performance?This is almost never a legitimate objection. If you can show me any case where code written in conformance to any of the following policies has become the performance bottleneck, you have my blessing to optimize that code with an exception to the policy, as long as how you’re breaking the policy and why are clearly documented in the code.WHY?
These policies target code safety, clean memory management, understandability, and maintainability. Most of them are not performance enhancers. Nonetheless, writing code with performance in mind is never the way to go first. Always write clean, correct code and then optimize only when and where profiling indicates you need it. In a typical UI-driven application, the single biggest bottleneck is almost always the user, followed by network access, and then disk access. Nonetheless, premature optimization is rampant among programmers who are concerned that if they don’t use every optimization trick they’ve been taught, their application will be dog slow. This simply isn’t true.Important General AdviceEven if you understand how your code operates when you write it, that doesn’t mean others will understand it so completely before they attempt to modify it, and it doesn’t mean that you will understand it yourself when you revisit it months later. ALWAYS strive to make your code self-documenting by using verbose symbol names and add detailed comments in sections of code where meaning is not obvious despite clear and descriptive symbols. Any time you need to work around a bug or misfeature in an API you are using, isolate your workaround to a single method, explain your workaround and put a keyword like KLUDGE in your comments so you can easily find and fix these trouble spots later.Things This Article Doesn’t Cover (Yet)Key Value Coding (KVC) and Key Value Observing (KVO). You should use them.
Added April 26, 2012THOU SHALT…
ALWAYS use Automatic Reference Counting (ARC). This article has been revised to remove references to pre-ARC code style. All new code should be written using ARC, and all legacy code should be updated to use ARC (or kept in a very tight box with a pretty API covering it up.)THOU SHALT…
ALWAYS use the @private directive before any instance variable declarations, and NEVER access instance variables directly from outside the class.WHY?
Preserves information hiding (encapsulation)
Limits to methods in the class implementation cases where instance variables are accessed directly.
The default privacy for instance variables is @protected, which means subclasses can access these instance variables freely. But there has to be a very good reason to allow subclasses to do this— everything that a superclass exposes to the outside world becomes part of its contract, and the ability to change the internal representation of a class’s data without changing its API or contract is an important benefit of object-oriented encapsulation. By keeping your instance variables private, you make it clear that they are implementation details and not part of your class’s API.
BAD
@interface Foo : NSObject {int a;NSObject* b;// …
}
// method & property declarations…
@end
BETTER
@interface Foo : NSObject {@privateint a;NSObject* b;// …
}
// method & property declarations…
@end
THOU SHALT…
ALWAYS create a @property for every data member and use “self.name” to access it throughout your class implementation. NEVER access your own instance variables directly.WHY?
Properties enforce access restrictions (such as readonly)
Properties enforce memory management policy (strong, weak)
Properties provide the opportunity to transparently implement custom setters and getters.
Properties with custom setters or getters can be used to enforce a thread-safety strategy.
Having a single way to access instance variables increases code readability.
BAD
@interface Foo : NSObject {@privateNSObject* myObj;
}
@end@implementation Foo
- (void)bar {myObj = nil;
}
@end
BETTER
@interface Foo : NSObject {@privateNSObject* myObj;
}@property(strong, nonatomic) NSObject* myObj;@end@implementation Foo
- (void)bar {self.myObj = nil;
}
@end
THOU SHALT…
ALWAYS use the “nonatomic” attribute on your properties, unless you are writing a thread-safe class and actually need access to be atomic, and then put a comment in the code that this was your intent.WHY?
Classes with properties that aren’t declared “nonatomic” may give the impression that they were designed with thread-safety in mind, when in fact they weren’t. Only drop the “nonatomic” qualifier from properties when you have actually designed the class for thread-safety.BAD
@interface Foo : NSObject@property(strong) NSObject* myObj;@end
BETTER
@interface Foo : NSObject@property(strong, nonatomic) NSObject* myObj;@end
BETTER
// This class and all it’s properties are thread-safe.
@interface Foo : NSObject@property(strong) NSObject* myObj;@end
THOU SHALT…
NEVER allow your instance variable names to be confused with property names, or with data member names. ALWAYS end your instance variable names with an underscore. UNLESS you are subclassing a 3rd-party class which already has a data member of the same name, in which case pick another non-similar name or add another underscore and put a comment in as to why you did this.Use “@synthesize name = name_;” in your implementation, instead of just “@synthesize name;”WHY?
Even within the class implementation you have to have a very good reason to access a data member directly, instead preferring property accessors.
Apple uses “_name” for their private instance variables, and by using “name_” for yours, you avoid naming conflicts.
Apple has begun to use the “name_” convention in code examples and templates that demonstrate developer-level code.
BAD
@interface Foo : NSObject@property(strong, nonatomic) NSObject* myObj;@end// …@implementation Foo@synthesize myObj;@end
BETTER
@interface Foo : NSObject@property(strong, nonatomic) NSObject* myObj;@end// …@implementation Foo@synthesize myObj = myObj_;@end
THOU SHALT…
NEVER redundantly add the data member to the class @interface yourself. Allow the @synthesize directive to implicitly add the data member.Following this practice will yield many classes that explicitly declare no instance variables in the @interface section. When a class has no instance variables, omit the @private declaration, and even omit the opening and closing braces of the data member section.WHY?
Reduces redundancy.
Simplifies class headers.
Avoids the need for forward class declarations in public headers merely so you can declare members of classes that are only actually used in the implementation.
BAD
@interface Foo : NSObject {@privateNSObject* myObj_;
}@property(strong, nonatomic) NSObject* myObj;@end// …@implementation Foo@synthesize myObj = myObj_;@end
BETTER
@interface Foo : NSObject@property(strong, nonatomic) NSObject* myObj;@end// …@implementation Foo@synthesize myObj = myObj_;@end
You may still need to declare the underlying name of the variable if you need to access it directly, as when writing custom getters and setters:WON’T WORK
@interface Foo : NSObject@property(strong, nonatomic) NSObject* myObj;@end// …@implementation Foo@synthesize myObj;- (NSObject*)myObj
{return self.myObj; // recursive call to this getter!
}- (void)setMyObj:(NSObject*)myObj
{self.myObj = myObj; // recursive call to this setter!
}@end
WILL WORK
@interface Foo : NSObject@property(strong, nonatomic) NSObject* myObj;@end// …@implementation Foo@synthesize myObj = myObj_;- (NSObject*)myObj
{return myObj_; // No problem.
}- (void)setMyObj:(NSObject*)myObj
{// no problemmyObj_ = myObj; // do the assignment (ARC handles any necessary retaining and releasing)
}@end
THOU SHALT…
NEVER access a data member through an underscore-suffixed symbol UNLESS you are writing a setter or getter.BAD
@interface Foo : NSObject@property(strong, nonatomic) Bar* myObj;@end// …@implementation Foo@synthesize myObj = myObj_;- (void)someMethod
{myObj_ = [[Bar alloc] init];
}@end
BETTER
@interface Foo : NSObject@property(strong, nonatomic) Bar* myObj;@end// …@implementation Foo@synthesize myObj = myObj_;- (void)someMethod
{self.myObj = [[Bar alloc] init];
}@end
THOU SHALT…
NEVER declare internal (private) methods or properties in the class header files. ALWAYS put all internal method and property declarations into a “class extension” in the implementation file.BAD
//
// Foo.h
//

@interface Foo : NSObject@property(nonatomic) int myPublicProperty;
@property(strong, nonatomic) Bar* myPrivateProperty; // This can be accessed by anyone who includes the header- (int)myPublicMethod;
- (int)myPrivateMethod; // So can this.@end
BETTER
//
// Foo.h
//

@interface Foo : NSObject// Only the public API can be accessed by including the header

@property(nonatomic) int myPublicProperty;- (int)myPublicMethod;@end
//
// Foo.m
//

@interface Foo () // This is a "class extension" and everything declared in it is private, because it’s in the implementation file

@property(strong, nonatomic) Bar* myPrivateProperty;- (int)myPrivateMethod;@end@implementation Foo
// …
@end
THOU SHALT…
NEVER use more than one return-statement in a method, and only then as the last statement of a method that has a non-void value-type.
In methods that have a non-void value-type, declare a variable for the value as the very first statement, and give it a sensible default value. Assign to it in code paths as necessary. Return it in the last statement. NEVER return it prematurely using a return-statement.WHY?
Premature return-statements increase the possibility that some sort of necessary resource deallocation will fail to execute.BAD
@implementation Foo- (Bar*)barWithInt:(int)n
{// Allocate some resource here…if(n == 0) {// …and you have to deallocate the resource here…return [[Bar alloc] init];} else if(n == 1) {// …and here…return self.myBar;}// …and here.return nil;
}@end
BETTER
@implementation Foo- (Bar*)barWithInt:(int)n
{Bar* result = nil;// Allocate some resource here…if(n == 0) {result = [[Bar alloc] init];} else if(n == 1) {result = self.myBar;}// …and deallocate the resource here, you’re done!return result;
}@end
THOU SHALT…
Understand what autorelease pools are for, when they are created and destroyed for you, and when to create and destroy your own.Automatically by NSRunLoop on each pass
Automatically by NSOperation
By you, at the start and end of threads
By you, whenever you must create and release a large number of objects before you’ll cede control back to the run loop.
Under ARC, you create autorelease pools with the @autoreleasepool { … } construct.THOU SHALT…
ALWAYS prefer class-level convenience constructors over init-constructors. All of the foundation framework container classes provide these.BAD
NSMutableDictionary* dict = [[NSMutableDictionary alloc] init];
BETTER
NSMutableDictionary* dict = [NSMutableDictionary dictionary];
THOU SHALT…
ALWAYS offer class-level convenience constructors in the API of the classes you write.WHY?
Clients of your class can reap the benefits of being able to keep the previous commandment.BAD
@interface Foo : NSObject- (id)initWithBar:(int)bar baz:(int)baz;@end
BETTER
@interface Foo : NSObject- (id)initWithBar:(int)bar baz:(int)baz;+ (Foo*)fooWithBar:(int)bar baz:(int)baz;@end
THOU SHALT…
ALWAYS understand when object ownership transfers happen. ARC handles a lot of this for you, but you should still be generally knowledgable of what is going on behind the scenes.Common ways of taking posession of an object:New objects are owned by you when you call +alloc on a class.
New objects are owned by you when you call -copy or -mutableCopy on an instance.
You become an owner of an existing object when you assign it to a property with the (retain) or (strong) attribute.
Common ways of releasing ownership an object:Assign another object (or nil) to a property with the (strong) attribute.
Let an owning local variable go out of scope.
Another object holding a reference to the object is destroyed.
THOU SHALT…
ALWAYS understand the memory management policies of your properties and instance variables, particularly when writing custom getters and setters. Make sure your instance variables have the correct storage policies declared on them in order to let ARC do it’s job. The easiest way to do this is simply to use @synthesize and then override the generated setter and/or getter to access the underlying instance variable, which will have the same storage policy as that declared by the property.@interface bar@property (strong, nonatomic) id foo;@end@implementation bar@synthesize foo = foo_;- (id)foo
{return foo_;
}- (void)setFoo:(id)foo;
{foo_ = foo;  // Retained/released automatically by ARC because of (strong) attribute on @property above[self syncToNewFoo];  // The reason for our custom setter
}@end
THOU SHALT…
NEVER have a -dealloc method if your class unless it must release special resources (close files, release memory acquired with malloc(), invalidate timers, etc.) ARC should be allowed to take care of everything else.THOU SHALT…
ALWAYS write a custom getter for a property where you have a custom setter, and vice versa.WHY?
Getters and setters need to have symmetrical behavior about memory management, thread safety, and any other side effects they create. You should not rely on a synthesized getter or setter having the proper symmetrical behavior to any custom getter or setter you write. Therefore, if you’re going to provide either the getter or setter yourself, you should provide both.THOU SHALT…
ALWAYS write a custom setter for a property, and assign that property to nil in your -dealloc method, if there’s something you must ALWAYS do when you release an object (such as invalidating a timer).BAD
@implementation Foo@synthesize myTimer;- (void)dealloc
{self.myTimer = nil; // Timer not invalidated, we could get called back if the timer fires after we’re dealloced!
}@end
BETTER
@implementation Foo@synthesize myTimer = myTimer_;- (NSTimer*)myTimer
{return myTimer_;
}- (void)setMyTimer:(NSTimer*)myTimer
{[myTimer_ invalidate];myTimer_ = myTimer;
}- (void)dealloc
{self.myTimer = nil; // Timer guaranteed not to fire after we’re gone! Still necessary under ARC.
}@end
WHY?
ARC takes care of releasing your (strong) or (retain) instance variables at -dealloc time, but it does it directly, rather than by calling your custom setters. So if your custom setters have other side effects (like invaliding timers) you must still make sure to invoke them yourself.THOU SHALT…
When writing constructors, ALWAYS minimize or eliminate the amount of code that executes before [super init] is called.WHY?
In general, the call to the superclass’ constructor may fail, causing it to return nil. If that happens, then any initialization you do before the call to the super constructor is worthless or worse, has to be undone.BAD
@implementation Foo- (id)initWithBar:(Bar*)bar
{[bar someMethod];// other pre-initialization hereif(self = [super init]) {// other initialization here} else {// oops! failed to initialize super class// undo anything we did above
}return self;
}@end
BETTER
@implementation Foo- (id)init
{if(self = [super init]) {// minimal initialization here
        }return self;
}// Other methods that put a Foo into a usable state@end
THOU SHALT…
When writing a UIViewController and not using a nib file, ALWAYS create and setup your view hierarchy in -loadView, and never in -init. Your implementation of -loadView is the ONLY place you should ever assign to the view attribute.THOU SHALT…
NEVER call -loadView yourself! The view attribute of a UIViewController loads lazily when it’s accessed. It can also be automatically purged in a low-memory situation, so NEVER assume that a UIViewController’s view is going to live as long as the controller itself.THOU SHALT…
ALWAYS set up the views you need once, then show, hide, or move them as necessary. NEVER repeatedly destroy and recreate your view hierarchy every time something changes.THOU SHALT…
NEVER call -drawRect on a UIView yourself. Call -setNeedsDisplay.THOU SHALT…
ALWAYS avoid long compound operations in your code. Local variables are your friend.BAD
NSMutableDictionary* listDict = [[NSMutableDictionary alloc] initWithDictionary:[[NSUserDefaults standardUserDefaults] objectForKey:@"foo"]];
BETTER
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
NSDictionary* dataModelDict = [defaults objectForKey:@"foo"];
NSMutableDictionary* listDict = [NSMutableDictionary dictionaryWithDictionary:dataModelDict];
WHY?
Self-documenting
Easier to understand
Easier to step through in debugger
THOU SHALT…
NEVER use method names like -drawUI when you aren’t really drawing anything. ALWAYS prefer names like -setupUI or -resetUI, especially if there’s the possibility the method can be called more than once.THOU SHALT…
NEVER repeat yourself. Have a single, authoritative location for each piece of information or functionality, and deploy it throughout the application, even when it means touching others’ code. NEVER program via copy-and-paste.Updated October 15, 2010THOU SHALT…
NEVER call -stringWithString UNLESS you:Are converting an NSString to an NSMutableString.
Are converting an NSMutableString to an NSString.
Need to guarantee that an NSString* you’ve been handed is really immutable
Really need a copy of an NSMutableString because you plan to modify them separately.
WHY?
NSStrings are immutable. You should never have to copy an NSString unless you want a mutable copy, or guarantee that an NSString pointer you want to save is not already an NSMutableString (and thus might have its value changed by some other code later.) In fact, attempts to copy NSStrings simply bump up the string’s retain count and return the original string.
[/code]Added April 26, 2012THOU SHALT...
ALWAYS use the (copy) storage class for properties that receive objects (like NSStrings or NSArrays) that have mutable subclasses.WHY?
The (copy) storage class of properties (as well as the -copy method) always make IMMUTABLE copies of these objects. So you can rely on the immutability of a copy, while you cannot rely on the immutability of the original. This is also a promise to the caller that you will not mutate objects that are passed to you this way that happen to be only incidentally mutable.THOU SHALT...
When overriding a method in a superclass, ALWAYS call the superclass' implementation, even if you know that super's implementation does nothing, unless you have a very good reason to not call super's implementation, in which case you must document your reason in the comments.

WHY?
Super's implementation might not ALWAYS do nothing in the future.
Other classes may eventually interpose themselves between your class and the superclass, with non-empty implementations.
Makes your code more self-documenting in that it is not possible to call a super implementation unless the method is an override.
BAD
@implementation Foo- (void)awakeFromNib
{// do my setup
}@end
BETTER
@implementation Foo- (void)awakeFromNib
{[super awakeFromNib];// do my setup
}@end
Added October 15, 2010THOU SHALT...
In conditional statements, NEVER treat pointers or numerical values as booleans.WHY?
Booleans have two values: true and false (by convention in Objective-C we use YES and NO.) Pointers, on the other hand, have a value that is an object, or nil. Numerical values have values that are some number, or zero. While zero and nil evaluate to boolean false in a conditional context, this is a form of implicit type coercion that can make the meaning of your code more difficult to understand. So explicitly test for nil when dealing with pointers, 0 when dealing with integers, 0.0 when dealing with floating point values, etc.BAD
- (void)fooWithBar:(Bar*)bar baz:(BOOL)baz quux:(float)quux
{if(bar && baz && quux) {// do something interesting
        }
}
BETTER
- (void)fooWithBar:(Bar*)bar baz:(BOOL)baz quux:(float)quux
{if(bar != nil && baz && quux != 0.0) {// do something interesting
        }
}
Added October 15, 2010THOU SHALT...
NEVER use conditional statements to check for nil when you don't have to. In particular, understand that in Objective-C, the act of calling any method on a nil object reference is a no-op that returns zero (or NO, or nil), and write your code accordingly.

BAD
- (void)setObj:(NSObject*)obj
{if(obj_ != nil) {[obj_ doCleanup];}if(obj != nil) {[obj doSetup];}obj_ = obj;
}
BETTER
- (void)setObj:(NSObject*)obj
{[obj_ doCleanup]; // Does nothing if obj_ == nil[obj doSetup]; // Does nothing if obj == nilobj_ = obj;
}

转: http://ironwolf.dangerousgames.com/blog/archives/913

转: The Code Commandments: Best Practices for Objective-C Coding (updated for ARC)相关推荐

  1. The Code Commandments: Best Practices for OCCoding

    2019独角兽企业重金招聘Python工程师标准>>> Preface I don't normally post highly technical stuff to my blog ...

  2. Android Bad Practices: Missing Google Play Services Updated Security Provider

    Android 程序的安全检测,检测到的漏洞. 解释: 应用程序不使用 Google Play 服务更新的安全提供程序,这可能使其未来易遭受 OpenSSL 库中漏洞的攻击. Android 依赖于可 ...

  3. 【通信系统仿真系列】基于Matlab的汉明码(Hamming Code)纠错传输以及交织编码(Interleaved coding)仿真

    基于Matlab的汉明码纠错传输以及交织编码仿真 前言 原理 汉明码 编码过程 冗余位数量计算 校验位位置计算 计算校验相关位 开始编码 解码过程 实验结果 仿真代码 可以修改的参数 下载链接 主函数 ...

  4. CVPR 2021 论文和开源项目合集(Papers with Code)

    摘自:https://github.com/amusi/CVPR2021-Papers-with-Code CVPR 2021 论文和开源项目合集 CVPR 2021 论文和开源项目合集(Papers ...

  5. HTG审核CODE键盘:老式建筑满足现代设施

    There's nothing quite as satisfying as the smooth and crisp action of a well built keyboard. If you' ...

  6. Code Review Checklist

    以下是流传的code review检查表,我不揣冒昧,为节省大家时间,翻译如下. 不当之处难免,故英文保留.   希望可以给各位眼下以至将来的工作都有所帮助.:) Terry 2003/12/18   ...

  7. 微软家族的首个跨平台开发工具 Visual Studio Code

    微软家族的首个跨平台开发工具 Visual Studio Code 长这样哦.很多童鞋说像 Atom,其实他们在官网就说了用的是 Electron Shell(Atom) Why Visual Stu ...

  8. SitePoint播客#124:iPhone贪婪的声音

    Episode 124 of The SitePoint Podcast is now available! This week the panel is made up of regular mem ...

  9. DO-178C Standard

    已剪辑自: https://ldra.com/do-178/ DO-178C/ED-12C (henceforth DO-178C) is the primary document reference ...

最新文章

  1. ubuntu14 备份
  2. 确定需要改变几个位,才能将整数A转变为整数B
  3. 046 实例11-自动轨迹绘制
  4. centos 计算器_Linux学习之CentOS(十五)--Linux常用命令之bc、man、shutdown...
  5. Upload LABS Pass-7
  6. mongodb有关的研究
  7. CISCO路由器、交换机密码恢复
  8. Mysql 哈希索引
  9. endnote导入参考文献及国标(Chinese standard)
  10. Disable STRICT_TRANS_TABLES @Mysql 5.7
  11. Windows7安装java
  12. uniapp canvas 合成头像
  13. 原神服务器不稳定,原神天空岛和世界树服务器哪个好
  14. Android 10 电池图标修改
  15. 双旦七大行业营销活动方案
  16. 【一起学系列】之策略模式:好多鸭子啊
  17. 最新Centos7.6 部署ELK日志分析系统
  18. WinForm(八)窗体,窗体
  19. 天平应什么放置_天平的使用规程
  20. vue核心面试题:v-for中为什么要用key

热门文章

  1. 一篇很全面的freemarker教程
  2. element表格取消全选_ElementUi 表格取消全选框,用文字表示
  3. 【Linux 内核】进程管理 - 进程优先级 ② ( prio 调度优先级 | static_prio 静态优先级 | normal_prio 正常优先级 | rt_priority 实时优先级 )
  4. 【错误记录】Groovy 注入方法报错 ( Cannot add new method [hello] for arguments [[]]. It already exists )
  5. 【Google Play】IARC 年龄分级 ( IARC 国际年龄分级联盟 | Google Play 设置应用年龄分级 )
  6. 【错误记录】Kotlin 编译报错 ( Type mismatch: inferred type is String? but String was expected )
  7. 【错误记录】BLE 蓝牙搜索失效 ( 关闭了 GPS 定位导致的问题 | 蓝牙串口工具推荐 )
  8. 【计算机网络】数据链路层 : 信道划分 介质访问控制 ( 数据链路 | 介质访问控制分类 | 频分多路复用 FDM | 时分多路复用 TDM | 波分复用 WDM | 码分多路复用 CDM 计算 )★
  9. Mac下web自动化环境部署
  10. iOS开发-登录界面开发(6)Toast-Swift的使用-Swfit4.1_Xcode9.3.1