由于这几天一直在学习ViewController之间的数据传输方法,学着学着就有了疑问:为什么在前向传输时(forward)可以使用属性传值,而后向传输(backward)时不能再使用,为了弄清楚这个问题,搜了很多文章,大部分都是在讲传输方法的使用,没有找到原因,但是根据蛛丝马迹找到了strong和weak这样的关键字,由于对这样的关键词还不了解,所以又专门来了解它们来了,看了下面这篇文章算是有了一个初步的认识,本来我想转化为自己的语言再描述一遍,但发现没有原文写的具体,所以就只好全文复制过来了,以防原网站无法访问,然后再贴上原链接供详细的了解。当然了,还有其他的参考链接就都贴在这里吧:
https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/EncapsulatingData/EncapsulatingData.html#//apple_ref/doc/uid/TP40011210-CH5-SW3
这篇文章也解释的很清楚,并且应该算是下面这篇的补充:http://www.cocoawithlove.com/2009/07/rules-to-avoid-retain-cycles.html

参考链接:http://www.informit.com/articles/article.aspx?p=1856389&seqNum=5

Strong and Weak References

So far, we’ve said that anytime a pointer variable stores the address of an object, that object has an owner and will stay alive. This is known as a strong reference. However, a variable can optionally not take ownership of an object it points to. A variable that does not take ownership of an object is known as a weak reference.

A weak reference is useful for an unusual situation called a retain cycle. A retain cycle occurs when two or more objects have strong references to each other. This is bad news. When two objects own each other, they will never be destroyed by ARC. Even if every other object in the application releases ownership of these objects, these objects (and any objects that they own) will continue to exist by virtue of those two strong references.

Thus, a retain cycle is a memory leak that ARC needs your help to fix. You fix it by making one of the references weak. Let’s introduce a retain cycle in RandomPossessions to see how this works. First, we’ll give BNRItem instances the ability to hold another BNRItem (so we can represent things like backpacks and purses). In addition, a BNRItem will know which BNRItemholds it. In BNRItem.h, add two instance variables and accessors

@interface BNRItem : NSObject
{NSString *itemName;NSString *serialNumber;int valueInDollars;NSDate *dateCreated;BNRItem *containedItem;BNRItem *container;
}+ (id)randomItem;- (id)initWithItemName:(NSString *)namevalueInDollars:(int)valueserialNumber:(NSString *)sNumber;- (void)setContainedItem:(BNRItem *)i;
- (BNRItem *)containedItem;- (void)setContainer:(BNRItem *)i;
- (BNRItem *)container;

Implement the accessors in BNRItem.m.

- (void)setContainedItem:(BNRItem *)i
{containedItem = i;// When given an item to contain, the contained// item will be given a pointer to its container[i setContainer:self];
}- (BNRItem *)containedItem
{return containedItem;
}- (void)setContainer:(BNRItem *)i
{container = i;
}- (BNRItem *)container
{return container;
}

In main.m, remove the code that populated the array with random items. Then create two new items, add them to the array, and make them point at each other.

#import <Foundation/Foundation.h>
#import "BNRItem.h"int main (int argc, const char * argv[])
{@autoreleasepool {NSMutableArray *items = [[NSMutableArray alloc] init];for (int i = 0; i < 10; i++) {BNRItem *p = [BNRItem randomItem];[items addObject:p];}for (BNRItem *item in items)NSLog(@"%@", item);BNRItem *backpack = [[BNRItem alloc] init];[backpack setItemName:@"Backpack"];[items addObject:backpack];BNRItem *calculator = [[BNRItem alloc] init];[calculator setItemName:@"Calculator"];[items addObject:calculator];[backpack setContainedItem:calculator];NSLog(@"Setting items to nil...");items = nil;}return 0;
}

Here’s what the application looks like now:

Figure 3.7 RandomPossessions with retain cycle

Per our understanding of memory management so far, both BNRItems should be destroyed along with their instance variables when items is set to nil. Build and run the application. Notice that the console does not report that these objects have been destroyed.

This is a retain cycle: the backpack and the calculator have strong references to one another, so there is no way to destroy these objects. Figure 3.8 shows the objects in the application that are still taking up memory once items has been set to nil.

Figure 3.8 A retain cycle!

The two BNRItems cannot be accessed by any other part of the application (in this case, themain function), yet they still exist in their own little world doing nothing useful. Moreover, because they cannot be destroyed, neither can the other objects that their instance variables point to.

