首页 > 解决方案 > `objc_setAssociatedObject` 和 `objc_getAssociatedObject` 不匹配

问题描述

我只想添加一个UIView像这样的属性,

#import "UIView+Extension.h"
#import <objc/runtime.h>

@implementation UIView (Extension)

- (void)setMaxWidth:(CGFloat)maxWidth {
    objc_setAssociatedObject(self, @selector(maxWidth), @(maxWidth), OBJC_ASSOCIATION_ASSIGN);
}

- (CGFloat)maxWidth {
    return [objc_getAssociatedObject(self, _cmd) doubleValue];
}

@end

但有时它是有用的,有时它是崩溃的。

我在 XCode 中得到的崩溃信息是这一行return [objc_getAssociatedObject(self, _cmd) doubleValue];

在此处输入图像描述

我在 Bugly 遇到的崩溃是这样的NSInvalidArgumentException -[_UILabelStringContent doubleValue]: unrecognized selector sent to instance 0x2804fb280

在此处输入图像描述

那么为什么会发生这种情况。我只是设置了一个float value但得到了一个_UILabelStringContent value

PS:我的项目是 swift 5.0,但我只是使用 Objective-C 运行时添加额外的属性。

PS2:我在另一个Objective-C项目中使用了相同的代码,它不会崩溃。

PS3:并不总是崩溃。

PS4:我在 and 中使用了 this 属性UIViewBaseLabel但只在BaseLabel.

PS5:我用它来更新框架,我BaseLabel是这样用的。

override func sizeToFit() {
        super.sizeToFit()
        if self.maxWidth > 0 {
            if numberOfLines != 1 {
                let size = self.sizeThatFits(CGSize(width: maxWidth, height: CGFloat.greatestFiniteMagnitude))
                self.size = size
            } else {
                if self.size.width > maxWidth {
                    self.size.width = maxWidth
                }
            }
        }
    }

PS6:崩溃电话_cmdobjc_getAssociatedObject(self, _cmd)吗?

标签: iosobjective-cswiftcrash

解决方案


在内部- (void)setMaxWidth:(CGFloat)maxWidth,您正在创建 NSNumber支持对象,该对象包含缩短语法CGFloat所暗示的值。@(maxWidth)因为你NSNumber的子类NSObject不应该使用OBJC_ASSOCIATION_ASSIGN它,它对由 ARC 处理的引用计数的类型无效(它对像这样的值类型有效Integer)。你应该OBJC_ASSOCIATION_RETAIN改用。将您的代码更新为

- (void)setMaxWidth:(CGFloat)maxWidth {
        objc_setAssociatedObject(self, @selector(maxWidth), @(maxWidth), OBJC_ASSOCIATION_RETAIN); 
}

关于您的 PS6 部分_cmd是给定目标 c 方法主体中的选择器名称(内部表示为char*)。它通过选择器作为每个方法调用的“隐藏”参数(从 obj-c 语法的角度来看)传递。除非方法调用以某种方式格式错误,否则访问它不是崩溃的可能原因。


推荐阅读