【iOS架构】iOS ReactiveCocoa函数响应式编程

article/2025/10/12 18:11:52

声明式编程

声明式编程(declarative programming)是一种编程范型,与命令式编程相对立。它描述目标的性质,让电脑明白目标,而非流程。声明式编程不用告诉电脑问题领域,从而避免随之而来的副作用,大幅简化了并行计算的编写难度。而指令式编程则需要用算法来明确的指出每一步该怎么做。

声明式语言包括数据库查询语言(SQL,XQuery),正则表达式,逻辑编程,函数式编程和组态管理系统。

Objective-C和C是命令式编程语言(imperative programming language),程序员得按计算机执行顺序写好一行行语句,产生的行为就是执行这些指令。如果开发者写的语句和顺序都没有错,那么产生的行为就应该能满足项目的需要。

然而,这种命令式的编程通常会有缺陷,一般我们会用手动或自动化测试来发现并减少这些问题。但有另外一种更好的方法,把这些指令都抽象出来,并将重心放在所需行为上,这就是声明式编程(declarative programming)。

命令式编程让开发者将重心放在如何(how)写程序来实现需求。而声明式编程让开发者将重心放在描述需求是什么(what)。

响应式编程

响应式编程是一种面向数据流和变化传播的编程范式。这意味着可以在编程语言中很方便地表达静态或动态的数据流,而相关的计算模型会自动将变化的值通过数据流进行传播。

举个例子

命令式编程:

以C语言代码举个例子

int a = 2;
int b = 2;
int c = a + b;
printf("c = %d",c);

显然输出结果是“c = 3”。
如果改变一下代码,增加一行。

int a = 2;
int b = 2;
int c = a + b;
a++;
printf("c = %d",c);

显然输出结果依然是“c = 4”。

如果我们希望c永远等于a和b的和,那么目前看来唯一的方法是每次a和b发生变化的时候,重新执行c = a + b

响应式编程:

在响应式编程中,a的值会随着b或c的更新而更新。

Excel就是响应式编程的一个例子。单元格可以包含字面值或类似”=B1+C1″的公式,而包含公式的单元格的值会依据其他单元格的值的变化而变化 。

iOS开发中,Objective-C提供了KVO机制,而ReactiveCocoa框架利用了这个机制,并且进行了各种各样的拓展。

什么是 ReactiveCocoa

ReactiveCocoa(其简称为 RAC)是由 Github 在2012年开源的一个应用于 iOS 和 OS X 开发的框架,目前最新版本为v4.0.0-alpha.4,支持OS X 10.9+ and iOS 8.0+。RAC 具有函数式编程和响应式编程的特性。它主要吸取了 .Net 的 Reactive Extensions 的设计和实现。

Wikipedia:

Functional reactive programming (FRP) is a programming paradigm for reactive programming (asynchronous dataflow programming) using the building blocks of functional programming (e.g. map, reduce, filter).

FRP has been used for programming graphical user interfaces (GUIs), robotics, and music, aiming to simplify these problems by explicitly modeling time.

ReactiveCocoa for a better world

Josh Abernathy, GitHub staff, May 5, 2012

RAC is a framework for composing and transforming sequences of values.

ReactiveCocoa gives us a lot of cool stuff:

  1. The ability to compose operations on future data.

  2. An approach to minimize state and mutability.

  3. A declarative way to define behaviors and the relationships between properties.

  4. A unified, high-level interface for asynchronous operations.

  5. A lovely API on top of KVO.

FRP的核心是信号,信号在ReactiveCocoa中是通过RACSignal来表示的,信号是数据流,可以被绑定和传递。

可以把信号想象成水龙头,只不过里面不是水,而是玻璃球(value),直径跟水管的内径一样,这样就能保证玻璃球是依次排列,不会出现并排的情况(数据都是线性处理的,不会出现并发情况)。水龙头的开关默认是关的,除非有了接收方(subscriber),才会打开。这样只要有新的玻璃球进来,就会自动传送给接收方。

  • 可以在水龙头上加一个过滤嘴(filter),不符合的不让通过。
  • 也可以加一个改动装置,把球改变成符合自己的需求(map)。
  • 也可以把多个水龙头合并成一个新的水龙头(combineLatest:reduce:),这样只要其中的一个水龙头有玻璃球出来,这个新合并的水龙头就会得到这个球。

