关于iOS开发
2015-08-01 20:46
477 查看
Adopting Modern Objective-C
历经多年,Objective-C语言已经得到了许多增长和演变。虽然核心概念和做法保持一致,但这个语言的部分已经发生显著的变化和改进。这些现代化的改进增强了Objective-C的类型安全、内存管理、性能和一些其他方面,使你可以更轻松地编写正确的代码。在你现有的和将来的代码中采用这些改进可以使你的代码变得更一致,可读性更强,更灵活。XCode提供了一个工具来帮你完成这些结构上的更改。但是在你开始使用这个工具之前,你应该想了解一下它会给你的代码带来什么样的改变,以及为什么会带来这样的改变。本文档重点介绍了一些你可以在你的代码中应用的最显著的和最有用的新特性。
instancetype
在返回类实例对象的方法中用instancetype关键词作为方法的返回值类型。这包括在
alloc、
init和类工厂方法等方法中。
在适当的地方用
instancetype代替
id,可以提高你的Objective-C代码的类型安全。例如,考虑下面的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | @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没有
count方法,编译器给出了一个关于x行的警告:
1 | main.m: ’MyObject’ may not respond to ‘count’ |
+factoryMethodB的返回值类型为
id,编译器无法给出关于y行的警告。由于id类型的对象可以是任何类,并且由于一个叫
count的方法可能存在于某个类的某个地方,对于编译器来说,它就认为
+factoryMethodB方法返回的对象可能实现了该方法。
为了确保instancetype工厂方法有正确的子类化行为,在alloc类内存的时候一定要用
[self class],而不要直接引用类名。遵循这一约定可以确保编译器能够正确的推断出子类类型。例如,考虑MyObject子类的情形:
12 | @interface MyObjectSubclass : MyObject @end void doSomethingElse() { NSString *aString = [MyObjectSubclass factoryMethodA]; } |
1 | main.m: Incompatible pointer types initializing ’NSString *’ with an expression of type ’MyObjectSubclass *’ |
如何应用
在你的代码中,在适当的地方用instancetype做返回值,替换掉
id。通常是在
init方法和类工厂方法的情形中。尽管编译器会自动将返回值类型为id的并且开头为”alloc”、”init”或”new”的方法的返回值类型转换成instancetype类型,但它不会去转换其他的方法。Objective-C的约定是,要为所有有需要的方法明确地写上instancetype。
要注意的是,只有在id作为返回值类型的地方才能用instancetype代替它,其他地方不行。与id不同的是,instancetype关键字只能在方法声明中被用作返回值类型。
例如:
12 | @interface MyObject - (id)myFactoryMethod; @end |
12 | @interface MyObject - (instancetype)myFactoryMethod; @end |
Properties
Objective-C的属性(property)是指用@property语法定义的公有的或私有的方法。
1 | @property (readonly, getter=isBlue) BOOL blue; |
尽量在尽可能多的地方使用属性来代替实例变量,会有许多好处:
自动生成getter和setter方法。 当你定义了一个属性,默认会自动为你创建对应的getter和setter方法。
更好的定义一组方法。 因为访问方法的命名约定,它会让getter和setter方法的用途更明确。
属性关键词可以表达出对应行为的额外信息。 属性提供了
assign(相对于 copy)、
weak、
atomic(相对于 nonatomic)等等特性。
属性方法遵循一个简单的命名约定。getter方法的名字和属性名字相同(例如,date),setter方法的名字是属性名字带一个”set”前缀并采用驼峰命名规则(例如,setDate)。布尔类型的属性还可以定义一个以”is”开头的getter方法:
1 | @property (readonly, getter=isBlue) BOOL blue; |
12 | if (color.blue) { } if (color.isBlue) { } if ([color isBlue]) { } |
init方法
copy和
mutableCopy方法
类工厂方法
开启某项操作并返回一个BOOL结果的方法
明确的改变了一个getter的内部状态的副作用方法。
另外,在你的代码中标示属性特性的时候请考虑以下规则:
一个可读写(read/write)的属性有两个访问方法。setter方法接受一个参数并且没有返回值,getter方法不接受任何参数并返回一个值。如果将这组方法转换成一个属性,就可以用
readwrite关键字来标记它。
一个只读(read-only)的属性只有一个访问方法。即getter方法,它不接受任何参数,并且返回一个值。如果将这个方法转换成一个属性,就可以用
readonly关键字标记它。
getter方法应当是幂等的(idempotent,如果一个getter方法被调用两次,那么第二次调用时返回的结果应该和第一调用时返回的结果相同)。然而,如果一个getter方法每次调用时,是被用于计算结果,这是可以接受的。
如何应用
识别出一组可以被转换成一个属性的方法,如这些方法:12 | - (NSColor *)backgroundColor; - (void)setBackgroundColor:(NSColor *)color; |
@property语法和适当的关键字将它们定义成一个属性:
1 | @property (copy) NSColor *backgroundColor; |
或者,你也可以用Xcode中的modern Objective-C转换器来自动转换你的代码。欲了解更多信息,请参阅“使用Xcode重构你的代码”。
枚举宏
NS_ENUM和
NS_OPTIONS宏提供了一种在基于C的语言中定义枚举的简洁而又简单的方法。这两个宏增强了XCode中相关的代码补全功能,并且明确地指定了枚举和可选项(options)的类型和大小。此外,该语法定义的枚举既可以使得旧的编译器能够正确地计算枚举值,也可以使新的编译器能够解析出枚举值的类型信息。
使用
NS_ENUM宏来定义枚举,即一组互斥的值:
12 | typedef NS_ENUM(NSInteger, UITableViewCellStyle) { UITableViewCellStyleDefault, UITableViewCellStyleValue1, UITableViewCellStyleValue2, UITableViewCellStyleSubtitle }; |
NS_ENUM宏同时定义了枚举的名称和类型,这个示例中名称是UITableViewCellStyle,类型是NSInteger。枚举的类型应该尽量是NSInteger的。
使用
NS_OPTIONS宏来定义可选项(options),即一组可以相互结合的按位掩码(bitmasked)的值:
12 | 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的。
如何应用
更换你的枚举声明,比如这个:12 | enum { UITableViewCellStyleDefault, UITableViewCellStyleValue1, UITableViewCellStyleValue2, UITableViewCellStyleSubtitle }; typedef NSInteger UITableViewCellStyle; |
12 | typedef NS_ENUM(NSInteger, UITableViewCellStyle) { UITableViewCellStyleDefault, UITableViewCellStyleValue1, UITableViewCellStyleValue2, UITableViewCellStyleSubtitle }; |
12 | enum { UIViewAutoresizingNone = 0, UIViewAutoresizingFlexibleLeftMargin = 1 << 0, UIViewAutoresizingFlexibleWidth = 1 << 1, UIViewAutoresizingFlexibleRightMargin = 1 << 2, UIViewAutoresizingFlexibleTopMargin = 1 << 3, UIViewAutoresizingFlexibleHeight = 1 << 4, UIViewAutoresizingFlexibleBottomMargin = 1 << 5 }; typedef NSUInteger UIViewAutoresizing; |
12 | 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 }; |
自动引用计数(ARC)
自动引用计数(Automatic Reference Counting)是一个编译器特性,它提供了Objective-C对象的自动内存管理功能。而你不必再需要记住什么时候使用retain、
release和
autorelease了,ARC会评估你的对象的生命周期,并在编译期自动帮你插入适当的内存管理调用。编译器也会为你生成相应的
dealloc方法。
如何应用
XCode提供了一个工具,可以自动帮你完成ARC转换过程中的手动操作部分(比如移除对retain和
release的调用),也可以帮助你解决迁移工具不能自动处理的问题。要使用ARC迁移工具,在XCode菜单中选择“Edit
> Refactor > Convert to Objective-C ARC”,迁移工具会将项目中的所有文件转换至使用ARC。
欲了解更多信息,请参阅《Transitioning to ARC Release Notes》。
使用XCode重构你的代码
XCode提供了一个现代的Objective-C转换工具,可以协助你完成转换工作。虽然该转换工具可以在某些潜在的地方帮助你识别和应用这些现代的特性,但它不解释你代码的语义。例如,它不能检测出你的-toggle方法是一个可以影响你的对象的状态的方法,它会错误地提议将这个方法转换成一个更现代化的属性。请务必手动检查和确认转换工具给你的代码提供的任何修改。
综上所述的现代化的特性,这个转换工具可以提供以下这些转换功能:
在适当的地方用instancetype替换id
将enum转换至NS_ENUM或NS_OPTIONS
将部分方法更新成@property语法
除了这些,这个转换工具还会建议你对代码做一些其他的修改,包括:
转换至字面值语法(literals),因此像
[NSNumber numberWithInt:3]这样的语句会变成
@3。
使用下标语法,因此像
[dictionary setObject:@3 forKey:key]这样的语句会变成
dictionary[key] = @3。
要使用这个现代的Objective-C转换工具,在Xcode菜单中选择“Edit > Refactor > Convert to Modern Objective-C Syntax”。
相关文章推荐
- iOS开发-项目 知识分享与经验积累(一)
- IOS中Json解析的四种方法
- [精通iOS开发(第5版)]第21章 集合视图 笔记
- IOS开发之NSOperation
- iOS8开发--在Xcode6中设置Launch Image(启动图片)
- iOS8开发-- 如何在Xcode6中添加pch全局引用文件
- 一、IOS程序执行顺序、APPdelegate代理方法执行顺序、ViewController 生命周期
- IOS开发之多线程队列
- IOS autoLayout之使用VFL语言进行代码自动布局
- hdu 1017 A Mathematical Curiosity
- ios 为图片加上水印详解
- iOS 扩展机制category与associative
- iOS内存管理机制
- iOS7.0及以上图片转换成base64编码的方法(oc)
- iOS-应用管理 点击按钮下载动画
- IOS 7 自动布局详解(一)
- iOS-mvc分文件夹
- iOS MKMapView 以某一坐标为中心进行缩放
- ios7 二维码图片生成
- iOS中判断两个圆是否重叠