To fix this problem, one of the pointers between the BNRItems needs to be a weak reference. To decide which one should be weak, think of the objects in the cycle as being in a parent-child relationship. In this relationship, the parent can own its child, but a child should never own its parent. In our retain cycle, the backpack is the parent, and the calculator is the child. Thus, the backpack can keep its strong reference to the calculator (containedItem), but the calculator’s reference to the backpack (container) should be weak.

To declare a variable as a weak reference, we use the __weak attribute. In BNRItem.h, change thecontainer instance variable to be a weak reference.

__weak BNRItem *container;

Build and run the application again. This time, the objects are destroyed properly.

Every retain cycle can be broken down into a parent-child relationship. A parent typically keeps a strong reference to its child, so if a child needs a pointer to its parent, that pointer must be a weak reference to avoid a retain cycle.

A child holding a strong reference to its parent’s parent also causes a retain cycle. So the same rule applies in this situation: if a child needs a pointer to its parent’s parent (or its parent’s parent’s parent, etc.), then that pointer must be a weak reference.

It’s good to understand and look out for retain cycles, but keep in mind that they are quite rare. Also, Xcode has a Leaks tool to help you find them. We’ll see how to use this tool in Chapter 21.

An interesting property of weak references is that they know when the object they reference is destroyed. Thus, if the backpack is destroyed, the calculator automatically sets its containerinstance variable to nil. In main.m, make the following changes to see this happen.

NSMutableArray *items = [[NSMutableArray alloc] init];BNRItem *backpack = [[BNRItem alloc] init];
[backpack setItemName:@"Backpack"];
[items addObject:backpack];BNRItem *calculator = [[BNRItem alloc] init];
[calculator setItemName:@"Calculator"];
[items addObject:calculator];[backpack setContainedItem:calculator];NSLog(@"Setting items to nil...");
items = nil;backpack = nil;NSLog(@"Container: %@", [calculator container]);calculator = nil;

Build and run the application. Notice that after the backpack is destroyed, the calculator reports that it has no container without any additional work on our part.

A variable can also be declared using the __unsafe_unretained attribute. Like a weak reference, an unsafe unretained reference does not take ownership of the object it points to. Unlike a weak reference, an unsafe unretained reference is not automatically set to nil when the object it points to is destroyed. This makes unsafe unretained variables, well, unsafe. To see an example, change container to be unsafe unretained in BNRItem.h.

__unsafe_unretained BNRItem *container;

Build and run the application. It will most likely crash. The reason? When the calculator was asked for its container within the NSLog function call, it obligingly returned its value – the address in memory where the non-existent backpack used to live. Sending a message to a non-existent object resulted in a crash. Oops.

As a novice iOS programmer, you won’t use __unsafe_unretained. As an experienced programmer, you probably won’t use it, either. It exists primarily for backwards compatibility: applications prior to iOS 5 could not use weak references, so to have similar behavior, they must use __unsafe_unretained.

Be safe. Change this variable back to __weak.

__weak BNRItem *container;

Here’s the current diagram of RandomPossessions. Notice that the arrow representing thecontainer pointer variable is now a dotted line. A dotted line denotes a weak (or unsafe unretained reference). Strong references are always solid lines.

Figure 3.9 RandomPossessions with retain cycle avoided

----------------------------分割线-----------------------

下面是我的理解:

在理解retain cycle形成的过程中有几个原则需要特别注意一下:

1.当Object的referenced counter为0时它会调用自己的dealloc()来企图销毁自己,也就是释放这块内存;

2.在调用dealloc()中会调用它的child object的release方法,目的是将它的child object的referenced counter减一,否则它把自己释放了,但是它曾经使用过的child object的referenced counter一直不能跟着更新,那么它的child object就会一直不能释放,因为referenced counter不能为0,而这是不能允许存在的情况,这叫做memory leak。

其实在上面的这篇原文中也提及到了一点,就是当使用关键字__weak来修改一个变量时,除了不会影响referenced object‘s counter(即它所指向的那个对象的引用计数。如果使用__strong的话就会被所指向的对象中的ounter加1)之外,还有一个智能的地方,就是当这个变量本身的referenced counter变为0时,我们伟大的ARC会自动给这个变量赋上nil,也就是回收了。关于这一点的解释在上面的参考中有实际的代码可以测试,我就贴个图吧:

注意看当打印[calculator container]的时候,实际的打印信息显示了Container:(null)。

关于这一点在apple的官方文档中也有描述,链接地址为:点击打开链接, 具体的内容为下面的描述,我好想什么都没干,就光copy和plast了。

use strong references for their synthesized instance variables. To declare a weak reference, add an attribute to the property, like this:

@property (weak) id delegate;

Note: The opposite to weak is strong. There’s no need to specify the strong attribute explicitly, because it is the default.

