编程进阶网 编程进阶网
首页
  • 在线工具
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 加解密
  • 时间日期
  • 网络工具
  • 颜色设计
  • 二维码
  • 开发实用
  • 计算机组成原理
  • 操作系统原理
  • 网络协议原理
  • 数据库系统原理
  • 序卷导读
  • 数据本质
  • 运行模型
  • 并发设计
  • 内存真相
  • 交互系统
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 体系建设
  • 代码品质
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 性能优化
  • 数据结构导论
  • 线性结构详解
  • 树哈希结构论
  • 容器设计实战
  • 经典算法思想
  • 工程案例剖析
  • 算法题库精练
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++编程技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript案例
  • JavaScript高级
  • Android库解读
  • Android专栏
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • 毛选解读
  • 自我精进
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接

杨充

专注编程 · 终身学习者
首页
  • 在线工具
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 加解密
  • 时间日期
  • 网络工具
  • 颜色设计
  • 二维码
  • 开发实用
  • 计算机组成原理
  • 操作系统原理
  • 网络协议原理
  • 数据库系统原理
  • 序卷导读
  • 数据本质
  • 运行模型
  • 并发设计
  • 内存真相
  • 交互系统
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 体系建设
  • 代码品质
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 性能优化
  • 数据结构导论
  • 线性结构详解
  • 树哈希结构论
  • 容器设计实战
  • 经典算法思想
  • 工程案例剖析
  • 算法题库精练
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++编程技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript案例
  • JavaScript高级
  • Android库解读
  • Android专栏
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • 毛选解读
  • 自我精进
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接
  • README
  • Android提升进阶

  • iOS开发和进阶

    • README
    • ObjC编程入门

      • README
      • ObjC基础语法
      • 面向对象编程
      • ObjC高级语法
        • 3.1 枚举的设计和使用
          • 3.1.1 枚举的定义
          • 3.1.2 枚举的使用
          • 3.1.3 枚举的场景
          • 3.1.4 枚举定义值
        • 3.2 协议设计和使用
          • 3.2.1 协议的作用
          • 3.2.2 协议的声明
          • 3.2.3 协议使用步骤
          • 3.2.4 协议实际案例
          • 3.2.5 协议使用场景
          • 3.2.6 协议的判断
        • 3.3 理解动态类型设计
          • 3.3.1 理解动态类型
          • 3.3.2 理解id类型
          • 3.3.3 动态类型检查
          • 3.3.4 动态类型转化
          • 3.3.5 动态类型调用
          • 3.3.6 动态类型弊端
        • 3.4 类别如何使用
          • 3.4.1 理解类别机制
          • 3.4.2 类别使用场景
          • 3.4.3 类别使用案例
        • 3.5 Blocks设计
          • 3.5.1 Block是什么
          • 3.5.2 Block使用
        • 3.6 KVO(键值观察)
          • 3.6.1 添加观察者
          • 3.6.2 回调
        • 3.7 多线程使用
          • 3.7.1 线程基本概念
          • 3.7.2 使用NSThread
          • 3.7.3 NSOperation
          • 3.7.4 GCD多线程
          • 3.7.5 线程安全
        • 3.8 Block 内存管理深入
          • 3.8.1 Block 的三种类型
          • 3.8.2 Block 修改外部变量
          • 3.8.3 Block 循环引用的防御清单
        • 3.9 GCD 实战模式总结
          • 3.9.1 串行队列做锁
          • 3.9.2 dispatch_once 实现单例
          • 3.9.3 目标队列与 QoS
      • ObjC内存管理
    • Swift编程入门

    • 专栏博客

  • Web开发和进阶

  • Linux应用开发

  • IoT智能硬件开发

  • Apps
  • iOS开发和进阶
  • ObjC编程入门
杨充
2025-07-23
目录

ObjC高级语法

# 03.ObjC高级语法

