翻译:采用现代Objective-C

多年来,Objective-C语言已经发展和演变。

虽然核心概念和实践保持不变,部分语言经历重大变化和改进。

这些现代化提高类型安全、内存管理、性能、和其他方面的Objective-C,使你更容易编写正确的代码。

采用这些变化是很重要的在你的现有的和未来的代码来帮助它变得更加一致,可读的,有弹性。


Xcode提供了一个工具来帮助使你的一些结构性变化。

但是在使用这个工具之前,你想了解什么改变它将提供您的代码,以及为什么。

本文强调了一些最重要的和有用的现代化采用你的代码库。


instancetype

使用instancetype关键字作为返回类型的方法,该方法返回类的实例,他们呼吁(或该类的子类)

这些方法包括alloc,init和类工厂方法。

使用instancetype而不是id在适当的地方提高在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;


    x = [[MyObject factoryMethodA] count]; // Return type of +factoryMethodA is taken to be "MyObject *"

    y = [[MyObject factoryMethodB] count]; // Return type of +factoryMethodB is "id"

}

因为+ factoryMethodA instancetype返回类型的消息的类型表达式MyObject *

MyObject没有计数方法,编译器会发出警告的x线:

main.m: ’MyObject’ may not respond to ‘count’

然而,由于+ factoryMethodB id返回类型,编译器可以给没有警告y。

因为一个id可以是任何类型的对象类,因为存在一个名为计数的方法在一些类,编译器可能的返回值+ factoryMethodB实现方法。


以确保instancetype工厂方法有正确的子类化的行为,一定要使用(自我类)在分配类而不是直接引用的类名。

遵循这个惯例确保编译器将推断正确类型的子类。

例如,考虑尝试这样做的一个子类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子类应该,不是超类的工厂方法被宣布。


如何采用

在你的代码中,出现的id作为返回值替换为instancetype在适当的地方。

这通常是init方法和类的工厂方法。

即使编译器自动转换方法,首先“alloc,”“init”或“新”,有一个返回类型id返回instancetype,它不会把其他方法。

objective - c约定是编写instancetype显式方法。


注意,应该替换id与instancetype返回值,而不是在你的代码。

与id、instancetype关键词可以使用结果输入方法声明。

例如:

@interface MyObject
- (id)myFactoryMethod;
@end
应该成为:
@interface MyObject
- (instancetype)myFactoryMethod;
@end
或者,您可以使用现代Objective-C变换器在Xcode中这种变化自动代码

有关更多信息,请参见使用Xcode重构您的代码。


属性

一个Objective-C属性是一个公共或私有方法使用@ property语法声明。

@property (readonly, getter=isBlue) BOOL blue;
属性获取一个对象的状态

他们反映对象的本质属性和其他对象之间的关系。

属性提供一个安全、方便的方式与这些属性,而无需编写一组自定义访问器方法(尽管属性允许定制的getter和setter,如果需要的话)。


使用属性而不是实例变量在尽可能多的地方提供了许多好处:

Autosynthesized getter和setter。

当你声明一个属性,默认情况下为你创建getter和setter方法。

更好的意图声明一组方法。

因为访问器方法的命名约定,明确的getter和setter是做什么。

属性关键字表示关于行为的额外信息。

属性提供潜在的声明的属性分配(vs副本),弱,原子(vs原子),等等。


属性方法遵循一个简单的命名约定。

getter是属性的名称(例如,日期),和setter属性的设置前缀的名字,写在驼峰式大小写(例如,设置当前日期)。

布尔属性的命名约定是声明他们叫getter开始这个词“是”:

@property (readonly, getter=isBlue) BOOL blue;
因此,所有的以下工作:

if (color.blue) { }
if (color.isBlue) { }
if ([color isBlue]) { }

在决定什么可能是一个性质,记住,不是属性如下:

init方法

copy方法,mutableCopy方法

一个类工厂方法

方法启动一个动作并返回一个BOOL的结果方法

明确内部状态变化作为一个getter的副作用此外


考虑以下的规则集,识别潜在的属性在代码中:

一个读/写属性有两个访问器方法。

setter接受一个参数并返回什么,和getter不接受参数并返回一个值。

如果你将这组方法转换为一个属性,标签与读写字。

一个只读属性有一个访问器方法,吸气,不接受参数并返回一个值。

如果你将这种方法转化为一个属性,用只读的关键字标记它。

getter应该幂等(如果调用getter两次,第二次调用导致相同的结果作为第一个)。

但是,它是可以接受的一个getter计算结果每次叫。

如何采用

确定一组方法,有资格被转化为一个属性,诸如此类的:

- (NSColor *)backgroundColor;
- (void)setBackgroundColor:(NSColor *)color;


并宣布他们使用@ property语法与适当的关键字:

@property (copy) NSColor *backgroundColor;

信息属性的关键字和其他注意事项,请参见封装数据。

或者,您可以使用现代Objective-C变换器在Xcode中这种变化自动代码。

有关更多信息,请参见使用Xcode重构您的代码。


枚举

NS_ENUM和NS_OPTIONS宏提供一个简洁、简单的定义枚举的方法,在基于c的语言选择。

这些宏改善代码完成在Xcode中显式地指定枚举和期权的类型和大小。

此外,这种语法声明枚举的方式由老的编译器,正确评估和更新的,可以解释潜在的类型信息。

使用NS_ENUM宏定义枚举,互斥的一组值:

typedef NS_ENUM(NSInteger, UITableViewCellStyle) {
        UITableViewCellStyleDefault,
        UITableViewCellStyleValue1,
        UITableViewCellStyleValue2,
        UITableViewCellStyleSubtitle
};

NS_ENUM宏观帮助定义枚举的名称和类型,在本例中名为UITableViewCellStyle NSInteger类型。

应NSInteger枚举类型。使用NS_OPTIONS宏来定义选项,一组位掩码值,可以组合在一起:

typedef NS_OPTIONS(NSUInteger, UIViewAutoresizing) {
        UIViewAutoresizingNone                 = 0,
        UIViewAutoresizingFlexibleLeftMargin   = 1 << 0,
        UIViewAutoresizingFlexibleWidth        = 1 << 1,
        UIViewAutoresizingFlexibleRightMargin  = 1 << 2,
        UIViewAutoresizingFlexibleTopMargin    = 1 << 3,
        UIViewAutoresizingFlexibleHeight       = 1 << 4,
        UIViewAutoresizingFlexibleBottomMargin = 1 << 5
};

像枚举,NS_OPTIONS宏定义一个名称和一个类型。然而,通常应该了NSUInteger类型选项。


如何采用

替换你的枚举声明,比如这个:

enum {
        UITableViewCellStyleDefault,
        UITableViewCellStyleValue1,
        UITableViewCellStyleValue2,
        UITableViewCellStyleSubtitle
};
typedef NSInteger UITableViewCellStyle;

NS_ENUM语法:

typedef NS_ENUM(NSInteger, UITableViewCellStyle) {
        UITableViewCellStyleDefault,
        UITableViewCellStyleValue1,
        UITableViewCellStyleValue2,
        UITableViewCellStyleSubtitle
};
但是当你使用枚举定义一个位掩码,例如:

enum {
        UIViewAutoresizingNone                 = 0,
        UIViewAutoresizingFlexibleLeftMargin   = 1 << 0,
        UIViewAutoresizingFlexibleWidth        = 1 << 1,
        UIViewAutoresizingFlexibleRightMargin  = 1 << 2,
        UIViewAutoresizingFlexibleTopMargin    = 1 << 3,
        UIViewAutoresizingFlexibleHeight       = 1 << 4,
        UIViewAutoresizingFlexibleBottomMargin = 1 << 5
};
typedef NSUInteger UIViewAutoresizing;
使用NS_OPTIONS宏。
typedef NS_OPTIONS(NSUInteger, UIViewAutoresizing) {
        UIViewAutoresizingNone                 = 0,
        UIViewAutoresizingFlexibleLeftMargin   = 1 << 0,
        UIViewAutoresizingFlexibleWidth        = 1 << 1,
        UIViewAutoresizingFlexibleRightMargin  = 1 << 2,
        UIViewAutoresizingFlexibleTopMargin    = 1 << 3,
        UIViewAutoresizingFlexibleHeight       = 1 << 4,
        UIViewAutoresizingFlexibleBottomMargin = 1 << 5
};
或者,您可以使用现代objective - c变换器在Xcode中这种变化自动代码。

