OC/swift 函数调用如何传参

2,165 阅读7分钟

函数/方法调用 参数传递规则总结:

  • C函数调用:参数先后顺序传入x0, x1, x2...寄存器。
  • OC方法调用:其本质是 objc_sendMsg C函数调用。参数依次为 self, sel, 参数0, 参数1... 即:x0=对象本身,x1=方法名, x2=参数0,x3=参数1…
  • C++方法调用:obj->func(a,b,c,d) 等价于 func(obj, a,b,c,d),即:x0=对象本身,x1=参数0,x2=参数1...
  • swift方法调用:obj.func(a, b, c) 等价于 func(a, b, c, self),即:x0=参数0, x1=参数1,x2=参数,最后是对象本身。

OC方法调用的参数传递规则:

首先来看一下测试代码,如下:

- (void)viewDidLoad
{
    [self sayH1:1 h2:2 H3:3 h4:4 H5:5 h6:6 H7:7 h8:8 H9:9 h10:10];
}
- (void)sayH1:(NSInteger)h1 h2:(NSInteger)h2
H3:(NSInteger)h3 h4:(NSInteger)h4
H5:(NSInteger)h5 h6:(NSInteger)h6
H7:(NSInteger)h7 h8:(NSInteger)h8
H9:(NSInteger)h9 h10:(NSInteger)h10 {
    
    int x1 = 1;
    int x2 = 2;
    int x3 = 3;
    int x4 = 4;
    int x5 = 5;
    int x6 = 6;
    int x7 = 7;
    int x8 = 8;
    
    NSLog(@"x * y * h10 = %ld");
}

参数传递规则如下: self, SEL, 参数1,参数2,参数3…参数6;除开 self, sel最多传递6个参数,多余的函数参数存储在上一个堆栈中。然后就是局部变量1,局部变量2。 内存如下:


(lldb) x/30xg 0x000000016f209c20 (sp 栈顶寄存器)
0x16f209c20: 0x4094780000000000 0x0000000000000000
0x16f209c30: 0x4079e00000000000 0x0000000000000000
0x16f209c40: 0x0000000700000008 0x0000000500000006    局部变量:x7,x8,x5,x6
0x16f209c50: 0x0000000300000004 0x0000000100000002    局部变量:x3,x4,x1,x2
0x16f209c60: 0x0000000000000006 0x0000000000000005    (参数6,参数50x16f209c70: 0x0000000000000004 0x0000000000000003    (参数4,参数30x16f209c80: 0x0000000000000002 0x0000000000000001    (参数2,参数10x16f209c90: 0x0000000100f5490e 0x0000000101910330    (SEL,self0x000000016f209ca0: fp 栈底寄存器
-----------------------------------------------------------------------------------
0x16f209ca0: 0x000000016f209ce0 0x0000000100c0c8d4
0x16f209cb0: 0x0000000000000007 0x0000000000000008    (参数7, 参数8)
0x16f209cc0: 0x0000000000000009 0x000000000000000a    (参数9,参数10)
0x16f209cd0: 0x00000001edd8024b 0x0000000101910330
0x16f209ce0: 0x000000016f209d10 0x00000001b88dd550

swift方法调用

override func viewDidLoad() {
    super.viewDidLoad()
    test(x1: 1, x2: 2, x3: 3, x4: 4, x5: 5, x6: 6, x7: 7, x8: 8, x9: 9, x10: 10)
}
func test(x1:Int, x2:Int, x3:Int, x4:Int, x5:Int, x6:Int, x7:Int, x8:Int, x9:Int, x10:Int) {
    let a = 16
    let b = 17
    let c = 18
    let d = 19
    print("\(a+b+c+d)")
}

其参数传递规则如下:参数1,参数2,...参数n, self. 内存如下:

完整的栈帧
—————————————————————  栈顶
0x16b4c5ae0: 0x0000000125e08870 0x0000000000000000
0x16b4c5af0: 0x000000016b4c5b40 0x152a63810c8704a4
0x16b4c5b00: 0x000000010c8683af 0x000000000000000e
0x16b4c5b10: 0x0000000000000008 0x000000000c868310
0x16b4c5b20: 0x0000000000000009 0x000000000000000a
0x16b4c5b30: 0x0000000000000004 0x0000000000000005
0x16b4c5b40: 0x0000000000000006 0x0000000000000007
0x16b4c5b50: 0x0000000000000008 0x0000000125e08870
0x16b4c5b60: 0x0000000000000003 0x0000000000000002
0x16b4c5b70: 0x0000000000000001 0x0000000125e08870 (self)
0x16b4c5b80: 0x000000000000000a 0x0000000000000009 (参数10,参数9)
0x16b4c5b90: 0x0000000000000008 0x0000000000000007 (参数8,参数7)
0x16b4c5ba0: 0x0000000000000006 0x0000000000000005 (参数6,参数5)
0x16b4c5bb0: 0x0000000000000004 0x0000000000000003 (参数4,参数3)
0x16b4c5bc0: 0x0000000000000002 0x0000000000000001 (参数2,参数1)
——————————————————————————————    栈底

OC对象内存模型:

typedef struct objc_object {
   Class isa;
} *id;

每个 Objective-C 对象都有相同的结构,如图:

0F948E37-DD18-4E55-AC4A-D31DF9C5D23F.png

不论是 普通的OC对象,还是类对象,元类对象,或者Block对象,第一个8字节都是isa.

swift对象内存模型:

class Human {
    var age: Int?
    var name: String?
    var nicknames: [String] = [String]()
}

swift中所有分配在堆上的东西都是一个HeapObject。我们看看HeapObject的定义:

/** 我们看到其实就两个变量,一个metadata,和一个InlineRefCounts */
struct HeapObject{
 /// This is always a valid pointer to a metadata object.
 HeapMetadataconst*metadata;
 InlineRefCounts refCounts;
};

E9521453-2C27-4652-945D-5DB2292312BD.png

例如 swfit Data对象的内存如下:

3FDF8D97-629F-456F-86B9-D4CBE278D3E8.png

注意字节顺序: (lldb) x/100xg 0x00000001703f0200 0x1703f0200: 0x46466751704a4159 0x6a744e4636516944 由于是以xg表示,因此每8个字节组成一个64位 16进制数字,例如: 0x46466751704a4159, 其实际表示的字节顺序为: 59,41,4a, 70, 51,67, 46, 46。