說說OC參數傳遞的那些坑

有一些原本我們認為很基礎的,而且很理所當然的,在實踐之后才發現,麻蛋原來是這樣

回顧一下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~

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容