感受本身给本身释疑,也是一个极为有趣的过程。此次,我还新增了“猜测”一栏,来尝试回答一些暂时没有足够资料支撑的问题。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 只能直接捕捉其真实对象.