objective-c 编写规范
2013-02-25 14:37:34 martinszj 阅读数 190

基本规则

  • 所有的花括号都放到下一行. 
  • 不使用TAB, 使用空格. 
  • 二元操作符之间需要有空格, 一元操作符之间没有空格. 

类声明

  • 示例代码:
    @interface WBUser : NSObject
  • {
  •     NSNumber *userID;                   // 用户UID.
  •     NSString *screenName;               // 微博昵称.
  •     NSString *name;                     // 友好显示名称, 如Bill Gates, 名称中间的空格正常显示(此特性暂不支持).
  •     NSString *desc;                     // 个人描述.
  •     Gender gender;                      // 性别, m--男, f--女, n--未知.
  •     int onlineStatus;                   // 在线状态.
  •     NSString *remark;                   // 备注信息, 在查询用户关系时提供此字段.
  • }
  • WBUser : NSObject之间需要有空格. 
  • NSNumber *userID;NSNumber*之间有空格, *userID之间没有空格, userID;之间也没有空格. 
  • 注释为了保持美观, 应统一缩进到一个位置上. 

属性

  • 示例代码:
    @property (nonatomic, retain) NSNumber *userID;
  • @property (nonatomic, retain) NSString *screenName;
  • @property (nonatomic, retain) NSString *name;
  • @property (nonatomic, retain) NSString *desc;
  • @property (nonatomic, assign) Gender gender;
  • @property (nonatomic, assign) int onlineStatus;
  • @property (nonatomic, retain) NSString *remark;
  • @synthesize userID;
  • @synthesize screenName;
  • @synthesize name;
  • @synthesize desc;
  • @synthesize gender;
  • @synthesize onlineStatus;
  • @synthesize remark;
  • @property (nonatomic, retain) NSNumber *userID;@property(之间有空格, nonatomic,retain之间有空格, )NSNumber之间有空格. 
  • @synthesize userID;@synthesizeuserID;之间有空格. 

方法声明

  • 示例代码:
    + (NSMutableArray *)objectsWithArray:(NSArray *)array;
  • + (id)objectWithDictionary:(NSDictionary *)dict;
  • - (id)initWithDictionary:(NSDictionary *)dict;
  • - (BOOL)updateWithDictionary:(NSDictionary *)dict;
  • +-与返回类型之间需要加空格. 
  • 返回类型或参数中的的类名与*之间需要加空格. 
  • :与参数之间不加空格. 

方法实现

  • 示例代码:
    + (NSMutableArray *)objectsWithArray:(NSArray *)array
  • {
  • }
  • - (void)imagePickerController:(UIImagePickerController *)picker 
  •         didFinishPickingImage:(UIImage *)img 
  •                   editingInfo:(NSDictionary *)editInfo
  • {
  • }
  • 写法与方法声明相同. 
  • 括号需要在下一行. 
  • 当方法名超过限定的80字符时, 需要将每个参数分行, 每个参数一行, 以:对齐. 

If语句

  • 示例代码:
    if ([genderString isEqualToString:@"m"])
  • {
  •     self.gender = kGenderMale;
  • }
  • else if ([genderString isEqualToString:@"f"])
  • {
  •     self.gender = kGenderFemale;
  • }
  • else ifelse单独在一行. 
  • ifelse if后面接空格, 再写小括号里的条件. 

循环语句

  • 示例代码:
    for (NSDictionary *dict in array)
  • {
  • }
  • for (int i = 0; i < 10; i++)
  • {
  • }
  • while (YES)
  • {
  • }
  • 注意空格的位置. 

switch语句

  • 示例代码:
    switch (self.responseDataType)
  • {
  •     case kWeiboResponseDataTypeStatuses:
  •         return [WBDataProcessor processStatuses:decodedObject];
  •         break;
  •     case kWeiboResponseDataTypeUser:
  •         return [WBDataProcessor processUser:decodedObject];
  •         break;
  •     default:
  •         break;
  • }
  • 每个case需要缩进. 
  • 必须要写default:
  • case中是否加花括号取决于是否有必要加, 如无必要可不加, 也可以加. 

使用pragma mark分割代码

  • 示例代码:
    #pragma mark - Properties
  • - (BOOL)hasReplyComment
  • {
  •     if (replyComment != nil)
  •     {
  •         return YES;
  •     }
  •     return NO;
  • }
  • #pragma mark - Equality Comparison
  • - (id)primaryKey
  • {
  •     return commentID;
  • }
  • #pragma mark - Memory Management
  • - (void)dealloc
  • {
  •     [super dealloc];
  • }
  • #pragma mark中使用英文, 每个单词首字母大写. 
  • #pragma mark是用于分离一类方法, 如某个delegate的全部方法等. 

释放变量时需置成nil

  • 示例代码:
    - (void)dealloc
  • {
  •     [loginUsername release], loginUsername = nil;
  •     [loginPassword release], loginPassword = nil;
  •     [oauthTokenKey release], oauthTokenKey = nil;
  •     [oauthTokenSecret release], oauthTokenSecret = nil;
  •     [gsid release], gsid = nil;
  •     [super dealloc];
  • }
  • dealloc中须将变量release后设置为nil
  • release与设置成nil在同一行, 并以逗号分隔. 
  • 不要忘记调用[super dealloc]

注释

  • 在开发过程中注释是为自己能看明白. 
  • 开发结束后需要将一些与外部接口的方法, 类等进行Doxygen风格注释, 以便生成文档.  
2011-09-05 10:45:00 iteye_7514 阅读数 15
本文将Objective-C讨论了语言的核心语法。这部分开始详述一些具体的语法。正如你期待的一样,涉及到了定义和类。

类并不是特殊的

在Smalltalk中,类是具有一些特性的对象。在Objective-C中也一样。一个类是一个对象,对象回应消息。Objective-C和C++都分离了对象分配和初始化。

在C++中,对象分配通过新的操作。在Objective-C中,这样的操作是通过给类发送分配消息—调用malloc()或者一个等价。

C++中的初始化是通过调用一个与类同名的函数。Objective-C并没有区别初始化方法和其他方法,但出于惯例缺省的初始化方法就是初始化。

当你声明一个方法让实例去回应,声明通常已“-”开头,并且“+”用作类的方法。在文档中对这些消息使用一些前缀是很普遍的,所以你也可以说+alloc和-init来暗示alloc是传给一个类,init传给实例。

