{%hackmd @themes/orangeheart %} # LeakDetect --- ## Leak ---- > `Retain Cycle` --- ## [ARC](https://docs.swift.org/swift-book/LanguageGuide/AutomaticReferenceCounting.html) ---- ![](https://i.imgur.com/bkeUNjQ.png) ```swift= john?.apartment = unit4A unit4A?.tennant = john ``` ---- ![](https://i.imgur.com/MJqRTqB.png) ```swift= john = nil unit4A = nil ``` ---- ![](https://i.imgur.com/l4Pq7jf.png) ```diff= class Apartment { - var tennant: Person? + weak var tennant: Person? ``` --- ## Implict Strong Capture ```swift= { print(self) } ``` ---- ## Implict Strong Capture ```swift= { [self] print(self) } ``` ---- ### Capture List > [vc1, vc2 = homeVC, weak this = self] ---- ### [weak self] ```swift= // in some func { [weak self] in print(self) } ``` ---- ## Case ```swift= let obj = Foo() obj.callback = { print(obj) } ``` --- ## Implict Strong Capture > Object Method ---- ### Curry Function ```swift= class Foo { func hello() { print("bar") } } func hello(_ `self`: Foo) -> () -> () { return { [self] in print("bar") } } let foo = Foo() let _hello = hello(foo) // let _hello = foo.hello _hello() // bar ``` ---- ### Case 1 ```swift= let callback = self.hello ``` ---- ### Case 2 ```swift= rx .subscribe(onNext: self.hello) .disposed(by: bag) ``` --- ## Implict Strong Capture > parent closure auto capture for child closure ---- ## Case ```swift= { { [weak self] in } } ``` ---- ## Case ```swift= /// parent closure { [self] /// parent closure 自動 capture self /// child closure { [weak self] in } } ``` --- ## 實作 * SwiftSyntax * SourceKit --- ## SwiftSyntax > [AST](https://swift-ast-explorer.com/) ---- ### 目標 * 找到所有 `closure` -> `ClosureExprSyntax` * 找到所有特定變數 -> `TokenSyntax` ---- ## SyntaxVisitor ```swift= import SwiftSyntax class YourVisitor: SyntaxVisitor { func visit(_ node: TokenSyntax) -> SyntaxVisitorContinueKind { .skipChildren } func visit(_ node: ClosureExprSyntax) -> SyntaxVisitorContinueKind { .skipChildren } } ``` --- ## ClosureExprSyntax ```swift= public struct ClosureExprSyntax: ExprSyntaxProtocol, SyntaxHashable { enum Cursor: Int { case leftBrace case signature case statements case rightBrace } } ``` ---- ## ClosureExprSyntax ```graphviz digraph ClosureExprSyntax { rankdir = TB; "ClosureExprSyntax" -> "leftBrace" -> "{" "ClosureExprSyntax" -> "signature" -> "ClosureSignatureSyntax" "ClosureExprSyntax" -> "statements" -> "CodeBlockItemListSyntax" "ClosureExprSyntax" -> "rightBrace" -> "}" } ``` ---- ## ClosureExprSyntax ```swift= { // leftBrace [self] (i: Int) in // signature code0 // statements[0] code1 // statements[1] code2 // statements[2] } // rightBrace ``` ---- ## ClosureExprSyntax ```graphviz digraph ClosureExprSyntax { rankdir = TB; "ClosureExprSyntax" -> "leftBrace" -> "{" "ClosureExprSyntax" -> "signature" -> "ClosureSignatureSyntax" "ClosureExprSyntax" -> "statements" -> "CodeBlockItemListSyntax" "ClosureExprSyntax" -> "rightBrace" -> "}" "CodeBlockItemListSyntax" -> "DispatchQueue.main.async {}" -> "..." -> "ClosureExprSyntax1" "CodeBlockItemListSyntax" -> "let a: Int = {1}()" -> "... " -> "ClosureExprSyntax2" "CodeBlockItemListSyntax" -> "Code3" -> "... " } ``` --- ## TokenSyntax ---- ## Token ```swift= a.b.c // ^ ^ ^ a // ^ a.b.c() // ^ ^ ^ a() // ^ ``` ---- ### MemberAccessExprSyntax ```swift= public struct MemberAccessExprSyntax: ExprSyntaxProtocol, SyntaxHashable { enum Cursor: Int { case base case dot case name case declNameArguments } } ``` ---- ### a.b.c ```graphviz digraph ClosureExprSyntax { rankdir = TB; "member1" -> "base 1" -> "member2" "member1" -> "." "member1" -> "name 1" -> "Token c" -> "c" "member2" -> "base 2" -> "Token a" -> "a" "member2" -> ". " "member2" -> "name 2" -> "Token b" -> "b" } ``` --- ## SourceKit ---- ### SourceKit ![](https://i.imgur.com/uoWgYkL.png) ---- ### SourceKitten ---- ### Cursor ![](https://i.imgur.com/4y2Xg6y.png) ---- ### Cursor Info ![](https://i.imgur.com/LcdW6OX.png) ![](https://i.imgur.com/B8eqVgD.png) ---- ### SourceKitResponse ```lldb= ▿ SourceKitResponse ▿ raw : 14 elements ▿ 0 : 2 elements - key : "key.typeusr" - value : "$sSiD" ▿ 1 : 2 elements - key : "key.line" - value : 18 ▿ 2 : 2 elements - key : "key.fully_annotated_decl" - value : "<decl.var.local><syntaxtype.keyword>let</syntaxtype.keyword> <decl.name>a</decl.name>: <decl.var.type><ref.struct usr=\"s:Si\">Int</ref.struct></decl.var.type></decl.var.local>" ▿ 3 : 2 elements - key : "key.annotated_decl" - value : "<Declaration>let a: <Type usr=\"s:Si\">Int</Type></Declaration>" ▿ 4 : 2 elements - key : "key.modulename" - value : "ViewController" ▿ 5 : 2 elements - key : "key.typename" - value : "Int" ▿ 6 : 2 elements - key : "key.name" - value : "a" ▿ 7 : 2 elements - key : "key.kind" - value : "source.lang.swift.ref.var.local" ▿ 8 : 2 elements - key : "key.length" - value : 1 ▿ 9 : 2 elements - key : "key.decl_lang" - value : "source.lang.swift" ▿ 10 : 2 elements - key : "key.column" - value : 13 ▿ 11 : 2 elements - key : "key.offset" - value : 351 ▿ 12 : 2 elements - key : "key.usr" - value : "s:14ViewControllerAAC11viewDidLoadyyF1aL_Sivp" ▿ 13 : 2 elements - key : "key.filepath" - value : "/Users/yume/Desktop/AAAAAAA/AAAAAAA/ViewController.swift" ``` ---- ## kind ```lldb= ▿ 7 : 2 elements - key : "key.kind" - value : "source.lang.swift.ref.var.local" ``` ---- ## Type ```lldb= ▿ 0 : 2 elements - key : "key.typeusr" - value : "$sSiD" ▿ 5 : 2 elements - key : "key.typename" - value : "Int" ``` ![](https://i.imgur.com/7DGAxAN.png) ---- ## USR ![](https://i.imgur.com/K56Y97g.png) ---- ## Offset ```lldb= ▿ 11 : 2 elements - key : "key.offset" - value : 351 ``` ![](https://i.imgur.com/wmoS7BP.png) --- ## Find Implict Strong Capture ---- ### 特定目標 * 找到所有 `closure` * 找到該 `closure` 所有特定變數 * 變數在該 `closure` 內 * 變數不能抓取超過該 `closure` ---- ### 變數在該 `closure` 內 ```swift= { let obj = self // ^ } ``` ---- ### 變數不能抓取超過該 `closure` ```swift= { { self // x } class Foo { func bar() { self // x } } } ``` ---- ### Final Impl ```swift= if closure.`{`.offset > token.from.offset { report(token) } ``` ---- ![](https://i.imgur.com/SMP5rRX.png) --- # Demo
{"metaMigratedAt":"2023-06-17T08:13:19.138Z","metaMigratedFrom":"YAML","title":"LeakDetect","breaks":true,"slideOptions":"{\"theme\":\"league\",\"transition\":\"fade\"}","description":"Retain Cycle","contributors":"[{\"id\":\"6883ab5f-8423-424e-bfcb-d0002f96698f\",\"add\":10310,\"del\":3195}]"}
   changed 3 years ago 475 views