从Object-C -> Swift3.0

作者: Colla | 来源:发表于2016-08-25 19:54 被阅读974次

    前言

    当我们开始接触一门新语言时,我们难免避免不了类型和基本语法规则。我们会急切的想知道如何用新的语法规则写我们原先所熟知的语句。本文旨在对于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了。
    本文比较了Swift3.0和Object-C在开发中主要的一些差别,限于篇幅(已经很长了)与作者本人认知能力,并未对Swift中更多特性做介绍或深究。如有疏漏或错误还望指正。
    Swift就像一个四不像,因为var而像javascript,因为@objc而又保留了Object-C特性,因为内部类而多了点Java的影子,因为deinit而带了些c++的气味。但无论如何,Swift必定是将来!
    参考:About Swift

    相关文章

      网友评论

        本文标题:从Object-C -> Swift3.0

        本文链接:https://www.haomeiwen.com/subject/ikuasttx.html