You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Objective-C执行方法时获取参数取值异常,求正确获取参数的方法

Fixing Dynamic Argument Retrieval in Objective-C Class Methods

Your core issue is a fundamental misunderstanding of how to access method arguments: you're using the class object's address (self) as the base for calculating argument positions, but self in a class method refers to the class itself, not the stack frame where method arguments are stored. This is why you're reading garbage memory instead of the actual argument value.

Why Your Current Code Fails

When you do ((uintptr_t)self) + sizeof(void *) * i, you're adding offsets to the memory address of the class object—this points to the class's internal data (like ivars, method lists, etc.), not the stack frame where the method's arguments reside. Even if you used the correct stack base, modern architectures (ARM64, x86_64) pass the first few arguments via registers, not the stack, so stack-based offset calculations won't work reliably.

Correct Approaches

1. Direct Access (Non-Dynamic)

If you don't need full dynamic introspection, just use the parameter name directly—this is the simplest and most reliable method for known parameters:

+ (void)simpleTest:(NSInteger)teamId {
    NSLog(@"Argument 1: %ld", (long)teamId);
}

2. Architecture-Specific Inline Assembly

For dynamic retrieval, you can use inline assembly to read arguments from the registers they're passed in. Note this is architecture-dependent:

ARM64 (iOS/macOS)

On ARM64, the first two arguments (self, _cmd) are in x0/x1, so your teamId is in x2:

+ (void)simpleTest:(NSInteger)teamId {
    unsigned int argumentCount = method_getNumberOfArguments(class_getClassMethod([self class], _cmd));
    
    for (unsigned int i = 2; i < argumentCount; i++) {
        const char *argumentType = method_copyArgumentType(class_getClassMethod([self class], _cmd), i);
        
        if (strcmp(argumentType, @encode(NSInteger)) == 0) {
            NSInteger argumentValue;
            // Read from x2 register (third argument)
            __asm__ volatile("mov %0, x2" : "=r"(argumentValue));
            NSLog(@"Argument %d: %ld", i - 1, (long)argumentValue);
        }
        
        free((void *)argumentType);
    }
}

x86_64 (macOS)

On x86_64, self is in rdi, _cmd in rsi, so teamId is in rdx:

+ (void)simpleTest:(NSInteger)teamId {
    unsigned int argumentCount = method_getNumberOfArguments(class_getClassMethod([self class], _cmd));
    
    for (unsigned int i = 2; i < argumentCount; i++) {
        const char *argumentType = method_copyArgumentType(class_getClassMethod([self class], _cmd), i);
        
        if (strcmp(argumentType, @encode(NSInteger)) == 0) {
            NSInteger argumentValue;
            // Read from rdx register (third argument)
            __asm__ volatile("mov %0, rdx" : "=r"(argumentValue));
            NSLog(@"Argument %d: %ld", i - 1, (long)argumentValue);
        }
        
        free((void *)argumentType);
    }
}

3. Wrapper Method with NSInvocation (Semi-Dynamic)

For a more portable solution, you can swizzle your method to wrap it with a function that uses NSInvocation to capture arguments:

#import <objc/runtime.h>

@implementation YourClass

+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Class class = [self class];
        SEL originalSelector = @selector(simpleTest:);
        SEL swizzledSelector = @selector(swizzled_simpleTest:);
        
        Method originalMethod = class_getClassMethod(class, originalSelector);
        Method swizzledMethod = class_getClassMethod(class, swizzledSelector);
        
        BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
        
        if (didAddMethod) {
            class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
        } else {
            method_exchangeImplementations(originalMethod, swizzledMethod);
        }
    });
}

+ (void)swizzled_simpleTest:(NSInteger)teamId {
    // Call the original method first
    [self swizzled_simpleTest:teamId];
    
    // Capture arguments using NSInvocation
    SEL selector = _cmd;
    NSMethodSignature *signature = [self methodSignatureForSelector:selector];
    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
    invocation.target = self;
    invocation.selector = selector;
    [invocation setArgument:&teamId atIndex:2]; // Index 0 is self, 1 is _cmd
    
    NSInteger argumentValue;
    [invocation getArgument:&argumentValue atIndex:2];
    NSLog(@"Argument 1: %ld", (long)argumentValue);
}

+ (void)simpleTest:(NSInteger)teamId {
    // Original implementation
}

@end

This approach is more portable but requires knowing the argument count upfront to set them in the invocation.

Key Takeaways

  • Never use self (or any object pointer) as a base for stack frame calculations—this points to object memory, not the method's execution stack.
  • Register-based calling conventions make stack-based argument retrieval unreliable on modern CPUs.
  • For full dynamic introspection, you'll need architecture-specific code or method wrapping via swizzling.

内容的提问来源于stack exchange,提问作者itenyh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 18:04:51