Objective-C 运行时更改已初始化对象的只读 属性

Objective-C Runtime changing readonly property of initialized object

我有一个问题,我无法在第 3 方库中更改 frame.size.width。找不到任何改变宽度的正常解决方案,所以我决定执行 objc/runtime.

我有 ViewController 及其 属性 DTAtributedTextView *v.

@interface DTAttributedTextView : UIScrollView
{
 // ivars needed by subclasses
 DTAttributedTextContentView *_attributedTextContentView;
}

 @property (nonatomic, strong) NSAttributedString *attributedString;
 @property (nonatomic, DT_WEAK_PROPERTY) IBOutlet 
 id<DTAttributedTextContentViewDelegate> textDelegate;
 @property (nonatomic, strong) IBOutlet UIView *backgroundView;
 ....
@end

v 得到了@属性(..,只读)DTAtributedTextContentView *attributedTextContentView

 @interface DTAttributedTextContentView : UIView
 {
      NSAttributedString *_attributedString;
      DTCoreTextLayoutFrame *_layoutFrame;

      UIEdgeInsets _edgeInsets;

      NSMutableDictionary *customViewsForAttachmentsIndex;

      BOOL _flexibleHeight;

      // for layoutFrame
      NSInteger _numberOfLines;
      NSLineBreakMode _lineBreakMode;
      NSAttributedString *_truncationString;
}

attributedTextContentView 得到了@属性 DTCoreTextLayoutFrame *layoutFrame

 @interface DTCoreTextLayoutFrame : NSObject 
 {
   CGRect _frame;

   NSArray *_lines;
   NSArray *_paragraphRanges;

   NSArray *_textAttachments;
   NSAttributedString *_attributedStringFragment;
 }

所以基本上我需要改变

self.v.attributedTextContentView.layoutFrame.frame.size.width

可惜我不会用

objc_setAssociatedObject(self.v.attributedTextContentView.layoutFrame,@"frame.size.width",@200,OBJC_ASSOCIATION_ASSIGN);

也不

objc_setAssociatedObject(self.v.attributedTextContentView.layoutFrame,@"frame",CGRectMake(0,0,200,1000),OBJC_ASSOCIATION_ASSIGN);

因为我不能通过点符号访问 ivars,如果 &.

作为这种情况的另一种解决方案,我看到使用运行时逐个对象创建对象,然后更改指针。也许可以使用复制来完成某些步骤。 我的问题是我在 objc/runtime 中完全是新手,而且文档真的很差。我正在努力学习这项重要技术,所以我故意不使用其他选项来解决确切的问题。

非常感谢任何帮助。 提前致谢。

看起来你做错了什么,但如果你真的需要为另一个实例 ivar 设置值(这可能导致不可预测的行为),这是你需要知道的:

1) 关联对象不是实例的一部分,因此如果您添加关联对象的键等于 property/ivar 名称,它不会更改 property/ivar

的值

2) 你cannot change the part of structure如果这个结构是属性对象

3) 如果 accessInstanceVariablesDirectly 属性 未被覆盖,您可以通过调用 valueForKey: / setValue:forKey: 来访问 ivars。

4) 在传递给 setValue:forKey:

之前你需要 wrap your structure to NSValue

因此,结果代码应如下所示:

DTCoreTextLayoutFrame *layoutFrame = self.v.attributedTextContentView.layoutFrame;
CGRect frame = [[layoutFrame valueForKey:@"_frame"] CGRectValue];
frame.size.width = 200.0;
NSValue* frameValue = [NSValue valueWithCGRect:frame];
[layoutFrame setValue:frameValue forKey:@"_frame"];

有关 KVC 的更多信息,请查看 Key-Value Coding Programming Guide

更新:

如果你想使用运行时函数,你需要:

1) 获取实例变量的偏移量(所有objective-c对象都是结构体)

2) 创建指向您感兴趣的 ivar 的指针

3) 直接读取/写入此指针

这是代码:

DTCoreTextLayoutFrame *layoutFrame = self.v.attributedTextContentView.layoutFrame;    
Ivar ivar = class_getInstanceVariable([DTCoreTextLayoutFrame class], "_frame");
ptrdiff_t offset = ivar_getOffset(ivar);
CGRect *framePtr = (__bridge void*)layoutFrame + offset;
CGRect frame = *framePtr;
frame.size.width = 100;
*framePtr = frame;