2015-01-15 23 views
16

Apple ha aggiunto un private helper _printHierarchy in iOS8 che può essere utilizzato in consolle LLDB:Come utilizzare _printHierarchy nella console LLDB con Swift?

po [[[UIWindow keyWindow] rootViewController] _printHierarchy] 

che stampa l'intera gerarchia View Controller in forma di testo.

Questo funziona solo se si esegue il debug di codice su Objective C. In Swift, tuttavia, questo non funziona:

(lldb) po [[[UIWindow keyWindow] rootViewController] _printHierarchy] 
error: <EXPR>:1:13: error: expected ',' separator 
[[[UIWindow keyWindow] rootViewController] _printHierarchy] 
      ^
      , 
<EXPR>:1:24: error: expected ',' separator 
[[[UIWindow keyWindow] rootViewController] _printHierarchy] 
        ^
         , 
<EXPR>:1:44: error: expected ',' separator 
[[[UIWindow keyWindow] rootViewController] _printHierarchy] 
             ^
              , 

un uso equivalente a Swift non funziona neanche:

po UIApplication.sharedApplication().keyWindow!.rootViewController!._printHierarchy 

finisce con un errore (probabilmente perché _printHierarchy è una proprietà privata):

(lldb) po UIApplication.sharedApplication().keyWindow!.rootViewController!._printHierarchy() 
error: <EXPR>:1:64: error: 'UIViewController' does not have a member named '_printHierarchy' 
UIApplication.sharedApplication().keyWindow!.rootViewController!._printHierarchy 
                  ^~~~~~~~~~~~~~~~ 

La domanda è: come stampare la gerarchia del controller di visualizzazione in Swift? O c'è un modo per usare ObjC nella console LLDB anche nei progetti Swift?

risposta

42

vi segnalo come si mostra la gerarchia controller della vista con:

po [[[UIWindow keyWindow] rootViewController] _printHierarchy] 

È poi dire:

Questo funziona solo se si esegue il debug di codice su Objective C. In Swift, tuttavia, questo non funziona

In realtà, questo dipende un po 'da come sospendi l'esecuzione del tuo programma Swift. Il problema è che il comando expression (che utilizza po) utilizzerà le espressioni Swift nei frame Swift e le espressioni Objective-C nei frame Objective-C.Così questo significa che il comportamento po varia a seconda di come l'esecuzione viene sospesa:

  • È possibile, ad esempio, premere il tasto "pausa" mentre l'applicazione è in esecuzione:

    pause

    Se lo fai, sarai in grado di utilizzare la sintassi po sopra descritta con l'espressione Objective-C senza incidenti.

  • Se, d'altra parte, si imposta un punto di interruzione all'interno del codice Swift, ci si troverà in un frame Swift quando si arriva al prompt (lldb). Ma si può dire in modo esplicito il comando expression che si desidera utilizzare la sintassi Objective-C con la -l (o --language) opzione:

    expr -l objc++ -O -- [[[UIWindow keyWindow] rootViewController] _printHierarchy] 
    

Questa capacità di specificare la lingua nel comando expr è discusso in WWDC 2014 video Advanced Swift Debugging in LLDB.

+0

Hmm, ho provato questo e Ho aggiornato i miei articoli su con un errore ottengo in console. –

+0

