Interacting with Objective-C APIs(与OC-API的交互)###
Initialization(初始化)####
要使用Swift初始化一个Objecive-C的类,只要使用Swift调用这个类的其中一个构造函数。
OC通常使用init
初始化,或者如果实例对象需要带一个或多个参数使用initWith:
,当OC的初始化转化为Swift,init
前缀是一个关键字,暗含了Swift初始化方法。如果初始化带一个参数,With
从选择器的后边移除之后成为相应的命名参数。
下面是OC的初始化声明:
- (instancetype)init;
- (instancetype)initWithFrame:(CGRect)frame
style:(UITableViewStyle)style;
相同的Swift初始化声明:
init() {/*...*/}
init(frame: CGRect, style: UITableViewStyle) {/*...*/}
在初始化的时候OC和Swift还是有明显不同的.
在OC中,你这么做:
UITableView *myTableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStyleGrouped];
在Swift中,你这么做:
let myTableView: UITableView = UITableView(frame: CGRectZero, style: .Grouped)
注意你不需要调用alloc
;Swift会自动为你处理。当调用Swift初始化的时候也不会出现init
.
当你给一个常量或变量赋值的时候,你可以明确的标明类型,或者你也可以去掉类型,Swift可以从初始化自动推理类型.
let myTextField = UITextField(frame: CGRect(x: 0.0, y: 0.0, width: 200.0, height: 200.0))
Class Factory Methods and Convenience Initializers(类工厂方法和方便的初始化)####
对于连贯性和简便性,在Swift中OC的类工厂方法是重要的简便的。初始化允许他们用相同的编译器。
例如,在OC中你可以这样调用工厂方法:
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)
Failable Initialization(可失败的初始化)####
在OC中,直接初始化返回初始化对象。当初始化失败的时候为了通知调用者,OC的初始化可以返回nil.在Swift中,这个模式被构建为语言特性叫做failable initialization(可失败的初始化)
许多OC的初始化在系统的framework被审查用来表明初始化是否可能失败.你可以在自己的类用空指针表明初始化是否失败,描述在这里 Nullability and Optionals.OC初始化表明是否失败等同于init(...)
表明如果初始化不失败,或者init?(...)
表明如果初始化失败.另外,OC初始化也可以为init!(...)
.
例如,UIImage(contentsOfFile:)
初始化可能失败,如果提供的路径图片不存在.如果初始化是成功的你可以使用可选值绑定对失败的结果进行解包.
if let image = UIImage(contentsOfFile: "MyImage.png") {
// loaded the image successfully
} else {
// could not load the image
}
Accessing Properties(访问属性)####
OC用@property属性声明和下列Swift的属性声明是一样的:
- Properties用
(nonnull, nullable, null_resettable)
的空属性等同于Swift在Nullability and Optionals可选或不可选类型. - Properties用
readonly
标记的属性等同于Swift的getter{ get }
. - Properties用
weak
标记的属性等同于Swift用weak
关键字标记(weak
). - 所有的属性并非都是
weak
(也有assign, copy, strong , unsafe_unretained
)等同于Swift有关的存储属性. - Properties用
class
属性等同于Swift的属性 - Atomicity属性(
atomic
和nonatomic
)不会在Swift的属性声明中响应,但是当Swift的重要的属性访问得时候,OC的原子性保护依然保持. - 在Swift访问属性(
getter=
和setter=
)被忽略.
OC对象在Swift中访问属性,用属性名不带括号.
例如,你可以设置用如下代码UITextField
的textColor
和text
:
myTextField.textColor = UIColor.darkGray()
myTextField.text = "Hello World"
Working with Methods###
在Swift中,你可以调用OC的方法使用点语法.
当OC的方法转化为Swift时,OC选择器的第一部分会成为方法名并且出现在括号前。第一个参数将直接在括号里边,并且没有名字。余下的参数名与参数则一一对应的填在括号中。
例如,在OC中你可以这么做:
[myTable insertSubview:mySubview atIndex:2];
在Swift中,你可以这么做:
myTableView.insertSubview(mySubview, at: 2)
如果你调用无参方法,你必须加上括号。
myTableView.layoutIfNeeded()
id Compatibility###
Swift包含一个AnyObject
的协议对象,这个就好比是OC的id
类型。AnyObject 协议可以让你写出类型安全的代码的同时维持无类型对象的通用性。因为AnyObject协议保证了这种安全,Swift将id对象导入为AnyObject。
例如,id
,你可以为AnyObject类型的对象分配任何其他类型的对象。你也同样可以为它重新分配其他类型的对象。
var myObject: AnyObject = UITableViewCell()
myObject = NSDate()
你也可以在调用Objective-C方法或者访问属性时不将它转换为具体类的类型。包括Objcive-C中标记为@objc的方法。
let futureDate = myObject.addingTimeInterval(10)
let timeSinceNow = myObject.timeIntervalSinceNow
Unrecognized Selectors and Optional Chaining###
因为AnyObject
对象的类型只有在运行时才能知道,所以我们不经意间,就写出了不安全的代码。另外,与Objective-C不一样的是,如果你调用方法或者访问的属性AnyObject
对象没有声明,将会报运行时错误。例如,下面的代码将会在运行时抛出unrecognized selector error
myObject.character(at: 5)
// crash, myObject doesn't respond to that method
尽管如此,你也可以借助Swift的optionals特性来排除这个Objective-C中常见的错误,当你用AnyObject对象调用一个Objective-C的方法,这次调用将会变成一次隐式展开optional(implicitly unwrapped optional)的行为。你可以通过optional特性来决定AnyObject类型的对象是否调用该方法,同样的,你可以把这种特性应用在属性上。
例如,在下列代码,第一行和第二行不会检查,因为count
属性和character(at:)
方法在NSDate
对象不存在。myCount
被推测为一个可选的Int
类型,并且被设置为nil
.你可以使用if-let
声明来展开这个方法的返回值,就像第三行一样:
// myObject has AnyObject type and NSDate value
let myCount = myObject.count
/// myCount has Int? type and nil value
let myChar = myObject.character?(at: 5)
// myChar has unichar? type and nil value
if let fifthCharacter = myObject.character?(at: 5) {
print ("Found \(fifthCharacter) at index 5")
}
// conditional branch not executed
Downcasting AnyObject###
在处理泛型,类型是已知或可以合理肯定的时候,把这些对象转为更具体的类型。然而,因为泛型可能是任意一种类型,不一定能够成功的转为具体的类型。
你可以用条件类型转化操作(as?
),它可以返回一个你尝试转化类型的可选值:
let userDefaults = UserDefaults.standard
let lastRefreshDate: AnyObject? = userDefaults.object(forKey: "LastRefreshDate")
if let date = lastRefreshDate as? NSDate {
print("\(date.timeIntervalSinceReferenceDate)")
}
如果你确定对象类型,你可以使用強转操作(as!
)代替。
let myDate = lastRefreshDate as! NSDate
let timeInterval = myDate.timeIntervalSinceReferenceDate
然而,如果强制转化失败,运行时会出错:
let myDate = lastRefreshDate as! NSString //Error
Nullability and Optionals###
在OC中,你引用的对象的原始指针可能为NULL
(也可能为nil
)。在Swift中,所有的值–包括结构体与对象的引用–都被保证为非空。作为替代,你将这个可以为空的值包装为optional type。当你需要指示该值为空,你需要使用nil,你可以阅读更多关于Optionals的内容。
OC可以用空指针来指定参数类型,属性类型,或者返回类型,也可以用NULL
或 nil
。单独的类型声明用_Nullable
和_Nonnull
指针可能被检查,单独的属性声明用nullable
,nonnull
和null_resettable
属性可能被检查,或者全部使用NS_ASSUME_NONNULL_BEGIN
和NS_ASSUME_NONNULL_END
宏都可能被检查。如果一个类型不是空信息,Swift不会区分可选与不可选的,作为隐形解析可选。
- 类型声明为非空,带
_Nonnull
指针或在检查区域,在Swift中是不可选的。 - 类型声明为空,带
_Nullable
指针,在Swift中可选。 - 类型声明不带空指针,在Swift是一个隐士解析可选
例如,看下列OC声明:
@property (nullable) id nullbaleProperty;
@property (nonnull) id nonNullProperty;
@property id unannotatedProperty;
NS_ASSUME_NONNULL_BEGIN
- (id)returnsNonNullValue;
- (void)takesNonNullParameter:(id)value;
NS_ASSUME_NONNULL_EDN
- (nullable id)returnsNullableValue;
- (void)takesNullableParameter:(nullable id)value;
- (id)returnsUnannotatedValue;
- (void)takesUnannotatedParameter:(id)value;
Swift写法:
var nullableProperty: AnyObject?
var nonNullProperty: AnyObject
var unannotatedProperty: AnyObject!
func returnsNonNullValue() -> AnyObject
func takesNonNullParameter(value: AnyObject)
func returnsNullableValue() -> AnyObject?
func takesNullableParameter(value: AnyObject?)
func returnsUnannotatedValue() -> AnyObject!
func takesUnannotatedParameter(value: AnyObject!)
大多OC的系统frameworks,包含Foundation,早已提供空注释,允许
你惯用和类型安全的方式使用值。
未完待续...
Swift with Cocoa and Object-C(第二部分)