iOS知识基础篇--@property,@synthesize, nonatomic,atomic,strong,weak,copy,assign,retain详解

时间:2023-03-08 17:41:51
iOS知识基础篇--@property,@synthesize, nonatomic,atomic,strong,weak,copy,assign,retain详解

一、@property

这个关键词的唯一作用就是声明getter、setter方法接口。

二、@synthesize

实现setter、getter方法,找不到实例变量则主动创建一个。

三、nonatomic与atomic

       atomic原子操作,提供多线程安全,setter方法会发生变化;基本来讲是防止在写未完成的时候被另外一个线程读取,造成数据错误,但是这种操作的弊端就是非常消耗内存资源,苹果手机的内存资源相当宝贵,所以没有使用多线程间的通讯编程,用nonatomic是一个不错的选择。

       指出访问器不是原子操作,而默认地,访问器是原子操作。也就是说
,在多线程环境下,解析的访问器提过一个队属性的安全访问,从获取器得到的返回值或者通过设置器设置的值可以一次完成,即便是别的线程也正在对其进行方未能。如果没有指定nonatomic,在自己管理内存的环境中,解析的访问器保留并自动释放返回的值,如果指定了nonatomic,那么访问器只是简单的返回这个值。

四、assign、retain、copy,这是三个关键词得好好说一下。

如:NSString * house = [[NSString alloc] initWithFormat:@"The house is mine"];
首先在堆上分配一段内存来存储@"The house is mine", 假设内存地址为:0X000 内容则为:@"The house is mine"

然后在栈上分配一段内存存储house,假设地址为0XFFFF,内容就是0X0000。

1、assign:NSString * myHouse = [house assign];

此时house和myHouse完全相同,地址都为0XFFFF,内容为0X0000,也就是说myHouse只是house的一个别名,对任何一个操作就等于对另一个操作,因此retainCount不需要增加。

所有属性都 默认 assign ,通常用于标量(简单变量 int , float , CGRect 等)

一种典型情况是用在对对象没有所有权的时候,通常是 delegate ,避免造成死循环(如果用 retain 的话会死循环)

2、retain:NSString * myHouse = [house retain];

此时myHouse的地址不再是0XFFFF,可能是0XEEEE,但内容为0X0000,所以说house和myHouse都可以管理"The
house is mine"所在的内存,因此retainCount需要增加1,

3、copy:NSString * myHouse = [house copy];

此时会在堆上重新开辟一段内存存放@"The house is mine",假设内存地址为:0X1111,内容则为:@"The house is mine"

同时会在栈上为myHouse分配内存,假设地址为:0XBBBB,内容为:0X1111,也就是说myHouse是一个新的对象,与house没有任何关系,很明显,retainCount增加1,

属性必须是 objc 对象,拥有对象所有权,必须在 dealloc 中 release 一次。且属性必须实现 NSCopying 协议

一般常用于 NSString 类型

五、strong与weak


参考文献:  iOS ARC 完全指南


提示:本文中所说的"实例变量"即是"成员变量","局部变量"即是"本地变量"



5-1、简介


ARC是自iOS 5之后增加的新特性,完全消除了手动管理内存的烦琐,编译器会自动在适当的地方插入适当的retain、release、autorelease语句。你不再需要担心内存管理,因为编译器为你处理了一切


注意:ARC 是编译器特性,而不是 iOS 运行时特性(除了weak指针系统),它也不是类似于其它语言中的垃圾收集器。因此 ARC 和手动内存管理性能是一样的,有时还能更加快速,因为编译器还可以执行某些优化




5-2、原理


ARC 的规则非常简单:只要还有一个变量指向对象,对象就会保持在内存中。当指针指向新值,或者指针不再存在时,相关联的对象就会自动释放。这条规则对于实例变量、synthesize属性、局部变量都是适用的




5-3、strong指针


控制器中有个文本输入框框属性

 

  1. @property (nonatomic, assign) IBOutlet UITextField *nameField;  

 

 


1.如果用户在文本框中输入mj这个字符串


iOS知识基础篇--@property,@synthesize, nonatomic,atomic,strong,weak,copy,assign,retain详解


那么就可以说,nameField的text属性是NSString对象的指针,也就是拥有者,该对象保存了文本输入框的内容


