Поймать UIViewAlertForUnsatisfiableConstraints в производстве

15

Можно ли уловить ограничения неопределенности автоопределения в производстве - эквивалент точки останова UIViewAlertForUnsatisfiableConstraints, но для производственных приложений?

Моей целью было бы добавить глобальный обработчик, который сообщал бы о таких ошибках в систему ведения журнала.

  • 0
    Это хороший вопрос. Насколько я понимаю, символические точки останова позволяют вам разбивать определенный символ, метод или селектор. Я попытался расширить глобальную функцию C UIViewAlertForUnsatisfiableConstraints() и попытался выяснить, является ли это экземпляром или методом класса в UIView но пока ничего не нашел.
Теги:
autolayout
nslayoutconstraint

2 ответа

5

Символ UIViewAlertForUnsatisfiableConstraints фактически является функцией:

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

Это личное, поэтому вы не можете его заменить.

Но он вызывается из частного метода -[UIView engine:willBreakConstraint:dueToMutuallyExclusiveConstraints:], который можно swizzled. Этот метод имеет примерно такое содержание:

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

Если я правильно понял из в этой статье, __UIConstraintBasedLayoutVisualizeMutuallyExclusiveConstraints всегда будет возвращать NO на iOS, так что вам нужно всего лишь проверить private bool, называемое _isUnsatisfiableConstraintsLoggingSuspended, и затем вызовите исходный метод.

Это пример кода результата:

#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

Он работает на iOS 8.2/9/10 (он не работает в iOS 8.1, поэтому будьте осторожны), но я не могу дать никаких гарантий. Кроме того, он улавливает проблемы ограничения в системных компонентах, таких как клавиатура/видеоплеер/и т.д. Этот код является хрупким (это может привести к сбою при обновлении любой версии системы, изменении параметров и т.д.), И я не буду рекомендовать использовать его в производстве (предположим, что он не пройдет даже автоматический процесс проверки). У вас последнее слово, но вас предупреждают.

Однако я думаю, что вы можете использовать его в сборках для внутренних/внешних тестеров для исправления ошибок в автозапуске перед выпуском.

Заметьте, что вы используете быстрый: вы можете добавить этот код в свой быстрый проект с мостом заголовочного файла.

  • 0
    Здорово! Как вы нашли объявление метода для _UIViewAlertForUnsatisfiableConstraints ?
  • 0
    @JAL Я немного перепроектировал и потратил некоторое время на отладку своего тестового проекта.
Показать ещё 3 комментария
0

Короткий ответ: это частный API, и вы не должны возиться с ним в производственном коде...

... по крайней мере, не зная связанных опасностей:

A) Apple отклонит ваше приложение, если вы попытаетесь переопределить SPI, как это, в продукте, представленном в хранилище приложений. И если он проскальзывает по какой-то причине, они поймают его позже, и это, как правило, хуже.

B) Метод swizzling, как говорит @Roman в своем ответе, часто приносит с собой некоторую вероятность того, что вы дестабилизируете все, над чем работаете дальше (или в будущем). Я все еще беспокоюсь, когда я пользуюсь сторонней библиотекой, что кто-то делает что-то хрупкое, как это под капотом.

С этими предупреждениями, продолжайте, переопределите частные методы и оцените их до вашего сердца. Просто не отправляйте этот код.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню