NSString属性,使用什么关键字修饰,使用copy和strong修饰,有什么区别?
因为 NSString 有可变子类:NSMutableString。
copy 修饰符可以确保你的属性始终保存的是一个不可变副本,防止外部传入可变字符串后被意外修改。
@interface Person : NSObject
@property (nonatomic, strong) NSString *name;
@end
NSMutableString *str = [NSMutableString stringWithString:@"Tom"];
Person *p = [Person new];
p.name = str; // strong 修饰,p.name 指向 str
[str appendString:@"my"]; // 修改原字符串
NSLog(@"%@", p.name); // 输出 Tommy ❌
因为把 str (NSMutableString) 赋值给了 name (NSString),name 属性用了strong,所以他不会copy 一份副本,而是增加了一个引用计数。所以单修改 str ,同样也么会把 name 修改掉
说说你对自动释放池的理解,它是什么时候释放的,为什么用__weak修饰的变量所指向的对象在释放时会自动把变量指针置为nil?
自动释放池(Autorelease Pool) 是 Objective-C 中用于延迟释放对象的一种机制。
它的核心目的是:
✅ 让对象在“稍后某个安全的时机”释放,而不是立即释放。
@autoreleasepool {
NSString *str = [NSString stringWithFormat:@"hello %@", @"world"];
// 此处 str 是 autoreleased 对象
}
// 离开 autoreleasepool 时,str 会被 release
本质上,这段代码会被翻译成
void *pool = objc_autoreleasePoolPush()
id obj = [[NSString alloc] initWithFormat:@"hello %@", @"world"];
[obj autorelease];
objc_autoreleasePoolPop(pool)
自动释放池什么时候释放
自动释放池在其作用域结束时(或主线程 RunLoop 进入休眠前)释放池中所有的对象。
当离开 @autoreleasepool 块时,自动调用 objc_autoreleasePoolPop(),
内部会对池中所有对象执行一次 release。
id 声明的对象有什么特性?
它是一个指向 任意 Objective-C 对象实例的指针。
换句话说,只要是继承自 NSObject 的对象(或者任何 @interface 定义的类实例),id 都可以指向。
id → 编译时不检查,运行时解析;
Category 和 Extension 的区别
| 概念 | 定义 |
|---|---|
| Category(分类) | 给已有类添加新的方法(实例方法或类方法),不需要访问源代码。 |
| Extension(扩展) | 类的匿名分类(Unnamed Category) ,通常在 .m 文件中,用于为类添加私有属性、方法声明。 |
Category 和 Extension 都可以添加方法,但是 Extension 是声明方法,需要在原始的.m 文件中实现。
Category 不能添加成员变量,Extension 可以
Category 有单独的.h 和 .m 文件,Extension 没有。
Category 的方法是运行时添加,Extension 是编译期整合。
Category 的 +load 方法 和 原类的 +load 方法都会调用。
Category 的方法列表会整合到原类中,调用的是inser(0),插入到第一位,所以多个 Category 有多个同名的方法A,调用方法A,最后真正执行的方法体和多个Category编译的顺序有关。
你了解isa 指针吗?
-
作用:
- 指向对象所属类的
Class对象; - runtime 通过
isa找到类的方法列表、父类、协议等信息; - 是实现动态消息发送(
objc_msgSend)的关键。
- 指向对象所属类的
-
优化(现代 64 位 iOS):
-
isa已经被 isa 压缩(isa_t / non-pointer isa) ,低位存储标志位:- 对象引用计数(retainCount)
- 是否有关联对象(associated objects)
- 是否是弱引用指针表(weak table)
- 是否正在 dealloc
-
真正指向
Class的地址在高位。
-
-
类型:
- 32 位:直接指向 Class 对象
- 64 位:pointer isa + flag bits(优化内存占用和访问效率)
NSObject 类对象和 实例对象的内存结构
对象内存布局示意:
| 内存区域 | 说明 |
|---|---|
| isa 指针 | ← 指向 Class |
| 父类实例变量 | ← NSObject 本身通常没有 ivar |
| 子类实例变量 | ← 自定义属性/成员变量 |
类对象内存布局
| 内存区域 | 说明 |
|---|---|
| isa 指针 | ← 指向元类(Meta-class) |
| superclass 指针 | ← 指向父类的 Class 对象 |
| 方法列表 | method list |
| 属性列表 | ivar list |
| 协议列表 | protocols |
谈谈对 OC 的 block 的理解
Block 是一种 封装了函数和捕获上下文的匿名函数对象
Objective-C 下 Block 主要有三种类型:
| 类型 | 内存位置 | 特点 |
|---|---|---|
| NSGlobalBlock | 全局区 | 不捕获外部变量,常驻内存,生命周期整个程序 |
| NSStackBlock | 栈区 | 捕获局部变量,生命周期随栈帧结束;默认局部 Block 都是栈 Block |
| NSMallocBlock | 堆区 | 通过 [block copy] 或 ARC 自动 copy,安全跨栈使用 |
Block 本质上是一个 对象(结构体),简化结构如下:
struct __block_impl {
void *isa; // 指向 Block 类
int flags; // 标志位
int reserved; // 保留
void (*invoke)(void *, ...); // 指向执行函数
struct __block_descriptor *descriptor; // 元信息
};
- **invoke**:Block 执行入口
- **descriptor**:Block 的大小、copy、dispose 方法
- **捕获变量**:按值或按引用存储在 Block 内部
成员变量必须要copy 修饰符
@property (nonatomic, copy) void (^completion)(void);
确保栈的block,copy 到堆上。