ReactiveObjC 响应函数式框架 简单实用

本文介绍了ReactiveObjC响应函数式框架的用途和基本使用方法,包括监听按钮点击、通知事件、对象属性变化,以及信号的组合、映射和其他操作。通过ReactiveObjC,可以简化异步编程,处理事件驱动的数据源,实现并行独立工作的简化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


ReactiveObjC 响应函数式框架

ReactiveObjC 是什么? 

RAC,简单来说,就是信号,提供RACSignal捕捉当前值和将来值的信号,通过对信号进行链接,组合和反应,软件可以以声明方式编写,而不需要连续观察和更新值的代码

// When self.username changes, logs the new name to the console.
//
// RACObserve(self, username) creates a new RACSignal that sends the current
// value of self.username, then the new value whenever it changes.
// -subscribeNext: will execute the block whenever the signal sends a value.
[RACObserve(self, username) subscribeNext:^(NSString *newName) {
	NSLog(@"%@", newName);
}];


ReactiveObjC 什么时候用? 

响应式编程,处理异步或事件驱动的数据源,链接依赖的操作,并行独立的工作,简化收集转换

@weakify(self);    @strongify(self);


ReactiveObjC的简单使用

1.监听按钮点击事件

@weakify(self)

[[self.submitBnt rac_signalForControlEvents:UIControlEventTouchUpInsidesubscribeNext:^(__kindof UIControl * _Nullable x) {

        @strongify(self)

        //按钮点击后响应内容

}];

 

2.监听通知事件

@weakify(self)

[[[[NSNotificationCenter defaultCenterrac_addObserverForName:KGestureLoginSueccessNotification object:niltakeUntil:[self rac_willDeallocSignal]]subscribeNext:^(NSNotification * _Nullable x) {

        @strongify(self)

        //接受通知后响应内容

}];

 

3.监听某一对象的属性变化

宏定义RACObserve监听某个对象的某个属性的改变

[[[RACObserve(self.viewModel, page) distinctUntilChangeddeliverOnMainThread]

     subscribeNext:^(id  _Nullable x) {

        @strongify(self)

        //响应内容

    }];

 

4.多个信号的处理

//当需要请求多个数据,在所有数据请求完成之后才进行更新UI或者其他操作。相当于多线程组

RACSignal *request1 = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber)

                               {

                                   [subscriber sendNext:@1];

                                   [subscriber sendCompleted];

                                   return nil;

                               }];

RACSignal *request2 = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber)

                               {

                                   [subscriber sendNext:@2];

                                   [subscriber sendCompleted];

                                   return nil;

                               }];

RACSignal *request3 = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber)

                               {

                                   [subscriber sendNext:@3];

                                   [subscriber sendCompleted];

                                   return nil;

                               }];

//使用注意:几个信号,参数一的方法中就有几个参数,每个参数与此方法参数二中的数组一一对应。

[self rac_liftSelector:@selector(qb_updateDateWithRequest1:request2:request3:)

      withSignalsFromArray:@[request1, request2, request3]];

//响应方法

- (void)qb_updateDateWithRequest1:(id)data1 request2:(id)data2 request3:(id)data3

{

    //对应操作

}

 

5.接收代理信号

//注入实现的方法,只能返回“void”类型

[[self rac_signalForSelector:@selector(setButtonAction:)

                     fromProtocol:@protocol(CenterHeaderCellDelegate)]

             subscribeNext:^(RACTuple * _Nullable x) {

                 //RACTuple可理解为数组,返回的为方法中的参数

            }];

 

6.将信号分配给某一对象的属性,设置给定的信号。当信号完成时,分配的对象属性发生改变

RAC(self.footer.agreeBtn, enabled) = [RACSignal combineLatest:@[cell.codeTextField.rac_textSignal]

                             reduce:^id (NSString *codeString)

                   {

                     //监听文本框,当输入满足条件时,改变分配按钮的点击状态

                          return @(codeString.length >=6);

                   }];

//也可以多个条件聚合成一个,满足某一条件时,改变分配对象的属性

RAC(self.button, enabled) = [RACSignal combineLatest:@[self.nameTextField.rac_textSignal,

                                                 self.IdTextField.rac_textSignal]

                                  reduce:^id(NSString *name,NSString *idString){

                                      return @(idString.length > 0 && idString.length > 0);

                                  }];

7.//map把原信号的值映射成一个新的值(还有其它concat(顺序),then(连接),merge(合并),zipWith(压缩),reduce(聚合)等信号操作方法,详情查询API)

[[self.textField.rac_textSignal map:^id _Nullable(NSString * _Nullable value) {

        value = [NSString stringWithFormat:@"are you ok +%@", value];

        return value;

}] subscribeNext:^(id  _Nullable x) {

        NSLog(@"%@", x);

}];

 

8.定时器

//定时器的2中用法

