對(duì)objc代碼改造氛悬,適應(yīng)swift調(diào)用的同時(shí),也能提升objc代碼質(zhì)量
1.可選值
//在這兩個(gè)宏之間的都默認(rèn)是nonnull型
NS_ASSUME_NONNULL_BEGIN
NS_ASSUME_NONNULL_END
//如要指定某個(gè)屬性、返回值或者參數(shù)為可選值類型俄占,則可以單獨(dú)使用下面的關(guān)鍵字
nullable _Nullable
nullable、nonnull
和_Nullable淆衷、_Nonnull
的區(qū)別
nullable缸榄、nonnull
用在方法參數(shù)或者屬性的修飾
_Nullable、_Nonnull
用在常量祝拯、任意指針的修飾
當(dāng)不確定是否會(huì)返回空值時(shí)甚带,可使用null_unspecified、_Null_unspecified
修飾
ps.當(dāng)使用以上關(guān)鍵詞修飾后佳头,需特別注意編譯器拋出的警告??
例如一個(gè)使用nonnull
修飾的NSString
屬性鹰贵,返回了nil
,此時(shí)編譯器會(huì)拋出警告康嘉,不影響編譯執(zhí)行砾莱,但在swift調(diào)用中,則會(huì)返回一個(gè)""
空的字符串凄鼻,這可能就會(huì)導(dǎo)致類似 if string == "xx" else
的條件語句結(jié)果與預(yù)期不一致腊瑟,導(dǎo)致邏輯錯(cuò)誤聚假。而如果是其它類型,則可能會(huì)導(dǎo)致閃退
2.枚舉闰非、常量膘格、宏
枚舉,首先要避免使用c的enum
财松,改為使用NS_ENUM
和NS_OPTION
瘪贱;然后使用NS_SWIFT_NAME
適配swift規(guī)范
如下一個(gè)OC的枚舉
typedef NS_ENUM(NSUInteger, NotificationServiceType) {
NotificationServiceTypeLocal,
NotificationServiceTypeRemote
};
系統(tǒng)會(huì)自動(dòng)轉(zhuǎn)換為
public enum NotificationServiceType : UInt {
case local = 0
case remote = 1
}
如果要像UITableViewCellStyle
轉(zhuǎn)換為UITableViewCell.Style
則可使用NS_SWIFT_NAME(NotificationService.Type)
typedef NS_ENUM(NSUInteger, NotificationServiceType) {
NotificationServiceTypeLocal,
NotificationServiceTypeRemote
} NS_SWIFT_NAME(NotificationService.Type);
@interface NotificationService : UNNotificationServiceExtension
@end
extension NotificationService {
public enum `Type` : UInt {
case local = 0
case remote = 1
}
}
open class NotificationService : UNNotificationServiceExtension {
open var type: NotificationService.`Type`
}
常量改造
字符串常量使用:NS_STRING_ENUM
NS_EXTENSIBLE_STRING_ENUM
常數(shù)常量使用:NS_TYPED_ENUM
NS_TYPED_EXTENSIBLE_ENUM
如下定義的字符串常量
FOUNDATION_EXTERN NSString *const NotificationServiceOptionTitle;
FOUNDATION_EXTERN NSString *const NotificationServiceOptionSubtitle;
FOUNDATION_EXTERN NSString *const NotificationServiceOptionBody;
如要更適用swift調(diào)用,則需做如下改造
1.首先上述的例子,在objc中也是不夠規(guī)范的,應(yīng)當(dāng)typedef NSString *const NotificationServiceOption;
進(jìn)行類型聲明
2.使用NS_STRING_ENUM
或者NS_EXTENSIBLE_STRING_ENUM
修飾NotificationServiceOption
改造如下:
typedef NSString *const NotificationServiceOption NS_STRING_ENUM;
FOUNDATION_EXTERN NotificationServiceOption NotificationServiceOptionTitle;
FOUNDATION_EXTERN NotificationServiceOption NotificationServiceOptionSubtitle;
FOUNDATION_EXTERN NotificationServiceOption NotificationServiceOptionBody;
swift轉(zhuǎn)換如下
//NS_STRING_ENUM
public struct NotificationServiceOption : Hashable, Equatable, RawRepresentable {
public init(rawValue: String)
}
extension NotificationServiceOption {
public static let title: NotificationServiceOption
public static let subtitle: NotificationServiceOption
public static let body: NotificationServiceOption
}
NS_STRING_ENUM
和NS_EXTENSIBLE_STRING_ENUM
的區(qū)別在于柠新,NS_EXTENSIBLE_STRING_ENUM
在swift中可擴(kuò)展
宏定義
swift無法完全識(shí)別objc中的宏定義贷笛,對(duì)于單個(gè)變量的宏定義用狱,如字符串、常數(shù)等,swift會(huì)識(shí)別為常量
#define NotificationServiceToken @"abcdefg"
會(huì)被識(shí)別為public var NotificationServiceToken: String { get }
。
而一些復(fù)雜的表達(dá)式主慰,方法宏則無法被swift識(shí)別
3.錯(cuò)誤處理
使用NS_ERROR_ENUM
定義error枚舉
以SDWebImage中的做法為例
FOUNDATION_EXPORT NSErrorDomain const _Nonnull SDWebImageErrorDomain;
typedef NS_ERROR_ENUM(SDWebImageErrorDomain, SDWebImageError) {
SDWebImageErrorInvalidURL = 1000,
SDWebImageErrorBadImageData = 1001,
};
會(huì)被swift識(shí)別為
public let SDWebImageErrorDomain: String
public struct SDWebImageError {
public init(_nsError: NSError)
public static var errorDomain: String { get }
public enum Code : Int {
public typealias _ErrorType = SDWebImageError
case invalidURL = 1000
case badImageData = 1001
}
public static var invalidURL: SDWebImageError.Code { get }
public static var badImageData: SDWebImageError.Code { get }
}
這樣就可以像swift原生一樣,使用SDWebImageError
處理異常
func testSDWebImageError() {
do {
let url = try testThrowSDError(path: "")
} catch let error as SDWebImageError {
switch error.code {
case .invalidURL:
// do something
print("invalidURL")
break
default: break
}
} catch {
}
}
func testThrowSDError(path: String?) throws -> URL {
if let path = path, !path.isEmpty, let url = URL(string: path) {
return url
} else {
throw SDWebImageError(.invalidURL)
}
}
4.api命名 NS_SWIFT_NAME
通常swift識(shí)別的objc方法鲫售,會(huì)根據(jù)返回值類型共螺、方法名、參數(shù)類型情竹、參數(shù)名等語義識(shí)別出恰當(dāng)?shù)膕wift函數(shù)名藐不、參數(shù)標(biāo)簽、參數(shù)名等秦效,但有時(shí)并不能完全符合開發(fā)者的期望雏蛮,此時(shí)可NS_SWIFT_NAME
手動(dòng)聲明swift轉(zhuǎn)換的函數(shù)名
5.構(gòu)造器
NS_DESIGNATED_INITIALIZER
,NS_UNAVAILABLE
6.第三方庫的引用
1.CocoaPods引用OC庫,在pod路徑后添加:modular_headers => true
參數(shù)
2....
7.使用objc泛型
//bad
@property (strong) NSArray *array;
@property (strong) NSDictionary *dictionary;
//good
@property (strong) NSArray<NSString *> *genericArray;
@property (strong) NSDictionary<NSString *, NSString *> *genericDictionary;
//bad
open var array: [Any]
open var dictionary: [AnyHashable : Any]
//good
open var genericArray: [String]
open var genericDictionary: [String : String]