《编写高质量代码:改善Objective-C程序的61个建议》——建议5:处理隐藏的返回类型,优先选择实例类型而非id

简介:

本节书摘来自华章出版社《编写高质量代码:改善Objective-C程序的61个建议》一 书中的第1章,第1.5节,作者:刘一道,更多章节内容可以访问云栖社区“华章计算机”公众号查看。

建议5:处理隐藏的返回类型,优先选择实例类型而非id

实例类型(Instancetype)是Objective-C语言中新添加的一个返回类型,实例类型作为方法返回的实例的类型,是苹果在2013年的年度大会上宣布的。这个新添加的实例类型不仅可用来作为Objective-C方法的返回类型,且能用这个实例类型来作为向编译器的提示,提示方法返回的类型将是方法所属的类的实例。
类的实例,作为方法返回类型,宜采用关键字instancetype作为方法的返回类型,如alloc 、init和类工厂方法等。使用instancetype作为类(或者类的子类)的实例返回类型,可以大大改善Objective-C代码的类型安全。例如,考虑下面的代码:

@interface MyObject : NSObject
+ (instancetype)factoryMethodA;
+ (id)factoryMethodB;
@end
 
@implementation MyObject
+ (instancetype)factoryMethodA { 
 return [[[self class] alloc] init]; 
}
+ (id)factoryMethodB {
  return [[[self class] alloc] init]; 
 }
@end
 
void doSomething() {
   NSUInteger x, y;
   // Return type of +factoryMethodA is taken to be "MyObject *"
   x = [[MyObject factoryMethodA] count]; 
  // Return type of +factoryMethodB is "id"  
   y = [[MyObject factoryMethodB] count]; }

通过上面的代码可以看到,instancetype作为+ factoryMethodA的返回类型,也就是说,该消息的类型表达式是MyObject *。但是MyObject由于先天缺乏一个-count方法,编译器将会对此给出一个关于x行的警告:

main.m: 'MyObject'may not respond to'count'

对于这样的情况,如果把instancetype换成id作为实例的方法返回类型,也就是如上面的代码中的实例,id作为类方法 + factoryMethodB的返回类型。在编译器编译的过程中不会发出关于y行的警告。
为什么编译器没有给出警告?因为 id类型的对象可以作为任何类,并且调用的方法-count在一些类中存在,故此向编译器发出方法+factoryMethodB返回值实现了-count的信息,从而编译器没有给出警告。对于该种编写代码的方法,在无形中埋下了隐患。
为了确保instancetype工厂方法有正确的子类的行为,一定要使用[self class]分配类,而不是直接引用类名。遵循这个惯例,记住,务必要使编译器能正确地推断出子类类型。例如,依据前面的示例,考虑尝试做一个前面MyObject子类示例:

@interface MyObjectSubclass : MyObject
@end
 
void doSomethingElse() {
       NSString *aString = [MyObjectSubclass factoryMethodA];
}

对于上述代码,编译器将会给出警告,如下面的警告:

main.m: Incompatible pointer types initializing 'NSString *'with an expression of
type 'MyObjectSubclass *'

在该示例中,+factoryMethodA消息发送之后,将返回一个类型MyObjectSubclass的对象实例。编译器就能恰当地确定 + factoryMethodA的返回类型应该是子类MyObjectSubclass,而不是工厂方法中所声明的超类。
在编写代码中,通常在处理init 方法和类工厂方法时,宜用instancetype类替换 id作为返回值。在新版本Xcode 5中,虽然,编译器会自动地把alloc、init、new方法之中的id转化为instancetype类型,但对于这几种方法之外的其他方法,编译器则不会进行转化。在Objective-C的公约之中,明确地建议对于所有方法尽可能用instancetype而非id。也就是说,作为返回值,id由于其自身的缺陷,在Objective-C中会逐渐退出,由instancetype来替代。
 仅有在作为返回值时,宜用instancetype来替换id,而不是代替代码中所有id。与id不同,instancetype关键字仅能作为方法声明的返回类型。也就说,在某一个特定区域,instancetype可以替代id,并非所有区域都可以替代id。
例如:

@interface MyObject
- (id)myFactoryMethod;
@end
应该成为:
@interface MyObject
- (instancetype)myFactoryMethod;
@end

 要点
(1)instancetype仅仅用来作为Objective-C方法的返回类型。
(2)使用instancetype可避免隐式转换id而造成的欺骗性编译无误通过的现象,防止程序正式运行时出现崩溃现象,可以大大改善Objective-C代码的类型安全。
(3)在某一个特定区域,instancetype可以替代id,并非所有区域都可以替代id。

相关文章
|
Shell Linux C语言
Windows 下使用 GNUstep 编译并运行 Objective-C 程序
今晚上开始看《Objective-C 程序设计(第4版)》这本书(OSChina 正在做此书的书评活动,详情请看这里),到现在为止看到第 7 章,于是想动手试试写两简单的程序编译跑跑看。
1476 0
|
程序员 iOS开发
《编写高质量代码:改善Objective-C程序的61个建议》——导读
我一直在思考,如何才能编写出高质量、优秀的代码,我也在不停地探寻,希望找出类似于武侠小说中所说的武功秘籍,在编写代码一途可以帮助大家走“捷径”从而达到事半功倍的效果。
1109 0
|
iOS开发
Objective-C特有类型——id
Objective-C特有类型——id OC里,id和int、double等一样,是一个类型 不同的是: id是一个万能指针,能指向/操作任何OC对象 相当于 (NS...
767 0
|
iOS开发
【《Objective-C基础教程 》笔记ch02】(二)Boolean类型及实例
一、布尔类型         布尔类型是一种对带符号的字符类型(signed char)的类型定义,使用8位的存储空间。         通过#define指令把YES定义为1,NO定义为0,都是8位的二进制数。
1031 0