@TomKraina Se si utilizza un punto di interruzione nel codice Swift, ci si trova in un frame Swift e quindi "po" si aspetta l'espressione Swift. Se, tuttavia, premete il pulsante di pausa mentre il programma Swift è in esecuzione (che è quello che sto facendo spesso), molto probabilmente non sarete in un frame Swift quando ottenete il prompt '(lldb)', e quindi l'espressione Objective-C va bene. Fortunatamente, se metti in pausa l'app in Swift frame, puoi comunque esplicitamente fornire l'opzione '--language' (o l'opzione' -l') per specificare che anche se sei nel frame Swift, che dovrebbe interpretare l'espressione Objective-C . Vedi la risposta rivista. – Rob

+1

È fantastico! Non sapevo che puoi specificare una lingua in 'lldb'! –

1

Sembra che, poiché questo è un helper "privato", in qualche modo non è esposto a Swift. Inoltre non è accessibile dall'interno Objective-C, cioè

UIViewController* vc = // Assign view controller 
[vc _printHierarchy]; 

risultati in un errore di fase di compilazione. Tuttavia, ciò che potrebbe funzionare è utilizzare NSSelectorFromString all'interno di un'intestazione di bridging, ad es.

-(void) printHierarchyWithVC:(UIViewController*) vc 
{ 
    [vc performSelector: NSSelectorFromString(@"_printHierarchy")]; 
} 

Una volta che questo è definito, è possibile chiamare printHierarchyWithVC da Swift.

1

Dopo alcune ricerche, ho scoperto che è solo una questione di esporre questo particolare API (copiato da iOS runtime headers) nell'intestazione di bridging del progetto in modo che diventa disponibile per Swift:

@interface UIViewController (Debugging) 
+ (id)_printHierarchy; 
@end 

Durante il runtime, questo metodo di classe può essere chiamato come segue:

(lldb) po UIViewController._printHierarchy() as NSString 
<UINavigationController 0x7f8a50733c70>, state: appearing, view: <UILayoutContainerView 0x7f8a5064def0> 
    | <MyApp.RootViewController 0x7f8a507341f0>, state: appearing, view: <UIView 0x7f8a5056d860> not in the window 

... stampare la gerarchia del controller di visualizzazione. Si noti che il metodo deve essere chiamato solo sul thread principale (UI).

+0

questo non funziona in Xcode 7. – Boon

11

Se si sono fermati nel codice Swift, incollare questa linea nella console debugger (dopo la (lldb) pronta) e premere Invio per stampare la gerarchia del controller della vista root:

po UIWindow.value(forKeyPath: "keyWindow.rootViewController._printHierarchy")! 

Se si sono fermati in codice Objective-C o codice assembly, usano questa linea, invece:

po [UIWindow valueForKeyPath:@"keyWindow.rootViewController._printHierarchy"] 
+1

Puoi aggiungere qualche contesto intorno alla tua risposta? – winhowes

+1

Perché? La domanda era "Come usare _printHierarchy nella console LLDB con Swift?" E una risposta è il comando che ho mostrato. –

0

mi consiglia di utilizzare expr -l objc++ -O -- [UIViewController _printHierarchy] nella console come si stamperà la vista gerarchia di piena in forma di testo, che ho trovato molto più utile di UIWindow.value ForKeyPath ... Nota che non è necessario aggiungere po per stampare la gerarchia, basta usare così com'è.

Questo funziona per me, Xcode 8/swift 3 Anche se penso che lo stesso comando funziona anche nelle versioni precedenti di Xcode anche perché sembra che è oggettivo C.

esempio output di questo comando:

(lldb) expr -l objc++ -O -- [UIViewController _printHierarchy] 

<MyProject.SwipeController 0x102213590>, state: disappeared, view: <UIView 0x102239ff0> not in the window 
    + <MyProject.CameraViewController 0x102215680>, state: disappeared, view: <UIView 0x102422fd0> not in the window, presented with: <_UIFullscreenPresentationController 0x102219c60> 
    | + <MyProject.MapViewController 0x102214820>, state: appeared, view: <UIView 0x10bf52fe0>, presented with: <_UIFullscreenPresentationController 0x10fd1f890> 
    | | | <MyProject.MapPlaceCollectionViewController 0x10bf54680>, state: appeared, view: <UICollectionViewControllerWrapperView 0x1022438d0> 
0

Dopo l'installazione Chisel si può semplicemente effettuare le seguenti operazioni e more:

(lldb) pvc

0

Le opzioni che sono già stati pubblicati qui sono grandi, un'altra opzione (simile a this answer), se è assolutamente necessario utilizzare il contesto Swift lldb (il che significa che non si vuole passare -l objc, è che si può chiamare performSelector:

che è colmato da Swift in questo modo:

func perform(_ aSelector: Selector!) -> Unmanaged<AnyObject>! 

per questo caso si potrebbe chiamare in questo modo:

po UIApplication.shared.keyWindow!.rootViewController!.perform("_printHierarchy")!.takeUnretainedValue() 
Problemi correlati