# 目录介绍

  • 3.1 枚举的设计和使用
    • 3.1.1 枚举的定义
    • 3.1.2 枚举的使用
    • 3.1.3 枚举的场景
    • 3.1.4 枚举定义值
  • 3.2 协议与委托
    • 3.2.1 协议的作用
    • 3.2.2 协议的声明
    • 3.2.3 协议使用步骤
    • 3.2.4 协议实际案例
    • 3.2.5 协议使用场景
    • 3.2.6 协议的判断
  • 3.3 理解动态类型设计
    • 3.3.1 理解动态类型
    • 3.3.2 理解id类型
    • 3.3.3 动态类型检查
    • 3.3.4 动态类型转化
    • 3.3.5 动态类型调用
    • 3.3.6 动态类型弊端
  • 3.4 类别如何使用
    • 3.4.1 理解类别机制
    • 3.4.2 类别使用场景
    • 3.4.3 类别使用案例
  • 3.5 Blocks设计
    • 3.5.1 Block是什么
    • 3.5.2 Block使用
  • 3.6 KVO(键值观察)
    • 3.6.1 添加观察者
    • 3.6.2 回调
  • 3.7 多线程使用
    • 3.7.1 线程基本概念
    • 3.7.2 使用NSThread
    • 3.7.3 NSOperation
    • 3.7.4 GCD多线程
    • 3.7.5 线程安全

# 3.1 枚举的设计和使用

# 3.1.1 枚举的定义

定义枚举类型:使用typedef enum关键字来定义枚举类型。在定义时,可以列出枚举的所有可能取值。

typedef enum {
    OptionA,
    OptionB,
    OptionC
} MyEnum;

枚举常量的默认值:如果没有为枚举常量指定具体的值,它们将按照顺序从0开始自动分配值。第一个常量的默认值为0,后续常量的值依次递增。

# 3.1.2 枚举的使用

使用枚举常量:定义枚举后,可以使用枚举常量来表示不同的取值。枚举常量的命名通常使用大写字母开头,以便与其他常量区分。

MyEnum myValue = OptionB;

一篇文章学会typedef enum、NS_ENUM、NS_OPTIONS和移位1 << 0:https://www.jianshu.com/p/dc26d719cc03

# 3.1.3 枚举的场景

应该用枚举来表示状态机的状态、传递给方法的选项以及状态码等值,给这些值起个易懂的名字。

用NS_ENUM与NS_OPRIONS宏来定义枚举类型, 并指明其底层数据类型.这样做可以确保枚举是用开发者所选的底层数据类型实现出来的,而不会采用编译器所选的类型。

# 3.1.4 枚举定义值

在 Objective-C 中,枚举(Enum)可以用来定义一组相关的命名常量。每个枚举常量都有一个与之相关联的整数值。

// 定义一个枚举类型
typedef NS_ENUM(NSInteger, Weekday) {
    Monday = 1,
    Tuesday = 2,
    Wednesday = 3,
    Thursday = 4,
    Friday = 5,
    Saturday = 6,
    Sunday = 7
};

# 3.2 协议设计和使用

# 3.2.1 协议的作用

协议(Protocol)是一种定义接口的机制,用于声明一组方法、属性或其他成员,以便其他类遵循(adopt)并实现(implement)。

协议的作用是定义了一套规范,使得不同的类可以遵循相同的接口,从而实现代码的重用和解耦。

# 3.2.2 协议的声明

//@protocol 是用于声明协议(Protocol)的关键字。协议定义了一组方法,用于描述类或对象应该遵循的行为和功能。
@protocol MyProtocol

//协议中的方法声明与类中的方法声明类似,使用以下语法:- (返回类型)方法名;

- (void) doSomething;
- (NSString *) getName;

//@required
@optional
- (NSString *) getAge;

@end

@required与@optional

  1. @required修饰的方法的声明,就必须实现,编译器会警告,默认属性
  2. @optional修饰的方法的声明,可以实现或者不实现,不实现编译器也不会警告

# 3.2.3 协议使用步骤

在实践中,可以按照以下步骤使用协议:

  1. 定义协议:使用@protocol关键字定义协议,并在其中声明需要的方法、属性等。
  2. 遵循协议:在类的声明中使用来遵循特定的协议。
  3. 实现协议方法:在类的实现中,根据协议的要求实现相应的方法。
  4. 使用协议:通过协议类型的指针或引用,可以调用协议中定义的方法,而不需要关心具体的类。

# 3.2.4 协议实际案例

// 遵循协议的类
@interface MyClass : NSObject <MyProtocol>
@end

@implementation MyClass
- (void)doSomething {
    NSLog(@"Doing something...");
}
@end