ReactiveCocoa原理

Signal & Subscriber

Signal(信号)是ReactiveCocoa中的核心。一个signal代表着一系列事件(事件流stream)的一个事件(event)。Subscribing(订阅)是访问signal的接口。

对于一个signal来说,刚刚创建的时候,它还是一个冷信号(Cold signal),只有在有了订阅者(Subscriber)之后,才会变为热信号(Hot signal)。订阅者就好比水龙头最下方的水盆,只有放好了水盆,水龙头才能打开。不然,水(value)不都浪费了么?

Subscribers subscribe to signals. Signals send their subscribers ‘next’, ‘error’, and ‘completed’ events.

在一个信号(Signal)的生命周期中,可以发送无数次next事件,和唯一一次complete或者error事件。

Signal:


// RACSignal.m
- (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock error:(void (^)(NSError *error))errorBlock completed:(void (^)(void))completedBlock {NSCParameterAssert(nextBlock != NULL);NSCParameterAssert(errorBlock != NULL);NSCParameterAssert(completedBlock != NULL);RACSubscriber *o = [RACSubscriber subscriberWithNext:nextBlock error:errorBlock completed:completedBlock];return [self subscribe:o];
}

Subscriber:

@protocol RACSubscriber <NSObject>
@required
/// Sends the next value to subscribers.
///
/// value - The value to send. This can be `nil`.
- (void)sendNext:(id)value;/// Sends the error to subscribers.
///
/// error - The error to send. This can be `nil`.
///
/// This terminates the subscription, and invalidates the subscriber (such that
/// it cannot subscribe to anything else in the future).
- (void)sendError:(NSError *)error;/// Sends completed to subscribers.
///
/// This terminates the subscription, and invalidates the subscriber (such that
/// it cannot subscribe to anything else in the future).
- (void)sendCompleted;/// Sends the subscriber a disposable that represents one of its subscriptions.
///
/// A subscriber may receive multiple disposables if it gets subscribed to
/// multiple signals; however, any error or completed events must terminate _all_
/// subscriptions.
- (void)didSubscribeWithDisposable:(RACCompoundDisposable *)disposable;
@end// A simple block-based subscriber.
@interface RACSubscriber : NSObject <RACSubscriber>// Creates a new subscriber with the given blocks.
+ (instancetype)subscriberWithNext:(void (^)(id x))next error:(void (^)(NSError *error))error completed:(void (^)(void))completed;@end

- (void)sendNext:(id)value方法为例看一看它的实现:

- (void)sendNext:(id)value {@synchronized (self) {void (^nextBlock)(id) = [self.next copy];if (nextBlock == nil) return;nextBlock(value);}
}

其实最核心的功能即时调用了自己的nextBlock并传入相应的参数而已。

UITextField创建信号的原理:

// UITextField (RACSignalSupport)
- (RACSignal *)rac_textSignal {@weakify(self);return [[[[[RACSignaldefer:^{@strongify(self);return [RACSignal return:self];}]concat:[self rac_signalForControlEvents:UIControlEventAllEditingEvents]]map:^(UITextField *x) {return x.text;}]takeUntil:self.rac_willDeallocSignal]setNameWithFormat:@"%@ -rac_textSignal", self.rac_description];
}// UIControl (RACSignalSupport)
- (RACSignal *)rac_signalForControlEvents:(UIControlEvents)controlEvents {@weakify(self);return [[RACSignalcreateSignal:^(id<RACSubscriber> subscriber) {@strongify(self);[self addTarget:subscriber action:@selector(sendNext:) forControlEvents:controlEvents];[self.rac_deallocDisposable addDisposable:[RACDisposable disposableWithBlock:^{[subscriber sendCompleted];}]];return [RACDisposable disposableWithBlock:^{@strongify(self);[self removeTarget:subscriber action:@selector(sendNext:) forControlEvents:controlEvents];}];}]setNameWithFormat:@"%@ -rac_signalForControlEvents: %lx", self.rac_description, (unsigned long)controlEvents];
}

UITextField用例:

    [[self.nameTextField.rac_textSignal distinctUntilChanged] subscribeNext:^(NSString *x) {@strongify(self);self.viewModel.nameText = x;}];

我们执行了subscribeNext方法创建了一个订阅者(Subscriber),这个订阅者的nextBlcok方法已经被赋值。而rac_textSignal这个信号的实现中,在每次text发生变化的时候,就会调用订阅者的sendNext方法,从而调用nextBlcok中的代码。

RACSignal的subscription过程

RACSignal的Subscription过程概括起来可以分为三个步骤:

  1. 通过[RACSignal createSignal]来获得signal
  2. 通过[signal subscribeNext:]来获得subscriber,然后进行subscription
  3. 进入didSubscribe,通过[subscriber sendNext:]来执行next block

信号(Signal)的各种操作

作为一个信号,我们关注它的两个方面:

处理逻辑
数据内容

处理逻辑指的是创建信号的时候,它是如何通知订阅者(Subscriber)并选择发送何种事件的。数据内容指的是信号会传递给订阅者(Subscriber)什么样的数据。

如果我们需要对这些内容进行自定义的修改,那么修改原信号显然是不可行的(信号已经被创建了)。因此,这就牵涉到信号之间的转换(Map)与组合(Combine)。

ReactiveCocoa提供了对信号的各种操作。这些操作几乎都用到了FlattenMap方法。意味着返回一个被修改之后的信号。同时,几乎每个操作还调用了return方法。

//这个return不是我们用于返回一个值的return,只是名字比较像。
+ (RACSignal *)return:(id)value {return [RACReturnSignal return:value];
}

该方法的主要作用是,返回一个新的信号,不过原始信号发送事件时的value将被新的value替换。
有了对绑定(Bind)方法、FlattenMap方法和return方法的理解,基本上就可以通过自己阅读源码搞定对信号(Signal)的各种操作了。这里列出几个常用的操作。

filter

filter方法返回一个新的signal。原始信号的value被替换为了符合要求的value,从而实现了筛选、过滤的目的。是否符合要求是由传入的block决定的。即原来的信号的value,如果传入block中返回YES,则新的信号也将输出这个value。

map

map方法返回一个新的signal。原始信号的value被替换为了经过block处理的value。

distinctUntilChanged

distinctUntilChanged方法返回一个新的signal。这个signal只在value和前一个value不同的时候才会发送事件。简记为求异存同。

ignore

这个方法需要传入一个value,当信号收到一个value时,会检查是否和传入的value相同,如果相同就不会发送事件给订阅者。

skip & take

顾名思义,就是跳过(只发送)前n条数据。这里的n就是传入的参数值。

doNext

创建一个新的信号,这个信号和原始信号一模一样,不过可以在创建的过程中调用传入的block。

combineLatest: reduce:

合并若干个信号,得到一个新的信号。把那些信号的value进行处理,得到一个处理过后的value作为新的信号的value。

throttle

throttle方法返回一个新的signal。只有在给定时间原始信号没有发送next事件,这个信号才会发送一个原始信号最近的一次next事件。

通过对信号的各种操作,我们把若干个水龙头连在一起,形成了一个水管。filter像是在两个水龙头之间加了一个过滤网,只有经过过滤网的水才能出现在下一个水龙头里。map像是在水龙头间加了一个转换器,前一个水龙头流出的水经过这个转换器就变成石油了。combineLatest: reduce:则是把若干个水龙头的水一起引入一个新的水龙头……

FRP ReactiveCocoa

ReactiveCocoa 试图解决什么问题

ReactiveCocoa 可以解决以下 3 个问题:

  • 传统 iOS 开发过程中,状态以及状态之间依赖过多的问题
  • 传统 MVC 架构的问题:Controller 比较复杂,可测试性差
  • 提供统一的消息传递机制

传统 iOS 开发过程中,状态以及状态之间依赖过多的问题

我们在开发 iOS 应用时,一个界面元素的状态很可能受多个其它界面元素或后台状态的影响。

例如,在用户帐户的登录界面,通常会有 2 个输入框(分别输入帐号和密码)和一个登录按钮。如果我们要加入一个限制条件:当用户输入完帐号和密码,并且登录的网络请求还未发出时,确定按钮才可以点击。通常情况下,我们需要监听这两个输入框的状态变化以及登录的网络请求状态,然后修改另一个控件的enabled状态。

传统的写法如下(该示例代码修改自 ReactiveCocoa 官网 ) :

static void *ObservationContext = &ObservationContext;- (void)viewDidLoad {[super viewDidLoad];[LoginManager.sharedManager addObserver:selfforKeyPath:@"loggingIn"options:NSKeyValueObservingOptionInitialcontext:&ObservationContext];[self.usernameTextField addTarget:self action:@selector(updateLogInButton)forControlEvents:UIControlEventEditingChanged];[self.passwordTextField addTarget:self action:@selector(updateLogInButton)forControlEvents:UIControlEventEditingChanged];
}- (void)updateLogInButton {BOOL textFieldsNonEmpty = self.usernameTextField.text.length > 0 && self.passwordTextField.text.length > 0;BOOL readyToLogIn = !LoginManager.sharedManager.isLoggingIn && !self.loggedIn;self.logInButton.enabled = textFieldsNonEmpty && readyToLogIn;
}- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)objectchange:(NSDictionary *)change context:(void *)context {if (context == ObservationContext) {[self updateLogInButton];} else {[super observeValueForKeyPath:keyPath ofObject:objectchange:change context:context];}
}

