[Investor Relations]  |  官方微博

C/C++培训

美国上市公司 · 亿元级外企Java培训企业

  • 全国服务监督电话400-111-8989
C++培训 > 学习笔记 > IOS nonatomic,retain,weak,strong用法详解
  • IOS nonatomic,retain,weak,strong用法详解

    发布:C++培训  来源:学习笔记  时间: 2017年05月22日

  • 本文用作对nonatomic和atomic,retain,weak,strong,assgin;还有深拷贝和浅拷贝,还是copy和strong修饰字符串的区别,一些IOS知识点的纪录,学习。还有非常多的不足,感谢您的斧正!...

  • iOS @preporty属性修饰词用法详解

    本文用作对nonatomic和atomic,retain,weak,strong,assgin;还有深拷贝和浅拷贝,还是copy和strong修饰字符串的区别,一些IOS知识点的纪录,学习。还有非常多的不足,感谢您的斧正!


    背景

    ARC苹果新引用了 strong 与 weak 对象变量属性。

    ARC引入了新的对象的新生命周期限定,即 零弱引用。如果零弱引用指向的对象被deallocated的话,零弱引用的对象会被自动设置为nil。

    1. weak, assgin, retain, strong


    @property(strong) MyClass *myObject; 相当于 @property(retain) MyClass *myObject; @property(weak) MyOtherClass *delegate; 相当于 @property(assign) MyOtherClass *delegate;

    强引用与弱引用的广义区别:

           强引用也就是我们通常所讲的引用,其存亡直接决定了所指对象的存亡。如果不存在指向一个对象的引用,并且此对象不再显示列表中,则此对象会被从内存中释放。

           弱引用除了不决定对象的存亡外,其它与强引用相同。即使一个对象被持有无数个若引用,只要没有强引用指向他,那麽其还是会被清除。

    那么ARC新引用的 weak 与 strong 对象变量属性,与之前的 assgin ,retain 有什么区别呢?

    • 简单讲 strong 等同 retain

      当然在声明Block属性务必用copy,在非ARC下不copy的Block会在栈中,ARC中的Block都会在堆上的。

      [想详细了解的铜须可以看看这篇文章](xxxxx)

    • weak比assign多了一个功能,当对象消失后自动把指针变成nil

    再说下 assign 修饰对象

    声明

    @interface ViewController () @property (nonatomic,weak) NSDate *weakDate; @property (nonatomic,assign) NSDate *assignDate; @property (nonatomic,strong) NSDate *strongDate; @end

    实现

    _strongDate = [NSDate date]; _weakDate = _strongDate; _assignDate = _strongDate; NSLog(@"%p, %p, %@", _strongDate, &_strongDate, _strongDate); NSLog(@"%p, %p, %@", _weakDate, &_weakDate, _weakDate); NSLog(@"%p, %p, %@", _assignDate, &_assignDate, _assignDate); _strongDate = nil; NSLog(@"strong属性: %@", _strongString); NSLog(@"weak属性: %@", _weakDate); NSLog(@"assign属性: %@", _assignDate);

    打印结果

    Text1[1383:70114] 0x60800000c110, 0x7ff970e05338, 2017-04-02 02:33:16 +0000 Text1[1383:70114] 0x60800000c110, 0x7ff970e05328, 2017-04-02 02:33:16 +0000 Text1[1383:70114] strong属性:(null) Text1[1383:70114] weak属性:(null) (lldb)

    从结论当中我们不难看出,当我们调用已经被赋nil的assgin修饰的对象属性时,编辑器会报错,weak属性则打印(null),这里便证实了assgin和weak变量属性的区别。

    weak 修饰对象时候,当对象被释放掉后,指针会指向 nil

    strong 修饰对象时候,当对象被释掉后,指针会指向 nil

    assgin 修饰对象时候,当对象被释掉后,会产生悬空指针,再次调用会导致程序崩溃。

    atomic 和 nonatomic


    我们直接看例子

    //@property(nonatomic, retain) UITextField *userName; //系统生成的代码如下: - (UITextField *) userName { return userName; } - (void) setUserName:(UITextField *)userName_ { [userName_ retain]; [userName release]; userName = userName_; }

    而 atomic 版本的要复杂一些:

    //@property(retain) UITextField *userName; //系统生成的代码如下: - (UITextField *) userName { UITextField *retval = nil; @synchronized(self) { retval = [[userName retain] autorelease]; } return retval; } - (void) setUserName:(UITextField *)userName_ { @synchronized(self) { [userName release]; userName = [userName_ retain]; } }

    简单来说,就是 atomic 会加一个锁来保障线程安全,并且引用计数会 +1,来向调用者保证这个对象会一直存在。假如不这样做,如有另一个线程调 setter,可能会出现线程竞态,导致引用计数降到0,原来那个对象就释放掉了。

    要注意那个锁并不能『保证线程安全』。

    浅拷贝与深拷贝


    关于这一块我是直接从网上直接摘抄过来的,然后自己做的验证,有兴趣的同学去看原文的铜学可以去文章末尾。

    对象拷贝有两种方式:浅复制和深复制。顾名思义,浅复制,并不拷贝对象本身,仅仅是拷贝指向对象的指针;深复制是直接拷贝整个对象内存到另一块内存中。

    再简单些说:浅复制就是指针拷贝;深复制就是内容拷贝。

    • 浅复制(shallow copy):在浅复制操作时,对于被复制对象的每一层都是指针复制.

    • 深复制(one-level-deep copy):在深复制操作时,对于被复制对象,至少有一层是深复制。

    • 完全复制(real-deep copy):在完全复制操作时,对于被复制对象的每一层都是对象复制。

    浅复制与深复制


    集合的浅复制有非常多种方法。当你进行浅复制时,会向原始的集合发送retain消息,引用计数加1,同时指针被拷贝到新的集合。

    现在让我们看一些浅复制的例子:

    NSArray *shallowCopyArray = [someArray copyWithZone:nil]; NSSet *shallowCopySet = [NSSet mutableCopyWithZone:nil]; NSDictionary *shallowCopyDict = [[NSDictionary alloc] initWithDictionary:someDictionary copyItems:NO];

    集合的深复制 (deep copy)


    集合的深复制有两种方法。可以用 initWithArray:copyItems: 将第二个参数设置为YES即可深复制,如

    NSDictionary shallowCopyDict = [[NSDictionary alloc] initWithDictionary:someDictionary copyItems:YES];

    如果你用这种方法深复制,集合里的每个对象都会收到 copyWithZone: 消息。如果集合里的对象遵循 NSCopying 协议,那么对象就会被深复制到新的集合。如果对象没有遵循 NSCopying 协议,而尝试用这种方法进行深复制,会在运行时出错。copyWithZone: 这种拷贝方式只能够提供一层内存拷贝(one-level-deep copy),而非真正的深复制。


    第二个方法是将集合进行归档(archive),然后解档(unarchive),如:

    NSArray *trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:oldArray]];

    系统对象的copy与mutableCopy方法 (deep copy)


    不管是集合类对象,还是非集合类对象,接收到copy和mutableCopy消息时,都遵循以下准则:

    • copy返回imutable对象;所以,如果对copy返回值使用mutable对象接口就会crash;

    • mutableCopy返回mutable对象;


    下面将针对非集合类对象和集合类对象的copy和mutableCopy方法进行具体的阐述

    1、 系统对象的copy与mutableCopy方法 (deep copy)


    系统非集合类对象指的是 NSString, NSNumber … 之类的对象。下面先看个非集合类

    immutable对象拷贝的例子

    NSString *string = @"origin"; NSString *stringCopy = [string copy]; NSMutableString *stringMCopy = [string mutableCopy];

    打印结果:

    string = origin,0x1029a4250 stringCopy = origin,0x1029a4250 stringMCopy=origin, 0x60000026af80

    通过查看内存,可以看到 stringCopy 和 string 的地址是一样,进行了指针拷贝;而 stringMCopy 的地址和 string 不一样,进行了内容拷贝;

    再看mutable对象拷贝例子

    NSMutableString *string = [NSMutableString stringWithString: @"origin"]; //copy NSString *stringCopy = [string copy]; NSMutableString *mStringCopy = [string copy]; NSMutableString *stringMCopy = [string mutableCopy]; //change value [mStringCopy appendString:@"mm"]; //crash [string appendString:@" origion!"]; [stringMCopy appendString:@"!!"];

    运行以上代码,会在第7行crash,原因就是 copy 返回的对象是 immutable 对象。注释第7行后再运行,查看内存,发现 string、stringCopy、mStringCopy、stringMCopy 四个对象的内存地址都不一样,说明此时都是做内容拷贝。

    综上两个例子,我们可以得出结论:

    在非集合类对象中:对immutable对象进行copy操作,是指针复制,mutableCopy操作时内容复制;对mutable对象进行copy和mutableCopy都是内容复制。用代码简单表示如下:

    • [immutableObject copy] // 浅复制

    • [immutableObject mutableCopy] //深复制

    • [mutableObject copy] //深复制

    • [mutableObject mutableCopy] //深复制

    2、集合类对象的copy与mutableCopy (deep copy)


    集合类对象是指NSArray、NSDictionary、NSSet … 之类的对象。下面先看集合类immutable对象使用copy和mutableCopy的一个例子:

    NSArray *array = @[@[@"a", @"b"], @[@"c", @"d"]; NSArray *copyArray = [array copy]; NSMutableArray *mCopyArray = [array mutableCopy];

    查看内容,可以看到copyArray和array的地址是一样的,而mCopyArray和array的地址是不同的。说明copy操作进行了指针拷贝,mutableCopy进行了内容拷贝。但需要强调的是:此处的内容拷贝,仅仅是拷贝array这个对象,array集合内部的元素仍然是指针拷贝。这和上面的非集合immutable对象的拷贝还是挺相似的,那么mutable对象的拷贝会不会类似呢?我们继续往下,看mutable对象拷贝的例子:

    NSMutableArray *array = [NSMutableArray arrayWithObjects:[NSMutableString stringWithString:@"a"],@"b",@"c",nil]; NSArray *copyArray = [array copy]; NSMutableArray *mCopyArray = [array mutableCopy];

    查看内存,如我们所料,copyArray、mCopyArray和array的内存地址都不一样,说明copyArray、mCopyArray都对array进行了内容拷贝。同样,我们可以得出结论:

    在集合类对象中,对immutable对象进行copy,是指针复制,mutableCopy是内容复制;对mutable对象进行copy和mutableCopy都是内容复制。但是:集合对象的内容复制仅限于对象本身,对象元素仍然是指针复制。用代码简单表示如下:

    • [immutableObject copy] // 浅复制

    • [immutableObject mutableCopy] //单层深复制

    • [mutableObject copy] //单层深复制

    • [mutableObject mutableCopy] //单层深复制


    最后说个题外的东西,在搜集资料的过程中,发现一个有可能犯错的点

    NSString *str = @"string"; str = @"newString";

    上面这段代码,在执行第二行代码后,内存地址发生了变化。乍一看,有点意外。按照 C 语言的经验,初始化一个字符串之后,字符串的首地址就被确定下来,不管之后如何修改字符串内容,这个地址都不会改变。但此处第二行并不是对 str 指向的内存地址重新赋值,因为赋值操作符左边的 str 是一个指针,也就是说此处修改的是内存地址。

    所以第二行应该这样理解:将@”newStirng”当做一个新的对象,将这段对象的内存地址赋值给str。

    我有如下的两个方法查看内存地址

    p str 会打印对象本身的内存地址和对象内容

    (lldb) p str (NSString *) $0 = 0x000000010c913680 @"a"

    po &str 则打印的是引用对象的指针所在的地址

    (lldb) po &str 0x00007fff532fb6c0

    NSString分别用Copy和Strong修饰的区别


    用@property声明的NSString(或NSArray,NSDictionary)经常使用copy关键字,为什么?如果改用strong关键字,可能造成什么问题?

    分别定义以 strong 和 copy 修饰的string

    @property (nonatomic, strong) NSString *strongString; @property (nonatomic, copy) NSString *copyedString;

    然后看下面的两个实现案例:

    1. 用不变字符串(NSString)来给它们赋值;

      NSString *string = [NSString stringWithFormat:@"abc"]; self.strongString = string; self.copyedString = string; NSLog(@"origin string: %p, %p, %@", string, &string, string); NSLog(@"strong string: %p, %p, %@", _strongString, &_strongString, _strongString); NSLog(@"copy string: %p, %p, %@", _copyedString, &_copyedString, _copyedString);

      打印结果

      origin string: 0xa000000006362613, 0x7fff5f96c658, abc strong string: 0xa000000006362613, 0x7fc3e48033e8, abc copy string: 0xa000000006362613, 0x7fc3e48033f0, abc

      这个结果我们看到 ,用 不可变字符串(NSString)给其赋值 不管是strong还是copy属性的对象,其指向的地址都是同一个,即为 NSString对象 指向的地址。

    2. 用可变字符串(NSMutableString)赋值;

      NSMutableString *string = [NSMutableString stringWithFormat:@"abc"]; self.strongString = string; self.copyedString = string; NSLog(@"origin string: %p, %p, %@", string, &string, string); NSLog(@"strong string: %p, %p, %@", _strongString, &_strongString, _strongString); NSLog(@"copy string: %p, %p, %@", _copyedString, &_copyedString, _copyedString); ///第二步 [string appendString:@"edf"]; [(NSMutableString *)_strongString appendString:@"ghjk"]; NSLog(@"origin string: %p, %p, %@", string, &string, string); NSLog(@"strong string: %p, %p, %@", _strongString, &_strongString, _strongString); NSLog(@"copy string: %p, %p, %@", _copyedString, &_copyedString, _copyedString);

      控制台结果:

      origin string: 0x61000026ad00, 0x7fff5dfde658, abc strong string: 0x61000026ad00, 0x7fb78ad04cd8, abc copy string: 0xa000000006362613, 0x7fb78ad04ce0, abc origin string: 0x61000026ad00, 0x7fff5dfde658, abcedfghjk strong string: 0x61000026ad00, 0x7fb78ad04cd8, abcedfghjk copy string: 0xa000000006362613, 0x7fb78ad04ce0, abc

      相信大家已经看到, 此时copy属性字符串已不再指向string字符串对象,而是深拷贝了string字符串,并让_copyedString对象指向这个字符串。

      这个时候我们根据第二步,我们如果去修改string字符串的话,可以看到:因为_strongString与string是指向同一对象,所以_strongString的值也会跟随着改变(需要注意的是,此时_strongString的类型实际上是NSMutableString,而不是NSString);而_copyedString是指向另一个对象的,所以并不会改变。

      NSString分别用Copy和Strong修饰的区别 :结论

      由于NSMutableString是NSString的子类,所以一个NSString指针可以指向NSMutableString对象,让我们的strongString指针指向一个可变字符串是OK的。

      而上面的例子可以看出,当源字符串是NSString时,由于字符串是不可变的,所以,不管是strong还是copy属性的对象,都是指向源对象,copy操作只是做了次浅拷贝。

      当源字符串是NSMutableString时,strong属性只是增加了源字符串的引用计数,而copy属性则是对源字符串做了次深拷贝,产生一个新的对象,且copy属性对象指向这个新的对象。另外需要注意的是,这个copy属性对象的类型始终是NSString,而不是NSMutableString,因此其是不可变的。

      这里还有一个性能问题,即在源字符串是NSMutableString,strong是单纯的增加对象的引用计数,而copy操作是执行了一次深拷贝,所以性能上会有所差异。而如果源字符串是NSString时,则没有这个问题。

      所以,在声明NSString属性时,到底是选择strong还是copy,可以根据实际情况来定。不过,一般我们将对象声明为NSString时,都不希望它改变,所以大多数情况下,我们建议用copy,以免因可变字符串的修改导致的一些非预期问题

  • 上一篇:10个经典的C语言面试基础算法及代码

    下一篇:IOS开发之Runtime常用示例总结

网站导航
2002-2018 达内时代科技集团有限公司 版权所有 京ICP证8000853号-56