Swift和Objective-C的API交互—Swift学习笔记(二十)

注:本文为自己学习Using Swift with Cocoa and Objective-C的笔记,其中的例子为引用原书和其他博文或自己原创的。每个例子都会批注一些实践过程中的经验或思考总结。

1.基础

互通性[Interoperability]指的是Swift和Objective-C之间相互的调用。互通性的一个方面就是使用Swift代码来调用Objective-C的API[Application Programming Interface],在import了Objective-C的框架[framework]后,我们可以使用Swift语法实例化它的类以及完成类的各种交互。

2.初始化

Objective-C使用init方法来实例化一个类,Objective-C初始化方法中的单独的init关键字对应Swift无参数构造函数,而initWith打头的含参数初始化方法对应Swift有外部参数名的构造函数,initWith后面跟的大写开头的参数名首字母改为小写,作为第一个外部参数名。

具体来说,Objective-C中实例化一个UITableView类:

UITableView *myTableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStyleGrouped];
在Swift中对应的代码是:

let myTableView: UITableView = UITableView(frame: CGRectZero, style: .Grouped)
可以看到Swift构造函数不含init关键字以及With前缀,alloc方法在Swift构造函数中隐式地完成,不需要在调用中出现。UITableViewStyle在Swift中是枚举类型,.Grouped作为枚举成员传入时可以推断[infer]出其类型。
由于Swift具有类型推断的特性,类型注释可以省略掉,上面的实例化简写为:

let myTableView = UITableView(frame: CGRectZero, style: .Grouped)
出于一致性和简明性的考虑,Objective-C的工厂方法[factory methods](具有简明的传入参数并调用自己的init方法返回一个实例的方法)和Swift的快捷构造函数[convenience initializer]相互对应。例如Objective-C中UIColor的工厂方法:

UIColor *color = [UIColor colorWithRed:0.5 green:0.0 blue:0.5 alpha:1.0];
对应Swift中的快捷构造函数:

let color = UIColor(red: 0.5, green: 0.0, blue: 0.5, alpha: 1.0)
3.访问属性

Swift用点运算符访问和设置Objective-C属性,Objective-C中只有标记为@property的属性才被转化为属性,而和属性名相同的无参数方法作为取值器[getter]的语法并不起作用。

4.处理方法

调用Objective-C方法也用点运算符,Objective-C选择器[selector]的第一部分作为Swift方法名,第一个参数直接在括号中没有外部参数名,它后面直接写选择器中其余的参数名和参数。例如:

[myTableView insertSubview:mySubview atIndex:2];
在Swift中写成:

myTableView.insertSubview(mySubview, atIndex: 2)
调用无参数方法时别忘了一对空括号。

5.id的一致性

Swift提供的AnyObject协议表示了任何类型的对象,它和Objective-C中的id一样。AnyObject协议在保证类型安全的同时为无类型对象[untyped object]提供灵活性。

和id一样,AnyObject类型的常量或变量可以被赋值为任何类型的对象,AnyObject变量可以用其它类型的对象重新赋值。

var myObject : AnyObject = UITableViewCell()
myObject = NSDate()
我们不需要将AnyObject的对象强制转化为其它类型就可以使用这些类型方法并访问修改其属性:

let futureDate = myObject.dateByAddingTimeInterval(10)
但是由于AnyObject的对象在运行之前都不知道它被赋值了哪一种类型的实例,因此有可能会无意地书写不安全的代码。调用一个不存在的方法或者访问一个不存在的属性都会引发运行时[runtime]错误,有时候和预期不同。

其时可以有效的避免引发运行时错误,解决的办法是利用Swift的可空[optional]类型:

let futureDate = myObject.dateByAddingTimeInterval?(10)
添加问号运算符进行可空检查,实例没有该方法时是不会调用的。当确定该对象的类型时可以用as运算符强制转换:

let futureDate = myObject.dateByAddingTimeInterval(10) as NSDate

5.nil相关工作

Objective-CNULL对应Swift的nil,Swift中非空类型的实例不允许为nil,而可空类型的实例才可以为nil。由于Objective-C并不保证对象非空,所以Swift把所有Objective-C的API中的参数类型和返回值类型都看作时可空的,这就意味着我们需要在使用它们检查来保证非空性。

但某些情况下你可以保证Objective-C方法或属性绝对不会返回一个nil对象的引用,为了让对象在这些特殊情境下使用方便,Swift提供的可空类型是一种特殊的可空类型——隐式打开可空类型[implicitly unwrapped optional type]。隐式打开可空类型包括了所有可空类型的安全特性,只是它不需要检查nil或显式打开[unwrap]这个对象就可以访问值。Swift隐式地为隐式打开可空类型检查非空性,如果为nil则引发一个运行时错误。

6.扩展

Swift的扩展[extension]和Objective-C的category类似,我们可以用扩展语法扩展Objective-C写的类型,无论它是来自系统架构还是自定义的。

extension CGRect {
    var area: CGFloat {
    return width * height
    }
}
let rect = CGRect(x: 0.0, y: 0.0, width: 10.0, height: 50.0)
let area = rect.area
扩展只能添加计算属性,而不能添加存储属性。有关于使用扩展来支持协议的用法,在这里也是通用的。

7.闭包

Objective-C的块[block]自动转换为Swift的闭包[closure],例如:

void (^completionBlock)(NSData *, NSError *) = ^(NSData *data, NSError *error) {/* ... */}
在Swift中表达为:

let completionBlock: (NSData, NSError) -> Void = {data, error in /* ... */}
Objective-C的块和Swift的闭包基本上用法是一致的,只有一点不同:Swift闭包的变量总是引用可变的而不是赋值的,和Objective-C的_block一致。

8.对象比较

Swift中==[equality]比较对象的内容是否相等,===[identity]比较对象是否指向同一个实例。但是对于继承自NSObject类的类,Swift通过调用isEqual方法来实现==,而NSObject的isEqual方法只比较identity。因此要比较Objective-C类的内容需要自己实现isEqual方法。在自己实现isEqual方法需要实现哈希属性,更进一步若要用自定义类作为字典的键,则需要遵从Hashable协议并实现hashable属性。

9.Objective-C的选择器

Objective-C选择器[selector]指的是一种指代Objective-C方法名称的类型,在Swift中Objective-C选择器是用Selector结构体实现的。我们可以使用字符串字面值构造选择器,例如:

let mySelector: Selector = "tappedButton:"
由于字符串字面值可以自动地转换为选择器,所以可以用它传给接受选择器的参数:

import UIKit
class MyViewController: UIViewController {
    let myButton = UIButton(frame: CGRect(x: 0, y: 0, width: 100, height: 50))
    
    init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
        super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
        myButton.addTarget(self, action: "tappedButton:", forControlEvents: .TouchUpInside)
    }
    
    func tappedButton(sender: UIButton!) {
        println("tapped button")
    }
}
在鼠标点击myButton按钮时根据选择器会执行tappedButton方法。

一个继承自Objective-C类的Swift类,类中所有的方法和属性都可以作为Objective-C选择器。但是如果不继承自Objective-C类,要使用Objective-C选择器就需要标注@objc特性。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值