[[RACScheduler mainThreadSchedulerafterDelay:5.f schedule:^{ NSLog(@"5秒后执行"); }];

[[[RACSignal interval:2.f onScheduler:[RACScheduler mainThreadScheduler]] takeUntil:self.rac_willDeallocSignal ] subscribeNext:^(id x) { NSLog(@"每两秒执行一次"); }];


9.RAC even makes it easy to bind to the result of an asynchronous operation:

// Creates a one-way binding so that self.imageView.image will be set as the user's
// avatar as soon as it's downloaded.
//
// The hypothetical -fetchUserWithUsername: method returns a signal which sends
// the user.
//
// -deliverOn: creates new signals that will do their work on other queues. In
// this example, it's used to move work to a background queue and then back to the main thread.
//
// -map: calls its block with each user that's fetched and returns a new
// RACSignal that sends values returned from the block.
RAC(self.imageView, image) = [[[[client fetchUserWithUsername:@"joshaber"] deliverOn:[RACScheduler scheduler]]
	map:^(User *user) {
		// Download the avatar (this is done on a background queue).
		return [[NSImage alloc] initWithContentsOfURL:user.avatarURL];
	}]
	// Now the assignment will be done on the main thread.
	deliverOn:RACScheduler.mainThreadScheduler];


10.ReactiveObjC makes this pattern particularly easy:


[[[[client logIn]
	then:^{
		return [client loadCachedMessages];
	}]
	flattenMap:^(NSArray *messages) {
		return [client fetchMessagesAfterMessage:messages.lastObject];
	}]
	subscribeError:^(NSError *error) {
		[self presentError:error];
	} completed:^{
		NSLog(@"Fetched all messages.");
	}];

11.Parallelizing independent work


__block NSArray *databaseObjects;
__block NSArray *fileContents;

NSOperationQueue *backgroundQueue = [[NSOperationQueue alloc] init];
NSBlockOperation *databaseOperation = [NSBlockOperation blockOperationWithBlock:^{
	databaseObjects = [databaseClient fetchObjectsMatchingPredicate:predicate];
}];

NSBlockOperation *filesOperation = [NSBlockOperation blockOperationWithBlock:^{
	NSMutableArray *filesInProgress = [NSMutableArray array];
	for (NSString *path in files) {
		[filesInProgress addObject:[NSData dataWithContentsOfFile:path]];
	}

	fileContents = [filesInProgress copy];
}];

NSBlockOperation *finishOperation = [NSBlockOperation blockOperationWithBlock:^{
	[self finishProcessingDatabaseObjects:databaseObjects fileContents:fileContents];
	NSLog(@"Done processing");
}];

[finishOperation addDependency:databaseOperation];
[finishOperation addDependency:filesOperation];
[backgroundQueue addOperation:databaseOperation];
[backgroundQueue addOperation:filesOperation];
[backgroundQueue addOperation:finishOperation];



Github地址: https://github.com/ReactiveCocoa/ReactiveObjC


<think>嗯,用户问为什么用Ollama本地部署的DeepSeek实现可视化UI操作都需要Docker。首先,我需要理解OllamaDeepSeek各自的用途。Ollama是一个本地运行大型语言模型的工具,而DeepSeek可能是指深度求索公司的模型,比如他们的AI模型。用户想要在本地部署后通过可视化UI操作,但发现需要Docker,为什么会这样呢? 首先,我应该考虑Ollama本身的功能。Ollama主要是命令行工具,可能没有内置的Web界面。所以用户如果想要可视化UI,可能需要额外的工具,比如Open WebUI或者其他第三方前端。这时候,Docker的作用可能在于方便地部署管理这些前端服务,同时保持环境隔离。 接下来,可能涉及到的组件包括Ollama服务、前端UI、以及可能的反向代理或者其他依赖项。使用Docker容器可以将这些组件打包,解决依赖问题,避免用户手动安装配置各种环境,尤其是不同系统之间的兼容性问题。 另外,Docker容器部署能够简化部署流程,用户只需要运行一个容器,而不需要关心底层系统的具体配置。这对于不熟悉服务器运维的用户来说非常方便,降低了使用门槛。 可能还需要考虑安全性问题。通过Docker容器隔离服务,可以防止前端UI直接访问宿主机的敏感资源,提高系统的安全性。此外,版本管理更新也更容易,每个组件可以独立更新,不会影响其他部分。 用户可能有隐藏的需求,比如希望简化部署流程,或者对Docker的使用不太熟悉,所以才会问为什么必须用Docker。可能需要解释除了Docker之外的其他方法,比如手动部署前端,但这样会更复杂,容易出错。 总结一下,Docker在这里的作用主要是简化部署、解决依赖、环境隔离方便管理。用户可能需要一个更详细的步骤说明,或者替代方案,但当前问题主要是解释为什么Docker成为必要选择。</think>关于使用Docker部署Ollama+可视化UI的技术逻辑,这里为您梳理关键要点: $$\begin{array}{|c|c|} \hline \textbf{核心组件} & \textbf{Docker化的必要性} \\ \hline \text{Ollama服务} & \text{需绑定端口(默认11434),容器化避免端口冲突} \\ \hline \text{Web UI(如Open WebUI)} & \text{依赖Node.js环境,容器隔离npm版本冲突} \\ \hline \text{反向代理(Nginx)} & \text{SSL配置/路由管理通过容器预置模板简化} \\ \hline \text{模型存储卷} & \text{容器持久化存储保证模型文件不丢失} \\ \hline \end{array}$$ 具体技术实现路径示例: ```docker version: '3' services: ollama: image: ollama/ollama ports: - "11434:11434" volumes: - ollama_models:/root/.ollama webui: image: ghcr.io/open-webui/open-webui ports: - "3000:8080" depends_on: - ollama environment: - OLLAMA_API_BASE_URL=http://ollama:11434 ``` **替代方案对比**(非Docker部署痛点): 1. 手动配置Node环境时: - 需处理`npm install`时的C++编译依赖(如node-gyp) - 不同系统下可能遇到`libstdc++.so.6`等库版本问题 2. 直接二进制部署时: - 更新需要重新下载全部文件(容器只需`docker pull`) - 难以实现多版本共存(容器可指定tag运行) 建议通过Docker部署的三大优势: 1. **依赖隔离**:避免污染主机环境(如同时存在Python3.8Python3.10) 2. **快速回滚**:出现问题时执行`docker-compose down && docker-compose up -d` 3. **资源限制**:可通过`--memory=16g`精确控制内存使用量 若坚持非Docker部署,可参考以下手动配置流程: ```bash # 安装Ollama curl -fsSL https://ollama.com/install.sh | sh # 编译Open WebUI git cl
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值