Local variables (and non-property instance variables) also maintain strong references to objects by default. This means that the following code will work exactly as you expect:

    NSDate *originalDate = self.lastModificationDate;
    self.lastModificationDate = [NSDate date];
    NSLog(@"Last modification date changed from %@ to %@",
                        originalDate, self.lastModificationDate);

In this example, the local variable originalDate maintains a strong reference to the initial lastModificationDate object. When thelastModificationDate property is changed, the property no longer keeps a strong reference to the original date, but that date is still kept alive by theoriginalDate strong variable.

Note: A variable maintains a strong reference to an object only as long as that variable is in scope, or until it is reassigned to another object or nil.

看完后得知,本地变量其实就是默认的strong类型,如果正巧需要一个strong类型的就没有必要再用"__strong"来修改了,直接写就得了。

只有当需要__weak时是需要特别修改的。

对上面的示例代码解释一下,以防我日后哪天给忘了。

定义了一个originalDate的指针,默认是strong类型的,就是说它会把被它引用的对象的referenced counter加1.

定义完了之后被self.lastModificationDate初始化,而lastModificationDate其实也是指向一个具体的对象的,比如叫xxDate吧,它描述的应该是时间日期这些东西。这样初始化一下子之后,xxDate的referenced counter就会因为originalDate的引用被加1。假设此时xxDate只有它们两个引用,那么referenced counter就是2。然后呢。lastModficicationDate被重新赋值了,重新赋值的意思就是说它不再指向xxDate了,而是指向了一个新的yyDate,这个yyDate代表的是另一个时间日期的描述,这样呢,原来的xxDate的referenced counter就由2减为1了,就是说此时只有originalDate在引用它关心它。如果此时originalDate不是strong类型,而是weak类型的,那么一旦lastModificationDate被重新赋值,那么xxDate的referenced counter就变为0了,因为weak类型的指针不会增加它引用的对象的referenced counter,即在一开始给originalDate赋值的时候,xxDate的referenced counter就一直是1,没有因为多了个引用它的originalDate而增加1,应为originalDate是weak类型的。

明白了上面的这个特点之后,接着就又看到了一个有意思的情况,就是如何给weak类型的变量初始化。可能你会想当然的觉得很简单,上例中初始化代码为:

NSDate *originalDate = self.lastModificationDate;

修改为weak后为:

NSDate __weak *originalDate = self.lastModificationDate;

注意了,如果此时lastModificationDate指向的那个object的referenced counter不是0,那么originalDate是能够指向lastModificationDate指向的那个object的。如果用一个referenced counter是0的object来初始化__weak修改的指针会怎么样?

示例代码:

NSObject * __weak someObject = [[NSObject alloc] init];

当alloc出来一个示例之后发现没人能使它的referenced counter由0增加1,它又会自动被ARC销毁的,且someObject也会被ARC赋值nil。其实质就是someObject就眼睁睁的看着到嘴的鸭子又飞了,这是一种怎么的样感受呢,我们不得而知。

所以呢,人们就想到了一种简单的办法,就是不管someObject能不能引用别人,它自己得先保证自己别被ARC灭了,referenced counter变为0就会被灭,不让它为0就行了,如果不为0呢?就是得找个人假装引用它就行了。

 NSObject *cachedObject = self.someWeakProperty;           // 1
    if (cachedObject) {                                       // 2
        [someObject doSomethingImportantWith:cachedObject];   // 3
    }                                                         // 4
    cachedObject = nil;  

定义一个strong类型的cachedObject来引用someWeakProperty,这样就保证后者的referenced counter不为0了,自保完成,接下来再去干革命。革命完成后就自杀吧。

好了,就先写到这里吧,目前刚看到这里。

-------------分割线-----------

好吧,再接着编辑一下,接下来是使用copy属性的情况,参考的文档还是apple官方的原文。点击打开链接

我的用了原文中的示例代码,源码和运行结果如下图:

原谅我没有把源码以文本的方式贴在帖子里,主要是太懒了,而且没有这样有说服力。

从h文件可以看到声明了两个property,一个是strong属性的firstName,哦对了,strong是默认的,可以不写的;另一个是copy属性的cfirstName;

补充一点,就是不是任何类型都可以使用copy属性的,能够使用它的需要遵从NSCopying协议:

Note: Any object that you wish to set for a copy property must support NSCopying, which means that it should conform to the NSCopying protocol. Protocols are described in Protocols Define Messaging Contracts. For more information on NSCopying, see NSCopying or the Advanced Memory Management Programming Guide.

