Google的Objective-C编码规范

GMObjC:基于OpenSSL的SM2SM3SM4ECDH库。(基于OpenSSL对国密SM2SM3SM4ECDH算法的OC封装) 转基因 OpenSSL 1.1.1及更高版本增加了对基于中文的SM2 / SM3 / SM4加密算法的支持,基于OC封装使用SM2非对称加密,SM2签名验证,ECDH密钥协议,SM3摘要算法和SM4对称加密。 入门 在终端中运行以下命令: git clone https : //github . com / muzipiao / GMObjC . git cd GMObjC / Example pod install open GMObjC . xcworkspace 要求 依赖于OpenSSL 1.1.1或更高版本,已打包为框架,并已上传cocoapods,可以拖动到项目中直接安装,或使用 立即下载

    iPhone项目新成立,也没有编码规范的积累,项目组本来是想拿老的C编码规范套用的,但评审一下就发现问题多多,之后找到了Google的Objective-C的编码规范,大家就先翻译一下咯  

        声明这是无版权翻译,也不对任何错误负责,不保证文章的完整性,我到现在也认不全语法。

        (大半年的事后,决定对这份文档做重审,当然不是对修辞手法,而是处理内部的硬伤)
总览 

背景知识 
        Objective-C是一个C语言的扩展语言,非常动态,非常的“面向对象”,它被设计成既拥有复杂的面向对象设计理念又可以轻松使用与阅读的语言,也是Mac OS X和iPhone开发的首选语言。

        Cocoa是Mac OS X的主要应用框架,提供迅速开发各种功能的Mac OS X应用的Objective-C类集合。

        Apple已经有一个很好也被广泛接受的Objective-C的编码规范,Google也有类似的C++编码规范,这份Objective-C编码规范很自然是Apple和Google的共同推荐的组合。所以,在阅读本规范前,确保你已经阅读了:

        Apple's Cocoa Coding Guidelines 
        Google's Open Source C++ Style Guide 

        注意所有已在Google的C++编码规范里的禁用条款在Objective-C里也适用,除非本文档明确指出反对意见。 

        本文档旨在描述可供可适用于所有Mac OS X代码的Objective-C(包括Objective-C++)编码规范和实践。规范中的许多条款已经改进也不断的被其他的项目和团队所证明其指导性。Google的相关开源项目都遵守此规范。

         Google已经发布了一份作为Google Toolbox for Mac project (文档中简称为GTM)的组成部分的遵守本规范的开源代码。这份开放代码也是本文很好的例证(原文看不太懂--Code meant to be shared across different projects is a good candidate to be included in this repository. )

        注意本文不是Objective-C的教学指南,我们假设读者已经了解语言。如果你是一个Objective-C的初学者或需要重温,请阅读The Objective-C Programming Language .


示例 

        人们说一个例子胜过千言万语,所以就让我们用例子来让你感受以下编码规范的风格,留间距,命名等等。

        下例是一份头文件,展示对@interface 声明正确的注释和留间距

Java代码   收藏代码
  1. //  GTMFoo.h  
  2. //  FooProject  
  3. //  
  4. //  Created by Greg Miller on 6/13/08.  
  5. //  Copyright 2008 Google, Inc. All rights reserved.  
  6. //  
  7.   
  8. #import <Foundation/Foundation.h>  
  9.   
  10. // A sample class demonstrating good Objective-C style. All interfaces,  
  11. // categories, and protocols (read: all top-level declarations in a header)  
  12. // MUST be commented. Comments must also be adjacent to the object they're  
  13. // documenting.  
  14. //  
  15. // (no blank line between this comment and the interface)  
  16. @interface GTMFoo : NSObject {  
  17.  @private  
  18.   NSString *foo_;  
  19.   NSString *bar_;  
  20. }  
  21.   
  22. // Returns an autoreleased instance of GMFoo. See -initWithString: for details  
  23. // about the argument.  
  24. + (id)fooWithString:(NSString *)string;  
  25.   
  26. // Designated initializer. |string| will be copied and assigned to |foo_|.  
  27. - (id)initWithString:(NSString *)string;  
  28.   
  29. // Gets and sets the string for |foo_|.  
  30. - (NSString *)foo;  
  31. - (void)setFoo:(NSString *)newFoo;  
  32.   
  33. // Does some work on |blah| and returns YES if the work was completed  
  34. // successfuly, and NO otherwise.  
  35. - (BOOL)doWorkWithString:(NSString *)blah;  
  36.   
  37. @end  




        下例是一份源文件,展示对接口的@implementation 的实现的正确注释和留间隔。它也包括了主要方法如getters,setters,init ,和dealloc 的相关实现。

Java代码   收藏代码
  1. //  
  2. //  GTMFoo.m  
  3. //  FooProject  
  4. //  
  5. //  Created by Greg Miller on 6/13/08.  
  6. //  Copyright 2008 Google, Inc. All rights reserved.  
  7. //  
  8.   
  9. #import "GTMFoo.h"  
  10.   
  11.   
  12. @implementation GTMFoo  
  13.   
  14. + (id)fooWithString:(NSString *)string {  
  15.   return [[[self alloc] initWithString:string] autorelease];  
  16. }  
  17.   
  18. // Must always override super's designated initializer.  
  19. - (id)init {  
  20.   return [self initWithString:nil];  
  21. }  
  22.   
  23. - (id)initWithString:(NSString *)string {  
  24.   if ((self = [super init])) {  
  25.     foo_ = [string copy];  
  26.     bar_ = [[NSString alloc] initWithFormat:@"hi %d", 3];  
  27.   }  
  28.   return self;    
  29. }  
  30.   
  31. - (void)dealloc {  
  32.   [foo_ release];  
  33.   [bar_ release];  
  34.   [super dealloc];  
  35. }  
  36.   
  37. - (NSString *)foo {  
  38.   return foo_;  
  39. }  
  40.   
  41. - (void)setFoo:(NSString *)newFoo {  
  42.   [foo_ autorelease];  
  43.   foo_ = [newFoo copy];    
  44. }  
  45.   
  46. - (BOOL)doWorkWithString:(NSString *)blah {  
  47.   // ...  
  48.   return NO;  
  49. }  
  50.   
  51. @end  