RAC 通过引入信号(Signal)的概念,来代替传统 iOS 开发中对于控件状态变化检查的代理(delegate)模式或 target-action 模式。因为 RAC 的信号是可以组合(combine)的,所以可以轻松地构造出另一个新的信号出来,然后将按钮的enabled状态与新的信号绑定。如下所示:

RAC(self.logInButton, enabled) = [RACSignalcombineLatest:@[self.usernameTextField.rac_textSignal,self.passwordTextField.rac_textSignal,RACObserve(LoginManager.sharedManager, loggingIn),RACObserve(self, loggedIn)] reduce:^(NSString *username, NSString *password, NSNumber *loggingIn, NSNumber *loggedIn) {return @(username.length > 0 && password.length > 0 && !loggingIn.boolValue && !loggedIn.boolValue);}];

可以看到,在引入 RAC 之后,以前散落在action-target或 KVO 的回调函数中的判断逻辑被统一到了一起,从而使得登录按钮的enabled状态被更加清晰地表达了出来。

除了组合(combine)之外,RAC 的信号还支持链式(chaining)和过滤(filter),以方便将信号进行进一步处理。

[[self.textField.rac_textSignal filter:^BOOL(NSString*value) {  return [value length]>= 3;  
}] subscribeNext:^(NSString*value) {  NSLog(@"Text field has been updated: %@", value);  
}];  

