OC内存管理教程之ARC(三)——与@property的羁绊

来源:互联网 发布:有源码怎么做辅助 编辑:程序博客网 时间:2024/05/02 02:01

前言:

       有了前两篇的基础,那么对内存管理已经有了大体的认识了。但知道了原理,还要明白怎么应用,前两篇使用的例子都是极其简单的,能最直观体现特性的,在做ios开发时,创建类,声明属性都更复杂,那么这时候内存管理是怎么应用呢?这篇就是来介绍这样的内容。


类Class中的属性property

        一般在ios开发中,自己实现一个继承于UIView(或者其他UI空间)的类时,声明属性都不会像前两篇的那样那么简单。不管是storyboard开发,还是纯coder,在声明属性的时候都是写成@property(.....)这样的形式,因为我们需要编译器为我们为这个属性生成set方法和get方法。

        在ios第一版中,我们为输出口同时声明了属性和底层实例变量,那时,属性是oc语言的一个新的机制,并且要求你必须声明与之对应的实例变量,例如:

@interface MyViewController :UIViewController{    UIButton *myButton;}@property (nonatomic, retain) UIButton *myButton;@end
        对于.m文件:

@synthesize myButton;
        这样写了之后,那么编译器会自动生成myButton的实例变量,以及相应的getter和setter方法。       

        而大概是在13年的时候,苹果将默认编译器从GCC转换为LLVM(low level virtual machine),从此不再需要为属性声明实例变量了。如果LLVM发现一个没有匹配实例变量的属性,它将自动创建一个以下划线开头的实例变量。因此,在这个版本中,我们不再为输出口声明实例变量。

        例如:MyViewController.h文件:

@interface MyViewController :UIViewController@property (nonatomic, retain) UIButton *myButton;@end
       在MyViewController.m文件中,如果写了@synthesize myButton;那么生成的实例变量就是myButton;如果没写@synthesize myButton;那么生成的实例变量就是_myButton。所以跟以前的用法还是有点细微的区别。当然一般都是不写的,使用生成的_myButton。那么在.m文件中可以直接的使用_myButton实例变量,也可以通过属性self.myButton.都是一样的。

       注意这里的self.myButton其实是调用的myButton属性的getter/setter方法。这与C++中点的使用是有区别的,C++中的点可以直接访问成员变量(也就是实例变量)。


  例如在oc中有如下代码
  .h文件

@interface MyViewController :UIViewController{    NSString *name;}@end
       .m文件中,self.name 这样的表达式是错误的。xcode会提示你使用->,改成self->name就可以了。因为oc中点表达式是表示调用方法,而上面的代码中没有name这个方法。
  oc语法关于点表达式的说明:"点表达式(.)看起来与C语言中的结构体访问以及java语言汇总的对象访问有点类似,其实这是oc的设计人员有意为之。如果点表达式出现在等号 = 左边,该属性名称的setter方法将被调用。如果点表达式出现在右边,该属性名称的getter方法将被调用。"
  所以在oc中点表达式其实就是调用对象的setter和getter方法的一种快捷方式, 例如:dealie.blah = greeble 完全等价于 [dealie.blah setBlah:greeble];

@property关键字

       经常可以看到类似这样的写法:

@property(nonatomic,readonly,retain) UINavigationItem *navigationItem;@property(nonatomic) BOOL hidesBottomBarWhenPushed;@property(nonatomic,readonly,retain) UINavigationController *navigationController;
       不同类别的属性,@property里的关键字也不一样。@property的常用属性关键字有nonatomic、atomic、readonly、writeonly、readwrite、assign、retain、copy、strong、weak、unsafe_unretained、nonnull、nullable、null_resettable,看着挺多的,但是经常用的也就那几个。

1、第一个位置的值,原子关键字
atomic:线程保护的,默认。
nonatomic:线程不保护的。
2、第二个位置的值,所有权关键字
assign: 默认关键字。非对象类型一般使用此关键字。
retain: 对象的引用计数+1。ARC下已经不再使用此关键字,用strong代替。
copy: 拷贝一个新的对象,新对象的引用计数+1,原对象不变。
strong: 能够维持对象的生命。

