Objective-C 学习笔记 | 回调

Objective-C 学习笔记 | 回调

  • Objective-C 学习笔记 | 回调
    • 运行循环
    • 目标-动作对(target-action)
    • 辅助对象
    • 通知
    • 回调与对象所有权
    • 深入学习:选择器的工作机制

参考书:《Objective-C 编程(第2版)》

Objective-C 学习笔记 | 回调

回调就是将一段代码和一个事件绑定起来,当事件发生时,就会执行那段代码。

在 Objective-C 中有 4 种方式来实现回调:

请添加图片描述

本文章将介绍如何通过前三种途径来实现回调,以及怎样根据情况选择合适的途径。

运行循环

NSRunLoop 类专门负责等待事件的发生。NSRunLoop 实例会在特定的事件发生时触发回调。

目标-动作对(target-action)

计时器使用的是目标-动作对机制。创建计时器时,要设定延迟、目标和动作。在指定延迟时间后,计时器会向设定的目标发送指定的消息。

创建一个程序,每隔 2 秒,NSTimer 对象会向其目标(BNRLogger)发送指定的动作消息。如下图所示:

请添加图片描述

main.m:

#import <Foundation/Foundation.h>
#import "BNRLogger.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        BNRLogger *logger = [[BNRLogger alloc] init];
        // __unused 修饰符,消除编译器警告
        __unused NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:2.0
                            target:logger // logger 是 timer 的目标
                            selector:@selector(updateLastTime:) // 传递动作消息的名称
                            userInfo:nil
                            repeats:YES];
        [[NSRunLoop currentRunLoop] run];
    }
    return 0;
}

BNRLogger.h:

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface BNRLogger : NSObject

@property (nonatomic) NSDate *lastTime;

- (NSString *)lastTimeString;
// 动作方法
- (void)updateLastTime:(NSTimer *)timer;

@end

NS_ASSUME_NONNULL_END

BNRLogger.m:

#import "BNRLogger.h"

@implementation BNRLogger

- (NSString *)lastTimeString
{
    // static 让所有的 BNRLogger 实例共享一个 NSDateFormatter
    static NSDateFormatter *dateFormatter = nil;
    if (!dateFormatter)
    {
        dateFormatter = [[NSDateFormatter alloc] init];
        [dateFormatter setTimeStyle:NSDateFormatterMediumStyle];
        [dateFormatter setDateStyle:NSDateFormatterMediumStyle];
        NSLog(@"created dateFormatter");
    }
    return [dateFormatter stringFromDate:self.lastTime];
}

// 动作方法总有一个实参,它是传入发送动作消息的对象
- (void)updateLastTime:(NSTimer *)timer
{
    NSDate *now = [NSDate date];
    [self setLastTime:now];
    NSLog(@"Just set time to %@", self.lastTimeString);
}

@end

运行程序,每隔 2 秒输出当前的日期和时间。

当要向一个对象发送一个回调时,使用目标-动作对。

辅助对象

我们使用一个异步的模式来使用 NSURLConnection,在异步模式下,NSURLConnection 会多次发送块状的数据,BNRLogger 实例会成为 NSURLConnection 的辅助对象,更确切的说,是委托对象。

请添加图片描述

NSURLConnection 有一套协议,协议是一系列方法声明,辅助对象可以根据协议实现这些方法。在下面的程序中,我们声明 BNRLogger 会实现 NSURLConnectionDelegate 和 NSURLConnectionDataDelegate 这两种协议方法,并实现 3 个回调方法。

main.m:

#import <Foundation/Foundation.h>
#import "BNRLogger.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        BNRLogger *logger = [[BNRLogger alloc] init];
        
        NSURL *url = [NSURL URLWithString:@"http://www.gutenberg.org/cache/epub/205/pg205.txt"];
        NSURLRequest *request = [NSURLRequest requestWithURL:url];
        // __unused 修饰符,消除编译器警告
        __unused NSURLConnection *fetchConn =
            [[NSURLConnection alloc] initWithRequest:request
                                        delegate:logger // logger 是 NSURLConnection 的委托对象
                                    startImmediately:YES];
        __unused NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:2.0
                            target:logger // logger 是 timer 的目标
                            selector:@selector(updateLastTime:) // 传递动作消息的名称
                            userInfo:nil
                            repeats:YES];
        [[NSRunLoop currentRunLoop] run];
    }
    return 0;
}

