多年来,Objective-C语言已经有了革命性的发展。虽然核心理念和实践保持不变,但语言中的部分内容经历了重大的变化和改进。现代化的Objective-C在类型安全、内存管理、性能、和其他方面都得到了增强。使你更容易编写正确的代码。在你现有和未来的代码中使用这些改进是很重要的,会使你的代码一致、可读、灵活。
 
Xcode提供了一个工具来帮助做这些结构性的变化。但在使用这个工具之前,你想了解工具为你的代码做了什么改变以及为什么。本文强调了一些最重要的和有用的现代化方式可以用在你的代码中。
 
instancetype
使用instancetype关键字作为返回类型的方法,该方法返回一个类的实例(或该类的子类)这些方法包括alloc,init,和类工厂方法。
 
使用instancetype代替id在适当的地方可以改善Objective-C代码类型安全。例如:考虑下面的代码:
  1. @interface MyObject : NSObject
  2. + (instancetype)factoryMethodA;
  3. + (id)factoryMethodB;
  4. @end
  5. @implementation MyObject
  6. + (instancetype)factoryMethodA
  7. {
  8. return [[[self class] alloc] init];
  9. }
  10. + (id)factoryMethodB
  11. {
  12. return [[[self class] alloc] init];
  13. }
  14. @end
  15. void doSomething()
  16. {
  17. NSUinteger x, y;
  18. // Return type of +factoryMethodA is taken to be "MyObject *"
  19. x = [[MyObject factoryMethodA] count];
  20. // Return type fo +factoryMethodB is "id"
  21. y = [[MyObject factoryMethodB] count];
  22. }
 
因为+factoryMethodA使用了instancetype作为返回类型,该消息的类型表达式为MyObject *.当MyObject没有-count方法的时候,编译器会发出警告的在x行:
  1. main.m: 'MyObject' may not respond to 'count'
 
然而,由于+factoryMethodB返回类型为id,编译器不可以给出警告。因为一个id可以是任何类型的对象类,由于存在一个名为-count的方法在一些类中,编译器可能返回一个+factoryMehtodB的实现的方法。
 
确保instancetype工厂方法有权利子类化行为,在初始化的时候一定要使用[self class]而不是直接引用的类名。遵循这个惯例确保编译器将正确判断出子类的类型。例如:考虑尝试这样做一个MyObject的子类从前面的示例:
  1. @interface MyObjectSubclass : MyObject
  2. @end
  3. void doSomethingElse()
  4. {
  5. NSString *aString = [MyObjectSubclass factoryMethodA];
  6. }
 
关于这个代码编译器将给出下面的警告:
  1. main.m: Incompatible pointer types initializing 'NSString *'
  2. with an expression of type 'MyObjectSubclass *'
 
在这个例子中,+factoryMethodA MyObjectSubclass类型的消息发送返回一个对象,这是接收者的类型。编译器确定适当的返回类型+factoryMethodA应该是MyObjectSubclass子类,而不是超类的工厂方法被调用。
 
怎样适配
在你的代码中,出现id作为返回值替换为instancetype在适当的地方。这通常是init方法和类的工厂方法。甚至编译器会自动转以“alloc”、“init”、“new”开头的方法,而不转换其他的方法。objective-c对instancetype的转换是显式的方式。
 
请注意,您仅应该用instancetype替换id作为返回值,而不是在你的代码的任何地方都这么做。不像id,instancetype在声明方法时仅仅只能作为返回值类型使用。
 
例如:
  1. @interface MyObject
  2. - (id)myFactoryMethod;
  3. @end
 
 
应该变为:
  1. @interface MyObject
  2. - (instancetype)myFactoryMethod;
  3. @end
或者,您可以在Xcode使用现代objective-c变换器自动进行转换您的代码。更多信息请看使用Xcode重构你的代码。
 

采用现代Objective-C (2)
 
Properties
一个public或private的Objective-C属性使用@property语法声明的。
  1. @property (readonly, getter=isBlue) BOOL blue;
属性持有着一个对象的状态。他们反映对象的本质属性和其他对象的关系。Properties提供一个安全、方便的方式来定义这些属性,而无需编写一组自定义访问器方法(虽然属性允许定制的getter和setter,如果需要的话)。
 
