本周的文章主要记录群里的同学提到的一个问题。我们先来看一下面这段代码:
UIView *view = [UIView new];
NSLog(@"%p", &view); //0x7fff59c86848
[self test: &view];
-(void)test:(UIView **)pView{
NSLog(@"%p", pView); // 0x7fff59c86840
}
代码非常简单,那么问题来了,为什么这段代码的两个打印值不一样?因为打印的这两个值都是引用(指针) view
本身的地址,按理说应该是相同的。
我们再来看下面这段代码:
NSInteger j = 1;
NSLog(@"%p", &j);//0x7fff5edc7ff8
[self test:&j];
- (void)test:(NSInteger *)pj{
NSLog(@"%p", pj);//0x7fff5edc7ff8
}
可以看到第二段代码的两个打印值是相同的,这才是我们期望中的结果啊,但是为什么第一段代码的打印值就不一样呢?两段代码的逻辑是一样,区别只有变量的类型,第一段代码中变量view
指向的是引用数据类型—对象,第二段代码中变量j
指向的是基本数据类型—整型。我们猜测这可能和变量的修饰类型有关。
当我们声明view
变量时:
UIView * view
ARC 下其实是这样的:
UIView __strong * view
而我们知道基本数据类型是用 assign
修饰的。尝试在 MRC 下运行第一段代码,果然打印的两个值是一样的。那么这其中原理是什么呢?在这里要感谢虾神和 StackOverFlow 上一位大神的帮助。
在 ARC 下当我们声明方法时:
-(void)test:(UIView **)pView;
实际上是这样的:
-(void)test: (UIView * __autoreleasing *)pView;
注意 pView 的修饰是 __autoreleasing
而不是我们想象的__strong
。因此在 ARC 下,引用类型的参数在传入方法之前会通过中间变量,从 __strong
转换成 __autoreleasing
。
所以第一段代码的在 ARC 下实际是这样的:
UIView *view = [UIView new];
NSLog(@"%p",&view);
UIView __autoreleasing * compilerTemp = view;
[self test:&compilerTemp];
view = compilerTemp;
所以,第一段代码中打印的第二个值其实是中间变量的地址。而且我们注意到,在方法调用结束后,会将中间变量 compilerTemp 的值传给 view,这是不是多此一举了呢?因为在上面这段代码中,view 和 compiler 指向的都是同一个对象,这两个变量的值肯定是一样的。但是我们考虑一下这样的情况,如果一开始 view 并没有被初始化,值为 nil 时,而中间变量 compilerTemp 在 test 方法内被赋值了,那么如果在方法调用结束不进行上述最后一步操作,view 和 compilerTemp 值就不一样了,对外表现就不一致了。