我想使用Message Forwarding让任何未实现的getter方法返回0,而不是抛出一个无法识别的选择器异常。像
MyClass *r = [[MyClass alloc] init];
NSNumber *n = (NSNumber *)r;
NSLog(@"%d", [n integerValue]); // output 0
NSLog(@"%f", [n doubleValue]); // output 0.00000
NSLog(@"%@", [n stringValue]); // output (null)
所以我写了这个例子:
#pragma mark -
#pragma mark Application lifecycle
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
NSNumber *n = (NSNumber *)self;
NSLog(@"%d", [n integerValue]);
NSLog(@"%f", [n doubleValue]);
NSLog(@"%@", [n stringValue]);
return YES;
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
NSMethodSignature *ms = [super methodSignatureForSelector:aSelector];
if(ms)
return ms;
// Q = uint64_t, so it should also works for double which is also 64bit
return [NSMethodSignature signatureWithObjCTypes:"Q@:"];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation {
uint64_t ZERO64 = 0;
[anInvocation setReturnValue:&ZERO64];
}
真实设备上的输出结果为0,0.00000,(null),但在模拟器上,它为0,NaN,(null)
所以double类型不能按预期工作。我的第一个想法是将NSMethodSignature改为“d @:”(d为double)
输出结果在设备和模拟器上都是正确的,但是模拟器上只有一些奇怪的事情发生。运行此代码,它将在第6个循环中崩溃,并出现某种CALayer异常:
#pragma mark -
#pragma mark Application lifecycle
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
for(NSInteger i = 0; i < 100; i++) {
NSInteger t = [(NSNumber *)self integerValue];
UIViewController *view = [[UIViewController alloc] init];
// it always crash on the 6th loop on this line**
UINavigationController *nc = [[UINavigationController alloc] initWithRootViewController:view];
}
return YES;
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
NSMethodSignature *ms = [super methodSignatureForSelector:aSelector];
if(ms)
return ms;
// we change to return double
return [NSMethodSignature signatureWithObjCTypes:"d@:"];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation {
uint64_t ZERO64 = 0;
[anInvocation setReturnValue:&ZERO64];
}
我很好奇两个问题,为什么在第一个例子中在模拟器上返回NaN,以及在第二个例子中发生了什么?
答案 0 :(得分:2)
关于你的第一个问题,这是我在模拟器上找到的
union {
double d;
uint64_t l;
} u;
NSNumber *n = (NSNumber *)self;
u.d = [n doubleValue];
NSLog(@"%f", u.d); // nan
NSLog(@"%llx",u.l); // fff8000000000000
bzero(&u, sizeof(double));
NSLog(@"%f", u.d); // 0.000000
NSLog(@"%llx",u.l); // 0
很明显返回NAN(fff8000000000000)而不是0.0。
要深入了解[NSMethodSignature signatureWithObjCTypes:"d@:"]
和[NSMethodSignature signatureWithObjCTypes:"Q@:"]
之间的不同之处,请查看此
NSLog(@"%@\n%@", [[NSMethodSignature signatureWithObjCTypes:"Q@:"] debugDescription], [[NSMethodSignature signatureWithObjCTypes:"d@:"] debugDescription]);
输出
<NSMethodSignature: 0x74a0950>
number of arguments = 2
frame size = 8
is special struct return? NO
return value: -------- -------- -------- --------
type encoding (Q) 'Q'
flags {}
modifiers {}
frame {offset = 0, offset adjust = 0, size = 8, size adjust = 0}
memory {offset = 0, size = 8}
argument 0: -------- -------- -------- --------
type encoding (@) '@'
flags {isObject}
modifiers {}
frame {offset = 0, offset adjust = 0, size = 4, size adjust = 0}
memory {offset = 0, size = 4}
argument 1: -------- -------- -------- --------
type encoding (:) ':'
flags {}
modifiers {}
frame {offset = 4, offset adjust = 0, size = 4, size adjust = 0}
memory {offset = 0, size = 4}
<NSMethodSignature: 0x74a1e80>
number of arguments = 2
frame size = 8
is special struct return? NO
return value: -------- -------- -------- --------
type encoding (d) 'd'
flags {isFloat} <<<<----- this flag should be set if the return value is float type
modifiers {}
frame {offset = 0, offset adjust = 0, size = 8, size adjust = 0}
memory {offset = 0, size = 8}
argument 0: -------- -------- -------- --------
type encoding (@) '@'
flags {isObject}
modifiers {}
frame {offset = 0, offset adjust = 0, size = 4, size adjust = 0}
memory {offset = 0, size = 4}
argument 1: -------- -------- -------- --------
type encoding (:) ':'
flags {}
modifiers {}
frame {offset = 4, offset adjust = 0, size = 4, size adjust = 0}
memory {offset = 0, size = 4}
您可以在第二个方法签名上看到返回值为flags {isFloat}
。我不是x86和AMR以及低级ObjC运行时的专家。但我认为CPU使用此标志来标识返回值的类型。如果不在x86 CPU上设置,则预期的浮点返回值将被解释为NAN。
对于你的第二个问题,我认为这是因为你告诉运行时它将返回64位大小的值,因此堆栈上64位大小的内存被清零。但是,调用者期望返回32位(NSInteger)。因此发生了某种堆栈溢出并导致崩溃。
我实际上实现了类似的功能,旨在使NSNull
像[{1}}一样工作。
nil
答案 1 :(得分:1)
如果你想使用Message Forwarding让任何未实现的getter方法返回0,而不是抛出一个无法识别的选择器异常,也许你可以使用+ resolveInstanceMethod?
这是返回NSString的示例。你必须调整它才能返回原语。如果你遇到麻烦,请告诉我。
如果您正在使用ARC,那么您也需要在空白*上进行桥接。
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
NSString* name = NSStringFromSelector(sel);
IMP imp = imp_implementationWithBlock((void*) objc_unretainedPointer(^(id me, BOOL selected)
{
return @"Hello!";
}));
class_addMethod(self, sel, imp, "@"); //The type '@' is an object. For int use 'i'. Google "obj-c runtime types"
return YES;
}
当我们使用class_addMethod时,第三个参数是类型代码。 。 。解决问题的一个好方法是制作一个真正的方法,然后对其进行反思。这是一个实用程序,用于返回类上(实际)选择器的类型代码: https://github.com/jasperblues/spring-objective-c/blob/master/Source/ ... - user404201 6分钟前