使用属性而不是实例变量在尽可能多的地方提供了许多好处:
1.自动合成getters和setters。当你声明一个属性,默认情况下为你创建getter和setter方法。
2.更好的意图声明一组方法。因为访问器方法的命名约定方便,很明显getter和setter方法是做什么的。
3.property关键字表示关于行为的额外信息。属性提供潜在的声明的属性像assign(vs copu),weak,atomic(vs nonatomic),等等。
 
属性方法遵循一个简单的命名约定。getter属性的名称(例如,date),setter属性在名称前加前缀,按驼峰式命名书写(例如,setDate)。Boolean属性的命名约定是声明他们的getter方法以'is'开头。
  1. @property (readonly, getter=isBlue) BOOL blue;
 
因此,以下所有调用方式都可以正常工作:
  1. if(color.blue){}
  2. if(color.isBlue){}
  3. if([color isBlue]){}
 
在决定什么可能是一个属性时,记住,如下不是属性:
1.init方法
2.copy方法,mutableCopy方法
3.一个类的工厂方法
4.一个初始化一个动作并返回BOOL值的方法
5.一个明确改变内部状态对getter有副作用的方法
 
此外,考虑以下的规则集当在你的代码中定义属性时:
1.一个读/写属性有两个访问器方法。setter接受一个参数什么也不返回,getter不接受参数并返回一个值。可以用readwrite关键字设置这个属性。
2.一个只读属性有一个访问器方法,getter不接受参数并返回一个值。可以使用readonly关键字设置。
3.getter应该幂等的(如果一个getter方法调用了两次,那么第二次结果应该和第一次是相同的)。
 
但是,每次geeter被调用返回结果是可接受的。
 
怎样适配
定义一组方法,有资格被转换成属性,诸如此类的:
  1. - (NSColor *)backgroundColor;
  2. - (void)setBackgroundColor:(NSColor *)color;
 
用@property语法和其他合适的关键字定义他们:
  1. @property (copy) NSColor *backgroundColor;
 
更多关于property关键字和其他的信息,请看“Encapsulating Data”
 

采用现代Objective-C (3)
 
Enumeration Macros
NS_ENUM和NS_OPTIONS宏提供一个简洁、简单的定义枚举的方法和基于c语言的选项。这些宏在Xcode中实现可以显式地指定枚举类型和选项的大小。此外,这种由旧的编译器语法声明枚举的方式,可以被新的编译器正确评估和解释潜在的类型信息。
 
使用NS_ENUM宏定义枚举,互斥的一组值:
  1. typedef NS_ENUM(NSInteger, UITableViewCellStyle){
  2. UITableViewCellStyleDefault,
  3. UITableViewCellStyleValue1,
  4. UITableViewCellStyleValue2,
  5. UITableViewCellStyleSubtitle
  6. }
 
NS_ENUM宏帮助定义枚举的名称和类型,在本例中名称为UITableViewCellStyle类型为NSInteger。枚举类型应该是NSInteger。
 
使用NS_OPTIONS宏来定义选项,一组位掩码值,可以组合在一起:
  1. typedef NS_OPTIONS(NSUInteger, UIViewAutoresizeing){
  2. UIViewAutoresizeingNone                    = 0,
  3. UIViewAutoresizeingFlexibleLeftMargin      = 1 << 0,
  4. UIViewAutoresizeingFlexibleWidth           = 1 << 1,
  5. UIViewAutoresizeingFlexibleRightMargin     = 1 << 2,
  6. UIViewAutoresizeingFlexibleTopMargin       = 1 << 3,
  7. UIViewAutoresizeingFlexibleHeight          = 1 << 4,
  8. UIViewAutoresizeingFlexibleBottomMargin    = 1 << 5
  9. }
 
像这样的枚举,NS_OPTIONS宏定义一个名称和一个类型。然而,通常类型应该是NSUInteger。
 
怎样适配
代替你的枚举声明,如:
  1. enum{
  2. UITableViewCellStyleDefault,
  3. UITableViewCellStyleValue1,
  4. UITableViewCellStyleValue2,
  5. UITableViewCellStyleSubtitle
  6. };
  7. typedef NSInteger UITableViewCellStyle;
 
用NS_ENUM语法:
  1. typedef NS_ENUM(NSInteger, UITableViewCellStyle){
  2. UITableViewCellStyleDefault,
  3. UITableViewCellStyleValue1,
  4. UITableViewCellStyleValue2,
  5. UITableViewCellStyleSubtitle
  6. }
 