类在Objective-C中,正如在其他一些面向对象语言,都是对象工厂。大多数类不用自行实现+alloc,而是从他们的父类中继承。在NSObject中,父类在大多数Objective-C程序中,+alloc方法调用+allocWithZone:.使NSZone作为一个参数,一个C结构包含对象分配的一些策略。回顾19世纪80年代,当Objective-C用在NeXTstep来实现设备驱动和只有8MB内存25MHZ的CPU机器的GUI上面时,NSZone对优化非常重要。同时,这或多或少的被Objective-C程序员所忽视。(很有可能成为象NUMA构架一样流行,更普遍。)

众多优秀的特性之一就是对象创建语义是由库定义的并且语言不是类簇的思想。当你传一个-init消息给对象时,它返回一个初始化对象。这可能是你发送消息的那个对象,但不一定肯定就是。这和其他初始化程序一致的。很有可能一些公共类的特殊子类在不同数据上更有效。

实现这个特性的通用方法叫做isa-swizzling。正如我前述,Objective-C对象是C结构,这些结构第一个元素是指向类的指针。这个元素是可存取的,正如其他实例变量一样;你可以在运行时通过分配新值来改变对象的类。当然,如果你对对象的类设置在内存中有着不同的布局,这些设置可能严重错误。

然而,你可以通过一个父类来定义布局和通过子集的集合定义行为,举例来说,这个技术用在标准化字符串类(NSString),它对不同的文本字符集、静态事物和其它一些有着各种各样的实例。

因为类是对象,你可以象操作对象一样操作他们。举例来说,你可以把他们放在集合。当我有一些输入事件需要通过不同的类的实例来处理时我就使用这种格式。你需要创建一个目录映射事件命名到类,然后为每一个输入事件实例化一个对象。如果你在一个库中这么做,它允许代码的使用者轻松的注册属于他们自己的句柄。

类型和指针

Objective-C没有公开允许在堆栈上定义对象。但并不是真的—很有可能在堆栈上定义对象,但有些困难,因为它破坏了对内存管理的一种假设。结果,每一个Objective-C对象都是一个指针。一些类型由Objective-C定义;这些类型在头部定义作为C类型。

在Objective-C中最普遍的3种类型就是id,Class和SEL。id就是指向Objective-C对象的指针,它等价于C语言中的void*,你可以映射任何对象指针类型指向它并且映射他指向其它的对象指针类型。

你可以传任何消息给id,但如果不支持的话会返回一个运行时异常。

类是指向Objective-C类的指针。类是对象,所以也可以接收消息。类名是一种类型,不可变的。标识符NSObject是一个NSObject实例的类型,但也可作为消息接受者。你可以获得一个类,如下:

[NSObjectclass];

发送一个+class消息给NSObject类,然后返回一个指向代表类的类结构指针。

这对我们回顾是非常有用的[FS:PAGE],正如我们在这个系列第二部分看到的一样。

第三种类型SEL,代表一个选择器—一个代表方法名的抽象。你可以在编译时通过@selector()直接创建,或在运行时通过C字符串调用运行时库函数,或用OpenStep NSSelectorFromString()函数,这个函数给Objective-C字符串一个选择器。这个技术允许你通过名字调用方法。你可以在C中通过使用类似dlsym(),但在C++中非常不同。在Objective-C中,你可以做的如下:

[objectperfomSelector:@selector(doSomething)];

这等价于如下:

[object doSomething]

显然,第二种格式速度稍微快些,因为第一种传送两个消息。后面,我们会看到通过选择器处理的一些细节。

C++没有与id相同的类型。因为对象总是可以类型化的。在Objective-C,你可以选择类型系统。下面的两种都是有效的:

  1. idobject=@”astring”;
  2. NSString*string=@”astring”;

常量字符串实际上是NSConstantString类的一个实例,NSConstantString类是NSString的子类。将它引用到NSString* 使编译时对消息进行类型检查和存储公共实例变量(这在Objective-C从未使用过)。注意你可以通过如下改变这一设置:

NSArray*array=(NSArray*)string;

如果给数组发送消息,编译器将会检查NSArray能接收的消息。这并不是非常有用,因为对象是一个字符串。如果发送一个NSArray和NSString实现的消息,可能会有作用。如果你发送的消息NSString没有实现,一个异常将会抛出。

强调Objective-C和C++的不同的这件事看起来比较奇怪。Objective-C有类型-值语法,而C++有类型-变量语法。在Objective-C,对象类型是对象专有的一种属性。在C++,类型取决于变量的类型。

在C++中,当你指派一个指针指向一个对象到一个变量定义一个指向父类的指针,两个指针可能没有相同的数值(这可以通过多继承实现,而Objective-C不支持这种。)

定义类

Objective-C类定义有一个接口和一个实现部分。与C++有相似的地方,但两个稍微有些混。

Objective-C中的接口仅定义位并且明确的需要公开。对于实现的原因,这包括私有实例变量在大部分的实现中,因为你无法继承一个类除非你知道它多大。最近的一些实现,象Apple的64位运行时则没有这种限制。

Objective-C对象的接口如下:

  1. @interfaceAnObject:NSObject
  2. {
  3. @private
  4. intintegerivar
  5. @public
  6. idanotherObject;
  7. }
  8. +(id)aClassMethod;
  9. -(id)anInstanceMethod:(NSString*)aStringwith:(id)anObject
  10. @end

第一行包含3个部分。标识符AnObject 是新类的名字。冒号后面的名字是NSObject。(这是可选的,但每一个Objective-C 对象都应拓展NSObject)。在括号内的名字是协议——与Java中的接口相似——通过类来实现。

正如C++实例变量(C++中的域)可以存取修饰符,不象C++,这些修饰符以@为前缀是为了避免与C标识符冲突。

Objective-C不支持多继承,所以只有一个父类。所以,对象第一部分的布局总是与父类实例的布局一致。这在过去常常定义为动态,意味着改变类中实例变量需要它所有子类重新编译。在较新的运行时这种限定并不要求,在存取实例实例变量上开支稍微大些。这种决策的另一个影响就是Objective-C其他特性中的一个。

  1. struct_AnObject
  2. {
  3. @defs(AnObject);
  4. };
