OC中对指针的深度理解

指针,顾名思义,在现实生活中就是指明特定的值或者特定的方向,如生活中常见的湿度表上的指针,指南针上的指针,一个指明当前湿度的值,一个指明南北方向,但是指针本身并不是值也不是方向,只是一种表示工具。

那么在计算机编程语言中的指针,也是一样的功能,只是在编程语言中的指针,既不是指明方向,也不是表明值,而是指向一块内存地址,即指向对象的对象。

什么叫指向对象的对象?就好像每个中国公民都有一个身份证,如果把人比作对象,那么身份证上的身份证号码,就是这个人的地址。

计算机中,每一个对象都要占用一块内存空间,因此每一个对象都会对应一个内存地址。就好像每一个人都有身份证,每个身份证上都有一个身份证号码一样。

因此,指针也是一个对象,指针也有它自己的内存地址,是一个特殊的对象,负责特殊的工作,就好像总经理助理。你想见总经理没门,有什么事都经过总经理助理传达,找到了总经理助理就相当于找到了总经理。

所以,总经理有身份证,总经理助理也有身份证,说白了,也就是指向另一个人的人,或者代表另一个人的人。

那么在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,所以在定义中和实际应用中有些许不同,绕的弯比较深,自行理解吧。。。。

你可能感兴趣的:(OC中对指针的深度理解)