iOS知识基础篇--@property,@synthesize, nonatomic,atomic,strong,weak,copy,assign,retain详解

 


2.如果执行了如下代码

 

 

  1. NSString *name = self.nameField.text;  

一个对象可以有多个拥有者,在上面代码中,name变量同样也是这个NSString对象的拥有者,也就是有两个指针指向同一个对象

 


iOS知识基础篇--@property,@synthesize, nonatomic,atomic,strong,weak,copy,assign,retain详解


3.随后用户改变了输入框的内容,比如


iOS知识基础篇--@property,@synthesize, nonatomic,atomic,strong,weak,copy,assign,retain详解


此时nameFeild的text属性就指向了新的NSString对象。但原来的NSString对象仍然还有一个所有者(name变量),因此会继续保留在内存中


iOS知识基础篇--@property,@synthesize, nonatomic,atomic,strong,weak,copy,assign,retain详解


4.当name变量获得新值,或者不再存在时(如局部变量方法返回时、实例变量对象释放时),原先的NSString对象就不再拥有任何所有者,retain计数降为0,这时对象会被释放


如,给name变量赋予一个新值

 

 

  1. name = @;  

iOS知识基础篇--@property,@synthesize, nonatomic,atomic,strong,weak,copy,assign,retain详解

 


我们称name和nameField.text指针为"Strong指针",因为它们能够保持对象的生命。默认所有实例变量和局部变量都是Strong指针


5-4、weak指针


weak型的指针变量仍然可以指向一个对象,但不属于对象的拥有者


1.执行下面的代码

 

  1. __weak NSString *name = self.nameField.text;  

iOS知识基础篇--@property,@synthesize, nonatomic,atomic,strong,weak,copy,assign,retain详解

 


name变量和nameField.text属性都指向同一个NSString对象,但name不是拥有者




2.如果文本框的内容发生变化,则原先的NSString对象就没有拥有者,会被释放,此时name变量会自动变成nil,称为空指针


iOS知识基础篇--@property,@synthesize, nonatomic,atomic,strong,weak,copy,assign,retain详解


weak型的指针变量自动变为nil是非常方便的,这样阻止了weak指针继续指向已释放对象,避免了野指针的产生,不然会导致非常难于寻找的Bug,空指针消除了类似的问题




3.weak指针主要用于“父-子”关系,父亲拥有一个儿子的strong指针,因此父亲是儿子的所有者;但为了阻止所有权循环,儿子需要使用weak指针指向父亲。典型例子是delegate模式,你的ViewController通过strong指针(self.view)拥有一个UITableView, UITableView的dataSource和delegate都是weak指针,指向你的ViewController


iOS知识基础篇--@property,@synthesize, nonatomic,atomic,strong,weak,copy,assign,retain详解


5-5、strong和weak指针的使用注意


1.下面代码是有问题的:

 

 

  1. __weak NSString *str = [[NSString alloc] initWithFormat:@];  

  2. "%@"  

str是个weak指针,所以NSString对象没有拥有者,在创建之后就会被立即释放。Xcode还会给出警告("Warning:
Assigning retained object to weak variable; object will be released
after assignment")

 


2.一般的指针变量默认就是strong类型的,因此一般我们对于strong变量不加__strong修饰,以下两行代码是等价的:

 

  1. NSString *name = self.nameField.text;  

  2. __strong NSString *name = self.nameField.text;  

 


3.属性可以是strong或weak,写法如下

  • weak一般应用: UI控件
  • strong一般应用: OC对象类型(NSArray、NSDate、NSNumber、模型类)一个对象只要有强指针引用着,就不会被销毁

  1. @property
    @property (nonatomic, weak) id delegate;  

4.以下代码在ARC之前是可能会行不通的,因为在手动内存管理中,从NSArray中移除一个对象时,这个对象会发送一条release消息,可能会被立即释放。随后NSLog()打印该对象就会导致应用崩溃

 

 

 

  1. id obj = [array objectAtIndex:];  

  2. ];  

  3. "%@", obj);  

在ARC中这段代码是完全合法的,因为obj变量是一个strong指针,它成为了对象的拥有者,从NSArray中移除该对象也不会导致对象被释放