@def表示着对特定对象所有域都插入这种结构,所以struct_AnObject 和AnObject类的实例有着相同的内存结构。举个例子来说,你可以通过这种规则可以直接存取实例变量。一个通常的用法就是允许C函数直接操作Objective-C对象,是基于性能原因。
正如我前面暗示的,与这个特性相关的另一件事就是可以在堆栈上创建对象。因为结构和对象在[FS:PAGE]内存布局中有着相同的结构,你可以简单的创建结构,设置他的指针指向正确的类,然后映射一个指针指向一个对象指针。然后你可以当做对象来使用,虽然你不得不小心没有什么可以保持指针不越界。(现实世界中我从没有使用这种方法,仅仅理论上可能。)

正如我前面暗示的,与这个特性相关的另一件事就是可以在堆栈上创建对象。因为结构和对象在[FS:PAGE]内存布局中有着相同的结构,你可以简单的创建结构,设置他的指针指向正确的类,然后映射一个指针指向一个对象指针。然后你可以当做对象来使用,虽然你不得不小心没有什么可以保持指针不越界。(现实世界中我从没有使用这种方法,仅仅理论上可能。)

不象C++,Objective-C没有私有的或受保护的方法。Objective-C对象上的任何方法可以被其他对象调用。如果你在接口中没有声明方法,就是非正式私有的。将会得到运行时警告:对象不回应这种消息,但是你任然可以调用它。

接口和C中头部声明很相似。但它仍然需要一个实现,这并不奇怪,可以使用@implementation来定义。

  1. @implementationAnObject
  2. +(id)aClassMethod
  3. {
  4. ...
  5. }
  6. -(id)anInstanceMethod:(NSString*)aStringwith:(id)anObject
  7. {
  8. ...
  9. }
  10. @end
注意参数类型是特定的,在括号里。这是从C重用映射语法来展示值映射到类型;他们可能不是类型。准确来说当映射时应用相同的规则。这意味着映射在不兼容对象指针类型间会导致一个警告(不是错误)。

内存管理

传统的,Objective-C不提供任何内存管理。在早期版本中,对象类实现一个+new方法调用malloc()来创建一个新对象。当使用完这个对象,传一个-free消息。任何一个对象从NSObject继承回应一个-retain和-release消息。当你使用完这个对象,你传一个-free消息。OpenStep添加了参考计算。

每一个从NSObject继承的对象都回应-retain和-release消息。当你想要保留一个指向对象的指针,你可以发送一个-retain消息。当你使用完以后,你可以发送一个-release消息。

这个设计有个细微问题。通常你不需要保持一个指向对象的指针,但是你也不想释放。一个典型的例子在返回一个对象时候,调用者需要保持指向对象的指针,但你不想这么做。

这个问题的解决方案就是NSAutoreleasePool类。加上-retain和-release,NSObject也回应-autorelease消息。当你发送其中一个,和现前的自动释放池一同注册。当这个池对象被注销,它发送一个-release消息给每个对象,而对象在这之前先收到-autorelease消息。在OpenStep应用中,一个NSAutoreleasePool实例在循环开始的时候创建,在结束的时候销毁。你也可以创建属于你自己的实例来自动释放对象。

这个机制减少了一些C++所需的复制。其实也不值得这么做,在Objective-C,易变性是对象的属性,不是参考。在C++,有常量指针和非常量指针。不允许在常量对象上调用非常量方法。这保证不了对象不会被改变——仅仅因为你不想改变。

在Objective-C中,一个常态模式定义了一个不变的类和可变的子类。NSString就是一个典型例子;

它有一个可变的子类NSMutableString。如果你得到NSString并且想保存下来,你可以传一个-retain消息并且不用复制操作就可以保存指针。相反地,你可以传一个+stringWithString:message给NSString。不管这个参数是否可变都会检查并返回原始指针。

在Apple和GNU运行时,Objective-C都支持存储性的垃圾回收,这会避免对-retain和-release的需要。在现存的框架中对语言的附加并不总是很好的支持的,并且在用的时候需要格外小心。

总结

既然我们已经浏览了Objective-C语言的核心,在这部分的总结我们将会看到更多的一些高级话题。


2011-09-05 10:45:00 iteye_14736 阅读数 6
本文将Objective-C讨论了语言的核心语法。这部分开始详述一些具体的语法。正如你期待的一样,涉及到了定义和类。

类并不是特殊的

在Smalltalk中,类是具有一些特性的对象。在Objective-C中也一样。一个类是一个对象,对象回应消息。Objective-C和C++都分离了对象分配和初始化。

在C++中,对象分配通过新的操作。在Objective-C中,这样的操作是通过给类发送分配消息—调用malloc()或者一个等价。

C++中的初始化是通过调用一个与类同名的函数。Objective-C并没有区别初始化方法和其他方法,但出于惯例缺省的初始化方法就是初始化。

当你声明一个方法让实例去回应,声明通常已“-”开头,并且“+”用作类的方法。在文档中对这些消息使用一些前缀是很普遍的,所以你也可以说+alloc和-init来暗示alloc是传给一个类,init传给实例。

类在Objective-C中,正如在其他一些面向对象语言,都是对象工厂。大多数类不用自行实现+alloc,而是从他们的父类中继承。在NSObject中,父类在大多数Objective-C程序中,+alloc方法调用+allocWithZone:.使NSZone作为一个参数,一个C结构包含对象分配的一些策略。回顾19世纪80年代,当Objective-C用在NeXTstep来实现设备驱动和只有8MB内存25MHZ的CPU机器的GUI上面时,NSZone对优化非常重要。同时,这或多或少的被Objective-C程序员所忽视。(很有可能成为象NUMA构架一样流行,更普遍。)

众多优秀的特性之一就是对象创建语义是由库定义的并且语言不是类簇的思想。当你传一个-init消息给对象时,它返回一个初始化对象。这可能是你发送消息的那个对象,但不一定肯定就是。这和其他初始化程序一致的。很有可能一些公共类的特殊子类在不同数据上更有效。

实现这个特性的通用方法叫做isa-swizzling。正如我前述,Objective-C对象是C结构,这些结构第一个元素是指向类的指针。这个元素是可存取的,正如其他实例变量一样;你可以在运行时通过分配新值来改变对象的类。当然,如果你对对象的类设置在内存中有着不同的布局,这些设置可能严重错误。

然而,你可以通过一个父类来定义布局和通过子集的集合定义行为,举例来说,这个技术用在标准化字符串类(NSString),它对不同的文本字符集、静态事物和其它一些有着各种各样的实例。