试图解决 MVC 框架的问题

对于传统的 Model-View-Controller (MVC)的框架,Controller 很容易变得比较庞大和复杂。由于 Controller 承担了 Model 和 View 之间的桥梁作用,所以 Controller 常常与对应的 View 和 Model 的耦合度非常高,这同时也造成对其做单元测试非常不容易,对 iOS 工程的单元测试大多都只在一些工具类或与界面无关的逻辑类中进行。

MVC

RAC 的信号机制很容易将某一个 Model 变量的变化与界面关联,所以非常容易应用 Model-View-ViewModel(MVVM) 框架。通过引入 ViewModel 层,然后用 RAC 将 ViewModel 与 View 关联,View 层的变化可以直接响应 ViewModel 层的变化,这使得 Controller 变得更加简单,由于 View 不再与 Model 绑定,也增加了 View 的可重用性。

在MVVM体系中,Controller可以被看成View,所以它的主要工作是处理布局、动画、接收系统事件、展示UI。ViewModel直接与View绑定,而且对View一无所知。拿做菜打比方的话,ViewModel就是调料,它不关心做的到底是什么菜。当Model的API有变化,或者由本地存储变为远程API调用时,ViewModel的public API可以保持不变。

因为引入了 ViewModel 层,所以单元测试可以在 ViewModel 层进行,iOS 工程的可测试性也大大增强了。

MVVM

Github 开源ReactiveViewModel

统一消息传递机制

ReactiveCocoa is inspired by functional reactive programming. Rather than using mutable variables which are replaced and modified in-place, RAC offers “event streams,” represented by the Signal and SignalProducer types, that send values over time.

