【iOS】Effective Objective-C第四章

作者:库奇噜啦呼日期:2026/3/27

【iOS】Effective Objective-C第四章

  • 协议与分类
  • 通过委托与数据源协议进行对象间通信
  • 将类的实现代码分散到便于管理的数个分类之中
  • 勿在分类中声明属性
  • 使用“class-continuation分类“隐藏实现细节
  • 通过协议提供匿名对象

协议与分类

协议和分类都是OC的一项重要语言特性。

  • 协议:OC不支持多重继承,因而我们把某个类该实现的一系列方法定义在协议里面。协议最为常见的用途是实现委托模式。
  • 分类:利用分类,我们无须继承子类即可直接为当前类添加方法。

通过委托与数据源协议进行对象间通信

OC使用一种名叫“委托模式”的编程设计模式来实现对象间的通信,其主旨是:定义一套接口,某对象若想接受另一个对象的委托,则需遵从此接口,以便成为其委托对象。而另一个对象则可以给其委托对象回传一些信息,也可以在发生相关事件时通知委托对象。此模式可将数据与业务逻辑解耦,OC中一般通过协议来实现此模式。

具体示例:获取网络数据的类(EOCNetworkFetcher)含有一个委托对象(EOCDataModel),在获取完数据之后回调这个委托对象。

在这里插入图片描述

1#import <Foundation/Foundation.h>
2@class EOCNetworkFetcher;
3
4@protocol EOCNetworkFetcherDelegate <NSObject>
5
6- (void)networkFetcher:(EOCNetworkFetcher *)fetcher didReceiveData:(NSData *)data;
7- (void)networkFetcher:(EOCNetworkFetcher *)fetcher didFailWithError:(NSError *)error;
8
9@end
10
11@interface EOCNetworkFetcher : NSObject
12
13@property (nonatomic, weak) id<EOCNetworkFetcherDelegate> delegate;
14
15@end
16

一定要注意这个属性必须是weak,因为两者之间必须为“非拥有关系”。通常情况下,扮演delegate的那个对象也要持有本对象,直到用完本对象之后,才会释放

如果声明属性时用strong了,那么就会引入“保留环”。

保留环实则就是强引用,使两个或多个对象互相强引用对方,导致它们都无法被系统释放,造成内存泄漏。

在这里插入图片描述

委托协议中的方法一般都是可选的,用关键字@optional标注。

如果要在委托对象上调用可选方法,必须提前判断该委托对线是否相应相关选择子。例如:

在这里插入图片描述

使用respondsToSelector:方法来判断委托对象是否实现相关方法。实现就调用,反之不执行任何操作。

委托模式的另一用法是:用协议定义一套接口,令某类经由该接口获取其所需的数据。旨在向类提供数据,因此又称“数据源模式”。常规委托模式中,信息从类流向受委托着,而该模式中信息从数据源流向类。

在这里插入图片描述

由上可知,无论是实现委托模式还是数据源模式,如果协议中方法是可选的,那么就要用上面提到的代码检查委托对象是否能响应特定选择子。可是如果频繁执行该操作,除了第一次检测结果有效之外,后续检测可能都是多余的。如果委托对象本身没变,那么不太可能会突然响应某个原来不能响应的选择子,同样也不会突然无法响应某个原来可以响应的选择子。因此,我们通常把委托对象能否响应某个协议方法这一信息缓存起来以优化程序效率

缓存的最佳途径是使用C语言特性------“位段”数据类型。

用结构体来缓存委托对象是否能响应特定选择子。实现缓存功能所用代码写在delegate属性所对应的设置方法里:

在这里插入图片描述
这样每次调用delegate相关方法之前就不用检测委托对象是否能响应给定选择子,而是直接查询结构体里的标志。

将类的实现代码分散到便于管理的数个分类之中

实现类方法代码可能会写在一个大文件里,源代码文件过大,难以管理。可以写在分类里:

1#import <Foundation/Foundation.h>
2
3@interface EOCPerson : NSObject
4
5@property (nonatomic, copy, readonly) NSString *firstName;
6@property (nonatomic, copy, readonly) NSString *lastName;
7@property (nonatomic, strong, readonly) NSArray *friends;
8
9- (id)initWithFirstName:(NSString *)firstName andLastName:(NSString *)lastName;
10
11@end
12
1#import "EOCPerson.h"
2
3@interface EOCPerson (Friendaship)
4
5- (void)addFriend:(EOCPerson *)person;
6- (void)removeFriend:(EOCPerson *)person;
7
8@end
9
10#import "EOCPerson.h"
11
12@interface EOCPerson (Work)
13
14- (void)performDaysWork;
15- (void)takeVacationFromWork;
16
17@end
18
19#import "EOCPerson.h"
20
21@interface EOCPerson (Play)
22
23- (void)goToTheCinema;
24- (void)goToSportsGame;
25
26@end
27

勿在分类中声明属性

属性是封装数据的方式。尽管分类里可以声明属性,但也尽量避免。这是因为除了“class-continuation分类“(“私有扩展”,写在.m文件里,没有名字)之外,其他分类都无法向类中新增实例变量,也就是无法把实现属性所需的实例变量合成出来。

如果写了会出现警告:

1#import "EOCPerson.h"
2
3@interface EOCPerson (Friendaship)
4
5@property (nonatomic, strong) NSArray *friends;
6
7- (BOOL)isFriendsWith:(EOCPerson *)person;
8
9@end
10

在这里插入图片描述

这段警告是说此分类无法合成与friends属性相关的实例变量,此首需要开发者把存取方法声明为@dynamic,然后手动实现在分类中。

虽说如此,但只读属性还是可以在分类中使用的。这是因为该获取方法并不访问数据,而且属性也不需要由实例变量来实现:

1#import "EOCPerson.h"
2
3@interface EOCPerson (Friendaship)
4
5@property (nonatomic, strong, readonly) NSArray *friendship;
6
7@end
8  
9#import "EOCPerson+Friendaship.h"
10
11@implementation EOCPerson (Friendaship)
12
13- (NSArray *)friendship {
14    return @[@"1", @"2"];
15}
16
17@end
18

这样编译器也不会发出警告了。

使用“class-continuation分类“隐藏实现细节

class-continuation分类“有以下几种用法:

  1. 我们常把不需对外公布但却应该具有的办法及实例变量写在“class-continuation分类“里。“class-continuation分类“必须定义在其所接续的那个类的实现文件里,它是唯一能声明实例变量的分类,而且此分类没有特定的实现文件,其中的方法都应该定义在类的主实现文件里。该分类没有名字。

写法如下:

在这里插入图片描述

这种分类之所以能在其中定义方法和实例变量,是因为“稳固的ABI“机制,这使得我们无须知道对象大小即可使用它。

“稳固的ABI“机制:

ABI是应用程序和操作系统、编译器、库之间的二进制接口。稳固的ABI使得对象内存布局固定、方法调用规则固定。

  • 对象内存布局固定:没有稳固 ABI,类扩展新增 ivar 就可能改变对象内存大小,导致原对象访问 ivar 越界。
  • 方法调用规则固定:没有稳固 ABI,方法表布局可能随编译器版本变化,class-continuation 方法调用可能出错

即便在公共接口将其标注为private,也还是会会泄漏实现细节,这是因为这些标注只是编译器告诉开发者的,并不会从二进制上真正隐藏。而定义在class-continuation分类里可以将其真正隐藏起来,供本类使用。

  1. 编写OC++的.mm文件时,class-continuation分类也尤为有用。、
  2. class-continuation分类也可将public接口中声明为“只读”的属性扩展为“可读写”,以便在类的内部设置其值。