因为类是对象,你可以象操作对象一样操作他们。举例来说,你可以把他们放在集合。当我有一些输入事件需要通过不同的类的实例来处理时我就使用这种格式。你需要创建一个目录映射事件命名到类,然后为每一个输入事件实例化一个对象。如果你在一个库中这么做,它允许代码的使用者轻松的注册属于他们自己的句柄。

类型和指针

Objective-C没有公开允许在堆栈上定义对象。但并不是真的—很有可能在堆栈上定义对象,但有些困难,因为它破坏了对内存管理的一种假设。结果,每一个Objective-C对象都是一个指针。一些类型由Objective-C定义;这些类型在头部定义作为C类型。

在Objective-C中最普遍的3种类型就是id,Class和SEL。id就是指向Objective-C对象的指针,它等价于C语言中的void*,你可以映射任何对象指针类型指向它并且映射他指向其它的对象指针类型。

你可以传任何消息给id,但如果不支持的话会返回一个运行时异常。

类是指向Objective-C类的指针。类是对象,所以也可以接收消息。类名是一种类型,不可变的。标识符NSObject是一个NSObject实例的类型,但也可作为消息接受者。你可以获得一个类,如下:

[NSObjectclass];

发送一个+class消息给NSObject类,然后返回一个指向代表类的类结构指针。

这对我们回顾是非常有用的[FS:PAGE],正如我们在这个系列第二部分看到的一样。

第三种类型SEL,代表一个选择器—一个代表方法名的抽象。你可以在编译时通过@selector()直接创建,或在运行时通过C字符串调用运行时库函数,或用OpenStep NSSelectorFromString()函数,这个函数给Objective-C字符串一个选择器。这个技术允许你通过名字调用方法。你可以在C中通过使用类似dlsym(),但在C++中非常不同。在Objective-C中,你可以做的如下:

[objectperfomSelector:@selector(doSomething)];

这等价于如下:

[object doSomething]

显然,第二种格式速度稍微快些,因为第一种传送两个消息。后面,我们会看到通过选择器处理的一些细节。

C++没有与id相同的类型。因为对象总是可以类型化的。在Objective-C,你可以选择类型系统。下面的两种都是有效的:

  1. idobject=@”astring”;
  2. NSString*string=@”astring”;

常量字符串实际上是NSConstantString类的一个实例,NSConstantString类是NSString的子类。将它引用到NSString* 使编译时对消息进行类型检查和存储公共实例变量(这在Objective-C从未使用过)。注意你可以通过如下改变这一设置:

NSArray*array=(NSArray*)string;

如果给数组发送消息,编译器将会检查NSArray能接收的消息。这并不是非常有用,因为对象是一个字符串。如果发送一个NSArray和NSString实现的消息,可能会有作用。如果你发送的消息NSString没有实现,一个异常将会抛出。

强调Objective-C和C++的不同的这件事看起来比较奇怪。Objective-C有类型-值语法,而C++有类型-变量语法。在Objective-C,对象类型是对象专有的一种属性。在C++,类型取决于变量的类型。

在C++中,当你指派一个指针指向一个对象到一个变量定义一个指向父类的指针,两个指针可能没有相同的数值(这可以通过多继承实现,而Objective-C不支持这种。)

定义类

Objective-C类定义有一个接口和一个实现部分。与C++有相似的地方,但两个稍微有些混。

Objective-C中的接口仅定义位并且明确的需要公开。对于实现的原因,这包括私有实例变量在大部分的实现中,因为你无法继承一个类除非你知道它多大。最近的一些实现,象Apple的64位运行时则没有这种限制。

Objective-C对象的接口如下:

  1. @interfaceAnObject:NSObject
  2. {
  3. @private
  4. intintegerivar
  5. @public
  6. idanotherObject;
  7. }
  8. +(id)aClassMethod;
  9. -(id)anInstanceMethod:(NSString*)aStringwith:(id)anObject
  10. @end

第一行包含3个部分。标识符AnObject 是新类的名字。冒号后面的名字是NSObject。(这是可选的,但每一个Objective-C 对象都应拓展NSObject)。在括号内的名字是协议——与Java中的接口相似——通过类来实现。

正如C++实例变量(C++中的域)可以存取修饰符,不象C++,这些修饰符以@为前缀是为了避免与C标识符冲突。

Objective-C不支持多继承,所以只有一个父类。所以,对象第一部分的布局总是与父类实例的布局一致。这在过去常常定义为动态,意味着改变类中实例变量需要它所有子类重新编译。在较新的运行时这种限定并不要求,在存取实例实例变量上开支稍微大些。这种决策的另一个影响就是Objective-C其他特性中的一个。

  1. struct_AnObject
  2. {
  3. @defs(AnObject);
  4. };
@def表示着对特定对象所有域都插入这种结构,所以struct_AnObject 和AnObject类的实例有着相同的内存结构。举个例子来说,你可以通过这种规则可以直接存取实例变量。一个通常的用法就是允许C函数直接操作Objective-C对象,是基于性能原因。
正如我前面暗示的,与这个特性相关的另一件事就是可以在堆栈上创建对象。因为结构和对象在[FS:PAGE]内存布局中有着相同的结构,你可以简单的创建结构,设置他的指针指向正确的类,然后映射一个指针指向一个对象指针。然后你可以当做对象来使用,虽然你不得不小心没有什么可以保持指针不越界。(现实世界中我从没有使用这种方法,仅仅理论上可能。)

正如我前面暗示的,与这个特性相关的另一件事就是可以在堆栈上创建对象。因为结构和对象在[FS:PAGE]内存布局中有着相同的结构,你可以简单的创建结构,设置他的指针指向正确的类,然后映射一个指针指向一个对象指针。然后你可以当做对象来使用,虽然你不得不小心没有什么可以保持指针不越界。(现实世界中我从没有使用这种方法,仅仅理论上可能。)

不象C++,Objective-C没有私有的或受保护的方法。Objective-C对象上的任何方法可以被其他对象调用。如果你在接口中没有声明方法,就是非正式私有的。将会得到运行时警告:对象不回应这种消息,但是你任然可以调用它。