Event streams unify all of Cocoa’s common patterns for asynchrony and event handling, including:

  • Delegate methods

  • Callback blocks

  • NSNotifications

  • Control actions and responder chain events

  • Futures and promises

  • Key-value observing (KVO)

Because all of these different mechanisms can be represented in the same way, it’s easy to declaratively chain and combine them together, with less spaghetti code and state to bridge the gap.

iOS 开发中有着各种消息传递机制,包括 KVO、Notification、delegation、block 以及 target-action 方式。各种消息传递机制使得开发者在做具体选择时感到困惑。

RAC 将传统的 UI 控件事件进行了封装,使得以上各种消息传递机制都可以用 RAC 来完成。示例代码如下:


// KVO
[RACObserve(self, username) subscribeNext:^(id x) {NSLog(@" 成员变量 username 被修改成了:%@", x);
}];// target-action
self.button.rac_command = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {NSLog(@" 按钮被点击 ");return [RACSignal empty];
}];// Notification
[[[NSNotificationCenter defaultCenter]rac_addObserverForName:UIKeyboardDidChangeFrameNotificationobject:nil]subscribeNext:^(id x) {NSLog(@" 键盘 Frame 改变 ");}
];// Delegate
[[self rac_signalForSelector:@selector(viewWillAppear:)] subscribeNext:^(id x) {debugLog(@"viewWillAppear 方法被调用 %@", x);
}];

RAC 的RACSignal 类也提供了createSignal方法来让用户创建自定义的信号,如下代码创建了一个下载指定网站内容的信号。

-(RACSignal *)urlResults {
    return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {NSError *error;NSString *result = [NSString stringWithContentsOfURL:[NSURL URLWithString:@"http://www.devtang.com"]encoding:NSUTF8StringEncodingerror:&error];NSLog(@"download");if (!result) {[subscriber sendError:error];} else {[subscriber sendNext:result];[subscriber sendCompleted];}
        return [RACDisposable disposableWithBlock:^{NSLog(@"clean up");}];}];}

ReactiveCocoa缺点

InfoQ:使用ReactiveCocoa与直接使用 Cocoa框架相比,性能上(事件的响应速度、回调速度)是否会有影响?

花瓣网移动开发主管 李忠:

ReactiveCocoa底层的实现是比较复杂的,在性能上确实会有一定的影响。一个简单的 [signal subscribeNext: ^(id x){}] 就会有造成很深的callback stack(近40次的调用),相比纯KVO不到10次的调用,速度上慢了至少1个数量级。不过尽管如此,只要subscribe的次数不要过多,性能上还是可以接受的。在事件响应上,RAC比KVO慢了大概5倍,不过问题不大,在iPhone5上测了下,也就1ms多一点,绝大多数的使用场景都不会有问题。在开发Mac App时,可以使用Cocoa Bindings,但iOS却不支持,可能也是出于性能上的考虑。既然RAC的性能不如直接使用原生的高,还有必要用它么?我觉得还是有的,性能是我们选择框架的一个参考因素,但不是决定性的因素。开发者在足够了解RAC的情况下,RAC可以提高开发效率并帮助开发者编写更易维护的代码,这两点就值得我们去研究、使用它。

已知采用ReactiveCocoa的公司:
美团
Bilibili
花瓣网
ReactiveCocoa在花瓣客户端的实践

延伸…

ReativeCocoa vs. ReactiveX

ReactiveCocoa was originally inspired, and therefore heavily influenced, by Microsoft’s Reactive Extensions (Rx) library. There are many ports of Rx, including RxSwift, but ReactiveCocoa is intentionally not a direct port.

Where RAC differs from Rx, it is usually to:

  • Create a simpler API
  • Address common sources of confusion
  • More closely match Cocoa conventions

[Languages]

* RxJava
* RxJS
* Rx.NET
* RxScala
* RxClojure
* RxSwift
* Others

编程范式

