采用现代Objective-C

转载地址:http://www.baidu.com/link?url=dPuMwKqDz2R0ggyWdCkWyBz4vKbBLxBHSJBsYzja9HoIIVpWG6Thq7bTfy3uMFO0

前言

这些年以来,随着Objective-C语言的不断发展和进化。尽管这门语言的核心概念和实践都保持不变,但是这其中的一部分已经有了显著的提升和改变。为了让我们更加容易的写出正确的代码,现代Objective-C语言提升了类型安全、内存管理、性能以及一些其他的方面。因此,在我们现在的以及将来的项目中采用这些新的改变让我们的代码变得更加一致、更加可读、更加可维护。

Xcode提供了一个工具来帮助我们做出这些结构上的改变。但是在使用这个工具之前,我们需要知道Xcode对我们的代码做出了什么样改变,以及它为什么要做出这样的改变。这篇文章会突出介绍如何在我们的代码中采用一些最有效、最有用的现代Objective-C编程方法。

instancetype

使用关键字instancetype作为需要返回一个类(或者这个类的子类)实例的方法的返回值类型。这些方法包括allocinit,以及类工厂方法。

在合适的场合使用instancetype代替id类型来提高你的Objective-C代码的类型安全。例如,看一下下面的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// .h
@interface MyObject : NSObject
+ (instancetype)factoryMethodA;
+ (id)factoryMethodB;
@end
// .m
@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类工厂方法能够拥有正确的子类化行为,确保我们在分配类时使用[self class]而不是直接使用类名。遵循这个约定以确保编译器能够正确的推断出子类的类型。例如:子类化上一个例子中的MyObject:

1
2
3
4
5
@interface MyObjectSubclass : MyObject
@end
void doSomethingElse() {
        NSString *aString = [MyObjectSubclass factoryMethodA];
}

对于这段代码编译器给出了如下的警告:

1
main.m: Incompatible pointer types initializing NSString * with an expression of type MyObjectSubclass *

在这个例子中,+factoryMethodA消息发送后返回了一个MyObjectSubclass类型的对象,是消息接收者类型的对象。编译器因此会相应的认为+factoryMethodA方法的返回值应该是MyObjectSubclass的子类,而不是其工厂方法中声明的那个父类。

如何采用?

在你的代码中,用instancetype类型恰当的代替出现id返回值类型的地方。比较典型的就是init方法和类工厂方法。尽管编译器会自动的将以“alloc”,“init”,“new”开头的方法的返回值类型由id转为instancetype,但是却不会为其他的方法进行自动转换。Objective-C的约定是为所有的方法明确的使用instancetype返回值类型。

请注意,你应该只是对于返回值类型用instancetype替换id,在其他处的代码是不可以的。与id类型不同的是,关键字instancetype只能用于方法声明中的返回值类型。

例如:

1
2
3
@interface MyObject
- (id)myFactoryMethod;
@end

应该转变成:

1
2
3
@interface MyObject
- (instancetype)myFactoryMethod;
@end

Properties

Objective-C的property是使用@property语法声明的公有方法或者私有方法。

1
@property (readonly, getter=isBlue) BOOL blue;

Properties捕获对象的状态。它能够表达出对象的内在的属性以及和其他对象之间的关系。Properties提供了一个安全、方便的方式与这些属性进行交互而不用自己写一组自定义的存取方法(尽管属性允许我们在需要的时候自定义getterssetters)。

在越来越多的场合下使用属性代替实例变量有以下的益处:

  • 自动合成getterssetters当你声明一个property,会默认自动为你创建gettersetter方法。
  • 意图表达更加清楚的一组方法。由于存取方法的命名规范,能够非常准确的表达gettersetter方法正在做什么。
  • Property关键字表达表达了一些关于行为的额外信息。属性为声明像assign(vs copy),weakatomic(vs nonatomic)等行为提供了可能。