四.属性( @property )与成员变量的那些事 :

  • 属性对成员变量扩充了存取方法 .
  • 属性默认会生成带下划线的成员变量 .
  • 早期的 Xcode 不支持自动合成成员变量的存取方法 , 所以古老的iOS工程师是愤怒的 .
  • 后来 Xcode 智能了一点 , 可以用 @synthesize 关键字自动合成成员变量的存取方法 , 此时的iOS工程师是郁闷的 .
  • 现在 Xcode 会在我们声明属性时自动合成存取方法 , 连@synthesize都不用写了 , 这样iOS工程师彻底解放了 .
  • 顺便提一下 @dynamic , 这个关键字是告诉编译器存取方法在运行时会有的 . 也可以说 @dynamic 是工程师自己来实现成员变量的存取方法 , @synthesize 是让 Xcode 帮你生成存取方法 .

属性中的修饰词 - 我的理解 :

  • assign ( ARC/MRC )

    1.这个修饰词是直接赋值的意思 , 整型/浮点型等数据类型都用这个词修饰 .
    2.如果没有使用 weak strong retain copy 修饰 , 那么默认就是使用 assign 了. ( 它们之间是有你没我的关系 )
    3.当然其实对象也可以用 assign 修饰 , 只是对象的计数器不会+1 . ( 与 strong 的区别 )
    4.如果用来修饰对象属性 , 那么当对象被销毁后指针是不会指向 nil 的 . 所以会出现野指针错误 . ( 与weak的区别 )

  • weak ( ARC )

    1.弱指针是针对对象的修饰词 , 就是说它不能修饰基本数据类型 .
    2.weak 修饰的对象计数器不会+1 , 也就是直接赋值 .
    3.弱引用是为打破循环引用而生的 .
    4.它最被人所喜欢的原因是 它所指向的对象如果被销毁 , 它会指向 nil . 而 nil 访问什么鬼都不会报野指针错误 .

  • strong ( ARC )

    1.直接赋值并且计数器 +1 .
    2.在 ARC 里替代了 retain 的作用 .

  • retain ( MRC )

    1.release 旧对象( 旧对象计数器 -1 ) , retain 新对象( 新对象计数器 +1 ) , 然后指向新对象 .
    2.在set方法里面是这样的 :

      if (_dog) {
    [_dog release];
    }
    _dog = [dog retain];
  • copy ( ARC/MRC )

    1.copy 在 MRC 时是这样做的 release 旧对象( 旧对象计数器 -1 ) , copy 新对象( 新对象计数器 +1 ) , 然后指向新对象 .(新对象是指最终指向的那个对象,不管深拷贝还是浅拷贝)
    1.1在set方法里面是这样的 :

      if (_dog) {
    [_dog release];
    }
    _dog = [dog copy];

    2.copy 在 ARC 时是这么干的 copy 新对象( 新对象计数器 +1 ) , 然后指向新对象 .
    2.1在set方法里面是这样的 :

      _dog = [dog copy];

    3.使用注意 :
    3.1 修饰的属性本身要不可变的 . 例如 NSMutableArray 采用 copy 修饰 , 添加元素表面上可以 一到运行就崩溃了 , 因为 copy 过后实际上成了NSArray了 . ( 队友 , 我们不吭你 )
    3.2 遵守 NSCopying 协议的对象使用 .

  • nonatomic ( ARC/MRC )

    1.不对set方法加锁 .
    2.性能好
    3.线程不安全

  • atomic ( ARC/MRC )

    1.原子属性就是对生成的 set 方法加互斥锁 @synchronized(锁对象) .

    @synchronized(self) {
    _delegate = delegate;
    }

    2.需要消耗系统资源 .
    3.互斥锁是利用线程同步实现的 , 意在保证同一时间只有一个线程调用 set 方法 .
    4.其实还有 get 方法 , 要是同时 set 和 get 一起调用还是会有问题的 . 所以即使用了 atomic 修饰 还是不够安全 .

  • readonly

    1.让 Xcode 只生成get方法 .
    2.不想把暴露的属性被人随便替换时 , 可以使用 .

  • readwrite

    1.让 Xcode 生成get/set方法 .
    2.不用 readonly 修饰时 , 默认就是 readwrite .

  • getter/setter 的自定义方法名 .

    1.一般对于 有/无 是/否 等这样的属性 , getter 方法名前面加个 is 会显得通俗易懂 .

    @property (nonatomic, getter = isFinish, setter = setFinish) BOOL finish;