面向代理
基于组件基于流渠道
连续式
并发计算
`声明式`(对比:命令式)`函数式`数据流面向细胞(电子表格)`响应式`面向图形目标导向约束逻辑回答集编程约束逻辑溯因逻辑归纳逻辑
事件驱动面向服务时间驱动
功能导向
函数级(对比:价值级)
`命令式`(对比:声明式)非结构化矢量(对比:标量)迭代式结构化过程式模块化递归式面向对象基于类基于原型自动机根据关注分离:`面向方面`面向主题面向角色
元编程面向属性自动泛型模板基于原则面向语言领域特定面向语法方言化意图反射式
不确定
并行计算面向过程
大规模编程与小规模编程
价值级(对比:函数级)

http://chatgpt.dhexx.cn/article/cK8emu43.shtml

相关文章

基于AntDesign Vue的响应式登录页面

为了做一个自己的前后端分离的后台管理系统&#xff0c;特地做了一下登录页面。大概的架子如下&#xff0c;后面需要替换一下顶部导航的信息。先大概贴一下代码&#xff0c;以后直接复制使用。整体的布局是自己写的样式&#xff0c;如果后面要替换为其他的UI框架&#xff0c;比…

Angular最新教程-第六节编写响应式导航栏

这节课我们讲解如何使用bootstrap 4 编写响应式布局。 参考图我们还是参照Angular中文社区http://www.angularjs.cn/ 图中标注红色的部分,我自己不是很喜欢,所以做了一点小改动。 他这里也没有做响应式布局,所以样式就不抄他的,我们自己重写。 首先我们先简要的分析一…

什么是响应式网页设计?响应式布局的实现原理

2019独角兽企业重金招聘Python工程师标准>>> 概念 响应式网页设计最初是由 Ethan Marcotte 提出的一个概念&#xff1a;为什么一定要为每个用户群各自打造一套设计和开发方案&#xff1f;Web设计应该做到根据不同设备环境自动响应及调整。当然响应式Web设计不仅仅是…

【Bootstrap】两个常用布局,居中布局和全屏左右布局,响应式布局

居中布局 居中布局&#xff0c;上面为菜单&#xff0c;下面为内容&#xff0c;内容居中&#xff0c;无论屏幕多宽&#xff0c;内容总是在中间 代码 <!DOCTYPE html> <html> <head><meta http-equiv"Content-Type" content"text/html; cha…

响应式页面实现

响应式网页设计最初是由 Ethan Marcotte 提出的一个概念&#xff1a;为什么一定要为每个用户群各自打造一套设计和开发方案&#xff1f;Web设计应该做到根据不同设备环境自动响应及调整。当然响应式Web设计不仅仅是关于屏幕分辨率自适应以及自动缩放的图片等等&#xff0c;它更…

移动端页面布局(响应式布局)以及meta标签的设置

响应式网站设计 什么是响应式布局? 1、服务器根据不同的浏览器用户端,为用户呈现不同的页面效果。 2、可以让一个网站兼容不同分辨率的设备,给用户更好的视觉使用体验。 3、移动互联网催生了响应式布局的诞生。 响应式设计优缺点 优点: 解决了设备之间的差异化展示,让不同的…

移动端基础及响应式布局

目录 1.移动端概述和hybird模式 2.响应式布局基础 3.响应式布局之流式布局 4.做移动端项目之前的准备 5.响应式布局demo 6.rem响应式布局 7.swiper的使用和轮播图 8.综合案例-微信场景应用 1.移动端概述和hybird模式 移动端&#xff1a;运行在移动设备上的产品 产品…

linux 透明图片,FreeImage 生成带透明通道的GIF

FreeImage 生成带透明通道的GIF 主要方法&#xff1a; 加载图像及读取参数 FreeImage_Load FreeImage_GetWidth FreeImage_GetHeight FreeImage_Allocate FreeImage_GetPixelColor FreeImage_SetPixelColor 保存GIF FreeImage_OpenMultiBitmap FreeImage_SetMetadata FreeImage…

freeimage转到cvmat 单通道图像转到3通道[freeimage][cvmat]

0 结果 1 代码 将freeImage转为cv::mat&#xff0c;代码如下&#xff1a; #include <FreeImage.h> #include <opencv2\opencv.hpp> using namespace cv;// #define _CRT_SECURE_NO_WARNINGS #pragma warning(disable : 4996) void FI2MAT(FIBITMAP* src, Mat&…

