-
类型系统
-
引用类型( refrence type)
- 类( class )
- 指针( pointer )
- 块( block )
-
值类型( value type )
- 基础数据类型( int, float, double... )
- 机构体( struct )
- 枚举( enum )
-
类型装饰
- 协议( protocol )
- 类别( category )
- 扩展( extension )
-
-
变量
-
成员变量: @interface {} 中定义的变量
- @public: 公开访问, 即使定义在 .m 文件中
- @private: 私有访问, 默认
- 使用 self->valueName 来访问
-
属性: @property 定义的变量 or 对象
-
修饰关键字
- atmoic: 对 setter/getter 方法加锁(spinlock 自旋锁), 防止多个线程被同时访问(其他线程访问是会等待当前操作完成), 但有额外的资源开销, 且不保证数据的正确性
- nonatmoic: 不加锁
- setter/getter = methodName: 指定 setter/getter 的实现方法名
- readwrite/readonly: 读写选项
- dynamic: 由开发者实现 setter 和 getter, 声明之后无法使用 _name 的方式访问属性.
- assign: 指针赋值, 引用计数不会增加, 一般用于基础数据类型和 c 数据类型. 不会自动置 nil. 即声明 A(assign), B=A, A 销毁后 B 的指针就变成了野指针.
- weak: 声明为 weak 弱引用的对象, 赋值时不会引起赋值对象的引用计数变动, 且赋值对象销毁时, 自身会自动置 nil.
- strong: 声明为 strong 强引用的对象, 赋值时会引起赋值对象的引用计数+1, 同时释放前一次持有的对象.
- copy: 声明为 copy 的对象, 每次赋值时都会对赋值对象进行 copy 操作, 使用的是赋值对象的副本, 所以不引起赋值对象的引用计数变动. 注意生成的是不可变版本(非 Mutable)
-
-
-
内存使用
- 每个函数被调用时都会分配一个独立的栈内存空间
- 栈(Stack): 由系统管理, 存储函数体的入参, 对象和属性参数地址
- 堆(Heap): 由开发者管理, 或进程结束时系统全部回收, 存储引用类型, 属性参数的实际内容(值)
-
析构器和初始化器
- alloc: 在堆中按对象类型来申请分配合适的空间, 同时将属性和实例变量的内存地址置为 nil(或 0)
- init: 调用父类 [super init], 并初始化对象的实例变量
- dealloc: ARC 下对对象属性的引用计数 -1
-
持有关系与引用计数
-
id obj = [[NSObject alloc] init];
- alloc: 以 NSObject 的数据结构, 在堆内存中开辟出相应大小的空间
-
init: 在堆内存中创建 NSObject 实例, 引用计数为 1, 即"自己持有自己"
- 我的理解: 内存释放的动作在每个 runLoop 循环都有进行, 若在一段非常大的代码块中(执行时间超过 1 次 runloop 循环即可), 创建了对象实例而又没有被赋值, 正因为"自己持有自己"(引用计数为 1)而可以在保活, 在代码块的较后位置被使用. 直到代码块结束, 里面每个对象进行"自己释放自己", 引用计数 -1 后被释放池安全挥手内存
- 在 ARC 中, 编译期会对 NSObject 实例自动追加 autoRelease 方法, 注册到 autoReleasePool, 当 [autoReleasePool drain] 时, 对象实例会自动调用 release 方法
- 声明了 id 类型的变量 obj, 存放在函数开辟的栈空间中, obj 的内容是 NSObject 实例指针
-
在 ARC 中, 编译期会对 obj 自动追加 retain 方法, 正式持有 NSObject 实例
-
我的理解:
- retain 会对引用计数 +1, 所以可将 autoRelease "未来某个时间必定 -1" 理解为 "现在先 -1, 未来某个时间点判断为 0 则释放", 代码执行完后 NSObject 的引用计数依然为 1, 持有者从自身转为变量 obj
- obj 内容为 x 的实例指针的话, 即可认为 obj 持有 x; 一旦 obj 内容被赋值为 y 的实例指针, 就变成 obj 持有 y, x 没有被持有, 引用计数 -1
-
-
id obj_2 = obj;
- 看源码
_class_createInstanceFromZone
部分, '='的源码实现
- 看源码
-
设置 property 源码
static inline void reallySetProperty(id self, SEL _cmd, id newValue, ptrdiff_t offset, bool atomic, bool copy, bool mutableCopy)
{
if (offset == 0) {
object_setClass(self, newValue);
return;
}
id oldValue;
id *slot = (id*) ((char*)self + offset);
if (copy) {
newValue = [newValue copyWithZone:nil];
} else if (mutableCopy) {
newValue = [newValue mutableCopyWithZone:nil];
} else {
if (*slot == newValue) return;
newValue = objc_retain(newValue);
}
if (!atomic) {
oldValue = *slot;
*slot = newValue;
} else {
spinlock_t& slotlock = PropertyLocks[slot];
slotlock.lock();
oldValue = *slot;
*slot = newValue;
slotlock.unlock();
}
objc_release(oldValue);
}
void objc_setProperty(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, signed char shouldCopy)
{
bool copy = (shouldCopy && shouldCopy != MUTABLE_COPY);
bool mutableCopy = (shouldCopy == MUTABLE_COPY);
reallySetProperty(self, _cmd, newValue, offset, atomic, copy, mutableCopy);
}