• Share
  • Email
  • Embed
  • Like
  • Save
  • Private Content
03 Managing Memory with ARC
 

03 Managing Memory with ARC

on

  • 233 views

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

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

Statistics

Views

Total Views
233
Views on SlideShare
233
Embed Views
0

Actions

Likes
0
Downloads
2
Comments
0

0 Embeds 0

No embeds

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

    03 Managing Memory with ARC 03 Managing Memory with ARC Presentation Transcript

    • 使⽤用 ARC 管理内存范圣刚,princetoad@gmail.com,www.tfan.org
    • • 学习在 iOS 中内存是如何被管理的,以及⾃自动引⽤用计数背后的概念
    • 堆(The Heap)• 所有的Objective-C 对象都存在堆中• alloc• memory chunk -> 包括对象实例变量需要的空间
    • 对象⼤大⼩小• NSDate • double类型 -> 存储从⼀一个固定参考点开始过去的秒数 • isa 指针 -> 从 NSObject 继承 • double + pointer = 8 + 4 = 12 字节• BNRItem • 四个指针(isa, itemName, serialNumber, dateCreated) • ⼀一个int (valueInDollars) • 4*4 + 4 = 20 字节
    • BNRItem 和 NSDate 实例的字节数
    • 栈(The Stack)• 本地变量,也就是在⽅方法内声明的变量的值都存储在栈内• ⽅方法执⾏行 -> 分配内存块(frame)• pushed on & popped off
    • 指针变量和对象所有权• 指针变量传递它们指向的对象的所有权• ⽅方法的本地变量指向⼀一个对象,⽅方法拥有(own)指 向的对象• 对象有指向另⼀一个对象的实例变量,我们说包含指针 的对象 own 被指向的对象
    • RandomPossessions 中的对象和指针• main ⽅方法中的 items 本地变量指向 NSMutableArray,NSMutableArray 属于 main 函数• NSMutableArray -> BNRItems• BNRItem -> 实例变量指向的对象
    • 内存管理• 有限的内存• 不再使⽤用的对象要释放,还需要的不要销毁• 对象所有者的理念帮助我们确定⼀一个对象是否应该被销毁:• 没有所有者的对象应该被释放(⽆无法被发送消息), 内存泄露。• ⾄至少拥有⼀一个所有者的对象不应该被释放。(过早释 放)
    • 新建 Quiz 项⺫⽬目
    • 对象是如何失去所有者的?• 指向对象的变量更改成指向另外⼀一个对象• 指向对象的变量被设置成 nil• 指向对象的变量⾃自⾝身被销毁
    • 指针更改当 itemName 的值从“Rusty Spork”字符串的地址更改成指向“Shiny Spork”字符串的地址时,“RustySpork”字符串就失去了所有者。
    • 指针设成 nil• serialNumber = nil;
    • 指针变量⾃自⾝身被销毁• 实例变量,在堆⾥里作为对象的⼀一部分• 对象销毁,实例变量也被销毁• 实例变量指向的对象就失去了所有者• 本地变量,存在于⽅方法的 frame 中• ⽅方法执⾏行完毕,frame 被弹出堆栈• 本地变量指向的对象就失去了所有者
    • 在集合对象中的对象• 位于数组中的对象,其拥有者是集合对象• [items removeObject:p];• 被移除的对象(p)失去所有者
    • 连锁反应• ⼀一个对象可以拥有其他对象,其他对象⼜又可以拥有对象• 单个对象的析构可能会引起(失去所有者,对象析构,释放内存)⼀一系列连锁反应
    • RandomPossessions 的变量和指针
    • RandomPossession 的连锁反应1.在 main.m 中,打印完 BNRItem 之后,我们把 items = nil;2.数组失去所有者被销毁3.NSMutableArray 中指向 BNRItem 的指针也被销毁4.BNRItem失去所有者,也被销毁5.BNRItem 实例变量被销毁,实例变量指向的对象 失去所有者,也被销毁
    • 重写 dealloc// BNRItem.m- (void)dealloc{ NSLog(@"被销毁: %@", self);}// main.m for (BNRItem *item in items) { NSLog(@"%@", item); } NSLog(@"设置 items 为 nil ..."); items = nil;
    • 强引⽤用和弱引⽤用• 任何时候只要⼀一个对象的地址被保存在⼀一个指针变量中,对象就有它的拥有者,并且保持存活,这就叫做 strong reference• 有时⼀一个变量并不拥有它指向的对象,称作 weakreference• retain cycle: 当两个或者更多对象互相之间具有 强引⽤用,当两个对象互相 own 对⽅方时,永远不会通过 ARC 销毁。
    • Retail Cycle Demo• 让 BNRItem 可以持有另外⼀一个 BNRItem• 同时 BNRItem 也可以知道被谁所持有• 步骤: • BNRItem.h 中增加两个实例变量和 accessors • BNRItem.m 中实现 accessors • 修改 main.m, 把原来的随机⽣生成 item 去掉,改成⽣生成 两个 item,⼀一个包含另外⼀一个。
    • 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;
    • 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;}
    • 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;
    • 带 retain cycle 的RandomPossessions
    • 带 retain cycle 的 运⾏行结果
    • ⼀一个 retain cycle• 两个 BNRItem 不会被销毁,同时它们实例变量指 向的对象也⽆无法被销毁。
    • 谁应该被设成弱引⽤用?• 两个 BNRItem 之中的⼀一个要设成 weak,which?• ⽗父⼦子关系 • 每⼀一个 retain cycle 都可以被分解成⽗父⼦子关系 • parent 通常保存⼀一个强引⽤用到它的 child • 所以如果 child 也需要⼀一个到 parent 的指针,这个指 针必须是弱引⽤用才可以避免 retain cycle• __weak BNRItem *container;• parent’s parent• Xcode Leaks ⼯工具
    • 弱引⽤用销毁的⾃自动检测• weak reference 的有趣属性
    • __unsafe_unretained• 和弱引⽤用⼀一样,不拥有它指向的对象的所有权• 和弱引⽤用不⼀一样的是,不⾃自动设成 nil• 向后兼容的需要• 尽量使⽤用 __weak 替代 __unsafe_unretained
    • 避免了 retain cycle 的 RandomPosessions
    • 属性• 实例变量 -> 声明和实现⼀一对 accessor ⽅方法• 使⽤用 properties 替代 -> 简化输⼊入,代码更加清晰 易读
    • 声明属性• 属性在类的接⼝口中声明,形式:• @peoperty NSString *itemName;• 声明⼀一个属性,相当于给实例变量隐式声明了⼀一个同 名的 setter 和 getter ⽅方法。 • - (void)setItemName:(NSString *)str; • - (NSString *)itemName;
    • property 的 attributes• 属性的属性 -> accessors ⽅方法的⾏行为• 在 @property 指⽰示符后⾯面的括号中声明 • @property (nonatomic, readwrite, strong) NSString *itemName;• 共有三个 peoperty attributes, 每个 attributes 有两 个或三个options,其中⼀一个是 default 所以不需要 显式声明。
    • property 的第⼀一个 attribute• 2 个选项 • nonatomic • atomic
    • 使⽤用属性替换访问器⽅方法 • 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 的。
    • 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;
    • 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;
    • 合成属性 • synthesize: 为 accessors ⽅方法⽣生成代码@synthesize itemName;- (void)setItemName:(NSString *)str{ itemName = str;}- (NSString *)itemName{ return itemName;}• 除了使⽤用 property 来声明访问器⽅方法以外,还可以 synthesize ⼀一个 property 在实现⽂文件中为 accessor
    • BNRItem.m 中的 Synthesize • 可以在⼀一⾏行或多⾏行中 synthesize properties@synthesize itemName;@synthesize container, containedItem,serialNumber, valueInDollars, dateCreated;
    • 在 accessor 中执⾏行额外⼯工作的情况- (void)setContainedItem:(BNRItem *)item{ containedItem = item; // 设置被包含的 item 的时候,同时要给该 item 设置⼀一个包含它的容器 [item setContainer:self];}- (void)setContainedItem:(BNRItem *)item{ containedItem = item;} • 我们增加的任意实现会覆盖 synthesize 的版本 :) • ⼀一般我们都会 synthesize 我们在头⽂文件中声明的 property,除⾮非 getter 和 setter 都有附加⾏行为。
    • 实例变量和属性• 更进⼀一步的代码清晰化• 默认:synthesized property 将会访问同名的实例变量• 举例:itemName 属性访问 itemName 实例变量 • itemName ⽅方法返回 itemName 实例变量的值 • setItemName ⽅方法改变 itemName 实例变量• 如果不存在和 synthesized property 的名字相匹配的 实例变量呢? • 会⾃自动创建⼀一个• 所以既声明实例变量⼜又 synthesizing property 是冗余 的。可以把实例变量声明包括⼤大括号都删除掉
    • 复制• 注意:两个指向 NSString 实例的属性• 具有mutable subclass 的类:NSString ,NSArray • ⽣生成⼀一个对象的拷⻉贝并指向,⽐比指向⼀一个可能已经有 了其他 owner 的现有对象要安全些• 使⽤用 copy 属性选项替换 strong
    • NSString CopyingNSMutableString *mutableString = [[NSMutableString alloc] init];BNRItem *item = [[BNRItem alloc] initWithItemName:mutableString valueInDollars:5 serialNumber:@"4F2W7R"];@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;- (void)setItemName:(NSString *)str{ itemName = [str copy];} • 原有的字符串没有以任何形式被更改,没有获得 也没有失去 owner ,数据也不会被更改。
    • 点号语法 • 虽然可⽤用,但是不建议使⽤用,避免 confusing// 下⾯面的两⾏行是完全相等的int value = [item valueInDollars];int value = item.valueInDollars;[item setValueInDollars:5];item.valueInDollars = 5;