前言
当我们开始接触一门新语言时,我们难免避免不了类型和基本语法规则。我们会急切的想知道如何用新的语法规则写我们原先所熟知的语句。本文旨在对于Object-C和Swift做一些基本的对比。通过阅读本文,您能快速的了解Swift3.0和Object-C的一些差别。
- 基本类型
同所有的面向对象语言一样,Swift里也只有两种类型:
1.值类型
2.引用类型
值类型包括struct,enum;
引用类型主要是class,嗯还有闭包也是引用类型哦。
- 代码不再需要分号结尾
Swift代码单行结束,不需要强制加分号,但也可以选择加
- 类型推断
Swift可以自动根据上下文为一个变量推断出其类型;
- 强类型语言
尽管Swift可以支持类型推断,可以用var来声明变量。但Swift是强类型语言。这样意味着,只要涉及不同类型之间的运算,都需要进行强制类型转换
- 变量、常量定义
let constant name : type = expression
var variable name : type = expression
在开始所有介绍前,我们先看简单的几行代码.大家采一些预期结果是什么?
var num=3.0
var integer=6
print(num+integer) //输出结果是?
结果:编译不过。因为Swift是一个强类型语言
从Object-C向Swift过渡
接下来将会从Protocol,block,GCD等大家在Object-C里熟知的内容开始,一步步过渡到Swift3.0。
1.protocol
2.block
3.GCD
4.property
5.函数
6.extension,category
7.可选类型?与??
8.类型判断与转换
9.id类型与Any,AnyObject,nil
10.typedef与typealias
11.宏定义
12.混合编程
13.API设计与函数命名对比
1.protocol
Swift也和OC一样有protocol,但不同的是Swift的protocol可以被class,struct,enum实现,甚至还能进行extension扩展。
参考:Protocols
- Protocol以及optional protocol定义
//Object-C
@protocol RefreshHeaderDelegate <NSObject>
@optional
- (void)willBeginRefresh:(PullRefreshHeader*)header;
- (void)didRefresh:(PullRefreshHeader*)header;
@required
- (void)didFinishRefresh:(PullRefreshHeader*)header;
@end
//Swift
@objc protocol RefreshHeaderDelegate {
@objc optional func willBeginRefresh(header:PullRefreshHeader) -> Void
@objc optional func didRefresh(header:PullRefreshHeader) -> Void
}
//加了class的protocol才能被使用在类的delegate模式中,因为protocol不加class标记则还可以被enum,struct实现
protocol RefreshHeaderDelegate :class{
func willBeginRefresh(header:PullRefreshHeader) -> Void
func didRefresh(header:PullRefreshHeader) -> Void
}
- 定义了optional方法后,如何判断delegate是否实现了该方法?
//Object-C
if([self.delegate respondsToSelector:@selector(willBeginRefresh:)]){
//执行delegate的方法
[self.delegate willBeginRefresh:self];
}
//Swift中我们可以更优雅的实现,optional是很方便的
self.delegate?.willBeginRefresh?(header: self)
2.block
Swift中任何两个{}之间的代码都算是闭包,因此函数也是一种特殊的闭包。
参考:closure-expressions-in-swift
- 闭包定义
//Swift闭包定义
{ (obj:AnyObject) ->Void in
//闭包内容开始
print(obj)
pring($0)
//可以用$<index>来指向闭包的形参,下标从0开始
}
- 如何避免闭包中的循环引用?
//Object-C
__weak __typeof(self) __weak_self = self
[button onClicked:^(MttTopBannerButton * button) {
MttBottomBanner *banner=[[MttBottomBanner alloc] init];
[__weak_self.view addSubview:banner];
[banner show];
button.hidden=YES;
}];
//Swift更优雅
button.onClicked({
[weak self]
(button:MttTopBannerButton) ->Void in
var banner = MttBottomBanner()
self?.view.addSubview(banner)
banner.show()
button.hidden=true
})
- weakself,strongself
//Swift
DispatchQueue.main.sync(execute: {
[weak self]
(_:AnyObject?) ->Void in
self?.draw()
self?.color=UIColor.white()
DispatchQueue.main.sync(execute: {
if let strongSelf=self {
strongSelf.lock.lock()
strongSelf.layer.contents=strongSelf.display?.cgImage
strongSelf.lock.unlock()
}
})
})
//请思考为何此处我是NSLock来加锁?
//因为Swift里没有@synchonized关键字的替代品了啊,需要的话,得自行调用GCD接口实现了.
//Object-C
__weak __typeof(self) __weak_self = self
dispatch_async(dispatch_get_main_queue(),^{
[self draw];
self.color=[UIColor whiteColor];
__typeof(__weak_self) strongSelf = __weak_self
dispatch_async(dispatch_get_main_queue(),^{
[strongSelf.lock lock];
strongSelf.layer.contents=strongSelf.display.CGImage;
[strongSelf.lock unlock];
})
});
3.GCD
Grand Dispatch Queue在Object-C中是一组C语言接口,虽然在swif1,swift2中仍保留了这种用法习惯,但它毕竟不太符合一门强类型与面向对象的语言的要求(就像这些人取消了++,--运算符一样,无时无刻不再想着摒弃老式的C语言编程思维),于是Swift3中GCD终于也面向对象了.
- dispatch_async
//Swift
DispatchQueue.main.async(execute:{blk(nil)})
//Object-C
dispatch_async(dispatch_get_main_queue(),^(id obj){
});
- dispatch_after
//Object-C
- (void)performBlockOnMainThread:(void (^)())block afterDelay:(CGFloat)delay
{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
block();
});
}
//block参数可为空,因为是optional
func performOnMainThread(_ block:AsyncLoaderTask? ,delay:Double) -> Void {
if let blk=block {
let time=DispatchTime.now()+delay
//DispatchWorkItem相当于之前的dispatch_queue_t所以直接用
DispatchQueue.main.after(when:time,execute:{blk(nil)})
}
}
4.Property属性
在Swift中类里定义了的变量或常量即是属性
属性分为2种:
- 存储型属性
跟Object-C的property基本差不多。但 - 计算型属性
比较特别,并不是用于真正的存储一个值或变量,而是提供了getter和setter方法来间接的获取其他属性或变量的值。其本身的存在价值是依赖于其它属性或变量的存在
1.属性定义
2.属性观察器
3.getter,setter
4.delegate模式
5.普通属性
6.lazy标记
7.optional属性
8.completion block模式
9.初始化
typealias AsyncLoaderTask = (_:AnyObject?) ->Void
class PullHeaderTable: UITableView {
// MARK: Public Properties
//////////////
//1.标准属性定义方式
//常量属性,以及属性定义的两种方式,可以隐身让Swift自行推断类型,也可以明确指定类型
let source=PullHeaderTableSource()
let src : PullHeaderTableSource = PullHeaderTableSource()
var s : PullHeaderTableSource = PullHeaderTableSource()
//////////////
//2.property观察器
var data : [AnyObject]?{
didSet{
source.data=data
print(oldValue)
}
willSet{
print(newValue)
}
}
//////////////
//3.getter,setter,通过getter返回的,叫做计算型属性
var src : PullHeaderTableSource {
get{
return PullHeaderTableSource()
}
set{
print(newValue)
}
}
//////////////
//4.delegate模式
weak var tableDelegate : PulllHeaderTableDelegate?
//////////////
//5.普通属性,必须在init方法里最先被初始化
var header : UIView
//////////////
//6.lazy标记,该属性只会在被调用时自动调用初始化赋值,而不是在init之前就初始化了,thread unsafe
lazy var footer = UIView()
//7.optional属性
var listener : AnyObject?
//8.optional block属性(和OC的completion block模式)
var completion : AsyncLoaderTask?
//////////////
//9.对于非optional的属性,在init时必须要初始化,否则会出错
//init初始化与OC的不一样,是先把当前实例的非optional属性初始化完毕,然后再调用父类的init
//原因在于Swift定义了的任何类型property编译器都不会给默认值,因此非optional property必须在init中有优先初始化
init(frame:CGRect) {
header=UIView()
super.init(frame:frame, style:UITableViewStyle.plain)
self.onCreate()
}
required init?(coder aDecoder: NSCoder) {
header=UIView()
super.init(coder: aDecoder)
self.onCreate()
}
override init(frame:CGRect,style:UITableViewStyle){
header=UIView()
super.init(frame: frame, style: style)
self.onCreate()
}
deinit {
self.dataSource=nil
self.delegate=nil
}
// MARK: Private Functions
private func onCreate() -> Void {
self.scrollsToTop=false
self.separatorColor=UIColor.clear()
self.separatorStyle=UITableViewCellSeparatorStyle.none
self.contentOffset=CGPoint.zero
self.delegate=source
self.dataSource=source
}
}
5.函数
使用 func 来声明一个函数,使用名字和参数来调用函数。使用 -> 来指定函数返回值的类型。
参考:Functions
1.如何让函数返回多个返回值?
2.如何传入1个或N个参数
3.重构->函数内嵌套函数
4.函数类型与函数返回值,函数参数
Swift的函数相比较于OC发生了很多很好的改进,我们可以更自由的使用定义函数了.
同时还加入了函数类型。
参考:函数类型
class App: NSObject {
//func test() -> Void {}
func test() {
print(calculate(scores: [0,1,2,3,4,5]))
print(sumOf(numbers: 0,1,2,3,4,5))
runAnotherFunction(longlongFunction)
}
// MARK:Function Usage
//1.使用元组返回多个返回值
//下述函数从数组中返回最小,最大,求和.返回值是一个元组.
//其中调用了Swift里的min,max函数,全局泛型函数,用于比较大小返回结果.
//min,max函数类似于OC宏定义的MIN(),MAX()
func calculate(scores: [Int]) -> (min: Int, max: Int, sum: Int) {
var vmin=scores[0]
var vmax=vmin
var sum : Int = 0
for val in scores {
vmin=min(vmin, val)
vmax=max(vmax, val)
sum+=val
}
return (vmin,vmax,sum)
}
//2.传入可变个数的参数
func sumOf(numbers: Int...) -> Int {
var sum=0
for val in numbers {
sum+=val
}
return sum
}
//3.嵌套函数,用于部分代码的重构,其实也可以里面定义一个block一样的。不过比OC多了个选择
func longlongFunction() {
var x=10
//猜猜看 _ 这里下划线的作用?看看如何调用的你就会明白了
func increament(_ val:Int) -> Int {
return val+1
}
print(increament(x))
func add() {
//x++
//Swift 3.0不支持++,--了哦
x=x+1
}
add()
}
//4.函数作为参数,当然函数也可以作为返回值的.函数类型
func runAnotherFunction(_ fun: () -> ()) {
fun()
}
}
6.extension,category
Swift中没有category的概念,因为Swift中的extension基本涵盖了所有原先Object-C中需要的一切
1.添加计算型属性和计算型静态属性
2.定义实例方法和类型方法
3.提供新的构造器
4.定义下标[]
5.定义和使用新的嵌套类型
6.支持协议
参考:下标运算符
extension String {
func toHex() -> String {
return ""
}
}
protocol AppProtocol: class {
func app() -> Void
}
class App: NSObject {
var data : Array = Array<Int>()
static var instance = App()
private override init() {
for i in 0...8 {
self.data.append(i)
}
}
}
extension App: AppProtocol {
//////////////
//1.添加计算性属性,存储型的不可以啊
var hashCode : Int {
return Int(Date.timeIntervalSinceReferenceDate)+8
}
//非法,存储性属性不能再extension里存在
//var newProperty : Int = 0
//////////////
//2.添加新的构造函数
convenience init(_ id:Int, time:Int) {
self.init()
}
//////////////
//3.函数或实现协议
func app() -> Void {
print("app")
}
//////////////
//4.为对象实现下标运算符
subscript(index: Int) -> Int {
get{
return self.data[index]
}
set{
self.data[index]=newValue
}
}
//////////////
//5.嵌套类型,class,enum,也就是相当于定义内部类
class UI : NSObject {
}
}
func testApp() -> Void {
let app = App.instance
app[0]=1
print(app[1])
}
7.可选类型?与??
可空链式调用(Optional Chaining)
这个名词太专业了,我们就简单的来谈谈吧
定义属性或变量时,如果要允许一个属性为空即nil,像Object-C一样的话。那么我们需要制定其位optional类型
只需定义结束末尾在加一个?符号即可,如下
var obj : AnyObject?
//定义了一个相当于Object-C的 id obj=nil;东西
对于optional类型的变量或方法(如optional protocol method)我们在调用时需要把optional类型的值/对象取出来,这一步叫做unwrap;反之定义时就叫wrap;
通过?来unwrap或者直接利用!来强制unwrap(强制的前提是你能确保变量是有有效值的),如下
var str : String?
str="value"
//前面已经为str赋值过了,所以我们是已知强制unwrap是不会有异常的
if str!.characters.count>0 {
}
//如果签名没有为str赋值,或者不确定有值与否,需要尝试unwrap
if str?.characters.count>0 {
}
var count : Int
count=str?.characters.count ?? 0
print(count)
大家是否在好奇上述的??符号到底是什么意思呢?很简单,如果你理解选择表达式,那么可以把它看做对于optional类型的选择表达式吧
等价于如下代码
if str != nil {
count=str!.characters.count
}
else
{
count=0
}
8.类型判断与转换
1.在Object-C中我们经常会对一个id类型或者不确定具体类型的实例对象做类型判断,如isKindOfClass;在Swift中我们可以更简单来实现
参考:Type Casting
is类型检查运算符
//Object-C判断类型
UIView *v=XXX;
if([v isKindOfClass:[UIWindow class])
{
}
//Swift
var v=XXX
if v is UIWindow {
}
2.在Object-C中我们要做类型转换可以直接使用C语言的方式来强制类型赋值,但在Swift中需要用as运算符实现
as 用于向上转型(upcasts)
as? 和as! 用于向下转型(Downcasting)
备注:
1.向上转型:由派生类转为父类对象
2.向下转型:有父类对象转为具体子类对象
当不确定类型转换是否成功时,用类型转换的条件形式( as? )
as?如果失败会返回nil
当确定一定是时,用强制形式式( as! )
//Object-C
for(UIViewController *ct in controllers)
{
if([ct isKindOfClass:[MttBaseViewController class]])
{
NSLog(@"MttBaseViewController");
}
else if([ct isKindOfClass:[MttRootViewController class]])
{
NSLog(@"MttRootViewController");
}
}
//Swift
for ct in controllers {
if let mtt = ct as? MttBaseViewController {
print("MttBaseViewController")
} else if let nav = ct as? MttRootViewController {
print("MttRootViewController")
}
}
9.id类型与Any,AnyObject,nil
Any 和 AnyObject 是 Swift 中两个妥协的产物;
AnyObject 可以代表任何 class 类型的实例
Any 可以表示任意类型,甚至包括方法 (func) 类型
AnyObject用于在Swift中替换Object-C的id类型;
Swift中编译器不会对 AnyObject 实例的方法调用做出检查,甚至对于 AnyObject 的所有方法调用都会返回 Optional 的结果。因此使用AnyObject实例之前我们务必需要检测对象是否存在和类型有效性。
参考:ANY 和 ANYOBJECT
nil:
Swift中的nil与Object-C的nil是不一样的。Object-C中,nil是一个指针指向一个不存在的object。在Swift中,nil不是指针,而是对于特定类型的缺省值。任何optional类型都可以设置为nil,而不仅仅是对象类型;
optional类型变量的值默认就是nil;
10.typedef与typealias
typealias相当于Object-C中的typedef,用来为已经存在的类型重新定义名字。通过命名,可以使代码变得更加清晰。
参考:swift-typealias,typelias
//重命名闭包,和Object-C一样
typealias AsyncLoaderTask = (_:AnyObject?) ->Void
func submit(_ block:AsyncLoaderTask?,completion:AsyncLoaderTask?) -> Void {
if let blk=block {
self.queue.async(execute:{
blk(nil)
if let cmp=completion {
cmp(nil)
}
})
}
}
//protocol组合
protocol changeName{
func changeNameTo(name:String)
}
protocol changeSex{
func changeSexTo(sex:String)
}
typealias changeProtocol = protocol<changeName,changeSex>
struct Person:changeProtocol{
func changeNameTo(name:String){
}
func changeSexTo(sex:SEX){
}
}
//swift中很多类型别名即是typealias定义的
public typealias AnyClass = AnyObject.Type
public typealias NSInteger = Int
11.宏定义
很抱歉,Swift中终于不支持宏定义了。
Object-C中的宏暴露到Swift中的话,只有简单展开宏会被直接变为同名常量。
参考:converting-complex-objective-c-macros-swift-functions,Objective的宏到swift
//Object-C
#defien MTT_DEBUG 1
//Swift
let MTT_DEBUG = 1
//以上二者等价
其余复杂的宏定义以及宏嵌套都无法被翻译到Swift里,也无法在Swift中调用或使用。
所以要使用宏,只能在Object-C中使用.
12.混合编程
处于某些目的,我们还是有必要在Swift代码中使用Object-C,或者在Object-C代码中装一下样子,搞点Swift。那这时就涉及到混合编程了。
参考:混和编程
1.OC中使用Swift
在工程的 Build Settings 中把 defines module 设为 YES.即可.(如需必要,再把把 product module name 设置为项目工程的名字。理论上不需要了,XCode 8.0已经默认是这样了)
最后一步,在你的OC文件中导入 ProjectName-Swift.h.即(productModuleName-Swift.h)
2.Swift中使用OC
Swift代码引用OC,需依靠 Objective-C bridging header 将相关文件暴露给Swift。
创建 Objective-C bridging header 有两种方法
a.当你在Swift项目中尝试创建OC文件时,系统会自动帮你创建 Objective-C bridging header
b.自己创建 Objective-C bridging header
File > New > File > (iOS or OS X) > Source > Header File
切记,名字 一定要 是 项目工程名-Bridging-Header.
然后还有一步,在项目的 Build Settings 选项里,要确保Swift Compiler 选项里有这个 Bridging Header 文件的设置,路径必须指向文件本身,而不是目录!
13.API设计与函数命名对比
篇幅原因,这里就不copy&&paste了,大家可以自行看原文或者译文。这是一篇很好的API设计指导,终于可以抛弃Object-C的冗长命名了。
参考:Swift API设计 , 译文
参考资料
Swift学习资料
http://www.swiftguide.cn/
https://github.com/ipader/SwiftGuide参考文献
https://swift.org/about/
https://developer.apple.com/library/prerelease/content/documentation/Swift/Conceptual/Swift_Programming_Language/Protocols.html
http://www.devtalking.com/articles/closure-expressions-in-swift/
https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Functions.html#//apple_ref/doc/uid/TP40014097-CH10-XID_270
http://www.jianshu.com/p/989999a5104b
http://blog.csdn.net/xbenlang/article/details/31787973
http://swifter.tips/any-anyobject/
http://www.jianshu.com/p/082202b9dc17
http://swifter.tips/typealias/
https://www.andrewcbancroft.com/2015/01/29/converting-complex-objective-c-macros-swift-functions/
http://www.cnblogs.com/xilifeng/p/5243986.html
http://kittenyang.com/swiftandoc/
http://www.jianshu.com/p/fce426e4f1c4
结语
如果你在一门语言上,可以看到很多让你曾痴狂,压抑,热爱,也失望的特性时。那就一定是Swift了。
本文比较了Swift3.0和Object-C在开发中主要的一些差别,限于篇幅(已经很长了)与作者本人认知能力,并未对Swift中更多特性做介绍或深究。如有疏漏或错误还望指正。
Swift就像一个四不像,因为var而像javascript,因为@objc而又保留了Object-C特性,因为内部类而多了点Java的影子,因为deinit而带了些c++的气味。但无论如何,Swift必定是将来!
参考:About Swift