有关更多信息,请参见使用Xcode重构您的代码。

对象初始化

在bjective-C中,对象初始化是基于指定初始值设定项的概念,一个初始化方法,负责调用超类的初始化,然后初始化自己的实例变量。

初始化不指定初始值设定项被称为便利初始值设定项。

连锁便利初始化通常委托给另一个initializer-eventually终止在指定initializer-rather比执行初始化。

指定初始化模式有助于确保继承初始化正确初始化实例变量。

子类需要执行重要的初始化应该覆盖其超类的所有指定的初始化,但它不需要覆盖方便初始化。

关于初始值设定项的更多信息,请参阅对象初始化。

澄清之间的区别和指定的初始化器指定清晰,你可以添加任何方法的NS_DESIGNATED_INITIALIZER宏观init的家人,表示指定初始值设定项。

使用这个宏介绍一些限制:指定初始值设定项的实现必须链到一个超类的init方法([超级init…])这是一个超类的指定初始值设定项。

方便的实现初始化器(一个初始化不标记为一个指定的初始化器内至少有一个初始化的类标记为一个指定的初始化器)必须委托给另一个初始化([自我init…])。
如果一个类提供了一个或多个指定的初始化程序,它必须实现所有的指定初始化它的超类。

如果违反这些限制,你收到来自编译器的警告。

如果你使用NS_DESIGNATED_INITIALIZER宏在你的类,你需要马克你所有的指定初始化这个宏。

所有其他初始化器将被认为是方便初始值设定项。


如何采用

确定指定的类初始化器,和标记它们NS_DESIGNATED_INITIALIZER宏。例如:

-(instancetype)init NS_DESIGNATED_INITIALIZER;


自动引用计数(ARC)

自动引用计数(ARC)是一个编译器特性,它提供了自动Objective-C对象的内存管理。

而不是你要记得使用retain,release, 和autorelease, ARC评估对象和自动插入适当的一生要求内存管理要求你在编译时间。

编译器也会产生适当的dealloc方法给你。


如何采用

Xcode提供了一个工具,自动化电弧转换的机械部件(如删除保留和释放调用),帮助你解决问题,移居者不能自动处理。

使用ARC迁移工具, 选择 Edit > Refactor > Convert to Objective-C ARC. 

迁移工具将所有文件在一个项目中使用ARC。

有关更多信息,请参见ARC过渡释放笔记。


使用Xcode重构您的代码

Xcode提供了一个现代Objective-C变换器可以在现代化过程中帮助你。

虽然转换器有助于识别和潜在应用现代化的机制,它不能解释代码的语义。

例如,它不会发现你的切换方法是一种行动,影响你的对象的状态,并将错误地提供现代化这一行动是一个性质。

确保手动审查和确认转换器提供做出任何更改代码。

前面描述的现代化,转换器提供了:

改变id instancetype在适当的地方

enum NS_ENUM或NS_OPTIONS

变化更新@ property语法

除了这些现代化,这个转换器推荐额外的代码更改,包括:

转换成文字,所以这样的声明[NSNumber numberWithInt:3]成为@3。

使用加下标,所以这样的声明[dictionary setObject:@3 forKey:key] 成为dictionary[key] = @3

使用现代的objective - c变换器,选择Edit > Refactor > Convert为现代Objective-C的语法。


翻译地址:https://developer.apple.com/library/ios/releasenotes/ObjectiveC/ModernizationObjC/AdoptingModernObjective-C/AdoptingModernObjective-C.html#//apple_ref/doc/uid/TP40014150-CH1-SW1



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值