间隔与格式化 

    空格对tab键 

        仅使用空格,缩进两个。

        我们使用空格用于缩进,不要在编码时使用tab键,你应该设置你的编辑器将tab键转换成对应的空格。


    行长度 
    
        代码中的每行文本不要超过80个字符的长度。

        尽管Objective-C正变得比C++更加繁冗,为了保持规范的互通性,我们还是决定保持80字符长度的限制。这比你想象中的更容易做到。

        我们知道本条款是有争议的,但已有此多的代码已经遵从了本条款,即使只是保持一致性也是一个充足的理由。

        你可以在Xcode里清楚地发现代码中的违规,设置  Xcode > Preferences > Text Editing > Show page guide.(之后就可以在代码编辑区域里看到一条指定字符长度的指示线了)


    方法声明与定义 

        留一个空格在-或+和返回类型之间,但参数列表里的参数之间不要留间隔。

        方法应该写成这样:

Java代码   收藏代码
  1. - (void)doSomethingWithString:(NSString *)theString {  
  2.   ...  
  3. }  



        星号前的空格是可选的,你可以根据原来的代码风格自行决定。

        如果参数过多,推荐每个参数各占一行。使用多行的情况下,以参数前的冒号用于对齐:
        (很遗憾这里仅有Google Chrome浏览器能看出是冒号对齐的......)

Java代码   收藏代码
  1. - (void)doSomethingWith:(GTMFoo *)theFoo  
  2.                    rect:(NSRect)theRect  
  3.                interval:(float)theInterval {  
  4.   ...  
  5. }  



        当第一个关键字比其他的短时,后续行至少缩进四个空格。这样你可以让后续的关键字垂直对齐,而不是用冒号对齐:

Java代码   收藏代码
  1. - (void)short:(GTMFoo *)theFoo  
  2.     longKeyword:(NSRect)theRect  
  3.     evenLongerKeyword:(float)theInterval {  
  4.   ...  
  5. }  




方法调用 


        方法调用的格式和方法声明时的格式时一致的,如果格式风格可选,遵从原有代码的风格。

        调用应该将所有参数写在一行:

Java代码   收藏代码
  1. [myObject doFooWith:arg1 name:arg2 error:arg3];  



        或者每个参数一行,用冒号对齐:
        (对齐效果如前说明)

Java代码   收藏代码
  1. [myObject doFooWith:arg1  
  2.                name:arg2  
  3.               error:arg3];  



        不要使用如下风格的写法

Java代码   收藏代码
  1. [myObject doFooWith:arg1 name:arg2  // some lines with >1 arg  
  2.               error:arg3];  
  3.   
  4. [myObject doFooWith:arg1  
  5.                name:arg2 error:arg3];  
  6.   
  7. [myObject doFooWith:arg1  
  8.           name:arg2  // aligning keywords instead of colons  
  9.           error:arg3];  



        在声明和定义时,如果因为关键字长度使就算有四个空格在前仍然无法用冒号对齐,那么就让后续行缩进四个空格而不是冒号对齐:

Java代码   收藏代码
  1. [myObj short:arg1  
  2.     longKeyword:arg2  
  3.     evenLongerKeyword:arg3];  




    @public 和 @private 

        权限控制符@public 和@private 缩进一个空格.

        类似C++的public,protected,private:

Java代码   收藏代码
  1. @interface MyClass : NSObject {  
  2.  @public  
  3.   ...  
  4.  @private  
  5.   ...  
  6. }  
  7. @end  




    异常 

        每个异常标签的@ 和开括号({ )写在统一行,标签和开括号间隔一个空格,同样适用于@catch 语句。

        如果你决定使用Objective-C的异常,那么就按如下格式,不过你最好先看看Avoid Throwing Exceptions(见后)条款,了解为何你不应使用异常。

Java代码   收藏代码
  1. @try {  
  2.   foo();  
  3. }  
  4. @catch (NSException *ex) {  
  5.   bar(ex);  
  6. }  
  7. @finally {  
  8.   baz();  
  9. }  




命名 


        命名规则对于维护代码来说是非常重要的。Objective-C方法名往往很长,不过这也有好处,读代码就像读散文(放屁),让很多注释变得毫无意义。

        写纯Objective-C代码时,我们基本上遵守标准Objective-C naming rules ,这些规则和C++的规则有很大的不同。比如Google的C++代码规范推荐变量名构词之间使用下划线隔开,而本文档推荐驼峰法,也是Objective-C社区的标准。

        所有类,类别, 方法,以及变量如包括缩写,则缩写部分使用全大写的缩写(Initialisms )形式。这遵守Apple的标准,比如URL,TIFF以及EXIF。

       当写Objective-C++代码时,情况就不是那么单一了。许多项目需要实现带一些Objective-C代码的跨平台的C++APIs或者连接后台的C++代码与前台的原生Cocoa代码.这会造成两种规范直接冲突。

        我们的解决方法是根据方法/函数风格来决定。如果在@implementation 块,就使用Objective-C的命名规则;如果在C++的方法实现块,就使用C++的命名规则。避免了实体变量和本地变量在一个函数内命名规则冲突的情况,而这种情况是对可读性的极大损害。

    文件命名 

        文件名反映了它所包含的实现类的名字,遵从你所在项目的习惯。

        文件扩展名使用如下规则

.h    C/C++/Objective-C header file

 

.m    Objective-C implementation file

 

.mm   Objective-C++ implementation file

 

.cc   Pure C++ implementation file

 

.c    C implementation file



        类别的文件名应该包含扩展类的名字,比如GTMNSString+Utils.h or GTMNSTextView+Autocomplete.h 


   Objective-C++ 

        在一份源文件里,Objective-C++代码遵守当前方法/函数的风格

        为了尽量减少不同命名风格间的冲突,使用当前方法的风格。如果在@implementation块,使用Objective-C命名规则,如果在C++类的函数实现块,使用C++命名规则。

Java代码   收藏代码
  1. // file: cross_platform_header.h  
  2.   
  3. class CrossPlatformAPI {  
  4.  public:  
  5.   ...  
  6.   int DoSomethingPlatformSpecific();  // impl on each platform  
  7.  private:  
  8.   int an_instance_var_;  
  9. };  
  10.   
  11. // file: mac_implementation.mm  
  12. #include "cross_platform_header.h"  
  13.   
  14. // A typical Objective-C class, using Objective-C naming.  
  15. @interface MyDelegate : NSObject {  
  16.  @private  
  17.   int instanceVar_;  
  18.   CrossPlatformAPI* backEndObject_;  
  19. }  
  20. - (void)respondToSomething:(id)something;  
  21. @end  
  22. @implementation MyDelegate  
  23. - (void)respondToSomething:(id)something {  
  24.   // bridge from Cocoa through our C++ backend  
  25.   instanceVar_ = backEndObject->DoSomethingPlatformSpecific();  
  26.   NSString* tempString = [NSString stringWithInt:instanceVar_];  
  27.   NSLog(@"%@", tempString);  
  28. }  
  29. @end  
  30.   
  31. // The platform-specific implementation of the C++ class, using  
  32. // C++ naming.  
  33. int CrossPlatformAPI::DoSomethingPlatformSpecific() {  
  34.   NSString* temp_string = [NSString stringWithInt:an_instance_var_];  
  35.   NSLog(@"%@", temp_string);  
  36.   return [temp_string intValue];  
  37. }  




    类命名 

        类名(不包括类别和协议名)应该用大写开头的驼峰命名法。

        在应用级别的代码里,尽量不要使用带前缀的类名。每个类都有相同的前缀不能提高可读性。不过如果是编写多个应用间的共享代码,前缀就是可接受并推荐的做法了(型如 GTMSendMessage )。

    类别命名 

        类别命名应该以两三个字符的分类前缀作为一个项目或通用的公用部分。类别名应该包含类的扩展。

        举个例子,如果我们想要创建一个基于NSString 的类别用于解析,我们应该把类别放到名字是GTMNSString+Parsing.h 的文件里,而类别本身的名字则是GTMStringParsingAdditions (是的,我们明白这个类别和其文件名字不匹配,但这个文件可能还包括其他用于解析相关的类别)。类别的方法应该都使用一个前缀(型如gtm_myCategoryMethodOnAString ),以防止Objective-C代码在单名空间里冲突。如果代码本来就不考虑共享或在不同的地址空间(address-space),方法命名规则就没必要恪守了。

    Objective-C 方法命名 

        方法使用小写开头的驼峰法命名,每个参数都应该小写开头。

        方法名应该尽可能读起来像一句话,参数名就相对方法名的补充说明(比如convertPoing:fromRect: 或者replaceCharactersInRange:withString: ),详见Apple's Guide to Naming Methods 

        存取(Accessor)方法应该一致的在"取(getting)"的时候直接用变量名而不是在签名加"get",如下:

Java代码   收藏代码
  1. - (id)getDelegate;  // AVOID  
  2.   
  3. - (id)delegate;    // GOOD  


       不过这仅针对Objective-C代码,C++代码仍然遵循自己的代码规范。

    变量命名 

        变量名使用小写开头的驼峰法,类成员变量名最后加一个下划线,比如:myLovalVariable, myInstanceVariable_ . 下面看不懂,原文Members used for KVO/KVC bindings may begin with a leading underscore iff use of Objective-C 2.0's @property isn't allowed. 

      一般变量命名 
        
        不要使用匈牙利命名法去标记语法,比如静态类型或变量类型(int或pointer之类的)。使变量名尽量可以推测其用途属性具有描述性。别一心想着少打几个字母,让你的代码可以迅速被理解更加重要。比如:

Java代码   收藏代码
  1. // AVOID  
  2. int w;  
  3. int nerr;  
  4. int nCompConns;  
  5. tix = [[NSMutableArray alloc] init];  
  6. obj = [someObject object];  
  7. p = [network port];  
  8.   
  9. // GOOD  
  10. int numErrors;  
  11. int numCompletedConnections;  
  12. tickets = [[NSMutableArray alloc] init];  
  13. userInfo = [someObject object];  
  14. port = [network port];  



      实体变量 

        实体变量用驼峰法命名并后缀下划线,就像usernameTextField_ . 然而我们允许一种例外就是用KVO/KVC去绑定一个实体变量而Objective-C 2.0 不能用(因为操作系统的限制)的情况,此时也可用前缀下划线的方法给每个变量命名。如果可以使用Objective-C 2.0,@property 和 @synthesize 提供了遵守命名规范的解决方法。

      常量 
   
        常量(预定义,枚举,局部常量等)使用小写k开头的驼峰法,比如kInvalidHandle , kWritePerm . 


注释 

        尽管写起来很痛苦,但注释仍然是使代码保持可读性的极端重要的方式。下面的条款描述了你应该注释什么以及在哪里做注释。但是记住:即使注释是如此重要,最好的代码还是自说明式的。起一个有意义的名字比起一个晦涩的名字然后在用注释去解释它好的多。

        当你写注释的时候,记住注释是写给读者,即下一个要理解你的代码并继续开发的人。"下一个"完全可能就是你自己。

       同样,所有C++编码规范的条款仍然适用,只是增加了一些条款,如下.

    文件注释 

        每个文件的开头都是版权声明,接着是文件内容的描述。

      法律声明和作者栏 

        每个文件都应该包含如下信息:

            一份版权声明(比如 Copyright 2008 Google Inc .)
            许可版本 为项目选择合适的许可版本(比如Apache 2.0, BSD, LGPL, GPL)

        如果你把别人写的文件做了相当大的改动,就把自己添加到作者栏去。这样别的开发者就方便联系这个文件的实际开发人员了。

    声明注释 

        每个接口,类别,协议都必须伴随描述它的用途以及如何整合的注释。

Java代码   收藏代码
  1. // A delegate for NSApplication to handle notifications about app  
  2. // launch and shutdown. Owned by the main app controller.  
  3. @interface MyAppDelegate : NSObject {  
  4.   ...  
  5. }  
  6. @end  



        如果已经在文件的顶部写了接口的详细描述,你也可以简单的写如"见文件顶部的完整描述",当然要有这些注释的顺序安排。

        此外public接口的每个方法都应该添加关于函数,参数,返回值以及副作用的注释。

        文档默认类都是同步的,如果类实例可以多线程访问,必须要加上额外的说明。

    实现注释 

        使用竖线引用变量或符号,而不是用引号。

        这可以减少歧义,特别是当符号本身就是个常见的词时,可能使句子显得支离破碎,比如符号是"count":

Java代码   收藏代码
  1. // Sometimes we need |count| to be less than zero.  



        或者是对于那些已经存在引号的情况

Java代码   收藏代码
  1. // Remember to call |StringWithoutSpaces("foo bar baz")|  



    对象所有权 

        使指针所有权的模型尽可能清晰,当它属于Objective-C的使用惯例时(不懂,原文是Make the pointer ownership model as explicit as possible when it falls outside the most common Objective-C usage idioms. )

        实例变量指向NSObject派生类的对象时都假定是retain的,如果它们不是retain的则需要加上"weak"的文档说明。对应的,实体变量如果标记上IBOutlets则是假定为非retain的,若实际上用了retain,就必须加上"strong"的说明。

        当实例变量指向核心库,C++或其他非Objective-C对象时,必须永远用注释说明是strong还是weak的。必须注意为了支持Objective-C对象里的自动化C++对象的封装是默认被关闭的的(这句话有歧义,原文是Be mindful that support for automatic C++ objects encapsulated in Objective-C objects is disabled by default),这里 有说明。

        strong和weak说明的文档示例:

Java代码   收藏代码
  1. @interface MyDelegate : NSObject {  
  2.  @private  
  3.   IBOutlet NSButton* okButton_;  // normal NSControl  
  4.   IBOutlet NSMenu* myContextMenu_;  // manually-loaded menu (strong)  
  5.   
  6.   AnObjcObject* doohickey_;  // my doohickey  
  7.   MyController* controller_;  // so we can send msgs back (weak, owns me)  
  8.   
  9.   // non-NSObject pointers...  
  10.   CWackyCPPClass* wacky_;  // some cross-platform object (strong)  
  11.   CFDictionaryRef* dict_;  // (strong)  
  12. }  
  13. @end  



        strong
            对象会在类中retain 
        weak
           对象不会在类中retain   (比如一个委托)


Cocoa和Objective-C特性 

    成员变量应该定义为@private

Java代码   收藏代码
  1. @interface MyClass : NSObject {  
  2.  @private  
  3.   id myInstanceVariable_;  
  4. }  
  5. // public accessors, setter takes ownership  
  6. - (id)myInstanceVariable;  
  7. - (void)setMyInstanceVariable:(id)theVar;  
  8. @end  



    明确指定初始化 

       注释并说明指定的初始化。

       明确指定初始化对想要子类化你的类的时候时很重要的。那样,子类化时只需要做一个或多个初始化去保证初值即可。这也有助于在以后调试你的类时明了初始化流程。

    重写指定初始化 

        当重写一个子类并需要init... 方法,注意要重写父类的指定初始化方法。

        如果你没有正确重写父类的指定初始化方法,你的初始化方法可能不会被调用,这会导致很多微妙而难以排除的错误。

    初始化 

        没必要在初始化方法里把变量初始化为0 或者nil ,这是多余的。

        所有新分配内存的对象内容都初始化为0(除了 isa ),所以不要在init 方法里做无谓的重初始化为0的操作。


    保持公有API简明 

        保持你的类简单,避免"厨房水槽"似的APIs,如果一个方法没必要公开就不要公开。使用私有类别保证公开头文件的简洁。

        和C++不同,Objective-C无法区分公有私有方法,因为它全是公有的。因此,除非就是为了让用户调用所设计,不要把其他的方法放到公有API里。这样可以减少不期调用的可能性。这还包括重写父类的方法。对于那些内部实现的方法,在实现文件里使用类别而不是将方法定义在公有头文件里。

Java代码   收藏代码
  1. // GTMFoo.m  
  2. #import "GTMFoo.h"  
  3.   
  4. @interface GTMFoo (PrivateDelegateHandling)  
  5. - (NSString *)doSomethingWithDelegate;  // Declare private method  
  6. @end  
  7.   
  8. @implementation GTMFoo(PrivateDelegateHandling)  
  9. ...  
  10. - (NSString *)doSomethingWithDelegate {  
  11.   // Implement this method  
  12. }  
  13. ...  
  14. @end  



        在Objective-C 2.0之前,如果你在私有@interface 里声明了一个方法,但忘记在主@implementation 文件里实现了,编译器不会有什么反应(这是因为你没有在不同的类别里实现这些私有方法)。解决方案在是把函数写到@implementation 里并指明类别。

        如果你用的是 Objective-C 2.0,你应该使用类扩展 而不是声明私有类别,如下:

Java代码   收藏代码
  1. @interface GMFoo () { ... }  


       如此就可以保证函数做了声明但没有在@implememtation 里实现的时候编译器会警报。

        再者,"private"方法并不是真正的private,你可能会无意间重写了父类的一个"private"方法,这回导致bug的涌现。总的来说,私有方法应该使用更特别的名字以阻止子类化时并不期望的重写。

        最后,对于绝大多数类而言,Objective-C的类别是将@implelemtation做可理解的分块,添加新的应用级别的功能的最佳途径。比如,与其在你的项目里随便找个类来实现字符串的"中间截断"功能,不如创建一个新的NSString 类别。

    #import 和 #include 

        用#import 导入Objective-C或Objective-C++头文件,用#include 导入C或C++头文件

        根据头文件的语言去选择合适的导入方式。

            当导入的头文件使用Objective-C或Objective-C++语言时,使用#import .
            当导入标准C或 C++头文件时,使用#include . 头文件应该使用自己的#define 重加载保护

        有些Objective-C头文件没有#define 重加载保护,所以只应该用#import 导入。因此Objective-C头文件只应该被Objective-C源文件或其他的Objective-C头文件所导入。这种情况下全部使用#import 是合适的。

        标准C和C++头文件不包含任何Objective-C元素都可以被一般的C或C++文件导入。因为标准C和C++里根本没有#import ,所以也只能用#include 导入。在Objective-C代码中使用#include 一致的导入这些头文件。

        本条款有助于跨平台项目的无意错误。一位Mac开发者引入一份新C或C++头文件时可能会忘记添加#define重加载保护,因为在Mac上用#import 导入文件不会引发问题,但在别的使用#include 的平台就可能出问题。在所有平台一致的使用#include 意味着要么全部成功要么全部失败,避免了那种另人沮丧的一些平台上可以运作而另一些不行的情况。

Java代码   收藏代码
  1. #import <Cocoa/Cocoa.h>  
  2. #include <CoreFoundation/CoreFoundation.h>  
  3. #import "GTMFoo.h"  
  4. #include "base/basictypes.h"  




    使用框架根 

        导入框架根的头文件而不是分别导入框架头文件

        看起来从Cocoa或Foundation这些框架里导入个别的文件很不错,但实际上你直接导入框架根头文件效率更高。框架根已经被预编译故可更快的被加载。还有,记住用#import 指令而不是#include 导入Objective-C的框架。

Java代码   收藏代码
  1. #import <Foundation/Foundation.h>     // good  
  2.   
  3. #import <Foundation/NSArray.h>        // avoid  
  4. #import <Foundation/NSString.h>  
  5. ...  



    构建时即设定autorelease 

        当创建新的临时对象时,在同一行代码里就设定autorelease而不是写到这个方法的后面几行去

        即使这样可能会造成一些轻微的延迟,但这样避免了谁不小心把release 去掉,或在release 之前就return 而造成的内存泄露,如下

Java代码   收藏代码
  1. // AVOID (unless you have a compelling performance reason)  
  2. MyController* controller = [[MyController alloc] init];  
  3. // ... code here that might return ...  
  4. [controller release];  
  5.   
  6. // BETTER  
  7. MyController* controller = [[[MyController alloc] init] autorelease];  




    优先autorelease而非retain 

        对象赋值时尽量采用autorelease 而不是retian 模式。

        当把一个新创建的对象赋予一个变量的时候,第一件要做的事情就是先释放原来变量指向的对象以防止内存泄露。这里也有很多"正确的"方法去做这件事。我们选择autorelease时因为它更不倾向于出错。小心在密集的循环里可能会很快填满autorelease池,而且它也确实会降低效率,但权衡下来还是可以接受的。

Java代码   收藏代码
  1. - (void)setFoo:(GMFoo *)aFoo {  
  2.   [foo_ autorelease];  // Won't dealloc if |foo_| == |aFoo|  
  3.   foo_ = [aFoo retain];  
  4. }  



    以声明时的顺序dealloc处理实例变量 

        dealloc 应该用在@interface 声明时同样的顺序处理实例变量,这也有助于评审者鉴别。

        代码评审者检查或修正dealloc 的实现要确保所有retain 的实例变量都获得了释放。

       为了简化评审dealloc ,将释放retain 的实例变量代码保持和@interface 里声明的顺序一致。如果dealloc 调用了其他方法去释放实例变量,添加注释说明那些实例变量被这些方法所处理了。

    Setters copy NSStrings 

        在NSString 上调用Setters 方法时,永远使用copy 方式。(不太懂,原文是Setters taking an NSString, should always copy  the string it accepts. )

        永远不要retain 一个字符串,这可以防止调用者在你不知到的情况下修改了字符串。不要以为你可以改变NSString 的值,只有NSMutableString 才能做到。

Java代码   收藏代码
  1. - (void)setFoo:(NSString *)aFoo {  
  2.   [foo_ autorelease];  
  3.   foo_ = [aFoo copy];  
  4. }  



    避免抛出异常 

        不要 @throw Objective-C的异常,不过你还是要做好准备捕获第三方以及系统调用抛出的异常。

        我们的确在编译时加入了-fobjc-exceptions 指令(主要是为了获得@synchronized ),但我们并不@throw 。当然在使用第三方库的时候是允许使用@try,@catch, 以及@finally 的。如果你确实使用了,请务必明确到文档中哪个方向你想抛出什么异常。

        除非你写的代码想要泡在MacOS 10.2或更之前,否则不要使用NS_DURING, NS_HANDLER, NS_ENDHANDLER, NS_VALUERETURN and NS_VOIDRETURN 这些宏。

        另外你要小心当写Objective-C++代码的时候,如果抛出Objective-C异常,那些栈上的对象不会被清理。示例:

Java代码   收藏代码
  1. class exceptiontest {  
  2.  public:  
  3.   exceptiontest() { NSLog(@"Created"); }  
  4.   ~exceptiontest() { NSLog(@"Destroyed"); }  
  5. };  
  6.   
  7. void foo() {  
  8.   exceptiontest a;  
  9.   NSException *exception = [NSException exceptionWithName:@"foo"  
  10.                                                    reason:@"bar"  
  11.                                                  userInfo:nil];  
  12.   @throw exception;  
  13. }  
  14.   
  15. int main(int argc, char *argv[]) {  
  16.   GMAutoreleasePool pool;  
  17.   @try {  
  18.     foo();  
  19.   }  
  20.   @catch(NSException *ex) {  
  21.     NSLog(@"exception raised");  
  22.   }  
  23.   return 0;  
  24. }  


        将会有如下输出:

Java代码   收藏代码
  1. 2006-09-28 12:34:29.244 exceptiontest[23661] Created  
  2. 2006-09-28 12:34:29.244 exceptiontest[23661] exception raised  



        注意这里的析构函数永远没有机会被调用。这是在你想用栈上的智能指针比如shared_ptr,linked_ptr ,还有STL对象的时候不得不关注的一个核心问题。我们不得不痛心地说,如果你一定要在Objective-C++代码里抛出异常,那就请一定使用C++的异常。永远不要重新抛出一个Objective-C的异常,也不允许在异常块即@try,@catch,@finally 里生成栈上的 C++对象(比如std::string, std::vector 等).

    nil检查 

       仅在校验逻辑流程时做nil检查。

        使用nil检查不是为了防止程序崩溃,而是校验逻辑流程。向一个空对象发送一条消息是由Objective-C运行时处理的。方法没有返回结果,你也可以安心走下去.然而这里也有一种,依执行架构不同而返回尺寸和OS X的版本(这段不懂)(见)Apple's documentation for specifics 。

        注意这里和C/C++的空指针检查是完全不同的,在那些环境里,并不处理空指针情况并可能导致你的应用程序崩溃。不过你仍要自己确保提领的指针不为空。

    BOOL类型陷阱 

        整形的转换为BOOL 型的时候要小心。不要直接和YES做比较。

        BOOL 在Objective-C里被定义为unsigned char,这意味着它不仅仅只有YES (1)和NO (0)两个值。不要直接把整形强制转换为BOOL 型。常见的错误发生在把数组大小,指针的值或者逻辑位运算的结果赋值到BOOL型中,而这样就导致BOOL 值的仅取决于之前整形值的最后一个字节,有可能出现整形值不为0但被转为NO的情况。应此把整形转为BOOL型的时候请使用ternery操作符,保证返回YES 或NO 值。

        在BOOL,_BOOL 以及bool (见C++ Std 4.7.4, 4.12以及C99 Std 6.3.1.2)之间可以安全的交换值或转型。但BOOL 和Boolean 之间不可,所以对待Boolean 就像上面讲的整形一样就可以了。在Objective-C函数签名里仅使用BOOL 。

    对BOOL值使用逻辑运算(&&, ||, ! )都是有效的,返回值也可以安全的转为BOOL型而不需要ternery操作符。

Java代码   收藏代码
  1. // AVOID  
  2. - (BOOL)isBold {  
  3.   return [self fontTraits] & NSFontBoldTrait;  
  4. }  
  5. - (BOOL)isValid {  
  6.   return [self stringValue];  
  7. }  
  8.   
  9. // GOOD  
  10. - (BOOL)isBold {  
  11.   return ([self fontTraits] & NSFontBoldTrait) ? YES : NO;  
  12. }  
  13. - (BOOL)isValid {  
  14.   return [self stringValue] != nil;  
  15. }  
  16. - (BOOL)isEnabled {  
  17.   return [self isValid] && [self isBold];  
  18. }  



        还有,不要把BOOL 型变量直接与YES 比较。这样不仅对于精通C的人很有难度,而且此条款的第一点也说明了这样做未必能得到你想要的结果。

Java代码   收藏代码
  1. // AVOID  
  2. BOOL great = [foo isGreat];  
  3. if (great == YES)  
  4.   // ...be great!  
  5.   
  6. // GOOD  
  7. BOOL great = [foo isGreat];  
  8. if (great)  
  9.   // ...be great!  



    属性 

        属性总的来说是遵循如下告诫的: 属性是Objective-C 2.0的特性,所以只能跑在iPhone以及MacOS X 10.5(leopard)或更高的版本。点表示法访问属性是不被允许的。

      命名 

        一个有属性关联实例变量都要在后面加下划线,而该属性的名称就是实例变量不加尾部的下划线的名字。

        使用@synthesize 标识以正确的重命名属性。

Java代码   收藏代码
  1. @interface MyClass : NSObject {  
  2.  @private  
  3.   NSString *name_;  
  4. }  
  5. @property(copy, nonatomic) NSString *name;  
  6. @end  
  7.   
  8. @implementation MyClass  
  9. @synthesize name = name_;  
  10. @end  



      位置 

        在类接口声明里,属性的声明必须紧挨着实例变量块声明之后。类定义里,属性的定义必须紧挨着@implementation块。缩进和@interface或@implementation是一样的。

Java代码   收藏代码
  1. @interface MyClass : NSObject {  
  2.  @private  
  3.   NSString *name_;  
  4. }  
  5. @property(copy, nonatomic) NSString *name;  
  6. @end  
  7.   
  8. @implementation MyClass  
  9. @synthesize name = name_;  
  10. - (id)init {  
  11. ...  
  12. }  
  13. @end  



      对字符串使用拷贝特性 

       NSString 类型的属性应该永远声明为带有copy attribute 

        这点是因为NSString 的setters 永远使用copy 而不是retain 。

      永远不要对CFType属性使用synthesize 

        CFType类型应该永远遵守@dynamic的实现规则。

        因为CFType类型不能有reatin 属性特性,开发者必须自己维护。(下面不懂,原文In the rare case that you do actually want assignment it is better to make that completely clear by actually implementing the setter and getter and commenting why that is the case. )

      列出所有的实现指令 

        为所有属性使用实现指令,即使默认是@dynamic

        尽管@dynamic是默认的,显式的列出所有属性实现指令还是让类中的每个属性更加清楚。

Java代码   收藏代码
  1. // AVOID  
  2. @interface MyClass : NSObject  
  3. @property(readonly) NSString *name;  
  4. @end  
  5.   
  6. @implementation MyClass  
  7. .  
  8. .  
  9. .  
  10. - (NSString*)name {  
  11.   return @"foo";  
  12. }  
  13. @end  
  14.   
  15. // GOOD  
  16. @interface MyClass : NSObject  
  17. @property(readonly) NSString *name;  
  18. @end  
  19.   
  20. @implementation MyClass  
  21. @dynamic name;  
  22. .  
  23. .  
  24. .  
  25. - (NSString*)name {  
  26.   return @"foo";  
  27. }  
  28. @end  



      原子性 

        属性使用过犹不及。默认情况,所有的synthesized setters 以及 getters  默认都是原子性的,显然所有的get,set方法都过度使用了同步方法。所以除非你确实需要原子性,否则都将你的属性声明为nonatomic 

      点表示法 

        我们禁止使用点表示法是基于如下理由的:
            1. 点表示法不过是标准函数调用的语法糖,它的可读性很有争议。也不过就是换个形式调用函数而已。
            2. 它使得提领操作含糊不请.首先看: [foo setBar:1] 很清楚你就明白这是Objective-C对象上的操作。那么看 foo.bar = 1 就不太清楚到底是对Objective-C的对象还是结构体/公用体/C++类进行操作了。
            3. 它使得函数调用看起来就像getters

Java代码   收藏代码
  1. NSString *upperCase = @"foo".uppercaseString;  


               不仅使人困惑,也很难在代码审核的时候检查出来。
            4. 它隐藏了函数调用

Java代码   收藏代码
  1. bar.value += 10;  


               这实际上做了两次函数调用(一次get一次set),而如果你的类很复杂的话,你发现一大堆事都在幕后做完了。

Cocoa模式 

    委托模式 

        委托对象不应该是retain 

        一个实现委托模式的类应该:
            1. 实例变量命名为delegate_ 以显示这是个委托
            2. 因此,访存方法名就为delegate 和setDelegate 
            3. delegate_ 变量不能设为retain 

    MVC模式 

        将模型与视图分离,将控制器从视图和模型中分离,回调APIs使用@protocol 

        模型与视图分离: 不要对模型或数据源的表现形式做任何假设。保持数据与表现层的接口的抽象性。模型不对视图有任何了解(一个好的实践就是询问你自己是否能对数据有多个不同形式的表现方法)

        控制器从视图和模型中分离: 不要把"业务逻辑"放到视图相关的类;这会导致代码可复用性下降。让控制器的类承管代码,但不要对表现形式做太多假设。

        用@protocol 定义回调APIs: 如果不是所有的方法都必须实现就使用@optional (例外:在Objecitve-C 1.0的时候,@optional 还不可用,所以用类别去定义一个"非标准协议(informal protocol)")

国密SM2、SM3、SM4算法封装 本Demo是用Android代码来实现的【国密SM2、SM3、SM4】的算法,Java均可使用,代码叫简洁,每个加密算法几行代码均可实现。 立即下载

相关推荐

双卡RTX 3090实战:基于vLLM高效部署Qwen2.5-14B BF16大模型的完整指南

本文提供了在双卡RTX 3090上使用vLLM推理框架高效部署Qwen2.5-14B BF16大模型的完整实战指南。内容涵盖硬件配置、系统环境准备、vLLM核心原理与安装、模型获取、详细部署步骤(包括脚本启动与API服务),以及性能调优与避坑技巧,旨在帮助开发者充分利用硬件性能,实现稳定、高速的本地大模型推理服务。

yy01234的博客 1512

IOS开发 -- 利用GMObjC进行SM2,SM4加解密

"GMObjC"github链接 在podfile里添加: pod 'GMObjC' SM2的加密 SM2PubKey替换为公钥 +(NSString *)encrypt:(NSString *)url{ NSString *plaintext = url; // ordinary plaintext NSString *enResult1 =[GMSm2Utils encryptText:plaintext publicKey:SM2PubKey]; //openssl..

jieWang2的博客 4776

Linux下用SquareLine Studio开发LVGL界面:从安装到实战的完整避坑指南

本文提供了一份在Linux系统下使用SquareLine Studio开发LVGL图形界面的完整实战指南。内容涵盖从SDL库安装、SquareLine Studio环境配置、界面可视化设计,到工程导出、源码编译及常见错误排查的全流程。重点解析了如何将设计界面无缝集成到Linux开发流程中,并针对嵌入式设备移植提供了关键步骤,旨在帮助开发者高效避坑,快速上手LVGL界面开发。

egg99的博客 905

【亲测免费】 GMObjC安装与配置完全指南

**项目名称:** GMObjC **主要编程语言:** Objective-C **项目简介:** GMObjC 是一个基于 OpenSSL 实现的加密库,它支持国密算法 SM2(非对称加密)、SM3(哈希摘要算法)、SM4(对称加密)以及 ECDH 密钥交换协议。该库旨在简化在iOS开发中应用这些中国国家标准加密算法的过程。 ## 关键技术和框架 - **核心库:** OpenSS...

gitblog_07085的博客 838

Vivado 2018.3实战:用IDELAYE3和ISERDESE3搞定高速ADC数据采集(附仿真源码)

本文详细介绍了在Vivado 2018.3中使用Xilinx UltraScale架构的IDELAYE3和ISERDESE3原语实现高速ADC数据采集的实战方法。通过分析5GHz采样系统的时序挑战,提供从PCB布局到Vivado实现的完整解决方案,包括精密时序校准、多相位数据重组及系统级调试技巧,并附赠仿真源码助力工程师快速掌握高速数据采集技术。

weixin_30567225的博客 582

ANSYS ICEM CFD 2023R2 结构化网格实战:翼型绕流 10 步生成高质量六面体网格

在计算流体力学(CFD)分析中,网格质量直接决定了仿真结果的精度和收敛性。对于翼型绕流这类典型的外部流动问题,结构化网格因其规则的节点排列和优异的边界层捕捉能力,成为高精度模拟的首选方案。通过在后缘处添加局部H型拓扑,并将过渡区增长率降至1.1,最终使网格质量指标提升40%,计算收敛速度提高2倍。这印证了结构化网格在复杂流动模拟中的不可替代性——虽然初期投入时间较多,但能显著提升计算精度和稳定性。在开始网格划分前,合理的几何准备是确保后续流程顺利的关键。对于翼型绕流,O型网格拓扑能提供最佳的边界层适应性。

weixin_30731305的博客 364

IOS记录解决GMObjC 国密算法 SDK导入项目与百度地图SDK中的libssl.a冲突全过程

项目使用到百度地图的sdk(BaiduMapKit),最近需要集成一个第三方的SDK,其中用到GMObjC 国密算法 SDK。首先是按照集成文档通过cocopods集成一波:pod 'GMObjC','3.0.0',导入完成后编译项目发现报错,报错信息如下 接着是一脸的懵逼,这看来是这个路径下/BaiduMapKit/BaiduMapKit/thirdlibs/libssl.a的某些.o文件跟/GMOpenSSL/GMOpenSSL/openssl.framework/openssl下的.o文件有重.

j15087159186的博客 2546

《数据结构》02-线性结构1 两个有序链表序列的合并

题目 本题要求实现一个函数,将两个链表表示的递增整数序列合并为一个非递减的整数序列。 函数接口定义: List Merge( List L1, List L2 ); 其中List结构定义如下: typedef struct Node *PtrToNode; struct Node { ElementType Data; /* 存储结点数据 */ PtrToNode Next;...

叫我皮卡丘 7665

Google的Objective-C编码规范(代码规范很重要,规范的代码很有美感。)

Google的Objective-C编码规范 博客分类:  iPhone/Mac开发 Objective-CC++C#CGoogle          原文Google Objective-C Style Guide          iPhone项目新成立,也没有编码规范的积累,项目组本来是想拿老的C编码规范套用的,但评审一下就发现问题多多,之后找到了Goo

春风得意,马蹄仍急;少年豪情,一笑难回。 2237

Anaconda+清华源加速PyTorch GPU环境安装与CUDA版本匹配指南

深度学习环境管理常面临依赖冲突与下载缓慢问题,通过虚拟环境隔离与镜像源加速可显著提升效率。Anaconda利用conda机制创建独立Python环境,避免包版本冲突;清华源通过国内镜像同步大幅提高下载速度。GPU版PyTorch安装需严格匹配CUDA、cuDNN与显卡驱动版本,否则易出现CUDA不可用错误。该方案适用于图像分割、目标检测等需多版本PyTorch并存的场景,能快速搭建稳定GPU训练环境。本文详解Anaconda安装、清华源配置及PyTorch GPU版安装全流程。

weixin_30522983的博客 236

YOLO26模型应用过程中的报错处理及疑问解答,涉及环境搭建、模型训练、模块改进、论文写作

本文记录了YOLO26模型的这一系列过程中可能发生的问题,以及大家非常关心的问题,这里统一做了一个记录和解答。

Limiiiing的博客 241

转-Google的Objective-C编码规范

http://bukkake.iteye.com/blog/695492   原文Google Objective-C Style Guide         iPhone项目新成立,也没有编码规范的积累,项目组本来是想拿老的C编码规范套用的,但评审一下就发现问题多多,之后找到了Google的Objective-C的编码规范,大家就先翻译一下咯          声明这是无版权翻译,也不对任何...

weixin_33860528的博客 229

非结构化数据在大数据湖中的应用实践

想象一下,你正在整理一个杂乱的书房。有些书整整齐齐地放在书架上,每本书都有明确的标题、作者和出版日期——这些就是结构化数据,它们像数据库中的表格,有固定的格式和字段。但书房里还有更多东西:散落的照片、手写的笔记、录音带、视频光盘、甚至还有画作。这些物品没有固定的格式,每件都独一无二——这就是非结构化数据。在技术术语中,非结构化数据是指没有预定义数据模型或组织形式的信息。文本文档(Word、PDF、TXT)电子邮件和社交媒体内容图像和照片(JPG、PNG)音频文件(MP3、WAV)

软件工程实践的博客 361

[转]Google的Objective-C编码规范

总览背景知识 Objective-C是一个C语言的扩展语言,非常动态,非常的“面向对象”,它被设计成既拥有复杂的面向对象设计理念又可以轻松使用与阅读的语言,也是Mac OS X和iPhone开发的首选语言。 Cocoa是Mac OS X的主要应用框架,提供迅速开发各种功能的Mac OS X应用的Objective-C类集合。 App...

weixin_30808575的博客 191

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

Google Objective-C编码规范

<br />http://google-styleguide.googlecode.com/svn/trunk/objcguide.xml

verycctv的专栏 480

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

(转)Google的Objective-C编码规范

(转自 http://bukkake.iteye.com/blog/695492)(下面是人家翻译的,翻的挺好。ps:我记得有中文版的了。)     原文Google Objective-C Style Guide         iPhone项目新成立,也没有编码规范的积累,项目组本来是想拿老的C编码规范套用的,但评审一下就发现问题多多,之后找到了Google的Objective-C的编码...

weixin_34408717的博客 124

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

上一篇: 乐元素CTO凌聪-基于cocos2d-x二次开发的自有引擎方案分享
下一篇: 禅道项目管理软件介绍
YearWang
博客等级 码龄20年 143粉丝 153原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值