// 使用协议
MyClass *myObject = [[MyClass alloc] init];
id<MyProtocol> objectWithProtocol = myObject;
[objectWithProtocol doSomething];

# 3.2.5 协议使用场景

协议(Protocol)具有广泛的使用场景,以下是一些常见的协议使用场景:

  1. 委托模式(Delegate Pattern):协议常用于实现委托模式,其中一个类(委托方)将某些任务委托给另一个类(委托对象)来处理。
  2. 数据源模式(Data Source Pattern):协议可以用于实现数据源模式,其中一个类(数据源)提供数据给另一个类(数据使用者)。
  3. 视图控制器之间的通信:协议可以用于视图控制器之间的通信,例如一个视图控制器需要将数据传递给另一个视图控制器。
  4. 网络请求回调:协议可以用于处理网络请求的回调,例如在网络请求完成后通知调用方。

# 3.2.6 协议的判断

如何判断类是否实现了某个协议?要判断一个类是否实现了某个协议,可以使用conformsToProtocol:方法。这个方法可以检查一个类是否遵循了指定的协议。

// 判断类是否实现了某个协议
if ([MyClass conformsToProtocol:@protocol(MyProtocol)]) {
    NSLog(@"MyClass 实现了 MyProtocol 协议");
} else {
    NSLog(@"MyClass 没有实现 MyProtocol 协议");
}

如果你想检查一个对象是否实现了某个协议,可以使用NSObject类的conformsToProtocol:方法。示例如下:

// 判断对象是否实现了某个协议
if ([myObject conformsToProtocol:@protocol(MyProtocol)]) {
    NSLog(@"myObject 实现了 MyProtocol 协议");
} else {
    NSLog(@"myObject 没有实现 MyProtocol 协议");
}

# 3.3 理解动态类型设计

# 3.3.1 理解动态类型

动态类型是指在运行时确定对象的类型,而不是在编译时确定。Objective-C是一种动态类型语言,它允许在运行时进行类型检查和类型转换。

# 3.3.2 理解id类型

id类型:id是Objective-C中的一种特殊类型,表示一个未知类型的对象。

可以将任何对象赋值给id类型的变量,而不需要进行编译时的类型检查。在运行时,可以使用动态类型检查和消息传递来操作id类型的对象。

//1.未知类型的对象:当你不确定一个对象的具体类型时,可以使用id类型来存储该对象的引用。
id unknownObject = someObject;

//2.动态类型检查:通过将对象赋值给id类型的变量,可以使用动态类型检查方法来判断对象的具体类型。
if ([unknownObject isKindOfClass:[NSString class]]) {
    // 对象是NSString类型
} else if ([unknownObject isKindOfClass:[NSNumber class]]) {
    // 对象是NSNumber类型
}

//3.动态方法调用:使用id类型可以在运行时动态调用对象的方法,而不需要在编译时知道方法的具体实现。
id someObject = [SomeClass new];
[someObject performSelector:@selector(someMethod)];

//泛型集合:在Objective-C中,可以使用id类型来实现泛型集合,以存储不同类型的对象。
NSArray<id> *genericArray = @[object1, object2, object3];

要注意的是,由于id类型是一种未知类型,编译器无法提供类型检查和自动补全等功能。因此,在使用id类型时,需要确保在运行时进行适当的类型检查,以避免潜在的类型错误。

id 和 void * 的区别?id 表示一个指向 Objective-C 对象的指针,而 void * 可以表示为任何指针。

另外,使用 id 声明对象时编辑器不会报错,只有在运行时才会提示错误,所以,推荐使用 NSObject * 而不是直接使用 id 创建一个代表任何类的对象。

# 3.3.3 动态类型检查

动态类型检查:Objective-C提供了一些方法来进行动态类型检查,例如isKindOfClass:和isMemberOfClass:。

这些方法可以在运行时检查对象的类型,判断对象是否属于特定的类或其子类。

//该方法用于检查一个对象是否是指定类或其子类的实例。它会遍历整个继承链,包括父类和祖先类。
if ([someObject isKindOfClass:[NSString class]]) {
    // 对象是NSString类型或其子类的实例
}

//该方法用于检查一个对象是否是指定类的实例,而不包括其子类。
if ([someObject isMemberOfClass:[NSString class]]) {
    // 对象是NSString类型的实例,不包括其子类
}