3、第三个位置的值,读写关键字
readwrite:生成get/set方法,默认。
readonly:只生成get方法。

对于第二个位置的关键字,它们和一般声明属性时所使用的修饰符有以下的对应关系:



此外,作为ios开发,常常能听到或者学习到类似这样的总结:

  • 用strong修饰OC对象、没添加到父控件上的控件
  • 用weak修饰已经被添加到父控件上的UI控件、代理对象
  • 用assign修饰基本数据类型(int float bool等)、枚举、结构体(非OC对象)、class类型
  • 用copy修饰一种情况下的NSString-->不确定赋值过程中用的是可变还是不可变字符串、block
       这几条当中,有一些总结是出于优化的目的,像UI控件是建议用weak,用stong也不会出问题。有一些是必须遵守的,否则就容易出问题,像代理对象就必须用weak,如果用了strong就会造成循环引用而产生内存泄漏。当还没有学习内存管理的时候,只要谨记这几点也可以愉快地开发简单的ios程序(有时候也是感叹苹果真的好厉害,自己实现了优异的内存管理机制,让开发的门槛降低,让初学者可以今早尝试开发的乐趣)如今学习了内存管理之后,就要剖析其内部所蕴含的原理了。本文抽取其中的几点作详细的解释。

UI控件、代理对象用weak

关于UI控件
对于UI控件,既可以写成strong也可以写成weak,出于优化的目的,写成weak最好~
@interface ViewController()@property(nonatomic,weak) UIButton *btnA;@end
使用情况一般如下
- (void)viewDidLoad {    [super viewDidLoad];    UIButton *btnA = [[UIButton alloc] init];//创建一个UIButton    _btnA = btnA;//让属性指向这个UIbutton。注意_btnA才是上面@property声明的属性    //配置btnA的各个参数    [self.view addSubview:_btnA];    }
可以用图来解释:

当调用方法[self.view addSubview:_btnA]时,整个应用程序就会参数如下的引用关系(强指针用实线表示,弱指针用虚线表示):

       可以看到UIButton对象有两个强指针指向它。viewDidLoad方法结束后,btnA就会超出作用域而废弃,只要整个app没有被关闭,那么引用关系不会断裂,UIbutton对象就还有一个强指针指向它,因此,它不会被废弃。而我们又可以随时使用_btnA访问到这个UIButton对象。其实从这幅图来看,就算声明的时候把_btnA声明成strong也不会有什么问题,不过没必要,最好声明成weak。

关于代理
一张图就可以解释为什么代理不能用strong,需要用weak了。


如果把delegate声明称strong就造成循环引用了。

OC对象用strong

@interface ViewController()@property(nonatomic,strong) NSObject *obj;@end

- (void)viewDidLoad {    [super viewDidLoad];    NSObject *obj = [[NSObject alloc] init];    _obj = obj;    }
viewDidLoad方法结束后,随着obj的指针废弃,如果_obj不是强指针,NSObject对象就会因为没有强指针指向而废弃,白创建了。

NSString、NSMutableString、NSArray、NSMutableArray用copy

       首先介绍一个简单的概念:深拷贝与浅拷贝

       浅拷贝:把指针地址复制,就是日常的等号赋值操作的效果。两个变量指向同一个对象。

       深拷贝:把指针指向的对象在内存中复制一份,然后把新产生的对象的指针赋值。两个变量指向不同对象,但这两个对象的内容一致。

        而copy关键字,就能保证等号赋值的时候,编译器帮我们做一次深拷贝。如此就能理解为什么这些对象要用copy关键字了。

        copy只是不可变拷贝,而mutableCopy是可变拷贝。比如,NSArray *arr = [modelsArray copy],那么arr是不可变的。而NSMutableArray *ma = [modelsArray mutableCopy],那么ma是可变的。

总结:

        终于把内存管理系列的三篇文章写完了,自我感觉还是挺良好的,觉得是个不错的教程。写文章的时候,也补足了自己很多原来理解上的漏洞或者缺乏的地方,对内存管理有了更深刻的认识。也希望这系列的教程能给读者们带来启发。

阅读全文
0 0
原创粉丝点击