03 managing memory with arc

45
使 ARC 管理内存 范圣刚,[email protected] www.tfan.org

Upload: tom-fan

Post on 05-Dec-2014

336 views

Category:

Technology


8 download

DESCRIPTION

使用 ARC 管理内存,强引用,ruo'yin'yon

TRANSCRIPT

Page 2: 03 Managing Memory with ARC

•学习在 iOS 中内存是如何被管理的,以及⾃自动引⽤用计数背后的概念

Page 3: 03 Managing Memory with ARC

堆(The Heap)

•所有的Objective-C 对象都存在堆中

• alloc

•memory chunk -> 包括对象实例变量需要的空间

Page 4: 03 Managing Memory with ARC

对象⼤大⼩小•NSDate

• double类型 -> 存储从⼀一个固定参考点开始过去的秒数

• isa 指针 -> 从 NSObject 继承

• double + pointer = 8 + 4 = 12 字节

• BNRItem

•四个指针(isa, itemName, serialNumber, dateCreated)

•⼀一个int (valueInDollars)

• 4*4 + 4 = 20 字节

Page 5: 03 Managing Memory with ARC

BNRItem 和 NSDate 实例的字节数

Page 6: 03 Managing Memory with ARC

栈(The Stack)

•本地变量,也就是在⽅方法内声明的变量的值都存储在栈内•⽅方法执⾏行 -> 分配内存块(frame)

• pushed on & popped off

Page 7: 03 Managing Memory with ARC

指针变量和对象所有权•指针变量传递它们指向的对象的所有权•⽅方法的本地变量指向⼀一个对象,⽅方法拥有(own)指向的对象

•对象有指向另⼀一个对象的实例变量,我们说包含指针的对象 own 被指向的对象

Page 8: 03 Managing Memory with ARC

RandomPossessions 中的对象和指针• main ⽅方法中的 items 本地变量指向 NSMutableArray,NSMutableArray 属于

main 函数

• NSMutableArray -> BNRItems

• BNRItem -> 实例变量指向的对象

Page 9: 03 Managing Memory with ARC

内存管理•有限的内存•不再使⽤用的对象要释放,还需要的不要销毁•对象所有者的理念帮助我们确定⼀一个对象是否应该被销毁:•没有所有者的对象应该被释放(⽆无法被发送消息),内存泄露。

•⾄至少拥有⼀一个所有者的对象不应该被释放。(过早释放)

Page 10: 03 Managing Memory with ARC

新建 Quiz 项⺫⽬目

Page 11: 03 Managing Memory with ARC

对象是如何失去所有者的?•指向对象的变量更改成指向另外⼀一个对象•指向对象的变量被设置成 nil

•指向对象的变量⾃自⾝身被销毁

Page 12: 03 Managing Memory with ARC

指针更改当 itemName 的值从“Rusty Spork”字符串的地址更改成指向“Shiny Spork”字符串的地址时,“Rusty Spork”字符串就失去了所有者。

Page 13: 03 Managing Memory with ARC

指针设成 nil

• serialNumber = nil;

Page 14: 03 Managing Memory with ARC

指针变量⾃自⾝身被销毁•实例变量,在堆⾥里作为对象的⼀一部分•对象销毁,实例变量也被销毁•实例变量指向的对象就失去了所有者•本地变量,存在于⽅方法的 frame 中•⽅方法执⾏行完毕,frame 被弹出堆栈

•本地变量指向的对象就失去了所有者

Page 15: 03 Managing Memory with ARC

在集合对象中的对象•位于数组中的对象,其拥有者是集合对象• [items removeObject:p];

•被移除的对象(p)失去所有者

Page 16: 03 Managing Memory with ARC

连锁反应•⼀一个对象可以拥有其他对象,其他对象⼜又可以拥有对象

•单个对象的析构可能会引起(失去所有者,对象析构,释放内存)⼀一系列连锁反应

Page 17: 03 Managing Memory with ARC

RandomPossessions 的变量和指针

Page 18: 03 Managing Memory with ARC

RandomPossession 的连锁反应1.在 main.m 中,打印完 BNRItem 之后,我们把

items = nil;

2.数组失去所有者被销毁3.NSMutableArray 中指向 BNRItem 的指针也被销毁4.BNRItem失去所有者,也被销毁5.BNRItem 实例变量被销毁,实例变量指向的对象失去所有者,也被销毁

Page 19: 03 Managing Memory with ARC

重写 dealloc// BNRItem.m- (void)dealloc{ NSLog(@"被销毁: %@", self);}

// main.m for (BNRItem *item in items) { NSLog(@"%@", item); }

NSLog(@"设置 items 为 nil ..."); items = nil;

Page 20: 03 Managing Memory with ARC

强引⽤用和弱引⽤用•任何时候只要⼀一个对象的地址被保存在⼀一个指针变量中,对象就有它的拥有者,并且保持存活,这就叫做 strong reference