linux系统下替换图片,Linux(ubuntu系统)下使用FreeImage库

Linux(ubuntu系统)下使用FreeImage库 Linux(ubuntu系统)下使用FreeImage库 最近在搞一个图像处理的项目,需要用到FreeImage,之前在Windows下用过,很简单,因为FreeImage官网提供了可供使用的静态库动态库,直接包含就行了。现在需要在Linux平台下使用,发现官网并没有提供直…

计算机中缺失freeimage.dll,修复freeimage.dll

freeimage.dll是一款如果丢失会造成系统无法造成运行等问题的重要文件。今天久友下载站小编教大家一个修复freeimage.dll文件的方法&#xff0c;来久友下载站下载 freeimage.dll进行安装修复即可。Freeimage.dll是程序访问的组合文件&#xff0c;支持方便的程序打开&#xff0c…

Linux FreeImage安装编译

1.下载FreeImage 安照包 wget http://downloads.sourceforge.net/freeimage/FreeImage3170.zip #解压 unzip FreeImage3170.zip -d freeImage 2.对安装包进行编译 cd freeImagesudo make 类型转换错误解决&#xff1a; Source/LibRawLite/./internal/dcraw_common.cpp: 在…

c语言freeimage库文件,FreeImage使用方法amp;FreeImage Tutorial

前言 四大图像库&#xff1a;OpenCV&#xff1a;功能十分的强大&#xff0c;而且支持目前先进的图像处理技术&#xff0c;体系十分完善&#xff0c;操作手册很详细&#xff0c;手册首先给大家补计算机视觉的知识&#xff0c;几乎涵盖了近10年内的主流算法&#xff1b; http://s…

windows下FreeImage编译

windows下FreeImage编译 FreeImage下载 FreeImage下载地址 点击左边的“Download” 选择 “Download FreeImage 3.18.0 [WIN32/WIN64]” vs2019打开工程 下载后解压&#xff0c;直接打开&#xff1a;FreeImage.2017.sln 生成dll、lib文件 接下来编译C Wraper的 测试工程…

Qt Creator配置FreeImage库

第零步&#xff1a;前言 根据官网介绍&#xff1a; FreeImage 是一个开源库项目&#xff0c;面向希望支持当今多媒体应用程序所需的流行图形图像格式&#xff08;如 PNG&#xff0c;BMP&#xff0c;JPEG&#xff0c;TIFF 和其他格式&#xff09;的开发人员。FreeImage 易于使用…

FreeImage

freeimage.lib下载地址&#xff1a; http://freeimage.sourceforge.net/ FreeImage is an Open Source library project for developers who would like to support popular graphics image formats like PNG, BMP, JPEG, TIFF and others as needed by todays multimedia ap…

FreeImage库的配置及部分使用(windows)

1、首先下载FreeImage库&#xff0c;http://freeimage.sourceforge.net/download.html&#xff0c;下载如下 2、解压后&#xff0c;如下图所示&#xff1a; 打开2013sln&#xff0c;编译FreeImageLib这个项目&#xff08;设为启动项&#xff09;&#xff0c;根据debug/release版…

void *的使用

void * 为 “不确定类型指针”。 void *不可以解引用 &#xff08;1&#xff09;void *可以接受任何类型的赋值&#xff1a; 任何类型的指针都可以直接赋值给void *型指针&#xff0c;无需进行强制类型转换&#xff0c;相当于void *包含了其他类型的指针。 &#xff08;2&a…

C语言中void的高级应用

C语言中void的高级应用 C语言中的void关键字&#xff0c;void 表示为“无”、“空”、“没有”的意思。所以void是不能用来定义变量的&#xff0c;因为变量是需要固定的空间的 //定时时&#xff0c;编译不通过 void ch a; void a 10;一、void的常用场景 1.1 函数的参数 v…

【C语言】void 和 void* 类型

一. void 类型 1. 为何不能定义 void 类型的变量 为什么不能定义 void 类型的变量&#xff1f;因为它是空类型&#xff0c;不能够为变量提供空间吗&#xff1f; 定义变量的本质是开辟空间&#xff0c;我们用 sizeof 来计算 void 类型的大小&#xff1a; 在vs2017下&#xf…