接口和C中头部声明很相似。但它仍然需要一个实现,这并不奇怪,可以使用@implementation来定义。

  1. @implementationAnObject
  2. +(id)aClassMethod
  3. {
  4. ...
  5. }
  6. -(id)anInstanceMethod:(NSString*)aStringwith:(id)anObject
  7. {
  8. ...
  9. }
  10. @end
注意参数类型是特定的,在括号里。这是从C重用映射语法来展示值映射到类型;他们可能不是类型。准确来说当映射时应用相同的规则。这意味着映射在不兼容对象指针类型间会导致一个警告(不是错误)。

内存管理

传统的,Objective-C不提供任何内存管理。在早期版本中,对象类实现一个+new方法调用malloc()来创建一个新对象。当使用完这个对象,传一个-free消息。任何一个对象从NSObject继承回应一个-retain和-release消息。当你使用完这个对象,你传一个-free消息。OpenStep添加了参考计算。

每一个从NSObject继承的对象都回应-retain和-release消息。当你想要保留一个指向对象的指针,你可以发送一个-retain消息。当你使用完以后,你可以发送一个-release消息。

这个设计有个细微问题。通常你不需要保持一个指向对象的指针,但是你也不想释放。一个典型的例子在返回一个对象时候,调用者需要保持指向对象的指针,但你不想这么做。

这个问题的解决方案就是NSAutoreleasePool类。加上-retain和-release,NSObject也回应-autorelease消息。当你发送其中一个,和现前的自动释放池一同注册。当这个池对象被注销,它发送一个-release消息给每个对象,而对象在这之前先收到-autorelease消息。在OpenStep应用中,一个NSAutoreleasePool实例在循环开始的时候创建,在结束的时候销毁。你也可以创建属于你自己的实例来自动释放对象。

这个机制减少了一些C++所需的复制。其实也不值得这么做,在Objective-C,易变性是对象的属性,不是参考。在C++,有常量指针和非常量指针。不允许在常量对象上调用非常量方法。这保证不了对象不会被改变——仅仅因为你不想改变。

在Objective-C中,一个常态模式定义了一个不变的类和可变的子类。NSString就是一个典型例子;

它有一个可变的子类NSMutableString。如果你得到NSString并且想保存下来,你可以传一个-retain消息并且不用复制操作就可以保存指针。相反地,你可以传一个+stringWithString:message给NSString。不管这个参数是否可变都会检查并返回原始指针。

在Apple和GNU运行时,Objective-C都支持存储性的垃圾回收,这会避免对-retain和-release的需要。在现存的框架中对语言的附加并不总是很好的支持的,并且在用的时候需要格外小心。

总结

既然我们已经浏览了Objective-C语言的核心,在这部分的总结我们将会看到更多的一些高级话题。


2017-03-09 17:24:05 a184251289 阅读数 296

没有规矩不成方圆。说说我比较推崇的代码规范。求同存异,别的规范也是可取的,具体问题具体分析。

工程结构

  • 为整个工程创建 workspace

  • 合理的工程目录结构。

    • Core:通用的机制实现类:统一的任务管理,模块管理,服务管理。

    • General:公用类和方法,包括工程内基类(Base),公用Category,公用UI组件(CustomUI),公用辅助方法(Helper)和宏定义(Macro)。

    • Vendors:第三方库。

    • 先按业务划分,再按照MVC来划分。方便解耦,模块化。
      将TableViewCell放在View目录,把具体的ViewController放在Controller目录,图片等资源放到Resource目录,工具类放到Utils目录。


头文件

#import的顺序按照:系统类、第三方类、自己类

 #import <系统库> 
 #import <系统库> 

 #import <第三方库> 
 #import <第三方库> 

 #import “其他类”

常量

  • 常量用static,不使用#define

static NSString * const AFURLSessionManagerLockName = @"com.alamofire.networking.session.manager.lock";

static NSUInteger const AFMaximumNumberOfAttemptsToRecreateBackgroundSessionUploadTask = 3;
  • 开头用k标识

    字符k+模板名字首字母大写+作用名称,防止和其他的重复

NSString * const kTTTStrikeOutAttributeName = @"TTTStrikeOutAttribute";
  • 声明Cell的重用

    字符k+cell的名称+identifier

static NSString *const kQuestionCellIdentifier = @"kQuestionCellIdentifier";
  • const声明字符串提供外部使用

    h声明m实现且让其他的类用使用。如果导入是UIKit类就使用UIKIT_EXTERN,如果是Founction使用关键词FOUNDATION_EXTERN。


//h声明

FOUNDATION_EXPORT NSString * const AFNetworkingReachabilityDidChangeNotification;


//m实现

NSString * const AFNetworkingReachabilityDidChangeNotification = @"com.alamofire.networking.reachability.change";

对于#define宏命名

单词全部的大写,单词之间用_分割。

#define RCT_EXPORT_SHADOW_PROPERTY(name, type) \
+ (NSArray<NSString *> *)propConfigShadow_##name { return @[@#type]; }

枚举类型

使用NS_ENUM、NS_OPTIONS语法,不使用enum语法(除非c语言),新的语法有更强的类型检查和代码补全。

typedef NS_ENUM(NSInteger, NetworkStatus) {
    // Apple NetworkStatus Compatible Names.
    NotReachable = 0,
    ReachableViaWiFi = 2,
    ReachableViaWWAN = 1
};
typedef NS_OPTIONS(NSUInteger, SDWebImageDownloaderOptions) {
    SDWebImageDownloaderLowPriority = 1 << 0,
    SDWebImageDownloaderProgressiveDownload = 1 << 1,
    SDWebImageDownloaderUseNSURLCache = 1 << 2,
    SDWebImageDownloaderIgnoreCachedResponse = 1 << 3,
    }

对于NS_OPTIONS类型多值用|连接,不能用+。


NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay


代码组织

使用#pragma mark - 进行分组

#pragma mark - Lifecycle

- (instancetype)init {}
- (void)dealloc {}
- (void)viewDidLoad {}
- (void)viewWillAppear:(BOOL)animated {}
- (void)didReceiveMemoryWarning {}

#pragma mark - Public

- (void)publicMethod {}

#pragma mark - rewrite

- (void)fatherMethod {}

#pragma mark - Private

- (void)privateMethod {}

#pragma mark - Draw
- (void)drawRect:(CGRect)rect {}

#pragma mark - Events

- (IBAction)submitData:(id)sender {}
- (void)onBtnClicked:(id)sender {}

