在生产中捕获UIViewAlertForUnsatisfiableConstraints

时间:2016-08-26 14:30:53

标签: ios swift autolayout nslayoutconstraint

是否有可能在生产中捕获自动布局约束模糊 - 相当于UIViewAlertForUnsatisfiableConstraints断点,但对于生产应用程序?

我的目标是添加一个全局处理程序,将此类错误报告给日志记录系统。

2 个答案:

答案 0 :(得分:8)

符号UIViewAlertForUnsatisfiableConstraints实际上是一个函数:

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

它是私密的,因此您无法替换它。

但它是从私有方法-[UIView engine:willBreakConstraint:dueToMutuallyExclusiveConstraints:]调用的,可以调整。此方法大致包含以下内容:

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

如果我从this article正确理解,__UIConstraintBasedLayoutVisualizeMutuallyExclusiveConstraints将始终在iOS上返回NO,因此您需要做的就是检查名为_isUnsatisfiableConstraintsLoggingSuspended的私有bool属性,然后调用原始方法。

这是结果代码示例:

#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中无效,所以要小心),但我无法保证。 此外,它还可以捕获系统组件中的约束问题,例如键盘/视频播放器/等。 此代码很脆弱(它可能导致任何系统版本更新崩溃,参数更改等),我不建议在生产中使用它(猜测它甚至不会通过自动审查过程)。你有最后一个字,但是你被警告了。

但是我认为您可以在内部/外部测试人员的构建中使用它来修复生产前的autolayout中的错误。

注意到您使用的是swift:您可以使用桥接头文件将此代码添加到swift项目中。

答案 1 :(得分:0)

简短的回答是,这是一个私有API,你不应该在生产代码中搞乱它......

......至少在不知道相关危险的情况下:

A)如果您尝试在提交到应用商店的产品中覆盖此类SPI,Apple 拒绝您的应用。如果它由于某种原因而滑落,它们将在以后的某个时间捕获它,这通常会更糟。

B)方法调整,就像@Roman在他的回答中提到的那样,经常会带来一些你破坏稳定性的可能性,无论你是在进一步(或将来)工作。当我使用第三方库时,我仍然担心某人正在做一些像这样脆弱的事情。

有了那些警告,继续,覆盖私人方法,并将它们调到你心中。只是不要发送该代码。