感受本身給本身釋疑,也是一個極爲有趣的過程。此次,我還新增了「猜測」一欄,來嘗試回答一些暫時沒有足夠資料支撐的問題。html
Swift 版本是:4.0.3。不一樣版本的 Swift,可能沒法復現問題。swift
我的記錄,僅供參考,不保證嚴格意義上的正確性。安全
如下語句,是編譯不過的,提示:「static properties may only be declared on a type」app
func add() -> Int { static var base = 0 base += 1 return base } add() add() add()
能夠用內嵌類型的 static 屬性來解決,如:函數
func add() -> Int { struct Temp{ static var base = 0 } Temp.base += 1 return Temp.base } add() // --> 1 add() // --> 2 add() // --> 3
參考:https://stackoverflow.com/a/25354915性能
同一做用域的同名內嵌類型,屢次執行,只會真正定義一次.單元測試
全局埋點,依賴於 runtime 機制, 因此換種問法就是: swift 中如何繼續使用 objc 的runtime 機制.測試
純Swift類沒有動態性,但在方法、屬性前添加dynamic修飾能夠得到動態性。繼承自NSObject的Swift類,其繼承自父類的方法具備動態性,其餘自定義方法、屬性須要加dynamic修飾才能夠得到動態性。ui
若方法的參數、屬性類型爲Swift特有、沒法映射到Objective-C的類型(如Character、Tuple),則此方法、屬性沒法添加dynamic修飾(會編譯錯誤)this
參考: http://www.infoq.com/cn/articles/dynamic-analysis-of-runtime-swift
快速驗證,可以使用:
class A{ @objc dynamic func funcA(){ print("funcA") } } func methodSwizze(cls: AnyClass, originalSelector: Selector, swizzledSelector:Selector){ let originalMethod = class_getInstanceMethod(cls, originalSelector) let swizzledMethod = class_getInstanceMethod(cls, swizzledSelector) if let originalMethod = originalMethod, let swizzledMethod = swizzledMethod { method_exchangeImplementations(originalMethod, swizzledMethod) } } extension A{ @objc dynamic func funcB(){ print("funcB") } } methodSwizze(cls: A.self, originalSelector: #selector(A.funcA), swizzledSelector: #selector(A.funcB)) let a = A() a.funcB() // --> funcA a.funcA() // --> funcB
注意: swift 4 中, 加 dynamic 的同時,也必須加 @objc -- 即不容許單獨加 dynamic 標記.
dynamic 是在用性能換靈活性.生產環境下,將來更可能的方案,多是:
經過協議,約定必須實現的統計相關的方法 --> 經過單元測試,來保證遵循特定統計協議的類型,在特定的時機必定會調用協議規定的統計方法.
模塊A:
open class Book: NSObject { private func funcA(){ print("private funcA") } public func callFuncA(){ funcA() } }
模塊B:
public extension Book { func funcA(){ print("public funcA") } }
問題:
模塊B 中,如下代碼的輸出是?
let book = Book() book.funcA() // --> ? book.callFuncA() // --> ?
能夠直接運行觀察:
let book = Book() book.funcA() // --> public funcA book.callFuncA() // --> private funcA
因此: 經過 extension 覆蓋其餘模塊open類的private方法,不會有任何詭異的問題.兩個實現,都對彼此透明.
更進一步: 模塊B以 Optional 方式引入模塊A. 若是是在模塊B中,經過 extension 覆蓋模塊A的private 方法.而後在模塊 C 中同時引入了模塊 A 和 B,此時模塊C中相似的函數調用,會是哪一個模塊的方法實現生效?
let book = Book() book.funcA() // --> public funcA book.callFuncA() // --> private funcA
能夠看到,仍然是模塊B中的 public 級別的方法生效.
再進一步,若是模塊 A 中的方法,由 private 改成 public,即:
open class Book: NSObject { public func funcA(){ print("original public funcA") } public func callFuncA(){ funcA() } }
此時模塊C 中的調用,會報錯:
error: ambiguous use of 'funcA()'
book.funcA() ^
A.Book:2:17: note: found this candidate
public func funcA() ^
B.Book:2:17: note: found this candidate
public func funcA()
若是模塊 B 以 Required 方式引入模塊A,模塊C,只引入模塊B,此時的調用結果,會不會有什麼不一樣? --> 然而,並無什麼不一樣,依然是一樣的 ambiguous 錯誤.
總結一下:
擴展第三方模塊類時,使用自定義的前綴,老是一個好的習慣.
open class Book: NSObject { private class InnerBook{ open class DeeperBook{ } } }
在另外一個 swift 模塊中,能使用相似下面的類型初始化代碼嗎?
var book = Book.InnerBook.DeeperBook()
直接調用,會報錯:
error: 'InnerBook' is inaccessible due to 'private' protection level
嘗試修改成:
open class Book: NSObject { open class InnerBook{ open class DeeperBook{ } } }
依然報錯:
error: 'Book.InnerBook.DeeperBook' initializer is inaccessible due to 'internal' protection level
根據提示,再修改下 DeeperBook 的初始化方法的訪問級別:
open class Book: NSObject { open class InnerBook{ open class DeeperBook{ public init() { } } } }
內嵌類型的方法的訪問級別,並不會隨着類型自己訪問級別的寬鬆更變得比默認的 internal 更寬鬆.
仔細觀察如下不一樣代碼片斷的不一樣輸出:
片斷A:
class Book{ let name: String lazy var whoami:(()->String)? = { return self.name } init(name:String) { self.name = name } deinit { print("\(name) is being deinitialized") } } var aBook:Book? = Book(name: "風之影") print(aBook!.whoami!()) aBook = nil /* 輸出: 風之影 */
片斷B:
class Book{ let name: String lazy var whoami:(()->String)? = { return self.name } init(name:String) { self.name = name } deinit { print("\(name) is being deinitialized") } } var aBook:Book? = Book(name: "風之影") print(aBook!.whoami!()) aBook?.whoami = nil aBook = nil /* 輸出: 風之影 風之影 is being deinitialized */
片斷C:
class Book{ let name: String lazy var whoami:(()->String)? = { return self.name } init(name:String) { self.name = name } deinit { print("\(name) is being deinitialized") } } var aBook:Book? = Book(name: "風之影") aBook?.whoami = { return aBook!.name + " new" } print(aBook!.whoami!()) aBook = nil /* 輸出: 風之影 new 風之影 is being deinitialized */
片斷A, aBook 內存泄露,經典的 closure self 循環引用問題.
片斷B,是 closure self 循環引用的一個可選解決方案,即 self 主動切斷對 closure 的引用.
片斷C,比較詭異. aBook 引用了一個新的 closure,新的 closure 內又引用了 aBook 一次,可是 aBook 居然仍是能夠正確釋放,並無預期中的內存泄露問題.使人費解!?
片斷 D:
class Book{ let name: String lazy var whoami:(()->String)? = { return self.name } init(name:String) { self.name = name } deinit { print("\(name) is being deinitialized") } } var aBook:Book? = Book(name: "風之影") aBook?.whoami = { [aBook] in return aBook!.name + " new" } print(aBook!.whoami!()) aBook = nil /* 輸出: 風之影 new */
能夠看到,這樣 aBook 就會泄露了.片斷 D 與 片斷 C 的區別在於 closure 中的那句 [aBook] in .這個語法,是我"杜撰"的,語義上近似於以強引用方式捕捉 aBook 對應的真實對象. 官方文檔中並無提到有這種語法.
另外,參考 objc 中block 的行爲,我嘗試搜索相關 swift 中 棧(stack) block 的相關信息.若是 closure 也區分棧和堆,卻是還能夠勉強解釋.不過,並無相關的信息,並且 closure 自己也是不支持 copy 操做的.
注意: 當前復現此問題用的是 swift 4.0.3 版本,不一樣版本中的 closure 的行爲可能不一致.
或許 swift 中,只有內部有可能直接使用 self 的 closure,才須要特別考慮closure引發的內存泄露問題.
我的猜想,多是由於 self 比較特殊, closure 只能直接捕捉其真實對象.