1#import <Foundation/Foundation.h>
2
3@interface EOCPerson : NSObject
4
5@property (nonatomic, copy, readonly) NSString *firstName;
6@property (nonatomic, copy, readonly) NSString *lastName;
7
8@end
9
1#import "EOCPerson.h"
2
3@interface EOCPerson()
4
5@property (nonatomic, copy, readwrite) NSString *firstName;
6@property (nonatomic, copy, readwrite) NSString *lastName;
7
8@end
9
10@implementation EOCPerson
11
12@end
13

这样就可以使得既能令外界无法修改对象,又能在其内部按照需要管理其数据。

  1. 若对象所遵从的协议只应视为私有,则在“class-continuation分类“中声明。

通过协议提供匿名对象

协议可在某种程度上提供匿名对象。具体的对象类型可淡化为遵从某协议的id类型,协议里规定了对象所应实现的方法。用法有:

  • 可使用匿名对象来隐藏类型名称。
  • 如果具体类型不重要,重要的是对象能够响应特定方法,那么可以使用匿名对象来表示。

【iOS】Effective Objective-C第四章》 是转载文章,点击查看原文


相关推荐


【Linux】 Ubuntu 与 CentOS 新手安装指南,避坑要点全总结
我不是呆头2026/3/19

【Linux】Ubuntu 与 CentOS 新手安装指南,避坑要点全总结 摘要 (Abstract) 踏入 Linux 世界的第一步,往往是令人望而生畏的“安装”。在众多发行版中,Ubuntu 和 CentOS 无疑是两个最常被提及的名字:一个(Ubuntu)是桌面和开发者的宠儿,另一个(CentOS)则是企业级服务器的标杆。然而,对于新手而言,从选择版本、制作启动盘到最关键的磁盘分区,每一步都暗藏“坑点”。本文是一篇面向零基础新手的“避坑”指南,旨在通过详细对比 Ubuntu 和


人工智能、机器学习和深度学习,其实不是一回事
IvanCodes2026/3/10

一、人工智能、机器学习与深度学习的真正区别 在当今科技领域,我们经常听到人工智能、机器学习和深度学习这三个词。它们虽然相关,但含义不同。 1.1 人工智能 人工智能是计算机科学的一个分支,旨在研究如何合成与分析能够像人一样行动的计算主体。简单来说,AI 的目标是利用计算机来模拟甚至替代人类大脑的功能。 一个理想的 AI 系统通常具备以下特征:像人一样思考、 像人一样行动、理性地思考与行动。 1.2 机器学习 机器学习是实现人工智能的一种途径。它的核心定义是:赋予计算机在没有被显式


零基础搭建WordPress网站完整流程
柠檬味的Cat2026/3/2

WordPress(简称WP)作为全球占比超43%的开源内容管理系统(CMS),凭借免费易用、插件生态丰富、可扩展性强的优势,成为个人博客、企业官网、技术文档站的首选建站工具。本文针对CSDN新手用户,梳理从前期准备到网站上线、后期优化的全流程,每一步都附具体操作和避坑提示,无需专业开发基础,跟着做就能快速搭建属于自己的WP网站。 核心流程概览:准备工作(域名+服务器)→ 服务器环境配置 → WordPress程序安装 → 网站基础设置 → 主题与插件配置 → 安全加固与性能优化 → 网站上线


构建工具的第三次革命:从 Rollup 到 Rust Bundler,我是如何设计 robuild 的
sunny_2026/2/22

本文将从第一人称实战视角,深入探讨前端构建工具的技术演进,以及我在设计 robuild 过程中的架构思考与工程实践。 引言:为什么我们需要又一个构建工具? 在开始正文之前,我想先回答一个无法回避的问题:在 Webpack、Rollup、esbuild、Vite 已经如此成熟的今天,为什么还要设计一个新的构建工具? 答案很简单:库构建与应用构建是两个本质不同的问题域。 Webpack 为复杂应用而生,Vite 为开发体验而生,esbuild 为速度而生。但当我们需要构建一个 npm 库时,我们需