•有时⼀一个变量并不拥有它指向的对象,称作 weak reference

• retain cycle: 当两个或者更多对象互相之间具有 强引⽤用,当两个对象互相 own 对⽅方时,永远不会通过 ARC 销毁。

Page 21: 03 Managing Memory with ARC

Retail Cycle Demo

•让 BNRItem 可以持有另外⼀一个 BNRItem

•同时 BNRItem 也可以知道被谁所持有

•步骤:• BNRItem.h 中增加两个实例变量和 accessors

• BNRItem.m 中实现 accessors

•修改 main.m, 把原来的随机⽣生成 item 去掉,改成⽣生成两个 item,⼀一个包含另外⼀一个。

Page 22: 03 Managing Memory with ARC

BNRItem.h: 实例变量和 accessor 声明// BNRItem.h@interface BNRItem : NSObject{ NSString *itemName; NSString *serialNumber; int valueInDollars; NSDate *dateCreated; // 增加两个 BNRItem 实例变量 BNRItem *containedItem; BNRItem *container;}// 增加 accessor- (void)setContainedItem:(BNRItem *)item;- (BNRItem *)containedItem;

- (void)setContainer:(BNRItem *)item;- (BNRItem *)container;

Page 23: 03 Managing Memory with ARC

BNRItem.m: 实现 accessors- (void)setContainedItem:(BNRItem *)item{ containedItem = item; [item setContainer:self];}

- (BNRItem *)containedItem{ return containedItem;}

- (void)setContainer:(BNRItem *)item{ container = item;}

- (BNRItem *)container{ return container;}

Page 24: 03 Managing Memory with ARC

main.m: 包含和被包含的 item

NSMutableArray *items = [[NSMutableArray alloc] init];

BNRItem *backpack = [[BNRItem alloc] init]; [backpack setItemName:@"背包"]; [items addObject:backpack]; BNRItem *calculator = [[BNRItem alloc] init]; [calculator setItemName:@"计算器"]; [items addObject:calculator]; [backpack setContainedItem:calculator]; NSLog(@"设置 items 为 nil ..."); items = nil;

Page 25: 03 Managing Memory with ARC

带 retain cycle 的 RandomPossessions

Page 26: 03 Managing Memory with ARC

带 retain cycle 的 运⾏行结果

Page 27: 03 Managing Memory with ARC

⼀一个 retain cycle

•两个 BNRItem 不会被销毁,同时它们实例变量指向的对象也⽆无法被销毁。

Page 28: 03 Managing Memory with ARC

谁应该被设成弱引⽤用?•两个 BNRItem 之中的⼀一个要设成 weak,which?

•⽗父⼦子关系

• 每⼀一个 retain cycle 都可以被分解成⽗父⼦子关系

• parent 通常保存⼀一个强引⽤用到它的 child

•所以如果 child 也需要⼀一个到 parent 的指针,这个指针必须是弱引⽤用才可以避免 retain cycle

• __weak BNRItem *container;

• parent’s parent

• Xcode Leaks ⼯工具

Page 29: 03 Managing Memory with ARC

弱引⽤用销毁的⾃自动检测•weak reference 的有趣属性

Page 30: 03 Managing Memory with ARC

__unsafe_unretained

•和弱引⽤用⼀一样,不拥有它指向的对象的所有权•和弱引⽤用不⼀一样的是,不⾃自动设成 nil

•向后兼容的需要•尽量使⽤用 __weak 替代 __unsafe_unretained

Page 31: 03 Managing Memory with ARC

避免了 retain cycle 的 RandomPosessions

Page 32: 03 Managing Memory with ARC

属性•实例变量 -> 声明和实现⼀一对 accessor ⽅方法

•使⽤用 properties 替代 -> 简化输⼊入,代码更加清晰易读

Page 33: 03 Managing Memory with ARC

声明属性•属性在类的接⼝口中声明,形式:• @peoperty NSString *itemName;

•声明⼀一个属性,相当于给实例变量隐式声明了⼀一个同名的 setter 和 getter ⽅方法。• - (void)setItemName:(NSString *)str;

• - (NSString *)itemName;

Page 34: 03 Managing Memory with ARC

property 的 attributes

•属性的属性 -> accessors ⽅方法的⾏行为

•在 @property 指⽰示符后⾯面的括号中声明•@property (nonatomic, readwrite, strong) NSString

*itemName;

•共有三个 peoperty attributes, 每个 attributes 有两个或三个options,其中⼀一个是 default 所以不需要显式声明。

Page 35: 03 Managing Memory with ARC

property 的第⼀一个 attribute

• 2 个选项•nonatomic

• atomic

Page 36: 03 Managing Memory with ARC

使⽤用属性替换访问器⽅方法• BNRItem.h

• demo

