2016-08-26 34 views
15

Czy możliwe jest uchwycenie niejednoznaczności ograniczenia automatycznego w produkcji - odpowiednik punktu przerwania o wartości UIViewAlertForUnsatisfiableConstraints, ale w przypadku aplikacji produkcyjnych?Złap UIViewAlertForUnsatisfiableConstraints w produkcji

Moim celem byłoby dodanie globalnego programu obsługi, który zgłosiłby takie błędy do systemu rejestrowania.

+0

To jest dobre pytanie. Jak rozumiem, symboliczne punkty przerwania umożliwiają złamanie określonego symbolu, metody lub selektora.Próbowałem externing globalnej funkcji C 'UIViewAlertForUnsatisfiableConstraints()' i patrząc, czy to jest instancja lub metoda klasy na 'UIView', ale nie znalazłem nic do tej pory. – JAL

Odpowiedz

5

Symbol UIViewAlertForUnsatisfiableConstraints rzeczywiście jest funkcją:

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

To jest prywatne, więc nie można go zastąpić.

Ale jest wywoływana z prywatnej metody -[UIView engine:willBreakConstraint:dueToMutuallyExclusiveConstraints:], którą można przekręcić. Metoda ta ma w przybliżeniu tę zawartość:

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); 
    } 
    } 
} 

Jeśli dobrze rozumiem, ze this article, __UIConstraintBasedLayoutVisualizeMutuallyExclusiveConstraints zawsze nie zwraca na iOS, więc wszystko co musisz zrobić, to sprawdzić prywatną własność bool nazwie _isUnsatisfiableConstraintsLoggingSuspended a następnie wywołać metodę oryginalnego.

To jest przykład kodu wynikowego:

#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 

To działa na iOS 8.2/9/10 (nie działa w iOS 8.1, więc należy być ostrożnym), ale nie mogę dać żadnej gwarancji. Ponadto przechwytuje problemy z ograniczeniami w komponentach systemu, takich jak klawiatura/odtwarzacz wideo/itp. Ten kod jest delikatny (może prowadzić do awarii w przypadku każdej aktualizacji wersji systemu, zmiany parametrów itp.) I nie polecam używania go w produkcji (zgadnij, że nie przejdzie on nawet zautomatyzowanym procesem recenzowania). Masz ostatnie słowo, ale jesteś ostrzeżony.

Jednak myślę, że można go używać w kompilacjach dla wewnętrznych/zewnętrznych testerów, aby naprawić błędy w autolayout przed rozpoczęciem produkcji.

Zauważono, że korzystasz z szybkiego: możesz dodać ten kod do swojego szybkiego projektu z pomostowym plikiem nagłówkowym.

+0

Fajnie! Jak znalazłeś deklarację metody dla '_UIViewAlertForUnsatisfiableConstraints'? – JAL

+0

@JAL Zrobiłem trochę inżynierii wstecznej i spędziłem trochę czasu na debugowaniu mojego projektu testowego. –

+0

Czy możesz wyjaśnić, w jaki sposób tworzysz UIKit na potrzeby inżynierii wstecznej? Chciałbym dowiedzieć się więcej o jego wnętrzach i nie widziałem tej funkcji C w żadnym z prywatnych nagłówków. Czy stworzyłeś aplikację ze złamanymi ograniczeniami i zdemontujesz plik binarny? – JAL

0

Odpowiedź jest krótka, to jest prywatna API i nie należy bawić się z nim w kodzie produkcji ...

... przynajmniej nie bez znajomości niebezpieczeństw związanych z:

A) Jabłko będzie odrzuć swoją aplikację, jeśli spróbujesz zastąpić takie identyfikatory SPI w produkcie przesłanym do sklepu z aplikacjami. A jeśli z jakiegoś powodu się z tego wyślizgnie, to go złapią w jakimś późniejszym terminie, a to na ogół jest gorsze.

B) Metoda swizowania, jak wspomina @Roman w swojej odpowiedzi, często niesie ze sobą pewne prawdopodobieństwo destabilizacji tego, nad czym pracujesz dalej (lub w przyszłości). Nadal niepokoję się, gdy korzystam z biblioteki stron trzecich, że ktoś robi coś kruchego pod tym kapturem.

Z tymi ostrzeżeniami tam, śmiało, zastąp prywatnych metod i swizzle je do treści serc. Po prostu proszę nie wysyłaj tego kodu.