说说OC参数传递的那些坑

作者: 纸简书生 | 来源:发表于2016-04-01 18:44 被阅读3528次

    有一些原本我们认为很基础的,而且很理所当然的,在实践之后才发现,麻蛋原来是这样

    回顾一下c语言的参数传递

    C语言中的参数传递

    1、传值方式
    原理:形参和实参占不同内存单元,传递的实际上是实参变量或表达式的一个拷贝副本,将这个副本值传给形参,形参内存单元内容保存的正是这个副本值,相当于给形参进行初始化,形参的值发生变化也不会传回给实参,因此是单向传递。
    例如:

     void increase(int x)
     {
     x++;
     }
    

    当在主函数中调用上面这个函数时,会在ncrease函数内存栈中为形参x分配一个内存单元,然后把实参的值传到这个内存单元中,相当于给形参初初化了,然后形参x自增1,它改变的仅仅是形参内存单元中的内容,而实参内存单元中的内容并没有改变。当被调用函数执行完毕后,形参所分配的内存单元也被收回。

    2、传地址方式
    原理:和传值方式一样,当调用函数时也要为形参分配内存,被调函数执行完毕后也要收回内存。不同的是传递的是实参变量地址的拷贝值,而不是实参变量的值,在被调函数中对地址所指对象的操作会改变实参的值。但是形参的内容即存放的实参变量地址并不会改变。
    ** OC中传递对象就是用这种方式呀,但是并没有被改变 我很疑惑**
    例如:

     void increase(int * x)
     {
    (*x)++;
     }
     int main()
     {
     int i,*x;
     i=10;
     *x=20;
     increase(&i);//如果定义的不是指针变量,那变要加上个取址号&,当然如果形参实参是数组的话,直接用数组名即可,因为数组名本身代表的就是数组的首地址
     increase(x);
     }
    

    主函数调用被调函数后,主函数中的i和*x的值都会改变。

    OC参数传递

    NSString

    先看看测试的代码

    
    - (void)viewDidLoad {
      [super viewDidLoad];
     
      NSString *str = @"viewDidLoad";
      NSLog(@"viewDidLoad 改变前:%@",str);
      [self changNSString:str];
      NSLog(@"viewDidLoad 改变后:%@",str);
     
    }
    - (void)changNSString:(NSString *)str {
      NSLog(@"changNSString 改变前:%@",str);
      str = @"changNSString";
      NSLog(@"changNSString 改变后:%@",str);
    }
    

    大家觉得NSLog(@"viewDidLoad 改变后:%@",str);会打印什么.
    ** 可以改变str的值么**
    经过测试,** str ** 经过函数changNSString的修改后根本没有改变

    
    **2016-04-01 18:01:06.762 CommentDemo[8807:186874] viewDidLoad ****改变前****:viewDidLoad**
    **2016-04-01 18:01:06.762 CommentDemo[8807:186874] changNSString ****改变前****:viewDidLoad**
    **2016-04-01 18:01:06.762 CommentDemo[8807:186874] changNSString ****改变后****:changNSString**
    **2016-04-01 18:01:06.762 CommentDemo[8807:186874] viewDidLoad ****改变后****:viewDidLoad**
    

    这完全不符合常理呀!这是传递的地址呀.

    NSMutableString

    之后我换可变的字符串对象测试了一下:

    - (void)viewDidLoad {
      [super viewDidLoad];
     
      NSMutableString *mStr = [NSMutableString stringWithString:@"viewDidLoad"];
      NSLog(@"viewDidLoad 改变前:%@",mStr);
      [self changeMutableString:mStr];
      NSLog(@"viewDidLoad 改变后:%@",mStr);
    }
    
    - (void)changeMutableString:(NSMutableString *)mStr {
      NSLog(@"changNSMutableString 改变前:%@",mStr);
      mStr = [NSMutableString stringWithString:@"changeMutableString"];
      NSLog(@"changNSMutableString 改变后:%@",mStr);
    }
    

    测试结果

    
    **2016-04-01 18:07:33.382 CommentDemo[8856:191232] viewDidLoad ****改变前****:viewDidLoad**
    **2016-04-01 18:07:33.383 CommentDemo[8856:191232] changNSMutableString ****改变前****:viewDidLoad**
    **2016-04-01 18:07:33.383 CommentDemo[8856:191232] changNSMutableString ****改变后****:changeMutableString**
    **2016-04-01 18:07:33.383 CommentDemo[8856:191232] viewDidLoad ****改变后****:viewDidLoad**
    

    ** 同样没有改变 **

    NSMutableArray

    后来我换了可变的数组

    - (void)viewDidLoad {
      [super viewDidLoad];
     
      NSMutableArray *mArray = [NSMutableArray arrayWithObjects:@"1",@"2", nil];
      for (NSString *str in mArray) {
        NSLog(@"viewDidLoad 改变前 %@",str);
      }
     
      [self changeMutableArray:mArray];
     
      for (NSString *str in mArray) {
        NSLog(@"viewDidLoad 改变后 %@",str);
      }
     
     
    }
    - (void)changeMutableArray:(NSMutableArray *)array {
      [array addObject:@"3"];
      for (NSString *str in array) {
        NSLog(@"changeMutableArray %@",str);
      }
    }
    

    测试结果

    
    **2016-04-01 18:16:24.259 CommentDemo[8925:196443] viewDidLoad ****改变前**** 1**
    **2016-04-01 18:16:24.259 CommentDemo[8925:196443] viewDidLoad ****改变前**** 2**
    **2016-04-01 18:16:24.259 CommentDemo[8925:196443] changeMutableArray 1**
    **2016-04-01 18:16:24.260 CommentDemo[8925:196443] changeMutableArray 2**
    **2016-04-01 18:16:24.260 CommentDemo[8925:196443] changeMutableArray 3**
    **2016-04-01 18:16:24.260 CommentDemo[8925:196443] viewDidLoad ****改变后**** 1**
    **2016-04-01 18:16:24.260 CommentDemo[8925:196443] viewDidLoad ****改变后**** 2**
    **2016-04-01 18:16:24.260 CommentDemo[8925:196443] viewDidLoad ****改变后**** 3**
    

    ** 值他妈的改变了 **

    经过测试,可以知道在OC中,看似对象是声明为指针类型(* Str),由于传递的时候我们没有用取地址(&).结果根本没有改变. 我开始怀疑传递的是不是地址了.是不是因为自始至终没有用到过取地址符.

    然后自己改为了如下方式,就能够改变传进来的值了

    能够改变传递的值

    NSString正常改变

    - (void)viewDidLoad {
      [super viewDidLoad];
     
      NSString *str = @"viewDidLoad";
      NSLog(@"viewDidLoad 改变前:%@",str);
      [self changNSString:&str];
      NSLog(@"viewDidLoad 改变后:%@",str);
     
     
    }
    - (void)changNSString:(NSString **)str {
      NSLog(@"changNSString 改变前:%@",*str);
      *str = @"changNSString";
      NSLog(@"changNSString 改变后:%@",*str);
    }
    

    测试结果

    
    **2016-04-01 18:27:33.139 CommentDemo[8993:203983] viewDidLoad ****改变前****:viewDidLoad**
    **2016-04-01 18:27:33.139 CommentDemo[8993:203983] changNSString ****改变前****:viewDidLoad**
    **2016-04-01 18:27:33.139 CommentDemo[8993:203983] changNSString ****改变后****:changNSString**
    **2016-04-01 18:27:33.139 CommentDemo[8993:203983] viewDidLoad ****改变后****:changNSString**
    

    总结

    看到这种状况我能说服自己的就是:
    NSString *str = @"viewDidLoad";这样的定义在OC中其实就是类似于c语言的普通变量而已,而不是指针变量.智商有点不够用.

    原因或许很简单

    看似和c语言地址传递一样,同样是*传递。但c语言拿到这个指针之后取了指针指向的内容并改变了内容。而oc中我们习惯直接str =✘。让这个指针指向了新的地方。并没有改变函数外面原来指针指向的内容。这点特别重要,指针传过来那个str本质是值传递,相当于copy了一份。所以在change函数里面的str并不是外面的str。哈哈。今天有点短路。

    还是一点,c语言中改变了传进来指针原来指向的内容,而oc中只改变了新指针指向的地方。



    2016-4-11
    谈谈自定义对象,给对象赋值又是怎样的.
    经过测试,给对象的属性复制能够在另一个函数改变其属性
    看看测试结果

    **2016-04-11 14:19:47.708 CommentDemo[3027:130157] name: xiaomao, age: 17**
    **2016-04-11 14:19:47.709 CommentDemo[3027:130157] name: newxiaomao, age: 89**
    **2016-04-11 14:19:47.709 CommentDemo[3027:130157] name: newxiaomao, age: 89**
    

    测试代码

    - (void)createCat {
        Cat *cat = [Cat new];
        cat.name = @"xiaomao";
        cat.age = @"17";
        NSLog(@"%@",cat);
        [self catObjectTest:cat];
        NSLog(@"%@",cat);
    }
    - (void)catObjectTest:(Cat *)cat {
        cat.name = @"newxiaomao";
        cat.age = @"89";
        NSLog(@"%@",cat);
    }
    

    分析

    当我在调用对象的属性进行赋值的时候,其实是调用getter/setter方法,通过这样的赋值,属性值肯定改变呀.O(∩_∩)O~

    相关文章

      网友评论

      • 烟影很美:刚刚想到了这个问题, 跳过实验直接看结论:smile:
      • sonialiu:能总结一下为什么NSMutableArray值改变了吗
      • 0da97359b208:关于值传递你们总结的很全面了、
        还有就是str = @"changNSString",这句代码并没有在堆中分配内存
        NSString的几种定义方式还是很有区别的,有的方式是会在堆中分配内存
        若你的str在堆中分配内存了,则不会出现你上面的那种情况
        归根结底,这个坑在于 str = @"changNSString" 没有在堆中分配内存
        iOS_aFei:@咚咚qian @"changNSString" 是字符串常量,位于全局数据区。
        要加油啊小和尚:能解答下为什么没有在堆中分配内存吗?
      • AnLuoRidge:我也总结一下:形参与实参,如果是指针类型的话,它们本身的地址是不一样的,但值相同,即都指向同一个对象地址。
        形参若想影响实参,最简单的方法是直接操作形参指针指向对象的属性 / 成员变量。
        最彻底的方法则是改变实参指针所指向的地址,而要这样做的前提是要能拿到实参指针。方法是把形参类型改为 (AnyObject **),而实参传入 &AnyObject。这样,形参实际上就是一个指向实参指针的指针,*形参后就拿到了实参指针。
      • 戴仓薯:这么简单的错误不该犯呀。NSString* str 传递的就是一个指针,不是变量。但当你操作 str = @"changNSString" 的时候,你是把参数的指针指向另一个变量@"changeNSString"了,而传参之前的指针还指着原来那个变量呢。所以当然不可能变了。

        而你用 mutableString 测试的时候,mStr = ... 跟上面完全一样,它当然不会变;如果是 [mStr appendString:@"..."] 就肯定会变了。

        这点跟 C 语言是一致的,你在 C 语言里把传参后的指针赋给别人,原来那个指针和原来那块变量都不会变。比如 increase 方法你把 (*x)++; 改成 int b =3; x = &b 那原来的 i 和 x 也都不会变。如果是 java 也是一样……
        纸简书生: @戴仓薯 对,这是关键,非常关键。哈哈!
        戴仓薯:@搬运工和设计师 是的,总结得很到位~ 关键就是传递指针的时候,其实相当于是复制了一份,传过去的指针跟原来的不是同一个了~
        纸简书生:非常感谢,还是我基础不够扎实,谢谢指教。我来总结下吧!
        第一指针其实也是值传递,所以里面的指针变量和外面的指针变量不是同一个,只是在没有给里面的指针赋值前他们都指向同一个地方。所有可以通过取里面指针内容改变原来指针的内容,因为他们指向同一个地方。而没变的原因是我根本没有改变传进来指针指向的内容,而是让它指向一个新的位置。😂😂😂😂忽略了传过来的指针其实也是值传递。

      本文标题:说说OC参数传递的那些坑

      本文链接:https://www.haomeiwen.com/subject/jveylttx.html