Skip to content
junzhan edited this page Feb 25, 2016 · 1 revision

##需求及原理 为了避免一些属性的暴露,我们经常会在implementation里写私有实例变量如:

//Objective-C代码
@implementation TestPrivateMemberVC
{
    NSString *_title;
    NSDictionary *_infoDict;
    int _num;
    double _price;
    NSInteger _aInteger;
    CGFloat _aCGFloat;
    CGRect _frame;
}
...
@end

如下操作方式都没能作用到真正的变量:

print(_title)
_title = "abc"
self._title = "abc"

那如何在lua里对这些成员变量做get/set操作?
幸好Objc-runtime有函数可以直接获取实例变量,

//Objective-C代码
Ivar class_getInstanceVariable ( Class cls, const char *name );
id object_getIvar ( id obj, Ivar ivar );

通过这两个方法就能操作某个对象的各种object类型的实例变量,如

//Objective-C代码
Ivar var = class_getInstanceVariable([self class], "_infoDict");
id res = object_getIvar(self, var);
NSLog(@"res=%@", res);
object_setIvar(self, var, @{@"k11":@"v11"});
NSLog(@"_infoDict=%@", _infoDict);

object类型的对象好操作, 基础类型的怎么办?

//Objective-C代码
Ivar priceVar = class_getInstanceVariable([self class], "_price");
double priceRes = *(double *)((__bridge void*)(self) + ivar_getOffset(priceVar));
NSLog(@"_price=%f", _price);
*(double *)((__bridge void*)(self) + ivar_getOffset(priceVar)) = 34.56;
NSLog(@"_price=%f", _price);

通过ivar_getOffset就可以获取到某个变量的地址,直接修改对应的内存,从而改变基础类型实例变量的值

lua不能直接调objc-runtime的C函数, 但是我们可以将这些针对不同类型的操作封装成方法, 扩展给NSObject基类.

//Objective-C代码
@interface NSObject (TBIvarAccess)
#pragma mark - Getter -
- (NSArray *)getIvars;
- (Ivar)getIvar:(NSString *)ivarName;
- (id)getIvarObject:(NSString *)ivarName;

- (char)getIvarChar:(NSString *)ivarName;
- (short)getIvarShort:(NSString *)ivarName;
- (int)getIvarInt:(NSString *)ivarName;
- (long)getIvarLong:(NSString *)ivarName;
- (NSInteger)getIvarInteger:(NSString *)ivarName;
- (long long)getIvarLongLong:(NSString *)ivarName;

- (unsigned char)getIvarUnsignedChar:(NSString *)ivarName;
- (unsigned short)getIvarUnsignedShort:(NSString *)ivarName;
- (unsigned int)getIvarUnsignedInt:(NSString *)ivarName;
- (unsigned long)getIvarUnsignedLong:(NSString *)ivarName;
- (unsigned long long)getIvarUnsignedLongLong:(NSString *)ivarName;

- (float)getIvarFloat:(NSString *)ivarName;
- (double)getIvarDouble:(NSString *)ivarName;
- (CGFloat)getIvarCGFloat:(NSString *)ivarName;

- (BOOL)getIvarBool:(NSString *)ivarName;
- (void *)getIvarPointer:(NSString *)ivarName;

- (CGPoint)getIvarCGPoint:(NSString *)ivarName;
- (CGSize)getIvarCGSize:(NSString *)ivarName;
- (CGRect)getIvarCGRect:(NSString *)ivarName;

#pragma mark - Setter -
- (void)setIvar:(NSString *)ivarName withObject:(id)anObject;

- (void)setIvar:(NSString *)ivarName withChar:(char)aScalar;
- (void)setIvar:(NSString *)ivarName withShort:(short)aScalar;
- (void)setIvar:(NSString *)ivarName withInt:(int)aScalar;
- (void)setIvar:(NSString *)ivarName withLong:(long)aScalar;
- (void)setIvar:(NSString *)ivarName withInteger:(NSInteger)aScalar;
- (void)setIvar:(NSString *)ivarName withLongLong:(long long)aScalar;

- (void)setIvar:(NSString *)ivarName withUnsignedChar:(unsigned char)aScalar;
- (void)setIvar:(NSString *)ivarName withUnsignedShort:(unsigned short)aScalar;
- (void)setIvar:(NSString *)ivarName withUnsignedInt:(unsigned int)aScalar;
- (void)setIvar:(NSString *)ivarName withUnsignedLong:(unsigned long)aScalar;
- (void)setIvar:(NSString *)ivarName withUnsignedLongLong:(unsigned long long)aScalar;

- (void)setIvar:(NSString *)ivarName withFloat:(float)aScalar;
- (void)setIvar:(NSString *)ivarName withDouble:(double)aScalar;
- (void)setIvar:(NSString *)ivarName withCGFloat:(CGFloat)aScalar;
- (void)setIvar:(NSString *)ivarName withBool:(_Bool)aScalar;
- (void)setIvar:(NSString *)ivarName withPointer:(void *)aScalar;

- (void)setIvar:(NSString *)ivarName withCGPoint:(CGPoint)aPoint;
- (void)setIvar:(NSString *)ivarName withCGSize:(CGSize)aSize;
- (void)setIvar:(NSString *)ivarName withCGRect:(CGRect)aRect;

@end


##lua获取实例变量调用方式 这样lua想获取对象的实例变量就可以直接调他的方法了~

--lua代码
self:setIvar_withObject("_title", "abcdefg")
print(self:getIvarObject("_title"))

self:setIvar_withObject("_infoDict", {k11="v11", k22="v22"})
print(toobjc(self:getIvarObject("_infoDict")))

self:setIvar_withInt("_num", 456)
print(self:getIvarInt("_num"))

self:setIvar_withDouble("_price", 456.78)
print(self:getIvarDouble("_price"))

print(self:getIvarInteger("_aInteger"))
print(self:getIvarCGFloat("_aCGFloat"))

self:setIvar_withInteger("_aInteger", 567)
self:setIvar_withCGFloat("_aCGFloat", 567.89)


self:setIvar_withCGRect("_frame", CGRect(1, 2, 3, 4))
print(self:getIvarCGRect("_frame"))	

print(self:view():getIvarInt("_countOfMotionEffectsInSubtree"))

是哪种类型就选上面对应的方法吧~

##注意32位/64位兼容 由于32位NSInteger为int, 而64位为long long, 实际为long. 由于32为CGFloat为float, 而64位为double.

	print(self:getIvarInteger("_aInteger"))
	print(self:getIvarCGFloat("_aCGFloat"))

	self:setIvar_withInteger("_aInteger", 567)
	self:setIvar_withCGFloat("_aCGFloat", 567.89)
Clone this wiki locally