你的答案

0.AOP

1.copy、mutableCopy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// swift中的NSArray实现的copy与mutableCopy
open override func copy() -> Any {
return copy(with: nil)
}

open func copy(with zone: NSZone? = nil) -> Any {
if type(of: self) === NSArray.self {
// return self for immutable type
return self
} else if type(of: self) === NSMutableArray.self {
let array = NSArray()
array._storage = self._storage
return array
}
return NSArray(array: self.allObjects)
}

open override func mutableCopy() -> Any {
return mutableCopy(with: nil)
}

open func mutableCopy(with zone: NSZone? = nil) -> Any {
if type(of: self) === NSArray.self || type(of: self) === NSMutableArray.self {
// always create and return an NSMutableArray
let mutableArray = NSMutableArray()
mutableArray._storage = self._storage
return mutableArray
}
return NSMutableArray(array: self.allObjects)
}

可见如果属性修饰用了copy, 如果属性上声明NSArray, 即便在赋值时使用NSMutableArray, 属性新值依然得到NSArray的实例,而容器的内存也会是新开辟的。

这就是常说的浅拷贝

而copy与mutableCopy的区别仅在于新创建的容器是可变与不可变

array的这个构造方法会进行深拷贝initWithArray:<#(nonnull NSArray *)#> copyItems:<#(BOOL)#>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public convenience init(array: [Any], copyItems: Bool) {

let optionalArray : [AnyObject] =
copyItems ?
array.map { return __SwiftValue.store($0).copy() as! NSObject } :
array.map { return __SwiftValue.store($0) }

// This would have been nice, but "initializer delegation cannot be nested in another expression"
// optionalArray.withUnsafeBufferPointer { ptr in
// self.init(objects: ptr.baseAddress, count: array.count)
// }
let cnt = array.count
let buffer = UnsafeMutablePointer<AnyObject>.allocate(capacity: cnt)
buffer.initialize(from: optionalArray, count: cnt)
self.init(objects: buffer, count: cnt)
buffer.deinitialize(count: cnt)
buffer.deallocate()
}

会申请内存空间,存储每个对象定义copy的返回对象。注意的是如果对象需要实现copy方法。cocoa自带的对象一般都有默认实现。

对于NSArray、NSDictionary应使用copy来保证它们的封装性

如果使用比如strong修饰,当NSMutableArray的实例赋给属性时,属性就变成了NSMutableArray,

由于strong修饰,属性指向了可变数组的内存区域,如果可变数组改变了,属性也会跟随改变反之亦然,

再有,此种情况运行时调用addObject:依然有效,因为isa已经不是NSArray了

block

为什么不能修改截获的自动变量的值

我们知道,block会把截获的自动变量设置成自身的属性

在block内部修改值,相当于设置了自身的属性,但是却无法改变原本的自动变量的值(全局变量,静态变量除外),这样就造成矛盾了。

也就是在oc代码中,明明在block中修改了自动变量的值,结果却没有改变!

所以有了__block修饰符来说明意图

__block修饰的变量会被编译套壳 __Block_byref

1
2
3
4
5
6
7
8
9
10
11
struct __blockContext_block_impl_0 {
struct __block_impl impl;
struct __blockContext_block_desc_0* Desc;
__Block_byref_alan_0 *alan; // by ref
__blockContext_block_impl_0(void *fp, struct __blockContext_block_desc_0 *desc, __Block_byref_alan_0 *_alan, int flags=0) : alan(_alan->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};

在注释// by ref 可见意图,通过指针引用来间接修改值。

block属性只是自动变量的指针,在block内部通过属性找到自动变量的指针,进而改变值可以改变外部自动变量的值

1
2
3
4
5
6
7
8
9
int alan
// ↓
struct __Block_byref_alan_0 {
void *__isa;
__Block_byref_alan_0 *__forwarding;
int __flags;
int __size;
int alan;
};

关键是__forwarding

1
2
3
4
5
6
// block修改自动变量的部分
static void __blockContext_block_func_0(struct __blockContext_block_impl_0 *__cself) {
__Block_byref_alan_0 *alan = __cself->alan; // bound by ref

(alan->__forwarding->alan) = 2;
}
1
2
// block外部的自动变量的声明
__attribute__((__blocks__(byref))) __Block_byref_alan_0 alan = {(void*)0,(__Block_byref_alan_0 *)&alan, 0, sizeof(__Block_byref_alan_0), 1};
1
2
// 把自动变量赋给block
((void (*)())&__blockContext_block_impl_0((void *)__blockContext_block_func_0, &__blockContext_block_desc_0_DATA, (__Block_byref_alan_0 *)&alan, 570425344));

在block内部想改变外部的自动变量时,获取到的是外部变量壳对象的引用

通过引用找到__forwarding再找到真正的属性变量进行赋值

为什么不直接使用 alan->alan 即通过偏移直接赋值呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//
void (*tryBlock)(void) = ((void (*)())&__blockContext_block_impl_0((void *)__blockContext_block_func_0, &__blockContext_block_desc_0_DATA, alan));

struct __blockContext_block_impl_0 {
struct __block_impl impl;
struct __blockContext_block_desc_0* Desc;
int alan;
__blockContext_block_impl_0(void *fp, struct __blockContext_block_desc_0 *desc, int _alan, int flags=0) : alan(_alan) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
//
void (*tryBlock)(void) = ((void (*)())&__blockContext_block_impl_0((void *)__blockContext_block_func_0, &__blockContext_block_desc_0_DATA, (__Block_byref_alan_0 *)&alan, 570425344));

Block 的copy

swift 内存布局

swift如何实现多态