UILabel category block property crash when main thread checked

Viewed 53
@interface UILabel (Chainable)
@property (nonatomic, readonly) UILabel *(^color)(NSString *value);
@end

@implementation UILabel (Chainable)
- (UILabel *(^)(NSString *value))color {
  return ^(NSString *value) {
    self.textColor = UIColor.whiteColor; // 
    return self;
  };
}
@end

UILabel *label = [UILabel new].color(@"0xffffff");

Why coding like this cause crash:

Thread 1: EXCBADACCESS (code=1, address=0x0) when xcode -> edit scheme -> Diagnostics -> main thread checker is selected

Only crash when using debug mode.

2 Answers

The problem is the name. -color is an existing extension on UILabel. I believe it's been deprecated for longer than iOS has been public (textColor is from iOS 2), but it still exists:

dsdump /System/iOSSupport/System/Library/PrivateFrameworks/UIKitCore.framework | grep color\]
0x00000e95243 -[UILabel(UILabelDeprecatedMethods) color]

It just forwards to -textColor:

                     -[UILabel color]:
0000000000e95243         push       rbp                                         ; Objective C Implementation defined at 0x1629e80 (instance method), DATA XREF=0x1629e80
0000000000e95244         mov        rbp, rsp
0000000000e95247         mov        rsi, qword [0x1675228]                      ; argument "selector" for method _objc_msgSend, @selector(textColor)
0000000000e9524e         pop        rbp
0000000000e9524f         jmp        qword [_objc_msgSend_12b96d8]               ; _objc_msgSend, _objc_msgSend_12b96d8,_objc_msgSend

Making two extensions with the same method name is undefined behavior in ObjC, and you're getting the wrong one.

Without the main thread checker, or in release mode, I expect you're seeing that this is quietly doing nothing. When the main thread checker injects check-code, I expect things don't quite line up, which leads to the crash. (But this is all undefined behavior. Lots of things could happen. I haven't explored the compiler output to see exactly what does happen.)

You need to change the name of this extension.

Don't use the name UILabel you can use UILabelCustom interface and implementations then you can call it like

UILabelCustom *label = [UILabelCustom new].color(@"0xffffff");

or then you can also inherit it to the native one UILabel

Related