objective-c - Objective-C 等价于 Python 的 __getattr__ / __setattr__ 方法
问题描述
是否有 NSObject 的默认方法来模拟类似于 Python 的getattr / setattr的东西?我想获取和设置只有成员名称的实例的成员。
我想完成这样的事情:
/* someUnknownInstance is defined elsewhere, and has an instance variable named "x", with an initial value of 5 */
...
id myInstance = someUnknownInstance; /* myInstance.x = 5 */
NSNumber myInstanceVariable = [myInstance getAttr:@"x"]; /* myInstanceVariable = 5 */
[myInstance setAttr:@"x" value:(myInstanceVariable + 1)]; /* myInstance.x = 6 */
解决方案
The semi easy way..
Class _class_ = [myInstance class];
objc_property_t property = class_getProperty(_class_, "x");
// property is NULL when there is no such property
if (property) {
// because ObjC has internal members name scheme, leading '_' = "_x"
const char * publicname = property_getName(property);
// the public property name
NSString *publicKey = [NSString stringWithCString:publicname encoding:NSASCIIStringEncoding];
// getter
int i = [[myInstance valueForKey:publicKey] intValue];
// setter
[myInstance setValue:@(i+1) forKey:publicKey];
}
The crazy way.. lots of code for just adding + 1 to property "x".
#import <objc/runtime.h>
#import <string.h>
static void* getIvarPointer(id object, char const *propertyname) {
Ivar ivar = class_getInstanceVariable(object_getClass(object), propertyname);
if (!ivar) return 0;
return (uint8_t*)(__bridge void*)object + ivar_getOffset(ivar);
}
-(void)goCrazyWithObjcRuntime:(id)myInstance {
/// search class of id for specific property name and add 1
Class _class = [myInstance class];
objc_property_t property = class_getProperty(_class, "x");
if (property) {
Boolean isInt = false;
const char * attr = property_getAttributes(property);
// lets iterate the attributes of the property
char *attribs = strdup(attr);
char *pt = strtok(attribs,",");
while (pt != NULL) {
if (strcmp(pt, "N" ) == 0) printf("nonatomic ");
else if (strcmp(pt, "Ti") == 0) {
printf("integer ");
isInt = true;
}
else if (strcmp(pt, "R" ) == 0) printf("readonly ");
else if (strcmp(pt, "C" ) == 0) printf("copy ");
else printf("other property description:%s", pt);
pt = strtok(NULL, ",");
}
// the public propertyname
const char * name = property_getName(property);
NSLog(@"public propertyname:'%s' with attributes:'%s'",name, attr);
// transform public property name back to internal class member
char objcInternalPrefix[4] = "_";
char *backingPropertyName = strncat(objcInternalPrefix, name, strlen(name));
// getter, catch the Ivar
int *ptr = getIvarPointer(myInstance, backingPropertyName);
NSLog(@"x=%d",*ptr);
// setter, beware math without type check here
// Get a pointer to the instance variable. Returns NULL if not found.
int *intPtr = getIvarPointer(myInstance, backingPropertyName);
if (intPtr) {
// Now, add 1.
if (isInt) {
*intPtr = *intPtr + 1;
}
}
}
}
推荐阅读
- android - 在实时数据库 Firebase 中处理同时更新时出现问题
- html - 为地址标签创建可打印的 HTML 页面
- html - jQuery Mobile 上的关闭按钮关闭整个页面
- excel - 将变量导出到 Excel
- scala - Scala Akka Typed - 使用 ask 在行为内部发送请求
- python - 在python中将month_year值转换为月份名称和年份列
- flutter - 在 Flutter PageViewModel 中移除标题周围的光环
- firebase - Flutter 从 firebase 获取特定数据
- excel - 宏将行移动到另一个工作表但忽略新工作表中的公式
- pandas - 熊猫似乎两次合并相同的数据框