2016-08-26 4 views
15

Ist es möglich, Autolayout-Constraint-Ambiguitäten in der Produktion zu fangen - das Äquivalent eines UIViewAlertForUnsatisfiableConstraints Breakpoints, aber für Produktions-Apps?Catch UIViewAlertForUnsatisfibleConstraints in der Produktion

Mein Ziel wäre es, einen globalen Handler hinzuzufügen, der solche Fehler an ein Logging-System melden würde.

+0

Das ist eine gute Frage. Wie ich es verstehe, können Sie mit symbolischen Haltepunkten ein bestimmtes Symbol, eine Methode oder einen Selektor unterbrechen.Ich habe versucht, eine globale C-Funktion 'UIViewAlertForUnsatisfibleConstraints()' zu öffnen und zu sehen, ob das eine Instanz oder Klassenmethode auf 'UIView' ist, aber ich habe bisher nichts gefunden. – JAL

Antwort

5

Das Symbol UIViewAlertForUnsatisfiableConstraints ist eigentlich eine Funktion:

_UIViewAlertForUnsatisfiableConstraints(NSLayoutConstraint* unsatisfiableConstraint, NSArray<NSLayoutConstraint*>* allConstraints).

Es ist privat, so dass Sie es nicht ersetzen können.

Aber es wird aus der privaten Methode -[UIView engine:willBreakConstraint:dueToMutuallyExclusiveConstraints:] aufgerufen, die swizzled werden kann. Diese Methode hat etwa diesen Inhalt:

void -[UIView engine:willBreakConstraint:dueToMutuallyExclusiveConstraints:] { 
    if ([self _isUnsatisfiableConstraintsLoggingSuspended]) { 
    [self _recordConstraintBrokenWhileUnsatisfiableConstraintsLoggingSuspended:$arg4]; // add constraint to some pool 
    } 
    else { 
    if (__UIConstraintBasedLayoutVisualizeMutuallyExclusiveConstraints) { 
     // print something in os_log 
    } 
    else { 
     _UIViewAlertForUnsatisfiableConstraints($arg4, $arg5); 
    } 
    } 
} 

Wenn ich von this article richtig verstehe, wird __UIConstraintBasedLayoutVisualizeMutuallyExclusiveConstraints immer NO auf iOS zurückkehren, so alles, was Sie tun müssen, ist private bool Eigenschaft namens _isUnsatisfiableConstraintsLoggingSuspended und rief ursprüngliche Methode dann zu überprüfen.

Dies ist Ergebnis Codebeispiel:

#import <objc/runtime.h> 

void SwizzleInstanceMethod(Class classToSwizzle, SEL origSEL, Class myClass, SEL newSEL) { 
    Method methodToSwizzle = class_getInstanceMethod(classToSwizzle, origSEL); 
    Method myMethod = class_getInstanceMethod(myClass, newSEL); 
    class_replaceMethod(classToSwizzle, newSEL, method_getImplementation(methodToSwizzle), method_getTypeEncoding(methodToSwizzle)); 
    class_replaceMethod(classToSwizzle, origSEL, method_getImplementation(myMethod), method_getTypeEncoding(myMethod)); 
} 

@interface InterceptUnsatisfiableConstraints : NSObject 
@end 

@implementation InterceptUnsatisfiableConstraints 

+ (void)load { 
    static dispatch_once_t onceToken; 
    dispatch_once(&onceToken, ^{ 
    SEL willBreakConstantSel = NSSelectorFromString(@"engine:willBreakConstraint:dueToMutuallyExclusiveConstraints:"); 
    SwizzleInstanceMethod([UIView class], willBreakConstantSel, [self class], @selector(pr_engine:willBreakConstraint:dueToMutuallyExclusiveConstraints:)); 
    }); 
} 

- (void)pr_engine:(id)engine willBreakConstraint:(NSLayoutConstraint*)constraint dueToMutuallyExclusiveConstraints:(NSArray<NSLayoutConstraint*>*)layoutConstraints { 
    BOOL constrainsLoggingSuspended = [[self valueForKey:@"_isUnsatisfiableConstraintsLoggingSuspended"] boolValue]; 
    if (!constrainsLoggingSuspended) { 
    NSLog(@"_UIViewAlertForUnsatisfiableConstraints would be called on next line, log this event"); 
    } 
    [self pr_engine:engine willBreakConstraint:constraint dueToMutuallyExclusiveConstraints:layoutConstraints]; 
} 

@end 

Es funktioniert auf iOS 8.2/10.09 (es funktioniert nicht in iOS 8.1, so vorsichtig sein), aber ich kann keine Garantie geben. Es fängt auch Constraint-Probleme in Systemkomponenten wie Tastatur/Video Player/etc. Dieser Code ist zerbrechlich (es kann zu einem Absturz bei jedem System-Update führen, Parameter ändern, usw.) und ich werde nicht empfehlen, es in der Produktion zu verwenden (rate, dass es nicht sogar automatisierten Überprüfungsprozess passieren wird). Du hast das letzte Wort, aber du bist gewarnt.

Ich denke jedoch, dass Sie es in Builds für interne/externe Tester verwenden können, um Bugs im automatischen Layout vor der Produktion zu beheben.

Beachten Sie, dass Sie swift verwenden: Sie können diesen Code mit Bridging-Header-Datei zu Ihrem Swift-Projekt hinzufügen.

+0

Cool! Wie haben Sie die Methodendeklaration für '_UIViewAlertForUnsatisfibleConstraints' gefunden? – JAL

+0

@JAL Ich habe ein bisschen Reverse Engineering durchgeführt und einige Zeit damit verbracht, mein Testprojekt zu debuggen. –

+0

Können Sie näher erläutern, wie Sie UIKit rückentwickeln? Ich möchte mehr über seine Interna erfahren und habe diese C-Funktion in keinem der privaten Header gesehen. Haben Sie eine App mit defekten Constraints erstellt und die Binärdatei zerlegt? – JAL

0

Die kurze Antwort ist, ist dies eine private API und Sie sollten nicht mit ihm in der Produktion Code werden messing ...

... zumindest nicht ohne die damit verbundenen Gefahren zu wissen:

A) Apple-wird lehnen Sie Ihre App ab, wenn Sie versuchen, SPIs wie diese in einem Produkt zu überschreiben, das an den App Store gesendet wurde. Und wenn es aus irgendeinem Grund durchkommt, werden sie es zu einem späteren Zeitpunkt fangen, und das ist in der Regel schlimmer.

B) Das Verfahrenswechsel, wie @Roman in seiner Antwort erwähnt, bringt oft die Wahrscheinlichkeit mit sich, dass Sie das destabilisieren, woran Sie weiter (oder in der Zukunft) arbeiten. Ich mache mir immer noch Sorgen, wenn ich eine Bibliothek eines Drittanbieters benutze, dass jemand etwas Sprödes wie dieses unter der Haube macht.

Mit diesen Warnungen draußen, gehen Sie voran, überschreiben Sie private Methoden und swizzle sie nach Herzenslust. Nur bitte diesen Code nicht liefern.

Verwandte Themen