1. dispatch_once替代方案
OC中用来保证代码块只执行一次的dispatch_once
在swfit中已经被废弃了,取而代之的是使用static let
,let
本身就带有线程安全性质的.
例如单例的实现.
final public class MySingleton {
static let shared = MySingleton()
private init() {}
}
但如果我们不想定义常量,需要某个代码块执行一次呢?
private lazy var takeOnceTime: Void = {
// 代码块...
}()
_ = takeOnceTime
定义一个懒加载的变量,防止在初始化的时候被执行.后面加一个void
,为了在_ = takeOnceTime
赋值时不耗性能,返回一个Void
类型.
lazy var
改为static let
也可以,为了使用方便,我们用一个类方法封装下
class ClassName {
private static let takeOnceTime: Void = {
// 代码块...
}()
static func takeOnceTimeFunc() {
ClassName.takeOnceTime
}
}
// 使用
ClassName.takeOnceTimeFunc()
这样就可以做到和dispatch_once
一样的效果了.
2. 被废弃的+load()和+initialize()
我们都知道OC中两个方法+load()
和+initialize()
.
+load()
: app启动的时候会加载所有的类,此时就会调用每个类的load方法.
+initialize()
: 第一次初始化这个类的时候会被调用.
然而在目前的swift版本中这两个方法都不可用了,那现在我们要在这个阶段搞事情该怎么做? 例如method swizzling
.
JORDAN SMITH大神给出了一种很巧解决方案.UIApplication
有一个next
属性,它会在applicationDidFinishLaunching
之前被调用,这个时候通过runtime
获取到所有类的列表,然后向所有遵循SelfAware协议的类发送消息.
extension UIApplication {
private static let runOnce: Void = {
NothingToSeeHere.harmlessFunction()
}()
override open var next: UIResponder? {
// Called before applicationDidFinishLaunching
UIApplication.runOnce
return super.next
}
}
protocol SelfAware: class {
static func awake()
}
class NothingToSeeHere {
static func harmlessFunction() {
let typeCount = Int(objc_getClassList(nil, 0))
let types = UnsafeMutablePointer.allocate(capacity: typeCount)
let autoreleasingTypes = AutoreleasingUnsafeMutablePointer(types)
objc_getClassList(autoreleasingTypes, Int32(typeCount))
for index in 0 ..< typeCount {
(types[index] as? SelfAware.Type)?.awake()
}
types.deallocate()
}
}
之后任何遵守SelfAware
协议实现的+awake()
方法在这个阶段都会被调用.
3. 交换方法 Method Swizzling
黑魔法Method Swizzling
在swift中实现的两个困难点
- swizzling 应该保证只会执行一次.
- swizzling 应该在加载所有类的时候调用.
分别在上面给出了解决方案.
下面给出了两个示例供参考:
protocol SelfAware: class {
static func awake()
static func swizzlingForClass(_ forClass: AnyClass, originalSelector: Selector, swizzledSelector: Selector)
}
extension SelfAware {
static func swizzlingForClass(_ forClass: AnyClass, originalSelector: Selector, swizzledSelector: Selector) {
let originalMethod = class_getInstanceMethod(forClass, originalSelector)
let swizzledMethod = class_getInstanceMethod(forClass, swizzledSelector)
guard (originalMethod != nil && swizzledMethod != nil) else {
return
}
if class_addMethod(forClass, originalSelector, method_getImplementation(swizzledMethod!), method_getTypeEncoding(swizzledMethod!)) {
class_replaceMethod(forClass, swizzledSelector, method_getImplementation(originalMethod!), method_getTypeEncoding(originalMethod!))
} else {
method_exchangeImplementations(originalMethod!, swizzledMethod!)
}
}
}
class NothingToSeeHere {
static func harmlessFunction() {
let typeCount = Int(objc_getClassList(nil, 0))
let types = UnsafeMutablePointer.allocate(capacity: typeCount)
let autoreleasingTypes = AutoreleasingUnsafeMutablePointer(types)
objc_getClassList(autoreleasingTypes, Int32(typeCount))
for index in 0 ..< typeCount {
(types[index] as? SelfAware.Type)?.awake()
}
types.deallocate()
}
}
extension UIApplication {
private static let runOnce: Void = {
NothingToSeeHere.harmlessFunction()
}()
override open var next: UIResponder? {
UIApplication.runOnce
return super.next
}
}
在SelfAware
的extension
中为swizzlingForClass
做了默认实现,相当于一层封装.
1. 给按钮添加点击计数
extension UIButton: SelfAware {
static func awake() {
UIButton.takeOnceTime
}
private static let takeOnceTime: Void = {
let originalSelector = #selector(sendAction)
let swizzledSelector = #selector(xxx_sendAction(action:to:forEvent:))
swizzlingForClass(UIButton.self, originalSelector: originalSelector, swizzledSelector: swizzledSelector)
}()
@objc public func xxx_sendAction(action: Selector, to: AnyObject!, forEvent: UIEvent!) {
struct xxx_buttonTapCounter {
static var count: Int = 0
}
xxx_buttonTapCounter.count += 1
print(xxx_buttonTapCounter.count)
xxx_sendAction(action: action, to: to, forEvent: forEvent)
}
}
2. 替换控制器的viewWillAppear
方法
extension UIViewController: SelfAware {
static func awake() {
swizzleMethod
}
private static let swizzleMethod: Void = {
let originalSelector = #selector(viewWillAppear(_:))
let swizzledSelector = #selector(swizzled_viewWillAppear(_:))
swizzlingForClass(UIViewController.self, originalSelector: originalSelector, swizzledSelector: swizzledSelector)
}()
@objc func swizzled_viewWillAppear(_ animated: Bool) {
swizzled_viewWillAppear(animated)
print("swizzled_viewWillAppear")
}
}
本文收录于 SwiftTips
如有疑问,欢迎留言 :-D