基于华为openEuler系统部署Gitblit服务器
江湖有缘2026/2/14

基于华为openEuler系统部署Gitblit服务器 前言一、相关服务介绍1.1 openEuler系统介绍1.2 Gitblit介绍 二、本次实践介绍2.1 本次实践介绍2.2 本次环境规划 三、本地环境检查3.1 检查系统版本3.2 检查内核版本3.3 检查本地IP3.4 检查Docker环境 四、下载Gitblit软件包4.1 新建安装目录4.2 下载Gitblit软件包 五、部署Gitblit服务器4.1 修改配置文件4.2 修改service-centos.sh文件


Jira部署在Windows完整流程
WangShade2026/2/5

目录 1 本文目标2 安装文件3 安装Jira4 安装java5 安装Mysql 8.05.1 解压Mysql安装包5.2 配置环境变量5.3 安装依赖5.4 安装mysql服务5.5 修改配置my.ini5.6 启动mysql5.7 访问数据库并修改密码5.8 安装驱动 6 配置java-agent6.1 查询Jira服务名称6.2 配置Java Agent 7 配置Jira7.1 生成注册码7.2 查看工作成果 8 生成插件注册码 1 本文目标 提供完整的软件安装包


grep一下
J船长2026/1/27

grep 实战指南:把日志过滤一下 grep (缩写来自Globally search a Regular Expression and Print)是一种强大的文本搜索工具,它能使用特定模式匹配(包括正则表达式)搜索文本,并默认输出匹配行。 0. 准备:创建示例日志文件 在终端执行: nano test.log 粘贴下面内容: 2026-01-27 10:01:12 INFO App started 2026-01-27 10:01:15 INFO User login success


如何将 Safari 标签转移到新 iPhone 17?
TheNextByte12026/1/18

当换用新 iPhone 17时,很多人都希望将 Safari 标签页无缝转移到新 iPhone 上,以便继续浏览未完成的网页内容。如何将 Safari 标签转移到另一部 iPhone?本文将介绍几种方法来帮助您轻松转移 Safari 标签页。 第 1 部分:如何通过 Handoff 将 Safari 标签转移到新 iPhone Handoff 是 Apple 设备之间强大的连续性功能之一,允许用户跨设备无缝传输任务,包括 Safari 选项卡。如果您想知道如何将 Safari 标签转移到另一


windows2025服务器系统如何开启多人远程?
网硕互联的小客服2026/1/10

在 Windows Server 2025 系统中,为了支持多人远程桌面会话,需要正确配置远程桌面服务(RDS,Remote Desktop Services)。Windows服务器系统默认只允许两个管理员会话用于远程管理。如果需要开启多人远程桌面功能(允许多个用户同时连接),需配置远程桌面会话主机(RDSH)或通过调整策略实现。 以下是实现多人远程桌面功能的详细步骤: 一、通过远程桌面服务(RDS)实现多人远程 Windows Server 提供了 远程桌面服务(RDS),这是开启多


赫蹏(hètí):为中文网页内容赋予优雅排版的开源利器
修己xj2026/1/2

fHJ9cZeOp.jpg 在当今信息爆炸的时代,内容呈现的形式往往决定了阅读体验的优劣。对于中文网站来说,一个长期存在的挑战是如何实现符合传统中文排版美学的网页展示。尽管现代CSS技术已经十分强大,但针对中文特点的排版优化仍然不够完善。今天,我们将介绍一个专门为解决这一问题而生的开源项目——赫蹏(hètí)。 什么是赫蹏? 赫蹏是一个专为中文内容展示设计的排版样式增强库,名称取自古代对纸张的雅称。这个项目由开发者Sivan创建,基于通行的中文排版规范,旨在为网站的读者提供更加舒适、专业的文章阅

首页编辑器站点地图

本站内容在 CC BY-SA 4.0 协议下发布

Copyright © 2026 XYZ博客