指针,顾名思义,在现实生活中就是指明特定的值或者特定的方向,如生活中常见的湿度表上的指针,指南针上的指针,一个指明当前湿度的值,一个指明南北方向,但是指针本身并不是值也不是方向,只是一种表示工具。
那么在计算机编程语言中的指针,也是一样的功能,只是在编程语言中的指针,既不是指明方向,也不是表明值,而是指向一块内存地址,即指向对象的对象。
什么叫指向对象的对象?就好像每个中国公民都有一个身份证,如果把人比作对象,那么身份证上的身份证号码,就是这个人的地址。
计算机中,每一个对象都要占用一块内存空间,因此每一个对象都会对应一个内存地址。就好像每一个人都有身份证,每个身份证上都有一个身份证号码一样。
因此,指针也是一个对象,指针也有它自己的内存地址,是一个特殊的对象,负责特殊的工作,就好像总经理助理。你想见总经理没门,有什么事都经过总经理助理传达,找到了总经理助理就相当于找到了总经理。
所以,总经理有身份证,总经理助理也有身份证,说白了,也就是指向另一个人的人,或者代表另一个人的人。
那么在OC中方法外向方法内传值,到底传的是什么?
第一种情况,简单变量
- (void)viewDidLoad {
[super viewDidLoad];
NSString *str = @"12345";
NSLog(@"A-%@--对象:%p--指针:%p",str,str,&str);
Object_1 *objec = [[Object_1 alloc]init];
[objec stringChangeByStr2:str];
NSLog(@"A-%@--对象:%p--指针:%p",str,str,&str);
}
- (void)stringChangeByStr2:(NSString *)str{
NSLog(@"B-%@--对象:%p--指针:%p",str,str,&str);
str = @"45678";
NSLog(@"B-%@--对象:%p--指针:%p",str,str,&str);
}
打印结果:
A-12345--对象:0x105c08068--指针:0x7ffee9ff8178
B-12345--对象:0x105c08068--指针:0x7ffee9ff8128
B-45678--对象:0x105c080a8--指针:0x7ffee9ff8128
A-12345--对象:0x105c08068--指针:0x7ffee9ff8178
由打印结果可以看出:
1、指针的地址发生改变。从A到B,即方法外到方法内,指针发生改变,因此,变量由方法外传到方法内的时候,会新建一个指针来指向目标对象,目标对象的引用计数会加一。
2、目标对象发生改变。对str赋值之后,对象的地址发生改变,说明方法内str指针的指向发生了改变,指向一块新的内存地址,这块内存地址所代表的值是字符串@“45678”,引用计数器的变化是,对象“45678”的引用计数加一,对象“ 12345”的引用计数减一。
3、赋值改变的是指针的指向,而没有直接修改对象的内容。
4、方法外的指针,在方法调用前后,没有发生改变,指针所指向的对象,也没有发生改变。
第二种情况,复合变量
- (void)viewDidLoad {
[super viewDidLoad];
Object_1 *objec = [[Object_1 alloc]init];
Object_2 *obj = [[Object_2 alloc]init];
obj.objcStr = @"09876";
NSLog(@"6-%@--对象:%p--指针:%p--%p",obj.objcStr,obj,&obj,obj.objcStr);
[objec changeValueWith:obj];
NSLog(@"6-%@--对象:%p--指针:%p--%p",obj.objcStr,obj,&obj,obj.objcStr);
}
- (void)changeValueWith:(Object_2 *)obj{
NSLog(@"5-%@--对象:%p--指针:%p--%p",obj.objcStr,obj,&obj,obj.objcStr);
obj.objcStr = @"45678";
NSLog(@"5-%@--对象:%p--指针:%p--%p",obj.objcStr,obj,&obj,obj.objcStr);
}
打印结果:
6-09876--对象:0x600000af5600--指针:0x7ffee71a2168--0x108a5e0a8
5-09876--对象:0x600000af5600--指针:0x7ffee71a20e8--0x108a5e0a8
5-45678--对象:0x600000af5600--指针:0x7ffee71a20e8--0x108a5e0e8
6-45678--对象:0x600000af5600--指针:0x7ffee71a2168--0x108a5e0e8
由打印结果可以看出:
1、目标对象的地址一直没有改变,说明对象没有被改变。
2、指针的地址改变了,方法内和方法外的地址不一样,说明方法内新建了一个指针指向目标对象,因此,这个时候,是有两个指针指向同一个对象,对象的引用计数加一。
3、执行obj.objcStr = @"45678"之后,objcStr对象的地址改变了,说明指针objcStr的指向改变了,对objcStr赋值,其实就是改变objcStr的指向,使其指向一个新的内存地址,代表一个新的对象。
4、方法外的objcStr的值也被修改。那么这种情况就打破了OC的方法只能return一个返回值的尴尬。也就是说,我想要得到调用方法之后的处理结果,并不一定就只能用返回值来返回,我可以通过直接修改变量指针的赋值来实现。
结论
从第一和第二种情况能看出,想要从一个方法中获得多个返回值,只要把需要的返回值,用一个对象包装一下,就能直接得到方法调用处理之后的结果。
那么问题来了,这种方式的原理是什么?原理就与OC的内存管理机制相关了。
在第一种情况中,
赋值之前,方法外str指针指向对象“12345”
,方法内str指针也指向“12345”
,因为这个时候目标对象的地址都是0x105c08068
。
赋值之后,方法内指针赋值“45678”
,方法内str指针指向的内存地址是0x105c080a8
,指针的指向已经改变。
这个时候,方法外的指针指向内存地址A,方法内的指针指向内存地址B,此时二者之间已经完全没有联系,两个独立的指针变量指向两个完全不一样的对象。
方法内的指针变量,在其作用域结束之后(方法的两个花括号之间),就要被release掉,即对象“45678”
的引用计数减一,变成0,此时对象地址会被回收,同时对象被销毁。
既然对象“45678”
在作用域结束之后,就被销毁了,那么,在作用域之外,自然就无法获取到对象“45678”
。
所以,从无法获取到对象“45678”的原因就能看出,获取不到是因为引用计数变为0,内存被回收了,那么如果想要拿到对象“45678”,方法就是不让引用计数变为0。
此时将目光转向第二种情况,
在这种情况下,我们在方法内修改了obj.objcStr
之后,方法外依然能拿到obj.objcStr
修改之后的值,原因也很简单,就是因为对象obj没有被释放掉。
方法外的指针指向对象obj的地址0x600000af5600
,方法内的指针也指向对象obj的地址0x600000af5600
,所以,此时的对象obj的引用计数为2。
我们修改了obj的成员变量的值,obj.objcStr = @"45678";
只是对成员变量指针重新赋值,改变了成员变量的指针指向,但是并没有改变obj的引用情况,所以此时obj的引用计数还是2,方法内的obj指针依然指向对象地址0x600000af5600
。
当方法调用结束,目标对象0x600000af5600
的引用计数减一,此时,方法外的obj指针还指着目标对象0x600000af5600
,因此,目标对象的引用计数为1,依然被持有,因此不被释放,所以在方法外依然能够访问目标对象。因为目标对象依然存在,所以,目标对象的成员变量也依然被持有,所以也不会被释放。于是就能访问到obj.objcStr
修改之后的值。
第三种情况,指针的指针
除了第二种情况之外,还有没有一种方法,把对象直接赋值给指针,但是却不被释放掉呢?
答案是当然有,那就是用指针的指针,即**point
。这种方式的原理其实和第二种情况类似,就是把想要修改的对象,再包装一层。
例如
NSString *p = @“111111”;
意思就是新建一个NSString
类型的指针变量p
,使其指向对象@"111111"
的地址,
于是就有:
1、p
代表指针对象本身
2、*p
代表指针指向的对象@"111111"
3、&p
代表指针本身的地址
那么**a
是什么呢?
例如:
NSString **a = &p;
意思就是新建一个NSString
类型的二重指针变量a
,使其指向指针对象p
的地址。
于是就有:
1、a
代表指针变量本身
2、*a
代表指针所指向的指针对象p
3、**a
代表指针指向的指针对象p
所指向的对象@"111111"
,即*p
4、&a
代表指针本身的地址
- (void)viewDidLoad {
[super viewDidLoad];
NSString *str = @"12345";
NSLog(@"1-%@--对象:%p--指针:%p",str,str,&str);
Object_1 *objec = [[Object_1 alloc]init];
[objec stringChangeByStr:&str];
}
- (void)stringChangeByStr:(NSString *__autoreleasing *)str{
NSLog(@"2\n-- *str:%p\n--str:%p\n-- &str:%p",*str,str,&str);
}
打印结果
1-12345--对象:0x10208c068--指针:0x7ffeedb74178
2
-- *str:0x10208c068(指针p指向的对象)
-- str:0x7ffeedb74168(指针p的地址)
-- &str:0x7ffeedb74118(指针a的地址)
在OC实际应用中,用指针对象p
访问p
所指向的对象,即*p
,所以在定义中和实际应用中有些许不同,绕的弯比较深,自行理解吧。。。。