属性方法遵循一个简单的命名规范。getter方法名为属性的名字(例如:date),setter方法名位属性名带上set前缀,采用驼峰写法(例如:setDate)。布尔值属性的命名约定是的getter方法是以“is”开头的:

1
@property (readonly, getter=isBlue) BOOL blue;

因此,下面的每一个方法都是能够工作的:

1
2
3
if (color.blue) { }
if (color.isBlue) { }
if ([color isBlue]) { }

当想要决定什么可以是property时,一定要记住,下面的这些情况不能为属性:

  • init方法
  • copy方法,mutableCopy方法
  • 类工厂方法
  • 方法启动了一个操作,同时返回一个Bool
  • 方法明确改变了内部状态作为一个getter的副作用。

此外,在你的代码中识别潜在的属性时,需要考虑下面的这些原则:

  • 一个read/write property 拥有两个存取方法。setter方法拥有一个参数,无返回值,getter方法没有参数,有一个返回值。如果你想转变这组方法称为property,需要标注为readwrite关键字。
  • 一个read-only property只有一个存取方法,就是getter方法,无参数,有一个返回值。如果想要转变这个方法为property时,需要标注readonly关键字。
  • getter方法应该是idempotent(如果getter方法被调用两次,第二次调用的结果和第一次调用的结果应该是相同的)。然而,对于getter方法是合理的去计算它每一次被调用的值。

如何采用?

识别出一组方法,使其有资格转化成一个property,比如:

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

使用@property语法选择合理的关键字来声明成属性:

1
@property (copy) NSColor *backgroundColor;

Enumeration Macros

NS_ENUMNS_OPTIONS宏提供了一个方便、简单的方式来定义基于C语言的枚举和选择。这些宏在Xcode中改善了代码实现,明确指定枚举、选择的类型以及大小。除此之外,这种声明枚举的方式可以很好的兼容旧的的编译器,新的声明方法可以解释基础类型的信息。

使用NS_ENUM宏来定义enumerations,一组不同的值的集合:

1
2
3
4
5
6
typedef NS_ENUM(NSInteger, UITableViewCellStyle) {
        UITableViewCellStyleDefault,
        UITableViewCellStyleValue1,
        UITableViewCellStyleValue2,
        UITableViewCellStyleSubtitle
};

NS_ENUM宏帮助定义枚举的名字和类型,UITableViewCellStyle宏的类型为NSInteger。枚举的类型应该是NSInteger

使用NS_OPTIONS宏来定义options,一组结合在一起的位掩码:

1
2
3
4
5
6
7
8
9
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宏定义了名字和类型。然而,options的类型通常是NSUInteger

如何采用?

替代你的enum声明,比如:

1
2
3
4
5
6
7
enum {
        UITableViewCellStyleDefault,
        UITableViewCellStyleValue1,
        UITableViewCellStyleValue2,
        UITableViewCellStyleSubtitle
};
typedef NSInteger UITableViewCellStyle;

使用NS_ENUM语法:

1
2
3
4
5
6
typedef NS_ENUM(NSInteger, UITableViewCellStyle) {
        UITableViewCellStyleDefault,
        UITableViewCellStyleValue1,
        UITableViewCellStyleValue2,
        UITableViewCellStyleSubtitle
};

但是,当你使用enum来定义一个位掩码(bitmask),比如:

1
2
3
4
5
6
7
8
9
10
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宏:

1
2
3
4
5
6
7
8
9
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)

自动引用引用计数(ARC)是编译器特性,为Objective-C对象提供内存管理,不再需要记住什么时候应该使用retainrelease,和autorelease。ARC帮你管理对象的生存周期,在编译器帮你自动插入适当的内存管理代码,编译器也会在合适的时机帮你生成dealloc方法。

如何采用?

Xcode提供了ARC自动转换工具。选择ARC迁移工具:选择:Edit > Refactor > Convert to Objective-C ARC。更多信息请查看:Transitioning to ARC Release Notes

