iOS-内存管理

来源:互联网 发布:电脑软件助手 编辑:程序博客网 时间:2024/06/03 18:40

1. 什么情况使用 weak 关键字,相比 assign 有什么不同?

什么情况使用 weak 关键字?

  1. 在 ARC中,有可能出现循环引用的时候,往往要通过让其中一端使用 weak 来解决,比如: delegate 代理属性。
  2. 自身已经对它进行一次强引用,没有必要再强引用一次,此时也会使用 weak,自定义 IBOutlet 控件属性一般也使用 weak;当然,也可以使用strong。在下文也有论述:《IBOutlet连出来的视图属性为什么可以被设置成weak?》

相比 assign 有什么不同:

  1. weak 此特质表明该属性定义了一种“非拥有关系” (nonowning relationship)。为这种属性设置新值时,设置方法既不保留新值,也不释放旧值。此特质同assign类似,然而在属性所指的对象遭到摧毁时,属性值也会清空(nil out)。而 assign 的“设置方法”只会执行针对“纯量类型” (scalar type,例如 CGFloat 或 NSlnteger 等)的简单赋值操作。
  2. assign 可以用非 OC 对象,而 weak 必须用于 OC 对象。

2. IBOutlet连出来的视图属性为什么可以被设置成weak?

参考链接:Should IBOutlets be strong or weak under ARC?

文章告诉我们:因为既然有外链那么视图在xib或者storyboard中肯定存在,视图已经对它有一个强引用了。

不过这个回答漏了个重要知识,使用storyboard(xib不行)创建的vc,会有一个叫_topLevelObjectsToKeepAliveFromStoryboard的私有数组强引用所有top level的对象,所以这时即便outlet声明成weak也没关系

详细阐述:

From a practical perspective, in iOS and OS X outlets should be defined as declared properties. Outlets should generally be weak, except for those from File’s Owner to top-level objects in a nib file (or, in iOS, a storyboard scene) which should be strong. Outlets that you create will therefore typically be weak by default, because:

Outlets that you create to, for example, subviews of a view controller’s view or a window controller’s window, are arbitrary references between objects that do not imply ownership.

The strong outlets are frequently specified by framework classes (for example, UIViewController’s view outlet, or NSWindowController’s window outlet).

@property (weak) IBOutlet MyView *viewContainerSubview;@property (strong) IBOutlet MyOtherClass *topLevelObject;

什么是top level对象?apple doc这样说:

The top-level objects are the subset of these objects that do not have a parent object.
The top-level objects typically include only the windows, menubars, and custom controller objects that you add to the nib file. (Objects such as File’s Owner, First Responder, and Application are placeholder objects and not considered top-level objects.)

3. 怎么用 copy 关键字?

用途:
NSString、NSArray、NSDictionary 等等经常使用copy关键字,是因为他们有对应的可变类型:NSMutableString、NSMutableArray、NSMutableDictionary;block 也经常使用 copy 关键字。
block 使用 copy 是从 MRC 遗留下来的“传统”,在 MRC 中,方法内部的 block 是在栈区的,使用 copy 可以把它放到堆区。

在 ARC 中写不写都行:对于 block 使用 copy 还是 strong 效果是一样的,但写上 copy 也无伤大雅,还能时刻提醒我们:编译器自动对 block 进行了 copy 操作。如果不写 copy ,该类的调用者有可能会忘记或者根本不知道“编译器会自动对 block 进行了 copy 操作”,他们有可能会在调用之前自行拷贝属性值,这种操作多余而低效。

具体原因见官方文档:
Objects Use Properties to Keep Track of Blocks

copy 此特质所表达的所属关系与 strong 类似。
然而设置方法并不保留新值,而是将其“拷贝” (copy)。
当属性类型为 NSString 时,经常用此特质来保护其封装性,
因为传递给设置方法的新值有可能指向一个 NSMutableString 类的实例。
这个类是 NSString 的子类,表示一种可修改其值的字符串,
此时若是不拷贝字符串,
那么设置完属性之后,
字符串的值就可能会在对象不知情的情况下遭人更改。
所以,这时就要拷贝一份“不可变” (immutable)的字符串,
确保对象中的字符串值不会无意间变动。
只要实现属性所用的对象是“可变的” (mutable),
就应该在设置新属性值时拷贝一份。

用 @property 声明 NSString、NSArray、NSDictionary 经常使用 copy 关键字,是因为他们有对应的可变类型:NSMutableString、NSMutableArray、NSMutableDictionary,他们之间可能进行赋值操作,为确保对象中的字符串值不会无意间变动,应该在设置新属性值时拷贝一份。