#pragma mark - Protocol conformance
#pragma mark - UITextFieldDelegate
#pragma mark - UITableViewDataSource
#pragma mark - UITableViewDelegate

#pragma mark - Properties

- (void)setCustomProperty:(id)value {}
- (id)customProperty {}

#pragma mark - NSCopying

- (id)copyWithZone:(NSZone *)zone {}

#pragma mark - NSObject

- (NSString *)description {}

注释代码

应该尽量做到清晰可读,做到自解释。当需要注释时,注释应该更多地用来描述,为什么这样做。注释保持更新,历史的注释应该删除。

  • 可能存在问题的代码应该加上// FIXME:的注释,并详细描述(注意用半角的冒号)。

  • 需要添加逻辑的代码应该加上// TODO:,并详细描述。

  • 不是源码文件创建者修改了代码,应该在修改的代码上加上// Modified:,并写明修改人、修改时间、修改原因等信息。


命名

  • 必须尽量遵守苹果官方的命名规范,应该使用长的、描述清楚的函数名、变量名。应该使用三个字母的前缀来给类、常量进行命名。
  • 控件命名中UILabel结尾加上Label,UIImageView结尾记上ImageView等等。
@property(nonatomic,strong) UILabel  *userNameLabel;

函数(Method)

在函数签名中(函数声明或定义),在函数类型(-/+符号)后,必须有一个空格。参数之间也要有一个空格。参数的keyword不要为空。除了第一个参数,后面的参数的keyword尽量不要加with、and等连词的前缀,keyword更不应该直接用with、and。

//应该
- (void)setExampleText:(NSString *)text image:(UIImage *)image;
- (void)sendAction:(SEL)aSelector to:(id)anObject forAllCells:(BOOL)flag;
- (id)viewWithTag:(NSInteger)tag;
- (instancetype)initWithWidth:(CGFloat)width height:(CGFloat)height;
//不应该
-(void)setT:(NSString *)text i:(UIImage *)image;
- (void)sendAction:(SEL)aSelector :(id)anObject :(BOOL)flag;
- (id)taggedView:(NSInteger)tag;
- (instancetype)initWithWidth:(CGFloat)width andHeight:(CGFloat)height;
- (instancetype)initWith:(int)width and:(int)height;  // Never do this.

初始化方法

返回值应该使用instancetype来代替之前的id。

- (instancetype)init {
    self = [super init];
    if (self) {
        // ...
    }
    return self;
}

单例模式

单例模式应该使用dispatch_once来保证创建时的线程安全。

+ (instancetype)sharedInstance {
  static id sharedInstance = nil;

  static dispatch_once_t onceToken;
  dispatch_once(&onceToken, ^{
    sharedInstance = [[self alloc] init];
  });

  return sharedInstance;
}

Error处理

当函数返回NSError的引用,调用时要根据返回值进行判断,而不是NSError的值,因为苹果的一些api会往NSError中写一些垃圾数据,如果根据NSError进行判断可能会有问题。

ARC中严重不推荐使用try catch,可能内存泄露。

NSError *error;
if (![self trySomethingWithError:&error]) {
  // Handle Error
}

黄金路径(GoldenPath)

当if判断有多层嵌套,应该尽量使用黄金路径,即不符合条件的分支先返回,这样可以避免分支嵌套。

- (void)someMethod {
    if (!condition1) {
        return;
    }
    //Do something1
    if (!condition2) {
        return;
    }
    // Do something 2
}

尤达表达式

不要使用尤达表达式。

if ([myValue isEqual:@42]) {}

三元运算符

三元运算符, ?: , 应该只在它能提高代码整洁性而且逻辑清晰的情况下使用。判断条件应该只有一个,多于一个的判断条件应该用if或临时变量来写。


CGRect相关方法

CGRect frame = self.view.frame;

CGFloat x = CGRectGetMinX(frame);
CGFloat y = CGRectGetMinY(frame);
CGFloat width = CGRectGetWidth(frame);
CGFloat height = CGRectGetHeight(frame);
CGRect frame = CGRectMake(0.0, 0.0, width, height);

布尔值

  • 使用YES和NO来表示布尔值。
    • true和false除了CoreFoundation和C、C++代码中,不应该被使用。
    • nil被判断为NO,没有必要与nil进行比较。
    • BOOL值可以最大可以达到8位,而YES只是被设置为1,尽量不要跟YES比较。
//应该
if (someObject) {}
if (![anotherObject boolValue]) {}
//不应该
if (someObject == nil) {}
if ([anotherObject boolValue] == NO) {}
if (isAwesome == YES) {} // Never do this.
if (isAwesome == true) {} // Never do this.
  • 如果布尔类型的属性名是一个形容词,那么属性虽然可以忽略is 前缀,但最好仍提供带is前缀的getter方法。
@property (assign, getter=isEditable) BOOL editable;

使用NSInteger

Apple的UIKit等代码一般都是用的NSInteger。NSInteger在32位系统是 int,64位系统是long 。系统的代码用的是 NSInteger 的话,你使用int的话,可能不够大而造成崩溃。


摘录

https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CodingGuidelines/CodingGuidelines.html
http://www.aichengxu.com/ios/7566968.htm
http://reviewcode.cn/article.html?reviewId=9
http://www.cnblogs.com/netfocus/p/3896118.html
http://www.cocoachina.com/ios/20170105/18515.html
https://github.com/objc-zen/objc-zen-book

2011-09-05 10:45:54 hufeng825 阅读数 970
本文将Objective-C讨论了语言的核心语法。这部分开始详述一些具体的语法。正如你期待的一样,涉及到了定义和类。

类并不是特殊的

在Smalltalk中,类是具有一些特性的对象。在Objective-C中也一样。一个类是一个对象,对象回应消息。Objective-C和C++都分离了对象分配和初始化。

在C++中,对象分配通过新的操作。在Objective-C中,这样的操作是通过给类发送分配消息—调用malloc()或者一个等价。 

C++中的初始化是通过调用一个与类同名的函数。Objective-C并没有区别初始化方法和其他方法,但出于惯例缺省的初始化方法就是初始化。

当你声明一个方法让实例去回应,声明通常已“-”开头,并且“+”用作类的方法。在文档中对这些消息使用一些前缀是很普遍的,所以你也可以说+alloc和-init来暗示alloc是传给一个类,init传给实例。

