对objc代码改造,适应swift调用的同时,也能提升objc代码质量
1.可选值
//在这两个宏之间的都默认是nonnull型
NS_ASSUME_NONNULL_BEGIN
NS_ASSUME_NONNULL_END
//如要指定某个属性、返回值或者参数为可选值类型,则可以单独使用下面的关键字
nullable _Nullable
nullable、nonnull
和_Nullable、_Nonnull
的区别
nullable、nonnull
用在方法参数或者属性的修饰
_Nullable、_Nonnull
用在常量、任意指针的修饰
当不确定是否会返回空值时,可使用null_unspecified、_Null_unspecified
修饰
ps.当使用以上关键词修饰后,需特别注意编译器抛出的警告⚠️
例如一个使用nonnull
修饰的NSString
属性,返回了nil
,此时编译器会抛出警告,不影响编译执行,但在swift调用中,则会返回一个""
空的字符串,这可能就会导致类似 if string == "xx" else
的条件语句结果与预期不一致,导致逻辑错误。而如果是其它类型,则可能会导致闪退
2.枚举、常量、宏
枚举,首先要避免使用c的enum
,改为使用NS_ENUM
和NS_OPTION
;然后使用NS_SWIFT_NAME
适配swift规范
如下一个OC的枚举
typedef NS_ENUM(NSUInteger, NotificationServiceType) {
NotificationServiceTypeLocal,
NotificationServiceTypeRemote
};
系统会自动转换为
public enum NotificationServiceType : UInt {
case local = 0
case remote = 1
}
如果要像UITableViewCellStyle
转换为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
常数常量使用:NS_TYPED_ENUM
NS_TYPED_EXTENSIBLE_ENUM
如下定义的字符串常量
FOUNDATION_EXTERN NSString *const NotificationServiceOptionTitle;
FOUNDATION_EXTERN NSString *const NotificationServiceOptionSubtitle;
FOUNDATION_EXTERN NSString *const NotificationServiceOptionBody;
如要更适用swift调用,则需做如下改造
1.首先上述的例子,在objc中也是不够规范的,应当typedef NSString *const NotificationServiceOption;
进行类型声明
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转换如下
//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
的区别在于,NS_EXTENSIBLE_STRING_ENUM
在swift中可扩展
宏定义
swift无法完全识别objc中的宏定义,对于单个变量的宏定义,如字符串、常数等,swift会识别为常量
#define NotificationServiceToken @"abcdefg"
会被识别为public var NotificationServiceToken: String { get }
。
而一些复杂的表达式,方法宏则无法被swift识别
3.错误处理
使用NS_ERROR_ENUM
定义error枚举
以SDWebImage中的做法为例
FOUNDATION_EXPORT NSErrorDomain const _Nonnull SDWebImageErrorDomain;
typedef NS_ERROR_ENUM(SDWebImageErrorDomain, SDWebImageError) {
SDWebImageErrorInvalidURL = 1000,
SDWebImageErrorBadImageData = 1001,
};
会被swift识别为
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识别的objc方法,会根据返回值类型、方法名、参数类型、参数名等语义识别出恰当的swift函数名、参数标签、参数名等,但有时并不能完全符合开发者的期望,此时可NS_SWIFT_NAME
手动声明swift转换的函数名
5.构造器
NS_DESIGNATED_INITIALIZER
,NS_UNAVAILABLE
6.第三方库的引用
1.CocoaPods引用OC库,在pod路径后添加:modular_headers => true
参数
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]
网友评论