@property(nonatomic) BNRItem *containedItem;@property(nonatomic) BNRItem *container;

@property(nonatomic) NSString *itemName;@property(nonatomic) NSString *serialNumber;@property(nonatomic) int valueInDollars;@property(nonatomic) NSDate *dateCreated;

不幸的是nonatomic 不是默认值,因此每次都要显式声明 property 是 nonatomic 的。

Page 37: 03 Managing Memory with ARC

property 的第⼆二个 attribute

• 2 个 options

• readwrite:声明⼀一个 setter 和 getter

• readonly:只声明⼀一个 getter

•默认是:readwrite

•@property(nonatomic, readonly) NSDate *dateCreated;

@property(nonatomic) BNRItem *containedItem;@property(nonatomic) BNRItem *container;

@property(nonatomic) NSString *itemName;@property(nonatomic) NSString *serialNumber;@property(nonatomic) int valueInDollars;@property(nonatomic, readonly) NSDate *dateCreated;

Page 38: 03 Managing Memory with ARC

property 的 后⼀一个 attribute

•描述内存管理• 常⻅见的option:实例变量到它指向的对象是否有强引⽤用或弱引⽤用

•默认的 option: assign,为像 valueInDollars 这样的不指向⼀一个对象的 property 准备的。• container: weak

•其他: strong@property(nonatomic, strong) BNRItem *containedItem;@property(nonatomic, weak) BNRItem *container;

@property(nonatomic, strong) NSString *itemName;@property(nonatomic, strong) NSString *serialNumber;@property(nonatomic) int valueInDollars;@property(nonatomic, readonly, strong) NSDate *dateCreated;

Page 39: 03 Managing Memory with ARC

合成属性• synthesize: 为 accessors ⽅方法⽣生成代码

@synthesize itemName;

- (void)setItemName:(NSString *)str{ itemName = str;}

- (NSString *)itemName{ return itemName;}

•除了使⽤用 property 来声明访问器⽅方法以外,还可以 synthesize ⼀一个 property 在实现⽂文件中为 accessor

Page 40: 03 Managing Memory with ARC

BNRItem.m 中的 Synthesize

•可以在⼀一⾏行或多⾏行中 synthesize properties

@synthesize itemName;@synthesize container, containedItem, serialNumber, valueInDollars, dateCreated;

Page 41: 03 Managing Memory with ARC

在 accessor 中执⾏行额外⼯工作的情况

•我们增加的任意实现会覆盖 synthesize 的版本 :)

•⼀一般我们都会 synthesize 我们在头⽂文件中声明的 property,除⾮非 getter 和 setter 都有附加⾏行为。

- (void)setContainedItem:(BNRItem *)item{ containedItem = item; // 设置被包含的 item 的时候,同时要给该 item 设置⼀一个包含它的容器 [item setContainer:self];}

- (void)setContainedItem:(BNRItem *)item{ containedItem = item; }

Page 42: 03 Managing Memory with ARC

实例变量和属性•更进⼀一步的代码清晰化•默认:synthesized property 将会访问同名的实例变量

•举例:itemName 属性访问 itemName 实例变量• itemName ⽅方法返回 itemName 实例变量的值

• setItemName ⽅方法改变 itemName 实例变量

•如果不存在和 synthesized property 的名字相匹配的实例变量呢?• 会⾃自动创建⼀一个

•所以既声明实例变量⼜又 synthesizing property 是冗余的。可以把实例变量声明包括⼤大括号都删除掉

Page 43: 03 Managing Memory with ARC

复制•注意:两个指向 NSString 实例的属性

•具有mutable subclass 的类:NSString ,NSArray

•⽣生成⼀一个对象的拷⻉贝并指向,⽐比指向⼀一个可能已经有了其他 owner 的现有对象要安全些

•使⽤用 copy 属性选项替换 strong

Page 44: 03 Managing Memory with ARC

NSString Copying

@property(nonatomic, strong) BNRItem *containedItem;@property(nonatomic, weak) BNRItem *container;

@property(nonatomic, copy) NSString *itemName;@property(nonatomic, copy) NSString *serialNumber;@property(nonatomic) int valueInDollars;@property(nonatomic, readonly, strong) NSDate *dateCreated;

NSMutableString *mutableString = [[NSMutableString alloc] init];BNRItem *item = [[BNRItem alloc] initWithItemName:mutableString valueInDollars:5 serialNumber:@"4F2W7R"];

- (void)setItemName:(NSString *)str{ itemName = [str copy];}

•原有的字符串没有以任何形式被更改,没有获得也没有失去 owner ,数据也不会被更改。

Page 45: 03 Managing Memory with ARC

点号语法•虽然可⽤用,但是不建议使⽤用,避免 confusing

// 下⾯面的两⾏行是完全相等的int value = [item valueInDollars];int value = item.valueInDollars; [item setValueInDollars:5];item.valueInDollars = 5;