BNRLogger.h:

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface BNRLogger : NSObject
    <NSURLConnectionDelegate, NSURLConnectionDataDelegate> // 声明 BNRLogger 会实现这两种协议方法
{
    NSMutableData *_incomingData; // 保存接收的数据
}
@property (nonatomic) NSDate *lastTime;

- (NSString *)lastTimeString;
// 动作方法
- (void)updateLastTime:(NSTimer *)timer;

@end

NS_ASSUME_NONNULL_END

BNRLogger.m:

#import "BNRLogger.h"

@implementation BNRLogger

- (NSString *)lastTimeString
{
    // static 让所有的 BNRLogger 实例共享一个 NSDateFormatter
    static NSDateFormatter *dateFormatter = nil;
    if (!dateFormatter)
    {
        dateFormatter = [[NSDateFormatter alloc] init];
        [dateFormatter setTimeStyle:NSDateFormatterMediumStyle];
        [dateFormatter setDateStyle:NSDateFormatterMediumStyle];
        NSLog(@"created dateFormatter");
    }
    return [dateFormatter stringFromDate:self.lastTime];
}

// 动作方法总有一个实参,它是传入发送动作消息的对象
- (void)updateLastTime:(NSTimer *)timer
{
    NSDate *now = [NSDate date];
    [self setLastTime:now];
    NSLog(@"Just set time to %@", self.lastTimeString);
}