如果需要检查对象是否遵循特定的协议,可以使用conformsToProtocol:方法。

if ([someObject conformsToProtocol:@protocol(MyProtocol)]) {
    // 对象遵循MyProtocol协议
}

# 3.3.4 动态类型转化

动态类型转换:Objective-C提供了一些方法来进行动态类型转换,例如isKindOfClass:和respondsToSelector:。

这些方法可以在运行时检查对象的类型,并根据需要将对象转换为其他类型。

//可以使用respondsToSelector:方法来检查对象是否实现了特定的方法。
if ([someObject respondsToSelector:@selector(someMethod)]) {
    // 对象实现了someMethod方法
}

# 3.3.5 动态类型调用

1.performSelector:动态调用无参数方法。

[obj performSelector:@selector(doSomething)];

2.performSelector:withObject:动态调用带一个参数的方法。

[obj performSelector:@selector(doSomethingWith:) withObject:arg];

3.performSelector:withObject:withObject:动态调用带两个参数的方法。

[obj performSelector:@selector(doSomethingWith:with:) withObject:arg1 withObject:arg2];

4.NSInvocation,用于动态调用任意参数的方法。

SEL selector = @selector(doSomethingWith:with:);
NSMethodSignature *signature = [obj methodSignatureForSelector:selector];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
[invocation setTarget:obj];
[invocation setSelector:selector];
[invocation setArgument:&arg1 atIndex:2];
[invocation setArgument:&arg2 atIndex:3];
[invocation invoke];

动态类型调用的注意事项

  1. 性能开销:动态类型调用比静态类型调用慢,因为需要在运行时查找方法。
  2. 安全性:动态调用可能导致运行时错误(如方法不存在),需要做好检查。
  3. 可读性:过度使用动态类型调用会降低代码的可读性和可维护性。

# 3.3.6 动态类型弊端

动态类型的特性使得Objective-C具有灵活性和可扩展性。它允许在运行时根据实际情况进行类型检查和类型转换,以适应不同的需求和场景。

但需要注意,过度使用动态类型可能会导致代码的可读性和可维护性下降,因此需要谨慎使用。

# 3.4 类别如何使用

# 3.4.1 理解类别机制

类别(Category)是一种机制,用于在不修改原始类的情况下扩展类的功能。类别允许开发者向现有类添加新的方法,以及修改或重写现有方法的实现。

# 3.4.2 类别使用场景

1.扩展类的功能:类别允许在不创建子类或修改原始类的情况下,向现有类添加新的方法。这对于向系统类或第三方库的类添加自定义功能非常有用。

2.方法重写:类别可以重写原始类中的方法实现。当类别中的方法与原始类中的方法同名时,类别中的方法会覆盖原始类中的方法。这使得可以通过类别来修改现有方法的行为。

3.避免命名冲突:在使用类别时,需要注意避免与其他类别或原始类中的方法产生命名冲突。最好使用特定的前缀或命名约定来命名类别中的方法,以确保命名的唯一性。

# 3.4.3 类别使用案例

下面是一个简单的示例,演示了如何使用类别来扩展NSString类的功能:

// 假设有一个名为 NSString+CustomMethods 的类别文件
@interface NSString (CustomMethods)

//反转字符串
- (NSString *)reversedString;

@end

@implementation NSString (CustomMethods)

- (NSString *)reversedString {
    NSMutableString *reversedString = [NSMutableString string];
    for (NSInteger i = self.length - 1; i >= 0; i--) {
        [reversedString appendString:[NSString stringWithFormat:@"%C", [self characterAtIndex:i]]];
    }
    return reversedString;
}

@end

int main(int argc, char * argv[]) {
    @autoreleasepool {
        NSString *originalString = @"Hello, World!";
        NSString *reversedString = [originalString reversedString];
        NSLog(@"%@", reversedString); // 输出:!dlroW ,olleH
    }
    return 0;
}

# 3.5 Blocks设计

# 3.5.1 Block是什么

Blocks 是 Objective-C 中一种特殊的对象,它可以直接执行一段代码,类似其它语言中的 lambda 表达式。

# 3.5.2 Block使用

Block 字面量,我们可以使用 ^ 符创建一个 block 字面量:

