gpt4 book ai didi

ios - iPhone 模拟器和真实设备上关于消息转发的不同行为

转载 作者:可可西里 更新时间:2023-11-01 03:57:22 27 4
gpt4 key购买 nike

我想使用消息转发让任何未实现的 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,第二个示例发生了什么?

最佳答案

关于你的第一个问题,这是我在模拟器上发现的

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)。因此发生了某种 stackoverflow 并导致崩溃。


我实际上实现了类似的东西,旨在使 NSNullnil 一样工作。

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
NSMethodSignature *signature = [super methodSignatureForSelector:aSelector];
if (signature)
return signature;

const Class forwardClasses[] = {[NSNumber class], [NSString class], [NSArray class], [NSOrderedSet class]}; // add new classes if you think the list is not enough

for (int i = 0; i < sizeof(forwardClasses)/sizeof(Class); i++) {
Class cls = forwardClasses[i];
signature = [cls instanceMethodSignatureForSelector:aSelector];
if (signature) {
return signature;
}
}

return signature;
}

- (void)forwardInvocation:(NSInvocation *)anInvocation {
NSUInteger len = [[anInvocation methodSignature] methodReturnLength];
char buff[len];
bzero(buff, len);
[anInvocation setReturnValue:buff];
}

关于ios - iPhone 模拟器和真实设备上关于消息转发的不同行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14302371/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com