Objective-C Runtime изменяет свойство readonly инициализированного объекта

У меня есть проблема, я не могу изменить frame.size.width в сторонних lib. Не удалось найти нормальное решение для изменения ширины, поэтому я решил выполнить objc/runtime.

У меня есть ViewController и его свойство DTAttributedTextView * 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 получил @property ( .., только чтение) DTAttributedTextContentView * attributeTextContentView

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

      UIEdgeInsets _edgeInsets;

      NSMutableDictionary *customViewsForAttachmentsIndex;

      BOOL _flexibleHeight;

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

attributeTextContentView получил @property 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);

потому что я не могу получить доступ к ивару с помощью точечной нотации или отправить CGStruct в качестве события CGFloat, если &.

В качестве другого решения этой ситуации я вижу создание объекта объектом с использованием времени выполнения, а затем изменение указателя. Может быть, некоторые шаги можно сделать с помощью копирования. Моя проблема в том, что я полностью новичок в objc/runtime, а также документация действительно плохая. Я изо всех сил пытаюсь изучить эту важную технологию, поэтому я намеренно не решаю точную проблему, используя другие варианты.

Любая помощь будет высоко оценен. Спасибо заранее.

1 ответ

Решение

Кажется, что вы делаете что-то неправильно, но если вам действительно нужно установить значение для другого экземпляра ivar (это может привести к непредсказуемому поведению), это то, что вам нужно знать:

1) связанный объект не является частью экземпляра, поэтому, если вы добавите связанный объект с ключом, равным имени свойства /ivar, это не изменит значение свойства /ivar

2) вы не можете изменить свою часть структуры, если эта структура является свойством объекта

3) вы можете получить доступ к ivars, позвонив valueForKey: / setValue:forKey: если accessInstanceVariablesDirectly собственность не переопределена.

4) вам нужно обернуть вашу структуру в NSValue, прежде чем переходить к setValue:forKey:

Итак, код результата должен выглядеть так:

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, проверьте Руководство по программированию кодирования значения ключа.

Обновить:

Если вы хотите использовать функции времени выполнения, вам необходимо:

1) получить смещение переменной экземпляра (все объекты target-c являются структурами)

2) создать указатель на интересующий вас ивар

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;
Другие вопросы по тегам