类在Objective-C中,正如在其他一些面向对象语言,都是对象工厂。大多数类不用自行实现+alloc,而是从他们的父类中继承。在NSObject中,父类在大多数Objective-C程序中,+alloc方法调用+allocWithZone:.使NSZone作为一个参数,一个C结构包含对象分配的一些策略。回顾19世纪80年代,当Objective-C用在NeXTstep来实现设备驱动和只有8MB内存25MHZ的CPU机器的GUI上面时,NSZone对优化非常重要。同时,这或多或少的被Objective-C程序员所忽视。(很有可能成为象NUMA构架一样流行,更普遍。)

众多优秀的特性之一就是对象创建语义是由库定义的并且语言不是类簇的思想。当你传一个-init消息给对象时,它返回一个初始化对象。这可能是你发送消息的那个对象,但不一定肯定就是。这和其他初始化程序一致的。很有可能一些公共类的特殊子类在不同数据上更有效。

实现这个特性的通用方法叫做isa-swizzling。正如我前述,Objective-C对象是C结构,这些结构第一个元素是指向类的指针。这个元素是可存取的,正如其他实例变量一样;你可以在运行时通过分配新值来改变对象的类。当然,如果你对对象的类设置在内存中有着不同的布局,这些设置可能严重错误。

然而,你可以通过一个父类来定义布局和通过子集的集合定义行为,举例来说,这个技术用在标准化字符串类(NSString),它对不同的文本字符集、静态事物和其它一些有着各种各样的实例。

因为类是对象,你可以象操作对象一样操作他们。举例来说,你可以把他们放在集合。当我有一些输入事件需要通过不同的类的实例来处理时我就使用这种格式。你需要创建一个目录映射事件命名到类,然后为每一个输入事件实例化一个对象。如果你在一个库中这么做,它允许代码的使用者轻松的注册属于他们自己的句柄。

类型和指针

Objective-C没有公开允许在堆栈上定义对象。但并不是真的—很有可能在堆栈上定义对象,但有些困难,因为它破坏了对内存管理的一种假设。结果,每一个Objective-C对象都是一个指针。一些类型由Objective-C定义;这些类型在头部定义作为C类型。

在Objective-C中最普遍的3种类型就是id,Class和SEL。id就是指向Objective-C对象的指针,它等价于C语言中的void*,你可以映射任何对象指针类型指向它并且映射他指向其它的对象指针类型。

你可以传任何消息给id,但如果不支持的话会返回一个运行时异常。

类是指向Objective-C类的指针。类是对象,所以也可以接收消息。类名是一种类型,不可变的。标识符NSObject是一个NSObject实例的类型,但也可作为消息接受者。你可以获得一个类,如下:

[NSObject class];  

发送一个+class消息给NSObject类,然后返回一个指向代表类的类结构指针。

这对我们回顾是非常有用的[FS:PAGE],正如我们在这个系列第二部分看到的一样。

第三种类型SEL,代表一个选择器—一个代表方法名的抽象。你可以在编译时通过@selector()直接创建,或在运行时通过C字符串调用运行时库函数,或用OpenStep NSSelectorFromString()函数,这个函数给Objective-C字符串一个选择器。这个技术允许你通过名字调用方法。你可以在C中通过使用类似dlsym(),但在C++中非常不同。在Objective-C中,你可以做的如下:

[object perfomSelector:@selector(doSomething)]; 

这等价于如下:

[object doSomething] 

显然,第二种格式速度稍微快些,因为第一种传送两个消息。后面,我们会看到通过选择器处理的一些细节。

C++没有与id相同的类型。因为对象总是可以类型化的。在Objective-C,你可以选择类型系统。下面的两种都是有效的:

  1. id object = @”a string”;  
  2.  
  3. NSString *string = @”a string”;

常量字符串实际上是NSConstantString类的一个实例,NSConstantString类是NSString的子类。将它引用到NSString* 使编译时对消息进行类型检查和存储公共实例变量(这在Objective-C从未使用过)。注意你可以通过如下改变这一设置:

NSArray *array = (NSArray*)string; 

如果给数组发送消息,编译器将会检查NSArray能接收的消息。这并不是非常有用,因为对象是一个字符串。如果发送一个NSArray和NSString实现的消息,可能会有作用。如果你发送的消息NSString没有实现,一个异常将会抛出。 

强调Objective-C和C++的不同的这件事看起来比较奇怪。Objective-C有类型-值语法,而C++有类型-变量语法。在Objective-C,对象类型是对象专有的一种属性。在C++,类型取决于变量的类型。

在C++中,当你指派一个指针指向一个对象到一个变量定义一个指向父类的指针,两个指针可能没有相同的数值(这可以通过多继承实现,而Objective-C不支持这种。)

定义类

Objective-C类定义有一个接口和一个实现部分。与C++有相似的地方,但两个稍微有些混。

Objective-C中的接口仅定义位并且明确的需要公开。对于实现的原因,这包括私有实例变量在大部分的实现中,因为你无法继承一个类除非你知道它多大。最近的一些实现,象Apple的64位运行时则没有这种限制。

Objective-C对象的接口如下:

  1. @interface AnObject : NSObject  
  2.  
  3. {  
  4.  
  5. @private  
  6.  
  7. int integerivar  
  8.  
  9. @public  
  10.  
  11. id anotherObject;  
  12.  
  13. }  
  14.  
  15. + (id) aClassMethod;  
  16.  
  17. - (id) anInstanceMethod:(NSString*)aString with:(id)anObject  
  18.  
  19. @end  
  20.  

第一行包含3个部分。标识符AnObject 是新类的名字。冒号后面的名字是NSObject。(这是可选的,但每一个Objective-C 对象都应拓展NSObject)。在括号内的名字是协议——与Java中的接口相似——通过类来实现。

正如C++实例变量(C++中的域)可以存取修饰符,不象C++,这些修饰符以@为前缀是为了避免与C标识符冲突。

Objective-C不支持多继承,所以只有一个父类。所以,对象第一部分的布局总是与父类实例的布局一致。这在过去常常定义为动态,意味着改变类中实例变量需要它所有子类重新编译。在较新的运行时这种限定并不要求,在存取实例实例变量上开支稍微大些。这种决策的另一个影响就是Objective-C其他特性中的一个。

  1. struct_AnObject  
  2.  
  3. {  
  4.  
  5. @defs(AnObject);  
  6.  
  7. };