但是,当你使用enum去定义一个位掩码,像这样:
  1. enum {
  2. UIViewAutoresizeingNone                    = 0,
  3. UIViewAutoresizeingFlexibleLeftMargin      = 1 << 0,
  4. UIViewAutoresizeingFlexibleWidth           = 1 << 1,
  5. UIViewAutoresizeingFlexibleRightMargin     = 1 << 2,
  6. UIViewAutoresizeingFlexibleTopMargin       = 1 << 3,
  7. UIViewAutoresizeingFlexibleHeight          = 1 << 4,
  8. UIViewAutoresizeingFlexibleBottomMargin    = 1 << 5
  9. };
  10. typedef NSUInteger UIViewAutoresizing;
 
用NS_OPTIONS宏:
  1. typedef NS_OPTIONS(NSUInteger, UIViewAutoresizeing){
  2. UIViewAutoresizeingNone                    = 0,
  3. UIViewAutoresizeingFlexibleLeftMargin      = 1 << 0,
  4. UIViewAutoresizeingFlexibleWidth           = 1 << 1,
  5. UIViewAutoresizeingFlexibleRightMargin     = 1 << 2,
  6. UIViewAutoresizeingFlexibleTopMargin       = 1 << 3,
  7. UIViewAutoresizeingFlexibleHeight          = 1 << 4,
  8. UIViewAutoresizeingFlexibleBottomMargin    = 1 << 5
  9. }
 
或者,您可以在Xcode使用现代objective-c变换器自动进行转换您的代码。更多信息请看使用Xcode重构你的代码。
 
Automatic Reference Counting (ARC)
自动引用计数(ARC)是一个编译器特性,它提供了Objective-C对象的自动内存管理。代替你不必记得使用retain,release和autorelease。ARC评估对象的生命周期需求并自动插入适当的内存管理要求在编译时间。编译器也会为你产生适当的dealloc方法。
 
怎样适配
Xcode提供了一个工具,自动化转换的(如删除retain和release调用)帮助你解决不能自动修复的问题。使用ARC工具:选择Edit > Refactor > Convert to Objective-C ARC。这个工具转换项目中所有的文件使用ARC。
 
更多的信息,看Transitioning to ARC Release Notes.
 
Refactoring Your Code Using Xcode
Xcode提供了一个现代objective - c变换器,在转向现代化过程中可以帮助你。虽然转换器有助于识别和潜在应用现代化的机制,但它没有解释代码的语义。例如,它不会发现-toggle方法是一种动作,影响你的对象的状态,并将错误地提供现代化这一行动是一个属性。确保手动审查和确认任何转换器提供的使您的代码的更改。
 
前面描述的现代化,转换器提供了:
1.改变id到instancetype在合适的地方
2.改变enum到NS_ENUM或NS_OPTIONS
3.更新到@property语法
 
除了这些现代化,这个转换器推荐额外的代码变更,包括:
1.转换到字面意思,像[NSNumber numberWithInt:3]变成@3.
2.用下标,像[dictionary setObject:@3 forKey:key]变成dictionary[key] = @3.
 
使用modern Objective-C converter,Edit > Refactor > Convert to Modern Objective-C Syntax.
 
来源:简书