^{
NSLog(@"This is a block");
}

类似与 C 中的方法指针,我们可以使用下面的语法去引用一个 block:

void (^simpleBlock)(void);

以上可以理解为我们创建了一个 simpleBlock 变量来表示一个参数为 void,返回值也为 void 的方法。之后,我们可以创建方法体:

simpleBlock = ^ {
  NSLog(@"This is a block");
}

当然,我们也可以将上面两个步骤合二为一:

void (^simpleBlock)(void) = ^ {
  NSLog(@"This is a block");
}

另外,block 也可以包含参数和返回值:

double (^addBlock)(double, double) = 
  ^(double firstValue, double secondValue) {
    return firstValue * secondValue;
  };

# 3.6 KVO(键值观察)

# 3.6.1 添加观察者

// 添加观察者
[person addObserver:self
         forKeyPath:@"age"
            options:NSKeyValueObservingOptionNew
            context:nil];

# 3.6.2 回调

// 回调
- (void)observeValueForKeyPath:(NSString *)keyPath 
                      ofObject:(id)object 
                        change:(NSDictionary *)change 
                       context:(void *)context {
    if ([keyPath isEqualToString:@"age"]) {
        NSLog(@"New age: %@", change[NSKeyValueChangeNewKey]);
    }
}

# 3.7 多线程使用

# 3.7.1 线程基本概念

  • 线程:线程是程序执行的最小单位,一个进程可以包含多个线程。
  • 主线程:也称为 UI 线程,负责更新 UI 和处理用户交互。
  • 后台线程:用于执行耗时操作,避免阻塞主线程。

# 3.7.2 使用NSThread

NSThread 是 Objective-C 中最基础的线程管理类,可以显式创建和管理线程。

1.创建线程

// 方式 1:使用类方法
[NSThread detachNewThreadSelector:@selector(threadMethod:) toTarget:self withObject:nil];

// 方式 2:使用实例方法
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(threadMethod:) object:nil];
[thread start];

2.线程方法

- (void)threadMethod:(id)object {
    NSLog(@"Thread is running");
    // 执行耗时操作
    [NSThread sleepForTimeInterval:2]; // 模拟耗时操作
    NSLog(@"Thread finished");
}

3.线程同步

使用 @synchronized 实现线程同步。

@synchronized(self) {
    // 线程安全的代码块
}

# 3.7.3 NSOperation

NSOperation 和 NSOperationQueue 是基于 GCD 的高级抽象,提供了更强大的任务管理功能。

1.创建 NSOperation

NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"Operation is running");
    // 执行耗时操作
    [NSThread sleepForTimeInterval:2]; // 模拟耗时操作
    NSLog(@"Operation finished");
}];

2.创建 NSOperationQueue

NSOperationQueue *queue = [[NSOperationQueue alloc] init];
queue.maxConcurrentOperationCount = 3; // 设置最大并发数
[queue addOperation:operation];

3.依赖关系

可以设置 NSOperation 之间的依赖关系。

NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"Operation 1");
}];
NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"Operation 2");
}];
[operation2 addDependency:operation1]; // operation2 依赖于 operation1
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:operation1];
[queue addOperation:operation2];

# 3.7.4 GCD多线程

Grand Central Dispatch (GCD),GCD 是 Apple 提供的多线程编程解决方案,基于 C 语言 API,性能高效且易于使用。

1.创建队列

  • 主队列:串行队列,用于更新 UI。
  • 全局队列:并发队列,用于执行后台任务。
  • 自定义队列:可以是串行或并发队列。
// 主队列
dispatch_queue_t mainQueue = dispatch_get_main_queue();

// 全局队列
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

// 自定义队列
dispatch_queue_t customQueue = dispatch_queue_create("com.example.customQueue", DISPATCH_QUEUE_SERIAL);

2.执行任务

// 异步执行
dispatch_async(globalQueue, ^{
    NSLog(@"Background task is running");
    // 执行耗时操作
    [NSThread sleepForTimeInterval:2]; // 模拟耗时操作
    NSLog(@"Background task finished");

    // 回到主线程更新 UI
    dispatch_async(mainQueue, ^{
        NSLog(@"Update UI");
    });
});

// 同步执行(会阻塞当前线程)
dispatch_sync(customQueue, ^{
    NSLog(@"Sync task is running");
});

