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;