当使用nameString给它俩赋值的时候可以看出来,它们指向的object地址不同:firstName指向了与nameString相同的对象,而cfirstName则指向了另外一个地址,这个地址应该就是nameString的副本的地址,即nameString的copy的地址,这样一来的话就随便nameString变成什么样了,sfirstName的值都不会改变了。我想等我多个月份以后再回来看看我写篇日记我肯定会说:你行不行啊,连这样的内容都要记下来写成一篇日记,跟每天记三餐都吃什么有什么区别。但是现在的能力不够,只能记一下这些基本的知识。

strong和weak引用的讲解相关推荐

  1. iOS 5中的strong和weak关键字

    from:http://blog.csdn.net/yhawaii/article/details/7291134 iOS 5 中对属性的设置新增了strong 和weak关键字来修饰属性(iOS 5 ...

  2. iOS 中strong,weak,copy,assign区别

    1:ARC环境下,strong代替retain.weak代替assign 2:weak的作用:在ARC环境下,,所有指向这个对象的weak指针都将被置为nil.这个T特性很有用,相信很多开发者都被指针 ...

  3. ARC指南 strong和weak指针

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

  4. Objective-C中,ARC下的 strong和weak指针原理解释

    Objective-C中,ARC下的 strong和weak指针原理解释 提示:本文中所说的"实例变量"即是"成员变量","局部变量"即是& ...

  5. 无聊说说 strong VS weak

    关于strong VS weak strong:保持这个对象一直在堆栈中,直到我的指针不再指向它: 当我把我的指针设为nil的时候,这样我就不再指向它了: 或者当我自己已经被从堆栈中移出的时候,这样我 ...

  6. x264 编码器选项分析 (x264 Codec Strong and Weak Points) 2

    文章目录: x264 编码器选项分析 (x264 Codec Strong and Weak Points) 1 x264 编码器选项分析 (x264 Codec Strong and Weak Po ...

  7. x264 编码器选项分析 (x264 Codec Strong and Weak Points) 1

    文章目录: x264 编码器选项分析 (x264 Codec Strong and Weak Points) 1 x264 编码器选项分析 (x264 Codec Strong and Weak Po ...

  8. weak引用表原理探究

    一.weak引用实现原理探究 首先对<Xcode 10 下如何调试objc4-723>建立的objc源码调试工程表示感谢! 地址:https://www.jianshu.com/p/9e0 ...

  9. (ios实战):retain,copy,assign及autorelease ,strong,weak

    一,retain, copy, assign区别 1. 假设你用malloc分配了一块内存,并且把它的地址赋值给了指针a,后来你希望指针b也共享这块内存,于是你又把a赋值给(assign)了b.此时a ...

最新文章

  1. kernel 下串口serial输入输出控制,屏蔽log的输出
  2. keil用c语言怎么编辑器,用keil软件编写单片机程序的步骤
  3. Hadoop入门(十)Mapreduce高级shuffle之Sort和Group
  4. 虚拟机 VMware Workstation12 安装OS X 系统
  5. 案例 github_2019年12月Github上最热门的Java开源项目,速来围观!
  6. python异步asy_Python 异步编程之asyncio【转载】
  7. Spring DI注入规则
  8. PHP学习笔记 - 在Eclipse中使用XDebug调试代码 | Using XDebug debug code in eclipse
  9. eclipse集成python插件
  10. (HDU)1098 -- Ignatius's puzzle(Ignatius的困惑)
  11. jQuery 添加 input 表单提交 无数据
  12. P3174 [HAOI2009] 毛毛虫(树形DP)
  13. 扇形涂色问题(Python)
  14. python捕获屏幕的标准库_Python标准库urllib2的使用和获取网站状态举例
  15. 医院挂号系统c语言yan,C语言版医院管理系统
  16. 用Electron开发的Windows快捷启动工具:Dawn Launcher
  17. [句子成分] 三、宾语
  18. 学生成绩abcde怎样划分_中考成绩中ABCD等级代表的分数段是什么?
  19. 曾光谈防控禽流感:亚洲国家应反思禽畜饲养方式
  20. 计算机组成原理(第三版)唐朔飞-第八章CPU的结构和功能-课后习题

热门文章

  1. 好看好玩的韩剧电视- 厄运的恋人
  2. 训练样本制作--Annotating Object Instances with a Polygon-RNN
  3. u盘乱码怎么办?看他怎么恢复的(不花一分钱)
  4. 智慧磐石工程项目系统组成
  5. 笔试真题(笔记)—JAVA基础
  6. torch.mul() 和 torch.mm() 的区别
  7. Android第一行代码——第八章多媒体
  8. ATX安装及简单使用
  9. Python二级--奖学金评选
  10. Python入门习题大全——城市名