@def表示着对特定对象所有域都插入这种结构,所以struct_AnObject 和AnObject类的实例有着相同的内存结构。举个例子来说,你可以通过这种规则可以直接存取实例变量。一个通常的用法就是允许C函数直接操作Objective-C对象,是基于性能原因。
正如我前面暗示的,与这个特性相关的另一件事就是可以在堆栈上创建对象。因为结构和对象在[FS:PAGE]内存布局中有着相同的结构,你可以简单的创建结构,设置他的指针指向正确的类,然后映射一个指针指向一个对象指针。然后你可以当做对象来使用,虽然你不得不小心没有什么可以保持指针不越界。(现实世界中我从没有使用这种方法,仅仅理论上可能。)

正如我前面暗示的,与这个特性相关的另一件事就是可以在堆栈上创建对象。因为结构和对象在[FS:PAGE]内存布局中有着相同的结构,你可以简单的创建结构,设置他的指针指向正确的类,然后映射一个指针指向一个对象指针。然后你可以当做对象来使用,虽然你不得不小心没有什么可以保持指针不越界。(现实世界中我从没有使用这种方法,仅仅理论上可能。)

不象C++,Objective-C没有私有的或受保护的方法。Objective-C对象上的任何方法可以被其他对象调用。如果你在接口中没有声明方法,就是非正式私有的。将会得到运行时警告:对象不回应这种消息,但是你任然可以调用它。

接口和C中头部声明很相似。但它仍然需要一个实现,这并不奇怪,可以使用@implementation来定义。

  1. @implementation AnObject  
  2.  
  3. + (id) aClassMethod  
  4.  
  5. {  
  6.  
  7. ...  
  8.  
  9. }  
  10.  
  11. - (id) anInstanceMethod:(NSString*)aString with:(id)anObject  
  12.  
  13. {  
  14.  
  15. ...  
  16.  
  17. }  
  18.  
  19. @end  
  20.  
注意参数类型是特定的,在括号里。这是从C重用映射语法来展示值映射到类型;他们可能不是类型。准确来说当映射时应用相同的规则。这意味着映射在不兼容对象指针类型间会导致一个警告(不是错误)。

内存管理

传统的,Objective-C不提供任何内存管理。在早期版本中,对象类实现一个+new方法调用malloc()来创建一个新对象。当使用完这个对象,传一个-free消息。任何一个对象从NSObject继承回应一个-retain和-release消息。当你使用完这个对象,你传一个-free消息。OpenStep添加了参考计算。

每一个从NSObject继承的对象都回应-retain和-release消息。当你想要保留一个指向对象的指针,你可以发送一个-retain消息。当你使用完以后,你可以发送一个-release消息。

这个设计有个细微问题。通常你不需要保持一个指向对象的指针,但是你也不想释放。一个典型的例子在返回一个对象时候,调用者需要保持指向对象的指针,但你不想这么做。

这个问题的解决方案就是NSAutoreleasePool类。加上-retain和-release,NSObject也回应-autorelease消息。当你发送其中一个,和现前的自动释放池一同注册。当这个池对象被注销,它发送一个-release消息给每个对象,而对象在这之前先收到-autorelease消息。在OpenStep应用中,一个NSAutoreleasePool实例在循环开始的时候创建,在结束的时候销毁。你也可以创建属于你自己的实例来自动释放对象。

这个机制减少了一些C++所需的复制。其实也不值得这么做,在Objective-C,易变性是对象的属性,不是参考。在C++,有常量指针和非常量指针。不允许在常量对象上调用非常量方法。这保证不了对象不会被改变——仅仅因为你不想改变。

在Objective-C中,一个常态模式定义了一个不变的类和可变的子类。NSString就是一个典型例子;

它有一个可变的子类NSMutableString。如果你得到NSString并且想保存下来,你可以传一个-retain消息并且不用复制操作就可以保存指针。相反地,你可以传一个+stringWithString:message给NSString。不管这个参数是否可变都会检查并返回原始指针。

在Apple和GNU运行时,Objective-C都支持存储性的垃圾回收,这会避免对-retain和-release的需要。在现存的框架中对语言的附加并不总是很好的支持的,并且在用的时候需要格外小心。

总结

既然我们已经浏览了Objective-C语言的核心,在这部分的总结我们将会看到更多的一些高级话题。


Objective-C规范注释

阅读数 399

手工写文档是一件苦差事,幸好现在有从源码中抽取注释生成文档的专用工具。对于Objective-C来说,目前最好用的工具是appledoc和doxygen。可是这两种工具对于注释的要求略有区别。于是我经过一番摸索,找到了一套能同时兼容这两种工具的注释写法。  工具简介——appledoc:简单方便,适于生成apple风格的html文档,及直接集成到xcode帮助(docset)。官网ht

博文 来自: Calvin_CN

objective-c 内存管理规范

阅读数 169

Objective-C内存管理规范第一条原则只要调用了alloc方法,必须要有对应的release方法,并且谁alloc,谁release第二条原则对于set方法,如果成员变量是基本数据类型,那么直接赋值即可。如果变量是OC对象类型,则需要注意进行内存管理。第三条原则dealloc方法中,一定要进行[superdealloc],并且将它放在dealloc方法的最后一句。如果当前对象拥有一些OC对

博文 来自: tamarous

Objective-C 语法规范

阅读数 9

本文将Objective-C讨论了语言的核心语法。这部分开始详述一些具体的语法。正如你期待的一样,涉及到了定义和类。类并不是特殊的在Smalltalk中,类是具有一些特性的对象。在Objective-C中也一样。一个类是一个对象,对象回应消息。Objective-C和C++都分离了对象分配和初始化。在C++中,对象分配通过新的操作。在Objective-C中,这样的操作是通...

博文 来自: iteye_17686

Objective-C规范指南

阅读数 17

-点语法-间距-条件判断-三目运算符-错误处理-方法-变量-变量限定符-命名-注释-init和dealloc-字面量-CGRect函数-常量-枚举类型-位掩码-私有属性-图片命名-布尔-单例-导入-Xcode工程-其他Objective-C风格指南点语法应该始终使用点语法来访问或者修改属性,访...

博文 来自: weixin_34238633

Objective-c编程语言规范

阅读数 3

2019独角兽企业重金招聘Python工程师标准>>>...

博文 来自: weixin_33852020
没有更多推荐了,返回首页