【自问自答】关于 Swift 的多少个问号

感觉自己给协调解释,也是一个颇为有趣的长河。这一次,我还新增了“猜度”一栏,来尝试回答一些暂时髦未丰硕资料支撑的难点。

斯威夫特 版本是:4.0.3。分裂版本的 斯威夫特,可能无法复现难点。

民用记录,仅供参考,不保险严谨意义上的没错。

swift 中,怎么样在函数内,注脚 static 变量 ?

题材讲述:

以下语句,是编译不过的,提醒:“static properties may only be declared on
a type”

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

猜想:

无异于功用域的同名内嵌类型,多次推行,只会真的定义五次.

swift 有没有可以举办全局埋点的黑魔法机制?

题材讲述:

全局埋点,依赖于 runtime 机制, 所以换种问法就是: swift 中如何再而三应用
objc 的runtime 机制.

缓解方案:

纯Swift类没有动态性,但在措施、属性前添加dynamic修饰可以收获动态性。

延续自NSObject的斯威夫特类,其延续自父类的章程具有动态性,其余自定义方法、属性需要加dynamic修饰才方可博得动态性。

若方法的参数、属性类型为Swift特有、不能映射到Objective-C的种类(如Character、Tuple),则此办法、属性无法添加dynamic修饰(会编译错误)

参考:
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 是在用品质换灵活性.生产环境下,未来更可能的方案,可能是:

经过磋商,约定必须落成的统计有关的艺术 –>
通过单元测试,来担保遵循一定统计协议的品类,在特定的空子一定会调用协议确定的总括方法.

extension 中覆盖某个自定义的 framework 中的 open/public class 中的 private 方法,会生出什么事?

题材讲述:

模块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 错误.

小结一下:

  • 能够安全地在 extension 中覆盖任何模块中open/public类中定义的非
    public 方法.对于本来模块,会继续行使自家的非 public
    的艺术定义;定义其余模块,可以正确运用 extension 版本中的模块代码.

  • 永不尝试在 extension 中定义其余模块中 open/public类中定义的 public
    方法.固然可以定义,不过使用时,会引起 ambiguous 错误.

  • 在行使 extension
    增加其余模块中定义的类时,最好仍旧给自己壮大的方法加上一定前缀,不然第三方模块万一揭示的同名方法,自己的代码就到底跪了.

猜想:

扩大第三方模块类时,使用自定义的前缀,总是一个好的习惯.

嵌套定义的品种,若是外层类型是 private, 内层类型是 open,内层类型.那么内层类型有可能在别的模块中被利用吗 ?

标题讲述:

 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 更宽松.

闷葫芦: 为啥函数定义外的 closure 不会挑起成效域内其余变量引用计数的转移?

难点讲述:

细心察看以下两样代码片段的不等输出:

片段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 只好直接捕捉其诚实对象.