该问题在下文中也有论述:《用@property声明的NSString(或NSArray,NSDictionary)经常使用copy关键字,为什么?如果改用strong关键字,可能造成什么问题?》

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

因为父类指针可以指向子类对象,使用 copy 的目的是为了让本对象的属性不受外界影响,使用 copy 无论传入是一个可变对象还是不可对象,本身持有的就是一个不可变的副本。如果我们使用是 strong ,那么这个属性就有可能指向一个可变对象,如果这个可变对象在外部被修改了,那么会影响该属性。

举例说明:

定义一个以 strong 修饰的 array:

@property (nonatomic, readwrite, strong) NSArray *array;

然后进行下面的操作:

NSArray *array = @[ @1, @2, @3, @4 ];NSMutableArray *mutableArray = [NSMutableArray arrayWithArray:array];self.array = mutableArray;[mutableArray removeAllObjects];NSLog(@"%@",self.array);[mutableArray addObjectsFromArray:array];self.array = [mutableArray copy];[mutableArray removeAllObjects];NSLog(@"%@",self.array);

打印结果如下所示:

2015-09-27 19:10:32.523 CYLArrayCopyDmo[10681:713670] ()2015-09-27 19:10:32.524 CYLArrayCopyDmo[10681:713670] (   1,   2,   3,   4)

为了理解这种做法,首先要知道,两种情况:

对非集合类对象的 copy 与 mutableCopy 操作;对集合类对象的 copy 与 mutableCopy 操作。
1. 对非集合类对象的copy操作:

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

[immutableObject copy] // 浅复制[immutableObject mutableCopy] //深复制[mutableObject copy] //深复制[mutableObject mutableCopy] //深复制

比如以下代码:

NSMutableString *string = [NSMutableString stringWithString:@"origin"];//copyNSString *stringCopy = [string copy];

查看内存,会发现 string、stringCopy 内存地址都不一样,说明此时都是做内容拷贝、深拷贝。即使你进行如下操作:

[string appendString:@"origion!"]

stringCopy 的值也不会因此改变,但是如果不使用 copy,stringCopy 的值就会被改变。 集合类对象以此类推。所以,

(重申一遍) 用 @property 声明 NSString、NSArray、NSDictionary 经常使用 copy 关键字,是因为他们有对应的可变类型:NSMutableString、NSMutableArray、NSMutableDictionary,他们之间可能进行赋值操作,为确保对象中的字符串值不会无意间变动,应该在设置新属性值时拷贝一份。

2. 集合类对象的copy与mutableCopy

集合类对象是指 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] //单层深复制

这个代码结论和非集合类的非常相似。

参考链接:iOS 集合的深复制与浅复制

5. 这个写法会有什么问题: @property (copy) NSMutableArray *array;

两个问题:

1、添加,删除,修改数组内的元素的时候,程序会因为找不到对应的方法而崩溃。因为 copy 就是复制一个不可变 NSArray 的对象;
2、使用了 atomic 属性会严重影响性能。

第1条的相关原因在
《用@property声明的NSString(或NSArray,NSDictionary)经常使用 copy 关键字,为什么?如果改用strong关键字,可能造成什么问题?》 以及
《怎么用 copy 关键字?》 有论述。

第2条原因,如下:

该属性使用了同步锁,会在创建时生成一些额外的代码用于帮助编写多线程程序,这会带来性能问题,通过声明 nonatomic 可以节省这些虽然很小但是不必要额外开销。

在默认情况下,由编译器所合成的方法会通过锁定机制确保其原子性(atomicity)。如果属性具备 nonatomic 特质,则不使用同步锁。请注意,尽管没有名为“atomic”的特质,如果某属性不具备 nonatomic 特质,那它就是“原子的”(atomic)。

在iOS开发中,你会发现,几乎所有属性都声明为 nonatomic。

一般情况下并不要求属性必须是“原子的”,因为这并不能保证“线程安全” ( thread safety),若要实现“线程安全”的操作,还需采用更为深层的锁定机制才行。例如,一个线程在连续多次读取某属性值的过程中有别的线程在同时改写该值,那么即便将属性声明为 atomic,也还是会读到不同的属性值。

因此,开发iOS程序时一般都会使用 nonatomic 属性。但是在开发 Mac OS X 程序时,使用 atomic 属性通常都不会有性能瓶颈。

原创粉丝点击