То, что вы делаете, это почти наверняка безумие, и вы не должны с этим возиться.
Тем не менее, написанный вами код даже не компилируется.
Итак, давайте исправим это.
Вы не можете объявить Josh j
, потому что Josh
это переменная, а не тип. Вместо этого вы можете использовать id
, что означает «любой объект Objective-C».
id j = [[Josh alloc] init];
Вы также не можете сказать [j age]
, если не объявите где-нибудь метод age
, поэтому добавьте это куда-нибудь:
@protocol Dummy <NSObject>
- (int)age;
@end
Вы также не можете сказать NSLog([j age])
, потому что [j age]
возвращает int
, а не NSString *
. Так что сделайте это вместо:
NSLog(@"%d", [j age]);
Теперь вы обнаружите, что получаете ошибку «нераспознанный селектор», потому что вы не добавили метод age
в свой класс Josh
. Для этого сначала нужно импортировать среду выполнения Objective C:
#import <objc/runtime.h>
Затем вы можете добавить age
метод, подобный этому:
Ivar ageIvar = class_getInstanceVariable(Josh, "age");
ptrdiff_t ageIvarOffset = ivar_getOffset(ageIvar);
IMP ageImp = imp_implementationWithBlock(^int(id self) {
void *base = (__bridge void *)self;
return *(int *)((unsigned char *)base + ageIvarOffset);
});
class_addMethod(Josh, @selector(age), ageImp, "v@:");
Вот полная, компилируемая, не сбиваемая программа:
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
@protocol Dummy <NSObject>
- (int)age;
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
Class Josh = objc_allocateClassPair([NSObject class], "Josh", 0);
class_addIvar(Josh, "age", sizeof(int), rint(log2(sizeof(int))), @encode(int));
Ivar ageIvar = class_getInstanceVariable(Josh, "age");
ptrdiff_t ageIvarOffset = ivar_getOffset(ageIvar);
IMP ageImp = imp_implementationWithBlock(^int(id self) {
void *base = (__bridge void *)self;
return *(int *)((unsigned char *)base + ageIvarOffset);
});
class_addMethod(Josh, @selector(age), ageImp, "v@:");
objc_registerClassPair(Josh);
// Below is the desired result
id j = [[Josh alloc] init];
NSLog(@"%d", [j age]); // prints 10
}
return 0;
}
Но он по-прежнему просто печатает «0»:
2018-06-25 15:07:53.179809-0500 madness[35050:5762222] 0
Program ended with exit code: 0
Если вы хотите инициализировать age
в 10, вам нужно переопределить init
в вашем классе Josh
. Если бы вы писали метод init
нормально, он бы выглядел так:
- (instancetype)init {
if (self = [super init]) {
_age = 10;
}
return self;
}
Но мы можем отправлять super
только то, что компилятор распознает как тело метода, а блок (как мы передадим imp_implementationWithBlock
) не считается как тело метода. Таким образом, мы должны выполнить трудную часть [super init]
, позвонив по номеру objc_msgSendSuper
. Для этого нам нужно импортировать еще один заголовок:
#import <objc/message.h>
Тогда мы можем написать блок, который реализует init
:
IMP initImp = imp_implementationWithBlock(^id(id self) {
struct objc_super superInfo = {
.receiver = self,
.super_class = NSObject.self
};
id (*msgSendSuper)(struct objc_super *, SEL) = (id (*)(struct objc_super *, SEL))objc_msgSendSuper;
self = msgSendSuper(&superInfo, @selector(init));
if (self) {
void *base = (__bridge void *)self;
*(int *)((unsigned char *)base + ageIvarOffset) = 10;
}
return self;
});
class_addMethod(Josh, @selector(init), initImp, "@@:");
Вот полная программа:
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import <objc/message.h>
@protocol Dummy <NSObject>
- (int)age;
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
Class Josh = objc_allocateClassPair([NSObject class], "Josh", 0);
class_addIvar(Josh, "age", sizeof(int), rint(log2(sizeof(int))), @encode(int));
Ivar ageIvar = class_getInstanceVariable(Josh, "age");
ptrdiff_t ageIvarOffset = ivar_getOffset(ageIvar);
IMP ageImp = imp_implementationWithBlock(^int(id self) {
void *base = (__bridge void *)self;
return *(int *)((unsigned char *)base + ageIvarOffset);
});
class_addMethod(Josh, @selector(age), ageImp, "v@:");
IMP initImp = imp_implementationWithBlock(^id(id self) {
struct objc_super superInfo = {
.receiver = self,
.super_class = NSObject.self
};
id (*msgSendSuper)(struct objc_super *, SEL) = (id (*)(struct objc_super *, SEL))objc_msgSendSuper;
self = msgSendSuper(&superInfo, @selector(init));
if (self) {
void *base = (__bridge void *)self;
*(int *)((unsigned char *)base + ageIvarOffset) = 10;
}
return self;
});
class_addMethod(Josh, @selector(init), initImp, "@@:");
objc_registerClassPair(Josh);
// Below is the desired result
id j = [[Josh alloc] init];
NSLog(@"%d", [j age]); // prints 10
}
return 0;
}
А вот и вывод:
2018-06-25 15:31:41.837748-0500 madness[35369:5786038] 10
Program ended with exit code: 0