3.延迟执行

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    NSLog(@"Delayed task");
});

4.任务组

使用 dispatch_group 管理一组任务。

dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

dispatch_group_async(group, queue, ^{
    NSLog(@"Task 1");
});
dispatch_group_async(group, queue, ^{
    NSLog(@"Task 2");
});

dispatch_group_notify(group, dispatch_get_main_queue(), ^{
    NSLog(@"All tasks finished");
});

# 3.7.5 线程安全

1.使用 @synchronized

@synchronized(self) {
    // 线程安全的代码块
}

2.使用 NSLock

NSLock *lock = [[NSLock alloc] init];
[lock lock];
// 线程安全的代码块
[lock unlock];

3.使用 dispatch_semaphore

dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
// 线程安全的代码块
dispatch_semaphore_signal(semaphore);

# 3.8 Block 内存管理深入

# 3.8.1 Block 的三种类型

Block 在内存中分三种类型,了解它们的区别是避免崩溃的关键:

类型 存储区域 何时产生 是否需要 copy
__NSGlobalBlock__ 全局区 不捕获任何外部变量 否
__NSStackBlock__ 栈区 捕获外部变量,未 copy 生命周期限于作用域
__NSMallocBlock__ 堆区 StackBlock 执行 copy 后 可安全传递

关键规则:需要把 Block 作为属性、回调保存时,必须使用 copy 修饰符,将 StackBlock 拷贝到堆上。ARC 下系统自动处理了大部分场景,但理解原理有助于排查疑难 bug。

@property (nonatomic, copy) void (^completionBlock)(void); // ✅ 必须用 copy

# 3.8.2 Block 修改外部变量

Block 默认捕获外部变量为只读副本。如需在 Block 内部修改外部变量,使用 __block 修饰符:

__block int count = 0;
void (^increment)(void) = ^{
    count++; // ✅ __block 允许修改
};
increment();
NSLog(@"%d", count); // 输出 1

__block 的工作原理:编译器将变量从栈上移到堆上,Block 和外部作用域共享堆上的同一份变量。

# 3.8.3 Block 循环引用的防御清单

每次写 Block 时自问三问:① Block 内部有没有用 self.xxx 或 [self method]?② Block 会不会被 self 或其子对象长期持有(属性、数组、字典)?③ 如果会,是否使用了 weak-strong dance?

常见高危场景:网络回调、定时器回调、通知监听 Block、动画 completion。

# 3.9 GCD 实战模式总结

# 3.9.1 串行队列做锁

使用串行队列替代 @synchronized 或 NSLock,性能更好且避免死锁风险。

@property (nonatomic, strong) dispatch_queue_t syncQueue;
// 初始化
_syncQueue = dispatch_queue_create("com.app.sync", DISPATCH_QUEUE_SERIAL);

// 读操作:并发队列提高读取性能
- (id)objectForKey:(NSString *)key {
    __block id obj;
    dispatch_sync(_syncQueue, ^{ obj = _dict[key]; });
    return obj;
}

// 写操作:async + barrier 保证写入时独占
- (void)setObject:(id)obj forKey:(NSString *)key {
    dispatch_barrier_async(_syncQueue, ^{ _dict[key] = obj; });
}

# 3.9.2 dispatch_once 实现单例

+ (instancetype)sharedInstance {
    static MyManager *instance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[self alloc] init];
    });
    return instance;
}

dispatch_once 是线程安全且最高效的单例实现方式,比 @synchronized 快一个数量级。

# 3.9.3 目标队列与 QoS

为队列设置合理的目标队列(target queue)和 QoS(Quality of Service),让系统合理调度资源。UI 更新必须在主队列,耗时操作放在后台队列,敏感操作使用 DISPATCH_QUEUE_PRIORITY_HIGH。

上次更新: 2026/06/28, 17:55:19
面向对象编程
ObjC内存管理

← 面向对象编程 ObjC内存管理→

最近更新
01
科学方法实践论法
06-28
02
辩证思维矛盾论法
06-28
03
毛选中的调查观念
06-28
更多文章>
Theme by Vdoing | Copyright © 2019-2026 杨充 | MIT License | 鄂ICP备2024073355号-1 | 鄂ICP备2024073355号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式