/** 协议方法 */
// 来自 NSURLConnectionDataDelegate 协议
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{ // 收到一定字节的数据后就被调用
    NSLog(@"received %lu bytes", [data length]);
    if (!_incomingData)
    {
        _incomingData = [[NSMutableData alloc] init];
    }
    [_incomingData appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{ // 最后一部分数据处理完毕后会被调用
    NSLog(@"Got it all!");
    NSString *str = [[NSString alloc] initWithData:_incomingData encoding:NSUTF8StringEncoding];
    _incomingData = nil;
    NSLog(@"string has %lu characters", [str length]);
    NSLog(@"The whole string is %@", str);
}

// 来自 NSURLConnectionDelegate 协议
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{ // 获取数据失败时会被调用
    NSLog (@"connection failed %@", [error localizedDescription]);
    _incomingData = nil;
}

@end

运行程序,程序会陆续收到来自 Web 服务器的数据,调用回调方法打印接收到的字节数。最后,当获取数据结束时,委托对象会收到相应的消息,打印接收字符数和完整的数据。

当要向一个对象发送多个回调时,使用符合相应协议的辅助对象。

通知

NSNotificationCenter 类是通知中心,程序中的对象可以通过通知中心将自己注册为观察者。当系统发生变化时,会向通知中心发布特定的通知,然后通知中心会将该通知转发给相应的观察者。

我们将 BNRLogger 实例注册成通知中心的观察者,使之能在系统的时区设置发生变化时能够收到相应的通知,代码如下:

// main.m
				BNRLogger *logger = [[BNRLogger alloc] init];
        
        // 通知
        [[NSNotificationCenter defaultCenter]
            addObserver:logger // 将 logger 注册为观察者
            selector:@selector(zoneChange:)
            name:NSSystemTimeZoneDidChangeNotification // 通知名
            object:nil];

// BNRLogger.m
- (void)zoneChange:(NSNotification *)note
{ // 该方法将在系统发布 NSSystemTimeZoneDidChangeNotification 通知时被调用
    NSLog(@"The system time zone has changed");
}

当要触发多个(其他对象中的)回调的对象时,使用通知。

回调与对象所有权

如果一个对象拥有一个指向回调对象的指针,而回调对象也有指针指向该对象,那么就会陷入强引用循环,这两个对象都无法释放。

请添加图片描述

所以在编写回调相关代码时,应注意以下三点。

第一,通知中心不拥有观察者,释放对象的同时要将其移出通知中心:

- (void)dealloc
{
	[[NSNotificationCenter defaultCenter] removeObserver:self];
}

第二,对象不拥有委托对象或数据源对象。如果一个对象是另一个对象的委托对象或数据源对象,那么释放该对象时应该取消所有的关联:

- (void)dealloc
{
	[windowThatBossesMeAround setDelegate:nil];
  [tableViewThatBegsForData setDataSource:nil];
}

第二,对象不拥有目标。如果一个对象是另一个对象的目标,那么释放该对象时应该将相应的目标指针置空:

- (void)dealloc
{
  [buttonThatKeepsSendingMeMessages setTarget:nil]:
}

深入学习:选择器的工作机制

当某个对象收到消息时,会向该对象的类进行查询,检查是否有与消息名称相匹配的方法。该查询过程会沿着继承层次结构向上,直到某个类回应 “我有与消息名称相匹配的方法”。

请添加图片描述

实际上,为了加快查询速度,编译器会为每个方法附上唯一的数字,查询时按数字而不是方法名,这个数字被称为选择器(selector)。

请添加图片描述

通过编译指令 @selector,可以得到与方法名对应的选择器。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/695193.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Docker 基础使用 (4) 网络管理

文章目录 Docker 网络管理需求Docker 网络架构认识Docker 常见网络类型1. bridge 网络2. host 网络3. container 网络4. none 网络5. overlay 网络 Docker 网路基础指令Docker 网络管理实操 其他相关链接 Docker 基础使用(0&#xff09;基础认识 Docker 基础使用(1&#xff09;…

数据结构---树与二叉树

个人介绍 hello hello~ &#xff0c;这里是 code袁~&#x1f496;&#x1f496; &#xff0c;欢迎大家点赞&#x1f973;&#x1f973;关注&#x1f4a5;&#x1f4a5;收藏&#x1f339;&#x1f339;&#x1f339; &#x1f981;作者简介&#xff1a;一名喜欢分享和记录学习的…

TalkingData 是一家专注于提供数据统计和分析解决方案的独立第三方数据智能服务平台

TalkingData 是一家专注于提供数据统计和分析解决方案的独立第三方数据智能服务平台。通过搜索结果&#xff0c;我们可以了解到 TalkingData 的一些关键特性和市场情况&#xff0c;并将其与同类型产品进行比较。 TalkingData 产品特性 数据统计与分析&#xff1a;提供专业的数…

复数乘法IP核的使用

一、IP核解析 在这张图片中&#xff0c;我们看到的是一个“Complex Multiplier (6.0)” IP 核的配置界面。以下是各个配置参数的详细说明&#xff1a; 1.1 Multiplier Construction Use LUTs: 选择这个选项时&#xff0c;乘法器将使用查找表&#xff08;LUTs&#xff09;来实现…

TiDB-从0到1-配置篇

TiDB从0到1系列 TiDB-从0到1-体系结构TiDB-从0到1-分布式存储TiDB-从0到1-分布式事务TiDB-从0到1-MVCCTiDB-从0到1-部署篇TiDB-从0到1-配置篇 一、系统配置 TiDB的配置分为系统配置和集群配置两种。 其中系统配置对应TiDB Server&#xff08;不包含TiKV和PD的参数&#xff0…

显示子系统,显示子前后端,linuxfb,wayland

显示前端 显示前端通常指的是在图形系统中负责生成图形数据的部分或组件。它负责接收来自应用程序或图形引擎的图形数据&#xff0c;并将其转换成适合显示的格式&#xff0c;以便发送到显示后端进行处理和输出。 显示前端的功能通常包括以下几个方面&#xff1a; 图形数据生…

好书推荐之《生成式 AI 入门与亚马逊云科技AWS实战》

最近小李哥在亚马逊云科技峰会领到了一本关于如何在云计算平台上设计、开发GenAI应用的书&#xff0c;名字叫&#xff1a;《生成式 AI 入门与亚马逊云科技AWS实战》&#xff0c;今天仔细看了下&#xff0c;发现这本书讲的真的很好&#xff01;他涵盖了当下AI领域所有热门的技术…

探究IOC容器刷新环节初始化前的预处理

目录 一、IOC容器的刷新环节快速回顾 二、初始化前的预处理prepareRefresh源码分析 三、初始化属性源 &#xff08;一&#xff09;GenericWebApplicationContext初始化属性源 &#xff08;二&#xff09;StaticWebApplicationContext初始化属性源 四、初始化早期事件集合…

31、matlab卷积运算:卷积运算、二维卷积、N维卷积

1、conv 卷积和多项式乘法 语法 语法1&#xff1a;w conv(u,v) 返回向量 u 和 v 的卷积。 语法2&#xff1a;w conv(u,v,shape) 返回如 shape 指定的卷积的分段。 参数 u,v — 输入向量 shape — 卷积的分段 full (默认) | same | valid full&#xff1a;全卷积 ‘same…

简记:为Docker配置服务代理

简记 为Docker配置服务代理 - 文章信息 - Author: 李俊才 (jcLee95) Visit me at CSDN: https://jclee95.blog.csdn.netMy WebSite&#xff1a;http://thispage.tech/Email: 291148484163.com. Shenzhen ChinaAddress of this article:https://blog.csdn.net/qq_28550263/art…

NLP实战入门——文本分类任务(TextRNN,TextCNN,TextRNN_Att,TextRCNN,FastText,DPCNN,BERT,ERNIE)

本文参考自https://github.com/649453932/Chinese-Text-Classification-Pytorch?tabreadme-ov-file&#xff0c;https://github.com/leerumor/nlp_tutorial?tabreadme-ov-file&#xff0c;https://zhuanlan.zhihu.com/p/73176084&#xff0c;是为了进行NLP的一些典型模型的总…

论文阅读——MIRNet

项目地址&#xff1a; GitHub - swz30/MIRNet: [ECCV 2020] Learning Enriched Features for Real Image Restoration and Enhancement. SOTA results for image denoising, super-resolution, and image enhancement.GitHub - soumik12345/MIRNet: Tensorflow implementation…

idea打开hierarchy面板

hierarchy&#xff1a;查看类层级关系图 不同版本的IDEA的快捷键不一样&#xff0c;同时如果修改了IDEA快捷键&#xff0c;也可能会不一样&#xff0c;具体查看可通过IDEA上方的Navigate来查看navigate--Type Hierarchy&#xff0c;就可以看见其快捷键了&#xff0c;我的快捷键…

简单记录玩4399游戏flash插件问题

一、因谷歌浏览器默认禁止flash插件自动运行,所以玩家在使用谷歌浏览器,访问www.4399.com平台页面或者4399小游戏(flash资源)时,可能会出现加载异常的情况。今天教大家如何开启flash插件 二、下载falsh官方插件 地址:Flash Player官方下载中心-Flash中国官网 三、如果您…

【IoT NTN】3GPP R18中关于各类IoT设备在NTN中的增强和扩展

博主未授权任何人或组织机构转载博主任何原创文章&#xff0c;感谢各位对原创的支持&#xff01; 博主链接 本人就职于国际知名终端厂商&#xff0c;负责modem芯片研发。 在5G早期负责终端数据业务层、核心网相关的开发工作&#xff0c;目前牵头6G技术研究。 博客内容主要围绕…

动态路由协议RIP(思科、华为)

#交换设备 动态路由协议RIP 路由协议 静态路由 需要管理员手动配置 动态路由 是在路由器上启用某动态路由协议&#xff0c;进行自己直连网段的宣告&#xff0c;从而相邻的路由器就可以学习的相邻的路由器所宣告的网段&#xff0c;每一台路由器都把自己直连的网段宣告出去&am…

LINUX网络FTP服务

一、FTP服务 FTP服务&#xff1a;file transfer protocol :文件传输协议。在网络上进行双向传输&#xff0c;也是一个应用程序。不同的操作系统有不同的FTP软件&#xff0c;但使用的协议是一样的。 FTP协议基于TCP协议&#xff0c;有两个端口&#xff0c;即20和21。 20端口&…

《大道平渊》· 拾壹 —— 商业一定是个故事:讲好故事,员工奋发,顾客买单。

《大道平渊》 拾壹 "大家都在喝&#xff0c;你喝不喝&#xff1f;" 商业一定是个故事&#xff0c;人民群众需要故事。 比如可口可乐的各种故事。 可口可乐公司也只是被营销大师们&#xff0c; 作为一种故事载体&#xff0c;发挥他们的本领。 营销大师们开发故事…

《精通ChatGPT:从入门到大师的Prompt指南》第11章:Prompt与AI的未来

第11章&#xff1a;Prompt与AI的未来 11.1 技术发展的新方向 在迅速发展的人工智能领域&#xff0c;Prompt工程作为与AI模型交互的核心方式&#xff0c;正处于技术创新的前沿。未来几年&#xff0c;Prompt工程将沿着多个新方向发展&#xff0c;这些方向不仅会改变我们与AI互动…

SOA的设计模式_2.企业服务总线模式

1.企业服务总线&#xff08;|Enterprise Service Bus&#xff0c;ESB&#xff09; 在企业基于SOA实施EAI、B2B和BMP的过程中&#xff0c;如果采用点对点的集成方式存在着复杂度高&#xff0c;可管理性差&#xff0c;复用度差和系统脆弱等问题。企业服务总线&#xff08;…