相关链接

Adopting Modern Objective-C

使用CocoaPods来管理iOS项目的依赖库

DEC 28TH, 2013

CocoaPods Logo

前言

细细算来,我接触iOS已经有1.5f年的时间了,虽然其中有差不多一年的时间是在大四经历自学和实习的这个阶段。抛去那段时间不算,毕业后在现在的公司工作差不多半年了…

在经历过的几个项目上基本上每一个都会用到第三方开源库,比如SDWebImageAFNetworkingMBProgressHUD等。然而,每次把这些第三方库导入到我们的项目中要配置一些选项以及添加第三方库本身依赖的系统框架,这个工作是重复的而且非常没有技术含量。有没有什么工具能替代我们做这些工作呢?

一直到最近几天才知道,业界早已有了为iOS项目提供依赖管理的工具(我深深的感觉到我还没入行就先落伍了),这个工具就是:CocoaPods

CocoaPods简介

CocoaPods是一个负责管理iOS项目中第三方开源库的工具。CocoaPods的项目源码在Github上管理。该项目开始于2011年8月12日,在这两年多的时间里,它持续保持活跃更新。开发iOS项目不可避免地要使用第三方开源库,CocoaPods的出现使得我们可以节省设置和更新第三方开源库的时间

在我们有了CocoaPods这个工具之后,只需要将用到的第三方开源库放到一个名为Podfile的文件中,然后在命令行执行$ pod install命令。CocoaPods就会自动将这些第三方开源库的源码下载下来,并且为我的工程设置好相应的系统依赖和编译参数

CocoaPods的安装及使用

安装

安装的方式非常简单,Mac下已经自带了ruby,只要使用ruby的gem命令就可以安装了。打开的Mac的终端,在终端运行下面的命令:

1
2
$ [sudo] gem install cocoapods
$ pod setup

说明:执行$ pod setup这步可能比较慢,需要多等待一段时间,也可能是我网络的问题

更新

当然我们也可以更新我们的CocoaPods,同样也是使用ruby的gem命令:

1
$ [sudo] gem update cocoapods

然而你也可以更新CocoaPods的预览版,执行下面的命令:

1
$ [sudo] gem update cocoapods --pre

查找第三方库

如果我们不知道cocoaPods管理的库中,是否有你想要的库,那么你可以通过$ pod search xxx命令进行查找,以下是我用$ pod search sdwebimage查找到的所有可用的库:

1
2
3
4
5
6
7
8
9
10
11
12
-> SDWebImage (3.5.1)
   Asynchronous image downloader with cache support with an UIImageView
   category.
   pod 'SDWebImage', '~> 3.5.1'
   - Homepage: https://github.com/rs/SDWebImage
   - Source:   https://github.com/rs/SDWebImage.git
   - Versions: 3.5.1, 3.5, 3.4, 3.3, 3.2, 3.1, 3.0, 2.7.4, 2.7, 2.6, 2.5, 2.4
   [master repo]
   - Sub specs:
     - SDWebImage/Core (3.5.1)
     - SDWebImage/MapKit (3.5.1)
     - SDWebImage/WebP (3.5.1)

注:我省略了两个库,没有全列出。

使用

假设我的Desktop上有一个已经存在的一个项目名称叫做:CocoaPodsTest,首先,进入项目的根目录,并在根目录下创建一个名叫Podfile的文件(没有任何后缀):

1
2
$ cd Desktop/CocoaPodsTest/       '进入项目根目录,根据自己项目实际目录'
$ vim Podfile   '创建Podfile文件,你可以选择你自己喜欢的编辑器'

注:vim的简单用法,$ vim fileName创建文件fileName,并打开;按i进入插入模式,输入文本;按esc进入命令模式后,按:wqZZ退出并保存。

然后,在Podfile文件中按以下的格式将依赖库的名字列出:

1
2
3
platform :ios, '6.0'                '平台、版本'
pod 'SDWebImage', '~> 3.5.1'       '开源库名称、版本'
pod 'AFNetworking', '~> 2.0.3'     '开源库名称、版本'

保存Podfile文件后,执行如下安装的命令:

1
$ pod install

当安装命令执行成功后,会输出:

1
2
3
4
5
6
7
Analyzing dependencies
Downloading dependencies
Installing AFNetworking (2.0.3)
Installing SDWebImage (3.5.1)
Generating Pods project
Integrating client project
[!] From now on use `CocoaPodsTest.xcworkspace`.

哈哈,看到类似这样的输出就是成功了。你所需要的第三方开源库都下载好了,并且设置好了相应的依赖以及编译参数。在我们以后用的时候一定要记住以下两点:

1. 最后一行是一个警告,提醒我们需要注意:从现在开始,需要通过xxx.xcworkspace打开的我们的项目。而不是之前我们一直用的xxx.xcodeproj

2. 当我们每次修改了Podfile这个文件后,一定要记得执行命令:$ pod install,还可以执行$ pod update来更新类库

总结

用CocoaPods给我们的iOS项目添加依赖库真的太方便了,几个命令就搞定了,我个人建议像我一样还不会使用CocoaPods进行项目依赖的初级开发者,尤其是像我这样刚毕业的本科生,这个工具有必要学会,不能被鄙视,更能提高效率

有很多iOS大牛早已写了关于cocoaPods的相关教程,我个人又参考各大牛的博客写了一遍,只为能增加使用CocoaPods的熟练度。如有造成侵权行为,请联系本人

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
代码下载:完整代码,可直接运行 ;运行版本:2022a或2019b或2014a;若运行有问题,可私信博主; **仿真咨询 1 各类智能优化算法改进及应用** 生产调度、经济调度、装配线调度、充电优化、车间调度、发车优化、水库调度、三维装箱、物流选址、货位优化、公交排班优化、充电桩布局优化、车间布局优化、集装箱船配载优化、水泵组合优化、解医疗资源分配优化、设施布局优化、可视域基站和无人机选址优化 **2 机器学习和深度学习方面** 卷积神经网络(CNN)、LSTM、支持向量机(SVM)、最小二乘支持向量机(LSSVM)、极限学习机(ELM)、核极限学习机(KELM)、BP、RBF、宽度学习、DBN、RF、RBF、DELM、XGBOOST、TCN实现风电预测、光伏预测、电池寿命预测、辐射源识别、交通流预测、负荷预测、股价预测、PM2.5浓度预测、电池健康状态预测、水体光学参数反演、NLOS信号识别、地铁停车精准预测、变压器故障诊断 **3 图像处理方面** 图像识别、图像分割、图像检测、图像隐藏、图像配准、图像拼接、图像融合、图像增强、图像压缩感知 **4 路径规划方面** 旅行商问题(TSP)、车辆路径问题(VRP、MVRP、CVRP、VRPTW等)、无人机三维路径规划、无人机协同、无人机编队、机器人路径规划、栅格地图路径规划、多式联运运输问题、车辆协同无人机路径规划、天线线性阵列分布优化、车间布局优化 **5 无人机应用方面** 无人机路径规划、无人机控制、无人机编队、无人机协同、无人机任务分配 **6 无线传感器定位及布局方面** 传感器部署优化、通信协议优化、路由优化、目标定位优化、Dv-Hop定位优化、Leach协议优化、WSN覆盖优化、组播优化、RSSI定位优化 **7 信号处理方面** 信号识别、信号加密、信号去噪、信号增强、雷达信号处理、信号水印嵌入提取、肌电信号、脑电信号、信号配时优化 **8 电力系统方面** 微电网优化、无功优化、配电网重构、储能配置 **9 元胞自动机方面** 交通流 人群疏散 病毒扩散 晶体生长 **10 雷达方面** 卡尔曼滤波跟踪、航迹关联、航迹融合

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值