Lightweight Generics(輕量級泛型)###
OC類型聲明用輕量級泛型規(guī)范等同于Swift內(nèi)容的限制類型溯祸。例如,下面OC的屬性聲明:
@property NSArray<NSDate *> *dates;
@property NSCache<id, id <NSDiscardableContent>> *cacheData;
@property NSDictionary <NSString *, NSArray<NSLocale *>> *supportedLocales;
轉(zhuǎn)化為Swift:
var dates: [Date]
var cacheData: Cache<AnyObject, DiscardableContent>
var supportedLocales: [String: [Locale]]
在OC中規(guī)范類類等同于Swift帶相同類型參數(shù)通用類馁害。所有OC泛型類型等同于Swift符合(T: AnyObject)
的類型約束。如果OC的泛型對類限定,那么轉(zhuǎn)化的Swift類有約束,也約束了子類的類型秕脓。如果OC泛型進行協(xié)議限定,那么轉(zhuǎn)化為Swift類有約束儒搭,要求轉(zhuǎn)化為這個協(xié)議類型吠架。對于一個沒有具體說明的OC類型,Swift對類的類型約束表明通用規(guī)范搂鲫。例如傍药,下列OC類和類別聲明:
@interface List<T: ID<NSCopying>> : NSObject
- (List<T> *)listByAppendingItemsInList:(List<T> *)otherList;
@end
@interface ListContainer : NSObject
- (List<NSValue *> *)listOfValue;
@end
@interface ListContainer (ObjectList)
- (List *)listOfObjects;
@end
轉(zhuǎn)換為Swift:
class List<T: NSCopying> : NSObject {
func listByAppendingItemsInList(otherList: List<T>) -> List<T>
}
class ListContainer : NSObject {
func listOfValue() -> List<NSValue>
}
extension ListContainer {
func listOfObjects() -> List<NSCopying>
}
Extensions(擴展)###
Swift的擴展(extension
)類似OC的類別(category
)。擴展用來增加類魂仍、結(jié)構(gòu)體拐辽,和枚舉的行為。你可以定義一個系統(tǒng)類型或者自定義類型的擴展擦酌。導(dǎo)入相關(guān)的模塊俱诸,然后通過相同名字關(guān)聯(lián)類、結(jié)構(gòu)體或者枚舉赊舶。
例如睁搭,你可以擴展UIBezierPath
類,基于提供的參數(shù)邊長和起始點來制作一個等邊三角形的貝塞爾路徑笼平,
extension UIBezierPath {
convenience init(triangleSideLenght: CGFloat, origin: CGPoint) {
self.init()
let squareRoot = CGFloat(sqrt(3.0))
let altitude = (squareRoot * triangleSideLength) / 2
move(to: origin)
addLine(to: CGPoint(x: origin.x + triangleSideLength, y: origin.y))
addLine(to: CGPoint(x: origin.x +triangleSideLength /2, y: origin.y + altitude))
close()
}
}
你可以使用擴展來增加屬性(包括類和靜態(tài)屬性).然而园骆,這些屬性必須計算過;擴展不能對類寓调,結(jié)構(gòu)體或者枚舉增加存儲屬性锌唾。
這個例子擴展了CGRect
結(jié)構(gòu)體,主要包含了計算area
屬性:
extension CGRect {
var area: CGFloat {
return width * height
}
}
let rect = CGRect(x: 0.0, y: 0.0, width: 10.0, heigth: 50.0)
let area = rect.area
你不可以用擴展來否決在OC類型里存在的方法或?qū)傩浴?/p>
Closures(閉包)###
OC的blocks會自動導(dǎo)入為Swift的閉包捶牢。例如鸠珠,下面是一個OC中的block變量:
void (^completionBlock)(NSData *) = ^(NSData *data) {
// ...
}
在Swift中的形式為:
let completionBlock: Data -> Void = { data in
// ...
}
Swift的閉包和OC的blocks是一樣的巍耗,所以你可以把一個 Swift 閉包傳遞給一個把 block 作為參數(shù)的 Objective-C 函數(shù)。Swift 閉包與函數(shù)具有互通的類型渐排,所以你甚至可以傳遞 Swift 函數(shù)的名字炬太。
閉包與 blocks 語義上想通但是在一個地方不同:變量是可以直接改變的,而不是像 block 那樣會拷貝變量驯耻。換句話說亲族,Swift 中變量的默認(rèn)行為與 Objective-C 中 __block 變量一致。
Avoiding Strong Reference Cycles When Capturing self(用Self時如何避免循環(huán)引用)###
在OC中可缚,如果你需要在block中用self
霎迫,考慮內(nèi)存管理是很重要的。
Blocks給代碼塊中的對象維持強引用,包括self
帘靡。如果self
在block中維持強引用知给,就像一個屬性copy,這將造成強循環(huán)引用描姚。為了避免這種情況涩赢,你可以用對self
進行弱引用:
__weak typeof(self) weakSelf = self;
self.block = ^{
__strong typeof(self) strongSelf = weakSelf;
[strongSelf doSomething];
};
在Swift中,閉包也會給代碼塊中的對象進行強引用轩勘,包括self
筒扒。為了避免強引用,你可以在閉包中標(biāo)注self
為unowned
:
self.closure = { [unowned self] in
self.doSomething()
}
Object Comparison(對象比較)###
在Swift中有兩種方式類比較兩個對象绊寻。第一種花墩,使用(==
),判斷兩個對象的內(nèi)容。第二種澄步,使用(===
)冰蘑,判斷常量或變量是否為同一個實例對象。
Swift已經(jīng)實現(xiàn)了==
和===
的操作驮俗。調(diào)用isEqual:
方法來實現(xiàn)==
操作懂缕,并且實現(xiàn)===
操作是檢查指針相等。
NSObject
類僅僅做了身份的比較王凑,所以你需要在你自己的類中重新實現(xiàn) isEqual:
方法搪柑。因為你可以直接傳遞 Swift 對象給 Objective-C 的 API,你也應(yīng)該為這些對象實現(xiàn)自定義的isEqual:
方法索烹,如果你希望比較兩個對象的內(nèi)容是否相同而不是僅僅比較他們是不是由相同的對象派生工碾。
Hashing###
Swift采用Hashable
協(xié)議,并且規(guī)定NSObject
基類默認(rèn)實現(xiàn)hashValue
屬性百姓。hashValue
屬性的實現(xiàn)調(diào)用了hash
屬性
NSObject
的子類自定義實現(xiàn)isEquals:
方法也應(yīng)實現(xiàn)hash
屬性渊额。
Swift Type Compatibility(Swift類型兼容性)###
當(dāng)你定義了一個繼承Objective-C 類的 Swift 類,這個類和他的成員屬性,方法旬迹,下標(biāo)火惊,和初始化-它們與OC是兼容的。下面的這些不包括Swift的特性:
- Generics
- 元組(Tuples)
- 在Swift中不帶
Int
raw value類型的枚舉 - 在Swift中定義的結(jié)構(gòu)體
- 在Swift中高級函數(shù)
- 在Swift中全局變量
- Typealiases defined in Swift
- Swift-style variadics
- Nested types
- Curried functions
Swift Apis被翻譯成OC類似于如何將OC APIs翻譯為Swift奔垦,但反過來:
- Swift optional types are annotated as __nullable.
- Swift nonoptional types are annotated as __nonnull.
- Swift constant stored properties and computed properties become read-only Objective-C properties.
- Swift variable stored properties become read-write Objective-C properties.
- Swift type properties become Objective-C properties with the class property attribute.
- Swift type methods become Objective-C class methods.
- Swift initializers and instance methods become Objective-C instance methods.
- Swift methods that throw errors become Objective-C methods with an NSError ** parameter. If the Swift method has no parameters, AndReturnError: is appended to the Objective-C method name, otherwise error: is appended. If a Swift method does not specify a return type, the corresponding Objective-C method has a BOOL return type. If the Swift method returns a nonoptional type, the corresponding Objective-C method has an optional return type.
例如屹耐,下列是Swift的聲明:
class Jukebox: NSObject {
var library: Set<String>
var nowPlaying: String?
var isCurrentlyPlaying: Bool {
return nowPlaying != nil
}
class var favoritesPlaylist: [String] {
// return an array of song names
}
init(songs: String...) {
self.library = Set<String>(songs)
}
func playSong(named name: String) throws {
// play song or throw an error if unavailable
}
}
轉(zhuǎn)化為OC為:
@interface Jukebox : NSObject
@property (nonatomic, strong, nonnull) NSSet<NSString *> *library;
@property (nonatomic, copy, nullable) NSString *nowPlaying;
@property (nonatomic, readonly, getter=isCurrentlyPlaying) BOOL currentlyPlaying;
@property (nonatomic, class, readonly, nonnull) NSArray<NSString *> * favoritesPlaylist;
- (nonnull instancetype)initWithSongs:(NSArray<NSString *> * __nonnull)songs OBJC_DESIGNATED_INITIALIZER;
- (BOOL)playSong:(NSString * __nonnull)name error:(NSError * __nullable * __null_unspecified)error;
@end
Configuring Swift Interfaces in Objective-C###
在一些情況下,你需要在OC中使用Swift的API.你可以用@objc(name)
來改變類名椿猎,屬性惶岭,方法,枚舉類型犯眠。
例如按灶,如果你Swift特性在OC中不支持,你可以在OC中使用可替代的筐咧。如果你為Swift函數(shù)提供OC名鸯旁,用OC的語法。無論哪個選擇器的參數(shù)嗜浮,記得加(:
)羡亩。
enum Цвет: Int {
@objc(Red)
case Красный
@objc(Black)
case Черный
}
@objc(Squirrel)
class Белка: NSObject {
@objc(color)
var цвет: Цвет = .Красный
@objc(initWithName:)
init (имя: String) {
// ...
}
@objc(hideNuts:inTree:)
func прячьОрехи(количество: Int, вДереве дерево: Дерево) {
// ...
}
}
當(dāng)你在Swift類中使用@objc(name)
時,這個類在OC中沒有命名空間危融。這種特性可以在OC轉(zhuǎn)移到Swift時候可以用到。因為歸檔存儲類雷袋,你可以用@objc(name)
特性來具體說明OC中相同的類吉殃,以至于之前的歸檔可以使用心得Swift類進行接檔。
Requiring Dynamic Dispatch###
當(dāng)Swift在OC的運行時機制下楷怒,對屬性蛋勺、方法、下標(biāo)和初始化不能保證動態(tài)分發(fā)鸠删。在OC的運行時抱完,Swift編譯器會虛擬或內(nèi)聯(lián)成員訪問對你的代碼進行優(yōu)化。
在OC運行時刃泡,你可以動態(tài)修改你訪問的成員叫動態(tài)分發(fā)巧娱。在OC運行時,KVO或method_exchangeImplementations
函數(shù)還是很有必要用這種API的烘贴,
它可以在運行的時候動態(tài)替換一個方法的實現(xiàn)禁添。如果Swift編譯
注意:用dynamic
聲明的不能用@nonobjc
標(biāo)記屬性。
Selectors###
一個Objective-C 選擇器類型指向一個Objective-C的方法名桨踪。在Swift中老翘, Objective-C 的選擇器被Selector
結(jié)構(gòu)體替代,可以用#selector
創(chuàng)建。
#selector(MyViewController.tappedButton(sender:))
創(chuàng)建OC屬性的getter或setter方法铺峭,可以在名字前綴getter:
或setter:
,例如:
#selector(getter:MyViewController.myButton)
import UIKit
class MyViewController: UIViewController {
let myButton = UIButton(frame: CGRect(x: 0, y: 0, width: 100, height: 50))
override init?(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
let action = #selector(MyViewController.tappedButton)
myButton.addTarget(self, action: action, forControlEvents: .touchUpInside)
}
func tappedButton(sender: UIButton?) {
print("tapped button")
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
}
Unsafe Invocation of Objective-C Methods(OC方法的安全隱患)###
你可以用perform(_:)
方法調(diào)用OC的方法墓怀。用這個選擇器調(diào)用方法存在安全隱患,因為編譯器不能保證結(jié)果卫键,甚至不能保證對象響應(yīng)方法捺疼。因此強烈反對使用這些APIs,除非你的代碼基于OC的運行時機制動態(tài)調(diào)用方法永罚。例如啤呼,如果你需要使用target-action設(shè)計模式實現(xiàn)類,用這些可能是合適的呢袱。
像perform(:)
方法官扣,返回一個AnyObject
實例(Unmanaged<AnyObject>!
),因為perform
選擇器不能再編譯的時候確定返回值類型。像perform(_:on:with:waitUntilDone:modes:)
和perform(_:with:afterDelay:)
方法羞福,沒有返回值惕蹄。
let string: NSString = "hello, Cocoa!"
let selector = #selector(NSString.lowercased(with:))
let locale = Locale.current
if let result = string.perform(selector, with: locale) {
print(result.takeUnretainedValue())
}
//Prints "hello, cocoa!"
嘗試調(diào)用一個對象沒有實現(xiàn)的方法會造成對象接收消息并調(diào)用doesNotRecognizeSelector(_:)
,它會默認(rèn)的拋出NSInvalidArgumentException
異常治专。
let array: NSArray = ["delta", "alpha", "zulu"]
// Not a compile-time error because NSDictionary has this selector.
let selector = #selector(NSDictionary.allKeysForObject)
// Raises an exception because NSArray does not respond to this selector.
array.perform(selector)
Keys and Key Paths###
在OC中卖陵,key是對象特定屬性的id標(biāo)識。一個key path是點分割的keys的字符串张峰,它把對象的一些列屬性進行了串連泪蔫。keys 和 key paths在KVC中經(jīng)常使用,是一個用id標(biāo)識間接訪問對象屬性的機制喘批。keys 和 key paths 也可以用在KVO中撩荣,當(dāng)另外一個對象的屬性改變的時候能夠通知對象的一個機制。
在Swift中饶深,你可以用#keyPath
表達(dá)式來讓編譯器檢查keys 和 key paths餐曹,它可以用在KVC方法中,像value(forKey:)
和value(forKeyPath:)
敌厘,KVO方法像addObserver(_:forKeyPath:options:context:)
.這個#keyPath
表達(dá)式允許鏈方法或者屬性引用台猴,像#keyPath(Person.bestFriend.name)
.
class Person: NSObject {
var name: String
var friends: [Person] = []
var bestFriend: Person? = nil
init(name: String) {
self.name = name
}
}
let gabrielle = Person(name: "Gabrielle")
let jim = Person(name: "Jim")
let yuanyuan = Person(name: "Yuanyuan")
gabrielle.friends = [jim, yuanyuan]
gabrielle.bestFriend = yuanyuan
#keyPath(Person.name)
// "name"
gabrielle.value(forKey: #keyPath(Person.name))
// "Gabrielle"
#keyPath(Person.bestFriend.name)
// "bestFriend.name"
gabrielle.value(forKeyPath: #keyPath(Person.bestFriend.name))
// "Yuanyuan"
#keyPath(Person.friends.name)
// "friends.name"
gabrielle.value(forKeyPath: #keyPath(Person.friends.name))
// ["Yuanyuan", "Jim"]