采用现代objective - c
来源:http://www.cnblogs.com/chensheng12330/p/3950004.html
多年来,objective - c语言已经发展和演变。 虽然核心概念和实践保持不变,部分语言经历了重大的变化和改进。 这些现代化提高类型安全、内存管理、性能、和其他方面的objective - c,使你更容易编写正确的代码。 采用这些变化是很重要的在你的现有的和未来的代码来帮助它变得更加一致的,可读的,有弹性。
Xcode提供了一个工具来帮助使你的一些结构性变化。 但是在使用这个工具之前,你想了解什么改变它将提供您的代码,以及为什么。 本文强调了一些最重要的和有用的现代化采用你的代码库。
instancetype
使用 instancetype
关键字作为返回类型的方法,该方法返回类的实例,他们呼吁(或该类的子类)。 这些方法包括 分配
, 初始化
工厂方法和类。
使用 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" |
} |
因为 instancetype
返回类型的 + factoryMethodA
,消息的类型的表达式 MyObject *
。 自 myObject
没有 数
方法,编译器将发出警告 X
线:
main.m: ’MyObject’ may not respond to ‘count’ |
然而,由于 id
返回类型在 + factoryMethodB
,编译器可以给没有警告 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
在适当的地方。 这是通常的情况 初始化
方法和类的工厂方法。 即使编译器自动转换方法,首先“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 - cproperty是一个公共或私有方法声明
@property
语法。
@property (readonly, getter=isBlue) BOOL blue; |
属性获取一个对象的状态。 他们反映对象的本质属性和关系到其他对象。 属性提供一个安全、方便的方式与这些属性,而无需编写一组自定义访问器方法(虽然属性允许定制的getter和setter,如果需要的话)。
使用属性而不是实例变量在尽可能多的地方提供了许多好处:
-
Autosynthesized getter和setter。 当你声明一个属性,默认情况下为你创建getter和setter方法。
-
更好的意图声明一组方法。 因为访问器方法的命名约定,很明显的getter和setter。
-
属性关键字表示关于行为的额外信息。 属性提供潜在的声明的属性
assign
(vscopy
),weak
,atomic
(vsnonatomic
),等等。
属性方法遵循一个简单的命名约定。property是属性的名称(例如, 日期
), Setter 是属性的名字吗 集 前缀,写在驼峰式大小写(例如, 设置当前日期
)。 布尔属性的命名约定是声明他们叫getter开始这个词“是”:
@property (readonly, getter=isBlue) BOOL blue; |
因此,所有的以下工作:
if (color.blue) { } |
if (color.isBlue) { } |
if ([color isBlue]) { } |
在决定什么可能是一个属性,记住,不是属性如下:
-
初始化
方法 -
复制
方法,mutableCopy
方法 -
工厂方法类
-
一种方法,并返回一个启动一个动作
BOOL
结果 -
一种方法,明确内部状态变化getter的副作用
此外,考虑以下的规则集时识别潜在的属性在代码:
-
一个读/写属性有两个访问器方法。 setter接受一个参数并返回什么,getter不接受参数并返回一个值。 如果你把这组方法转换成属性,标记它
读写
关键字。 -
一个只读属性有一个访问器方法,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重构您的代码 。
对象初始化
在objective - c中,对象初始化是基于的概念 指定的初始化程序 一个初始化方法,负责调用它的一个超类的初始化,然后初始化自己的实例变量。 没有指定初始值设定项被称为初始化 方便的初始化 。 连锁便利初始化通常委托给另一个initializer-eventually终止在指定initializer-rather不是自己执行初始化。
指定的初始化程序模式有助于确保继承初始化正确初始化实例变量。 子类需要执行重要的初始化应该覆盖其超类的所有指定的初始化,但它不需要覆盖方便初始化。 关于初始值设定项的更多信息,请参阅 对象初始化 。
明确指定的区别和指定的初始化程序清晰,你可以添加 NS_DESIGNATED_INITIALIZER
宏观的任何方法 初始化
家人,表示它指定初始值设定项。 使用这个宏介绍一些限制:
-
指定的初始化器的实现必须连锁超类
初始化
方法((超级init……)
)这是一个超类初始化器指定。 -
方便的实现初始化器(初始化器不标记为一个指定的初始化器内至少有一个初始化的类标记为一个指定的初始化器)必须委托给另一个初始化器(带
(自我init……)
)。 -
如果一个类提供了一个或多个指定的初始化程序,它必须实现所有的指定初始化它的超类。
如果违反了这些限制,你收到来自编译器的警告。
如果您使用 NS_DESIGNATED_INITIALIZER
宏在你类,你需要马克你所有的指定初始化这个宏。 所有其他初始化被认为是便利的初始化。
如何采用
确定指定的类初始化器,和标记的 NS_DESIGNATED_INITIALIZER
宏。 例如:
- (instancetype)init NS_DESIGNATED_INITIALIZER; |
自动引用计数(ARC)
自动引用计数(ARC)是一个编译器特性,它提供了自动objective - c对象的内存管理。 而不是你的记得使用 保留
, 释放
, autorelease
一生,ARC评估需求的对象和自动插入适当的内存管理要求你在编译时间。 编译器也会产生适当的 dealloc
你的方法。
如何采用
Xcode提供了一个工具,自动化ARC转换的机械部件(如删除 保留
和 释放
调用),并且帮助你解决问题,移居者不能自动处理。
使用Xcode重构您的代码
Xcode提供了一个现代objective - c变换器,可以帮助你在现代化过程中。 虽然转换器有助于识别和潜在应用现代化的机制,它不解释代码的语义。 例如,它不会检测到你 切换
方法是一种行动,影响你的对象的状态,并将错误地提供现代化这一行动是一个属性。 确保手动审查和确认任何更改转换器提供使您的代码。
- 前面描述的现代化,转换器提供了:
-
-
改变
id
来instancetype
在适当的地方 -
改变
枚举
来NS_ENUM
或NS_OPTIONS
-
更新
@property
语法
-
- 除了这些现代化,这个转换器推荐额外的代码变更,包括:
-
-
转换成文字,所以这样的声明
[NSNumber numberWithInt:3)
简写为:@3
。 -
使用加下标,所以这样的声明
[dictionary setObject:@3 forKey:key]
。
-
使用现代objective - c变换器,在Xcode里操作路径 Edit > Refactor > Convert to Modern Objective-C Syntax。