采用现代Objective-C的更多相关文章

  1. 【转】从Go、Swift语言出发

    Google于2009年第一次提出了Go的构思,Facebook在去年春天引入了Hack,随后不久Apple也发布了其Swift语言. 在战争中,胜利者写历史书:在科技中,赢的公司都在写编程语言.互联 ...

  2. 从Go、Swift出发:语言的选择需谨慎

    本文转自 : http://www.csdn.net/article/2014-12-09/2823025 摘要:无论是开源的Go,还是闭源的Swift,新的语言总是利弊一体.不过可以确定的是,新的语 ...

  3. Automake

    Automake是用来根据Makefile.am生成Makefile.in的工具 标准Makefile目标 'make all' Build programs, libraries, document ...

  4. 浅谈Objective—C中的面向对象特性

    Objective-C世界中的面向对象程序设计 面向对象称程序设计可能是现在最常用的程序设计模式.如何开发实际的程序是存在两个派系的-- 面向对象语言--在过去的几十年中,很多的面向对象语言被发明出来 ...

  5. iOS开发——技术精华Swift篇&Swift 2.0和Objective-C2.0混编之第三方框架的使用

    swift 语言是苹果公司在2014年的WWDC大会上发布的全新的编程语言.Swift语言继承了C语言以及Objective-C的特性,且克服了C语言的兼容性问题.Swift语言采用安全编程模式,且引 ...

  6. 第一章 熟悉Objective -C 编写高质量iOS与OS X代码的52 个有效方法

    第一章 熟悉Objective -C   编写高质量iOS与OS  X代码的52 个有效方法   第一条: 了解Objective-C 语言的起源 关键区别在于 :使用消息结构的语言,其运行时所应执行 ...

  7. 论文翻译:2020_FLGCNN: A novel fully convolutional neural network for end-to-end monaural speech enhancement with utterance-based objective functions

    论文地址:FLGCNN:一种新颖的全卷积神经网络,用于基于话语的目标函数的端到端单耳语音增强 论文代码:https://github.com/LXP-Never/FLGCCRN(非官方复现) 引用格式 ...

  8. 采用MiniProfiler监控EF与.NET MVC项目(Entity Framework 延伸系列1)

    前言 Entity Framework 延伸系列目录 今天来说说EF与MVC项目的性能检测和监控 首先,先介绍一下今天我们使用的工具吧. MiniProfiler~ 这个东西的介绍如下: MVC Mi ...

  9. 采用EntityFramework.Extended 对EF进行扩展(Entity Framework 延伸系列2)

    前言 Entity Framework 延伸系列目录 今天我们来讲讲EntityFramework.Extended 首先科普一下这个EntityFramework.Extended是什么,如下: 这 ...

随机推荐

  1. 22.allegro中PCB打印设置[原创]

    1. -- 2. 3. 4. ----

  2. Android log日志

    LOG是用来记录程序执行过程的机制,它既可以用于程序调试,也可以用于产品运营中的事件记录.在Android系统中,提供了简单.便利的LOG机制,开发人员可以方便地使用. androidsdk中提供了l ...

  3. Altium designer总结

    itwolf原创文章,转载请注明出处 大概有半年没有画过PCB板了,最近突然又要画一个简单的小板子,却发现好多东西已经不是很熟练了,现在把Altium designer软件的使用中要注意的问题和一些小 ...

  4. Mac下安装HBase及详解

    Mac下安装HBase及详解 1. 千篇一律的HBase简介 HBase是Hadoop的数据库, 而Hive数据库的管理工具, HBase具有分布式, 可扩展及面向列存储的特点(基于谷歌BigTabl ...

  5. hdu 4920 Matrix multiplication (矩阵计算)

    题目链接 题意:给两个矩阵a, b, 计算矩阵a*b的结果对3取余. 分析:直接计算时间复杂度是O(n^3),会超时,但是下面第一个代码勉强可以水过,数据的原因. #include <iostr ...

  6. Android ContentProvider和Uri详解 (绝对全面)

        ContentProvider的基本概念 : 1.ContentProvider为存储和读取数据提供了统一的接口 2.使用ContentProvider,应用程序可以实现数据共享 3.andr ...

  7. Codeforces_GYM Flight Boarding Optimization

    (ACM ICPC 2013–2014, NEERC, Northern Subregional Contest) Flight Boarding OptimizationInput file: fl ...

  8. bzoj1499: [NOI2005]瑰丽华尔兹

    dp. 首先我们可以看到每个时间段只能往一个方向转移最多t步(t为时间段的长度),所以我们可以按时间段dp.因为这个前后值互不影响,也不用占用这一维空间就可以省去. 然后每个时间段内是一列一列(行) ...

  9. 把 HttpHandler.ashx 修改为 异步编程 异步操作

    在 ASP.NET 中,所有的处理程序类必须实现 IHttpHandler 接口或者实现 IHttpAsyncHandler 接口,这两个接口的区别是前者是一个同步接口,后者是一个异步处理模式的接口. ...

  10. Android SharedPreferences 权限设置

    说明: 由于目前打算采用两个app来完成一件事,采用SharedPreferences来做数据交换,于是突然想验证一下Java层的权限设置会不会就是设置Linux下文件的权限,验证的结果是这样的. T ...