值传递
最近突然遇到了指针的指针,又陷入了疑惑中,最近两天就学习、复习了一些相关的东西记录下来,使自己慢慢清醒吧!
一、直接引用
程序对变量的读写操作,实际上是对变量所在的存储空间进行写入或取出数据
int a ;
a=20;
系统会自动将变量名a转换为变量的存储地址,根据地址找到变量a的存储空间,然后再将数据20以2进制的形式放入变量a的存储空间中。
通过变量名引用变量,由系统自动完成变量名和其存储地址之间的转换,称为变量的"直接引用"方式
二、指针
变量a的地址存放另一个变量b中,通过变量b间接引用变量a,间接读写变量a的值为间接引用;
int a = 10;
int * b;// "*"是用来说明b是指针变量
b = &a ;
* b = 20;//"*"是指针运算符,代表根据指针变量b存储的变量a的地址,去访问a的存储空间。
用来存放变量地址的变量,称为指针变量。这里b就是指针变量,指针变量b存放的是变量a的地址,也可以说指针变量b指向变量a 。
三、指针的使用
- 1、交换a,b的值
- (void)viewDidLoad{
[super viewDidLoad];
int a = 12;
int b = 20;
[self swap:a b:b];
NSLog(@"a= %d b=%d",a,b);
}
- (void)swap:(int)a b:(int)b{
int temp;
temp = a;
a =b;
b=temp;
}//通过普通的变量实现交互,在无法在方法内部实现。需要通过指针间接实现
指针实现
- (void)viewDidLoad{
[super viewDidLoad];
int a = 12;
int b = 20;
[self swap:&a b:&b];
NSLog(@"a= %d b=%d",a,b);
}
- (void)swap:(int *)a b:(int *)b{
int temp;
temp = * a;
* a = *b;
*b=temp;
}
- 2、实现多个值返回
- (void)viewDidLoad{
[super viewDidLoad];
int sum;//和
int minus;//差
int a = 12;
int b =21;
sum=[self sumAndMinus:a v2:b minus:&minus];
NSLog(@"a+b=%d a-b=%d",sum,minus);
}
- (int)sumAndMinus:(int)v1 v2:(int)v2 minus:(int *)minus{
*minus = v1-v2;//通过指针在函数内部修改方法外的值
return v1+v2;
}
四、形参和实参
1、函数在没有被调用的时候,括号里的形参,并没有分配内存单元。
2、函数被调用的时候,形参分配内存单元,实参将其值赋值给形参,这称之为函数参数的传递。
3、形参分配的内存单元和实参是不相干的,形参分配内存单元时与实参是独立的内存单元,但是实参和形参内寸单元里的值是一样的。
五、打印地址
NSString *name=@"Lucy";
NSLog(@"%p",name);//打印对象的内存地址
NSLog(@"%p",&name);//打印指针自己的内存地址
六、值传递
对象方法被调用时,系统会给形参分配内存单元;值传递的过程其实就是对实参内存单元里的内容进行拷贝赋给形参的内存单元;形参内存单元里存储值和实参传递的值一样;当形参内存单元存储的值发生改变时,实参内存单元存储的值也不会改变。
- (void)viewDidLoad {
[super viewDidLoad];
int a = 12;
[self testInta:a];
NSLog(@"%d",a);
}
- (void)testInta:(int)b{
//进入方法,系统会给形参b分配地址为d0x7ffee7bc89的内存空间,并将实参的值拷贝份存储到内存0x7ffee7bc89ec中。
b= 20;//系统找到形参b的存储空间将数据20存到地址为d0x7ffee7bc89的内存空间中,形参和实参是两个不同的独立内存单元,因此实参的值不会改变
}
七、指针传递
指针传递其实也是个值传递,只是复制的是指针(内存地址)
形参为指针时,被实参传值后,形参内存单元里存储的地址与实参一样,即指向同一块内存单元.
- (void)viewDidLoad {
[super viewDidLoad];
NSString *name=@"Lucy";
NSLog(@"%p",name);
[self sendstring:name];
NSLog(@"%@",name);
}
- (void)sendstring:(NSString *)string{
NSLog(@"%p",name);
string = @"jack";
}
代码分析:执行了NSString *name=@"Lucy";后系统内存会在常量区分配一个内存空间(0x109d9a068)给字符串对象存储@"Lucy"常量。在栈区分配内存空间给变量name来存储字符串对象的内存地址0x109d9a068。也就是name指针指向内存地址为0x109d9a068的存储区域。执行了[self sendstring:name];之后系统开始分配内存空间给形参来存储实参的地址0x109d9a068,此时指针string和name指向的是同一存储区域。执行string = @"jack";这句系统会分配内存空间(0x10aeb60a8)给字符常量 @"jack",并把地址赋给形参的存储单元,此时指针string指向的是 0x10aeb60a8这块存储区域和name指向了不同的存储区域,且互不影响。
总结:值传递不会改变方法外的参数值。
八、二级传递(指针的指针)
- (void)viewDidLoad{
[super viewDidLoad];
NSString * __autoreleasing string = @"name";
NSLog(@"%p",string);
[self sendstring:&string];
NSLog(@"%@",string);
}
- (void)sendstring:(NSString **)str{
*str = @"20";//"*"代表根据str存储的地址(因为是传递的地址,这里的地址也就是实参变量的地址: 0x10eded068)去访问实参的存储空间,将@“20”的地址0x7ffeecabb9d8存储到实参的存储空间里,这样实参指针变变量就指向了字符串@“20”
NSLog(@"%p",str);
}
实用一
[array enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
*stop=YES;//通过指针在方法内部改变外部的变量
}];
实用一
NSString *path=[[NSBundle mainBundle]pathForResource:@"123.json" ofType:nil];
NSData *data=[NSData dataWithContentsOfFile:path];
NSError *error=nil;
[NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&error];//传指针变量error的地址,通过指针在方法内部改变外部的变量
九、开发案例
- 1、传递自定义对象
- (void)viewDidLoad { [super viewDidLoad]; Student *s=[[Student alloc]init]; [self student:s]; NSLog(@"%@",s.name);// lucy } - (void)student:(Student *)s{ s.name=@"lucy"; }
- 2、传递数组对象
- (void)viewDidLoad { [super viewDidLoad]; NSMutableArray * array=[NSMutableArray array]; [self array:array]; NSLog(@"%@",array); // ( 1) } - (void)array:(NSMutableArray *)array{ [array addObject:@"1"]; }
- 3、数组元素的拷贝
需求:一个对象数组array,然后想将这个数组拿出来用,并保持原始数据不被改变,开始想简单啊于是下了以下代码:- (void)viewDidLoad { [super viewDidLoad]; self.historyArray=[NSMutableArray array]; NSMutableArray *array=[NSMutableArray array]; for (int i=0; i<3; i++) { Student *s=[[Student alloc]init]; s.name=@"1"; [array addObject:s]; } [self modleArray:array]; Student *s=[array firstObject]; NSLog(@"%@",s.name); } - (void)modleArray:(NSArray *)array{ NSArray *arr=array; [arr enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { Student *s=obj; s.name=@"hhhhh"; [self.historyArray addObject:s]; }]; }
然打印一下 NSLog(@"%@",s.name);原始数据被修改了。
理性分析一下why?应该是指针传递,方法里的arr和外部的array指向的是同一块存储区域,在遍历arr的同时修改了arr内部的元素,当然就是修改了array啊。那么我们深深的拷贝一下 NSArray *arr=[array copy];经测试虽然数组地址的确变了但数组里面的元素地址还是没变,还是会收到影响。最后的最后发现苹果有这样一个初始化数组的方法 NSArray *arr=[[NSArray alloc]initWithArray:array copyItems:YES];果然好用。
纠正:
[super viewDidLoad];
self.historyArray=[NSMutableArray array];
NSMutableArray *array=[NSMutableArray array];
for (int i=0; i<3; i++) {
Student *s=[[Student alloc]init];
s.name=@"1";
[array addObject:s];
}
NSLog(@"%p",array);
[self modleArray:array];
Student *s=[array firstObject];
NSLog(@"%@",s.name);
}
- (void)modleArray:(NSArray *)array{
NSArray *arr=[[NSArray alloc]initWithArray:array copyItems:YES];//注意Item要遵守<NSCopying>,并实现- (nonnull id)copyWithZone:(nullable NSZone *)zone
NSLog(@"%p",arr);
[arr enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
Student *s=obj;
s.name=@"hhhhh";
[self.historyArray addObject:s];
}];
}
参考文章:http://www.cnblogs.com/mjios/archive/2013/03/16/2963645.html#3869766
https://www.jianshu.com/p/d53431351bd5
https://www.jianshu.com/p/1dc7c31fa06f