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

    xiaoxiao2023-08-22  150

    本节书摘来自华章出版社《编写高质量代码:改善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。

    相关资源:敏捷开发V1.0.pptx
    最新回复(0)