您的位置:首页 > Web前端 > React

iOS_43_Reactivecocoa

2017-12-12 16:30 519 查看
Reactivecocoa在5.0以后将 RAC 拆分为四个库:ReactiveCocoa、ReactiveSwift、ReactiveObjC、ReactiveObjCBridge。

其中的ReactiveCocoa和ReactiveObjC,前一个适用于纯Swift项目,后一个适用于纯OC项目。

若项目为Swift和OC混编,那么需要将ReactiveObjC和ReactiveCocoa都导入,同时还需要手动导入ReactiveObjCBridge。

命令行:

touch podfile

open podfile

//pod search reactivecocoa

pod search reactiveobjc

// 如果搜索不到最新的reactivecocoa,就使用下面的命令,升级pod先

pod repo update

pod install

如果pod导入三方库后,头文件不显示,

那么在TARGETS -> Build Settings -> Search Paths -> User Header Search Paths 中 写入 ${SRCROOT} 再将后面参数改为recursive,就可以解决了.

纯OC代码的 podfile示例

source 'https://github.com/CocoaPods/Specs.git'
platform :ios, ‘8.0’

use_frameworks!

target “06RAC” do
pod 'ReactiveObjC', '~> 3.0’

end


RACSignal信号与订阅者

//
//  ViewController.m
//  06RAC
//
//  Created by beyond on 2017/12/12.
//  Copyright © 2017年 beyond. All rights reserved.

#import "ViewController.h"
#import "ReactiveObjC.h"
@interface ViewController ()
// 用一个成员变量,保存一下 信号内部创建的订阅者,防止信号被自动取消订阅(以便将来自己手动用RACDisposable取消订阅)
@property (nonatomic,strong) id <RACSubscriber> subscriber;
// 用一个成员变量,保存一下,以便将来自己手动用取消订阅
@property (nonatomic,strong) RACDisposable *disposable;
@end

@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];

// 1.创建信号时,block_didSubscribe代码并未执行(只有在订阅了信号之后,才会执行block_didSubscribe)
RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {

_subscriber = subscriber;

// block_didSubscribe的作用是:描述当前信号的哪些数据需要发送
// block_didSubscribe会被保存到RACDynamicSignal的成员变量里
NSLog(@"sg_1_信号成功被激活,即将发送新的信号值");

// 3.只有订阅者RACPassthroughSubscriber才能发送信号的新值
[subscriber sendNext:@"新的信号值1"];
NSLog(@"sg_3_发送信号完毕");

return [RACDisposable disposableWithBlock:^{
// 默认会自动来到这个代码,一般作为信号被取消订阅时的清理收尾工作

// 注:但是,只要订阅者还在,就不会自动取消信号订阅(因此,要用成员变量保存一下subscriber)
NSLog(@"sg_4_清理收尾");
}];
}];

NSLog(@"sg_即将订阅信号");

// 2.只有订阅信号后,默认的冷信号才会变成热信号!
// nextBlockAfterValueChanged是当收到发送的信号改变后的新值时,要执行的代码
// 作用:2.1.创建订阅者
// 作用:2.2 .把nextBlockAfterValueChanged保存在了订阅者的成员变量里

_disposable = [signal subscribeNext:^(id  _Nullable newValue) {
// 只要[subscriber sendNext:@"新的信号值1"],就会调用这个nextBlockAfterValueChanged
NSLog(@"sg_2_当订阅的信号发生改变时,要执行的代码_%@",newValue);
}];

// 手动取消信号订阅
[_disposable dispose];

}

@end

/*
*	信号类(RACSiganl),它本身不具备发送信号的能力,而是交给内部一个<订阅者>去发送新数据。
默认一个信号都是冷信号,只有这个信号被订阅了,这个信号才会变为热信号。

*	如何订阅信号:调用信号RACSignal的subscribeNext就能订阅了。
*/

// RACSignal使用步骤:
// 1.创建信号 + (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe
// 2.订阅信号,就会激活信号. RACDynamicSignal的方法- (RACDisposable *)subscribeNext:(void (^)(id newValue))nextBlockAfterValueChanged
// 3.由订阅者 发送信号.RACPassthroughSubscriber的方法 - (void)sendNext:(id)value

// RACSignal底层实现:
// 1.创建信号时,首先把block_didSubscribe保存到信号(RACDynamicSignal)的成员变量中.

// 2.当信号被订阅,也就是调用RACDynamicSignal的方法- (RACDisposable *)subscribeNext:(void (^)(id newValue))nextBlockAfterValueChanged时.注意:nextBlockAfterValueChanged实际就是观察到信号改变后,要响应的操作
// 2.1 在上面的subscribeNext方法内部,会创建订阅者RACPassthroughSubscriber,并且把nextBlockAfterValueChanged保存到订阅者subscriber的成员变量中。
// 2.2 subscribeNext的方法内部会调用信号signal的成员变量block_didSubscribe,使信号变成了热信号.

// 3.在信号siganl的block_didSubscribe中执行[subscriber sendNext:@"signal_newValue"]方法,即可以告诉订阅者信号变化后的新值.
// 3.1 [subscriber sendNext:@"signal_newValue"]的底层于是会执行subscriber的成员变量nextBlockAfterValueChanged




RACSubject使用

//
//  ViewController.m
//  07RACSubject
//
//  Created by beyond on 2017/12/13.
//  Copyright © 2017年 beyond. All rights reserved.
//

#import "ViewController.h"
#import "ReactiveObjC.h"
@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];
// RACSubject既可以充当信号,又可以发送信号!因为继承自RACSignal并实现了RACSubscriber协议

// 1.创建RACSubject信号
RACSubject *subject = [RACSubject subject];

// 2.订阅信号(RACSubject必须先订阅,再发送信号)
// 不同的信号,处理订阅的方式不一样!!!
// RACSubject处理订阅:仅仅是将创建的订阅者 到 成员变量数组里(subscribers)
[subject subscribeNext:^(id  _Nullable newValue) {
NSLog(@"sg_第1个订阅者_newValue:%@",newValue);
}];

// 注:因为是成员变量数组,所以可以多次订阅(会创建一个新的订阅者,并加入到成员变量数组里)
[subject subscribeNext:^(id  _Nullable newValue) {
NSLog(@"sg_第2个订阅者_newValue:%@",newValue);
}];
// 3.发送信号
[subject sendNext:@"这个是要发送给订阅者的新信号值"];

// 总结:核心思想,2点:
// 底层实现:遍历成员数组里的所有订阅者,调用其nextBlock

// 执行流程:

// RACSubject被订阅,仅仅是保存创建的订阅者 到成员变量数组里(subscribers)
// RACSubject发送数据,遍历成员变量数组里的所有的订阅者,然后调用他们的nextBlock

}

@end


运行效果:



RACReplaySubject (可先发信号,再订阅信号)

//
//  ViewController.m
//  08RACReplaySubject
//
//  Created by beyond on 2017/12/26.
//  Copyright © 2017年 beyond. All rights reserved.
//

#import "ViewController.h"
#import "ReactiveObjC.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];
// 0.RACReplaySubject可先发送信号,再订阅信号

// 1.创建信号(RACReplaySubject新增了一个成员变量valuesReceived)
RACReplaySubject *replaySubject = [RACReplaySubject subject];

// 3.发送信号仅仅是:将newValue保存到valuesArray中;
// 然后调用父类RACSubject的sendNext方法:遍历所有的订阅者,将新数据发送出去
// 如果暂时还没有创建订阅者时(尚未被订阅),则不发送任何数据
[replaySubject sendNext:@"这个是新值"];
// 2.创建订阅者
// 注:会遍历valuesArray中所有的值,让新建的订阅者,[subscriber sentNext:value](即:执行下面的block)
// 目的就是:让该新建的订阅者获得valuesArray中所有的新值
[replaySubject subscribeNext:^(id  _Nullable newValue) {
NSLog(@"sg_newValue:%@",newValue);
}];
}

@end


RACSubject之代理示例

storyboard如下:



RedView如下:

//
//  RedView.h
//  09RACSubjectDemo
//
//  Created by beyond on 2017/12/26.
//  Copyright © 2017年 beyond. All rights reserved.
//

#import <UIKit/UIKit.h>
#import "ReactiveObjC.h"
@interface RedView : UIView
// 强引用
@property (nonatomic,strong) RACSubject *btnClickSignal;
- (IBAction)btnClicked:(UIButton *)sender;
@end


//
//  RedView.m
//  09RACSubjectDemo
//
//  Created by beyond on 2017/12/26.
//  Copyright © 2017年 beyond. All rights reserved.
//

#import "RedView.h"

@implementation RedView
// 懒加载
- (RACSubject *)btnClickSignal
{
if (_btnClickSignal == nil) {
// 1.创建信号
_btnClickSignal = [RACSubject subject];
}
return _btnClickSignal;
}

// 按钮点击
- (void)btnClicked:(UIButton *)sender
{
// 3.发送信号
[self.btnClickSignal sendNext:@"newValue"];

}
@end


控制器:

//
//  ViewController.h
//  09RACSubjectDemo
//
//  Created by beyond on 2017/12/26.
//  Copyright © 2017年 beyond. All rights reserved.
//

#import <UIKit/UIKit.h>
#import "RedView.h"

@interface ViewController : UIViewController
@property (nonatomic,weak) IBOutlet RedView *redView;

@end


//
//  ViewController.m
//  09RACSubjectDemo
//
//  Created by beyond on 2017/12/26.
//  Copyright © 2017年 beyond. All rights reserved.
//

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];
// 订阅信号
[_redView.btnClickSignal subscribeNext:^(id  _Nullable newValue) {
NSLog(@"sg_%@",newValue);
}];
}

@end


RACSequence集合类

model类

//
//  AnimeModel.h
//  10RAC集合类
//
//  Created by beyond on 2017/12/26.
//  Copyright © 2017年 beyond. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface AnimeModel : NSObject
@property (nonatomic,copy) NSString *anime;
@property (nonatomic,copy) NSString *actress;
@end


控制器

//
//  ViewController.m
//  10RAC集合类
//
//  Created by beyond on 2017/12/26.
//  Copyright © 2017年 beyond. All rights reserved.
//

#import "ViewController.h"
#import "ReactiveObjC.h"
#import "MJExtension.h"
#import "AnimeModel.h"
@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];
// [self tuple];
// [self sequence_arr];
// [self sequence_dic];
[self sequence_dictArrToModelArr];

}
- (void)sequence_dictArrToModelArr
{
// 将从Plist转化而来的字典数组,转成对象数组
NSArray *dictArrFromPlist = @[
@{@"anime":@"k-on",
@"actress":@"平泽唯"
},

@{@"anime":@"龙与虎",
@"actress":@"逢坂大河"
},

@{@"anime":@"未闻花名",
@"actress":@"面码"
},
@{@"anime":@"狼与香辛料",
@"actress":@"赫萝"
},
@{@"anime":@"俺妹",
@"actress":@"黑猫"
}
];
NSMutableArray *modelArr = [NSMutableArray array];
// 方法1:遍历信号的每一个值(即dict)
[dictArrFromPlist.rac_sequence.signal subscribeNext:^(NSDictionary *dict) {
AnimeModel *model = [AnimeModel mj_objectWithKeyValues:dict];
[modelArr addObject:model];
}];

// 方法2:直接mjextension
modelArr = [AnimeModel mj_objectArrayWithKeyValuesArray:dictArrFromPlist];
NSLog(@"sg_modelArr:%@",modelArr);

// 方法3:sequence高级用法map
NSArray *modelArr2 = [[dictArrFromPlist.rac_sequence map:^id _Nullable(NSDictionary *dict) {
// block返回一个对象,从遍历中的字典转化而来的对象,重新放回到sequence集合里
return [AnimeModel mj_objectWithKeyValues:dict];

// 最后将转化完成的Sequence集合,转成NSArray
}] array];
NSLog(@"sg_modelArr2:%@",modelArr2);

}
- (void)sequence_dic
{
// RAC中的集合类,用于统一数组和字典
NSDictionary *dic = @{
@"k-on":@"平泽唯",
@"龙与虎":@"逢坂大河",
@"未闻花名":@"面码",
@"狼与香辛料":@"赫萝",
@"俺妹":@"黑猫"
};
[dic.rac_sequence.signal subscribeNext:^(RACTwoTuple  *obj) {
//        NSString *key = obj[0];
//        NSString *value = obj[1];
//        NSLog(@"sg_遍历:%@_%@",key,value);

// 或者使用:强大的宏
RACTupleUnpack(NSString *key,NSString *value) = obj;
NSLog(@"sg_遍历:%@_%@",key,value);
}];
}
- (void)sequence_arr
{
// RAC中的集合类,用于统一数组和字典
NSArray *arr = @[@"k-on",@"大河",@"面码",@"赫萝",@"黑猫"];
// 1.数组先转成sequence
RACSequence *sequence = arr.rac_sequence;
// 2.sequence再转成信号
RACSignal *signal = sequence.signal;
// 3.再订阅信号,则会自动遍历
[signal subscribeNext:^(id  _Nullable obj) {
NSLog(@"sg_遍历:%@",obj);
}];

// 综上所述
[arr.rac_sequence.signal subscribeNext:^(id  _Nullable obj) {
NSLog(@"sg_遍历2:%@",obj);
}];
}

- (void)tuple
{
// 类似于NSArray,只能放对象
RACTuple *tuple = [RACTuple tupleWithObjects:@"N2",@"N1",@2018, nil];
NSLog(@"sg_%@",tuple);
NSLog(@"sg_%@",tuple[0]);
}

@end


podfile

source 'https://github.com/CocoaPods/Specs.git'
platform :ios, ‘8.0’

use_frameworks!

target “10RAC集合类” do
pod 'ReactiveObjC', '~> 3.0’
pod 'MJExtension', '~> 3.0’

end


RAC的使用场景 

storyboard如下:



控制器:

//
//  ViewController.h
//  11RAC场景
//
//  Created by beyond on 2017/12/26.
//  Copyright © 2017年 beyond. All rights reserved.
//

#import <UIKit/UIKit.h>
#import "RedView.h"
@interface ViewController : UIViewController
@property (weak, nonatomic) IBOutlet RedView *redView;
@property (weak, nonatomic) IBOutlet UIButton *btn;
@property (weak, nonatomic) IBOutlet UITextField *textField;

@end


//
//  ViewController.m
//  11RAC场景
//
//  Created by beyond on 2017/12/26.
//  Copyright © 2017年 beyond. All rights reserved.
//

#import "ViewController.h"
#import "ReactiveObjC.h"
#import "UIView+Frame.h"
// 注:rac_observeKeyPath的方法,默认情况下,并没有导入到主头文件中,需手动导入
#import "NSObject+RACKVOWrapper.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];
// 0.判断方法是否被执行
// [self rac_signal_for_selector];
// [self rac_signal_for_selector2];

// 1.rac代替代理
// [self rac_replace_delegate];

// 2.rac代替KVO
// [self rac_replace_kvo];
// [self rac_replace_kvo2];

// 3.rac监听事件,如按钮点击
// [self rac_replace_controlEvent];

// 4.rac代替通知
// [self rac_replace_notification];

// 5.监听文本框的文字
[self rac_replace_textFieldListen];

}

- (void)rac_replace_textFieldListen
{
// rac 监听 文本的改变
[[_textField rac_textSignal] subscribeNext:^(NSString * _Nullable x) {
NSLog(@"sg__textField has changed:%@",x);
}];
}
- (void)rac_replace_notification
{
// rac代替通知,将通知中心监听到的通知转成信号
[[[NSNotificationCenter defaultCenter]rac_addObserverForName:UIKeyboardWillShowNotification object:nil] subscribeNext:^(NSNotification * _Nullable concretNotification) {
/*
sg_监听到了键盘将要弹出的通知:NSConcreteNotification 0x6000002562f0 {name = UIKeyboardWillShowNotification; userInfo = {
UIKeyboardAnimationCurveUserInfoKey = 7;
UIKeyboardAnimation Duration UserInfoKey = "0.25";
UIKeyboard Bounds UserInfoKey = "NSRect: {{0, 0}, {320, 253}}";
UIKeyboard CenterBegin UserInfoKey = "NSPoint: {160, 460}";
UIKeyboard CenterEnd UserInfoKey = "NSPoint: {160, 441.5}";
UIKeyboard FrameBegin UserInfoKey = "NSRect: {{0, 352}, {320, 216}}";
UIKeyboard FrameEnd UserInfoKey = "NSRect: {{0, 315}, {320, 253}}";
UIKeyboard IsLocal UserInfoKey = 1;
}}
*/
NSLog(@"sg_监听到了键盘将要 弹出 的通知:%@",concretNotification);
}];

// rac代替通知,将通知中心监听到的通知转成信号
[[[NSNotificationCenter defaultCenter]rac_addObserverForName:UIKeyboardDidHideNotification object:nil] subscribeNext:^(NSNotification * _Nullable concretNotification) {
/*
sg_监听到了键盘将要关闭的通知:NSConcreteNotification 0x600000243ed0 {name = UIKeyboardDidHideNotification; userInfo = {

UIKeyboard Bounds UserInfoKey = "NSRect: {{0, 0}, {320, 253}}";
UIKeyboard CenterBegin UserInfoKey = "NSPoint: {160, 441.5}";
UIKeyboard CenterEnd UserInfoKey = "NSPoint: {160, 694.5}";
UIKeyboard FrameBegin UserInfoKey = "NSRect: {{0, 315}, {320, 253}}";
UIKeyboard FrameEnd UserInfoKey = "NSRect: {{0, 568}, {320, 253}}";

}}

*/
NSLog(@"sg_监听到了键盘将要 关闭 的通知:%@",concretNotification);
}];

}
- (void)rac_replace_controlEvent
{
// rac监听按钮点击事件,将事件转化成signal
[[self.btn rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(__kindof UIControl * _Nullable btn) {
// sg_监听到了按钮的controlEvent:<UIButton: 0x7fcb16507130; frame = (83.5 144; 153 30); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x610000232b20>>
NSLog(@"sg_监听到了按钮的controlEvent:%@",btn);
}];
}
- (void)rac_replace_kvo2
{
// rac代替kvo,监听redView的frame发生新值的改变
[[_redView rac_valuesForKeyPath:@"frame" observer:nil] subscribeNext:^(id  _Nullable newValue) {
// 启动时就打印了一次sg_newValue:NSRect: {{67.5, 273.5}, {240, 120}}
// touch屏幕时,又打印了一次sg_newValue:NSRect: {{0, 224}, {240, 120}}
NSLog(@"sg_newValue:%@",newValue);
}];
}
- (void)rac_replace_kvo
{
// rac代替kvo,监听redView的frame发生新值的改变
// 注意,此方法的缺点是:
// rac_observeKeyPath的方法,默认情况下,并没有导入到主头文件中,需手动导入
[_redView rac_observeKeyPath:@"frame" options:NSKeyValueObservingOptionNew observer:nil block:^(id value, NSDictionary *change, BOOL causedByDealloc, BOOL affectedOnlyLastComponent) {
// sg_NSRect: {{0, 224}, {240, 120}}
// 注意:NS开头为NextStep,与Mac开发同源
NSLog(@"sg_%@",value);
}];
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
_redView.x = 0;
}
- (void)rac_signal_for_selector2
{
// 控制器 监听redView中的按钮有没有被点击
// 好处是:监听某个对象有没有调用某个方法
// 坏处是:不能传递参数(要传递数据就用RACSubject)
[[_redView rac_signalForSelector:@selector(btnClicked:)]subscribeNext:^(RACTuple * _Nullable x) {
/*
<RACTuple: 0x61800001b890> (
"<UIButton: 0x7ff1b5e08570; frame = (137 269; 46 30); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x608000038540>>"
)
*/
NSLog(@"sg_控制器 感知到了 redView中的btnClick_%@",x);
}];
}
- (void)rac_signal_for_selector
{

// 将方法订阅成信号,从而判断方法是否被触发(执行)
// 好处是:监听某个对象有没有调用某个方法
// 坏处是:不能传递参数(要传递数据就用RACSubject)
[[self rac_signalForSelector:@selector(didReceiveMemoryWarning)] subscribeNext:^(RACTuple * _Nullable x) {
// x是一个空的RACTuple
NSLog(@"sg_控制器的didReceiveMemoryWarning方法被触发了_%@",x);
}];
}

- (void)rac_replace_delegate
{
// 参照RACSubject那个示例代码
}

@end

RACLiftSelector 多个请求完成后才执行某操作

//
//  ViewController.m
//  12RAC多次请求
//
//  Created by beyond on 2017/12/27.
//  Copyright © 2017年 beyond. All rights reserved.
//

#import "ViewController.h"
#import "ReactiveObjC.h"
@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];

[self rac_lift_selector];
}

- (void)rac_lift_selector
{
RACSignal *signal1 = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {

NSLog(@"sg_模拟网络操作1");

[subscriber sendNext:@"sg_返回的网络数据1"];
return nil;
}];
RACSignal *signal2 = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {

NSLog(@"sg_模拟网络操作2");

[subscriber sendNext:@"sg_返回的网络数据2"];
return nil;
}];

NSArray *signalArr = @[signal1,signal2];
// rac 实现 所有网络请求都完成的时候,才更新UI操作
// 注意:所有请求完成时才执行的方法的 参数 必须 和信号的个数 一一对应
[self rac_liftSelector:@selector(updateUIWithData1:andData2:) withSignalsFromArray:signalArr];
}
- (void)updateUIWithData1:(id)x1 andData2:(id)x2
{
NSLog(@"sg_所有请求都完成时,才被执行_x1:%@,x2:%@",x1,x2);
}

@end


RAC宏示例

storyboard



主控制器

//
// ViewController.m
// 13RAC常见宏
//
// Created by beyond on 2017/12/27.
// Copyright © 2017年 beyond. All rights reserved.
//

#import "ViewController.h"
#import "ReactiveObjC.h"
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UITextField *textField;
@property (weak, nonatomic) IBOutlet UILabel *titleLabel;

@end

@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];
[self rac_define_1];
[self rac_define_2];
[self rac_define_3];
[self rac_define_4];
}
- (void)rac_define_4
{
// 元组的包装和解包
// 将参数们包装成元组
RACTuple *tuple = RACTuplePack(@"龙与虎",@"逢坂大河");
NSLog(@"sg_%@_%@",tuple[0],tuple[1]);

// 将元组解包
RACTupleUnpack(NSString *anime,NSString *actress) = tuple;
NSLog(@"sg_%@_%@",anime,actress);
}
- (void)rac_define_3
{
// 解决block的循环引用问题,下面两个配套使用,外面+block里面
// 详细示例见NextViewCtrl
// @weakify(self)
// @strongify(self)
}

- (void)rac_define_2
{
// 监听某个对象的某个属性,返回值为信号
// 比如:监听label的文本,转化成信号
[RACObserve(_titleLabel, text) subscribeNext:^(id _Nullable newTextValue) {
NSLog(@"sg_%@",newTextValue);
}];
}
- (void)rac_define_1
{
// 对某个对象的某个属性绑定
// 比如:将label的文字 与 输入框的文本signal进行绑定
RAC(_titleLabel,text) = _textField.rac_textSignal;

}

@end


Next控制器(演示循环引用,nextViewCtrl消失时,dealloc未被执行)

//
// NextViewCtrl.m
// 13RAC常见宏
//
// Created by beyond on 2017/12/27.
// Copyright © 2017年 beyond. All rights reserved.
//

#import "NextViewCtrl.h"
#import "ReactiveObjC.h"

@interface NextViewCtrl ()
@property (nonatomic,strong) RACSignal *signal;

@end

@implementation NextViewCtrl
- (IBAction)dismissBtnClicked:(id)sender {
[self dismissViewControllerAnimated:YES completion:nil];
}

- (void)viewDidLoad {
[super viewDidLoad];

// 演示 循环引用,出现内存泄露(控制器dismiss时dealloc没被调用)
// [self rac_define_memoryleak];

// 使用宏避免循环引用
[self rac_define_avoidMemoryLeak];

}

// 使用宏避免循环引用
- (void)rac_define_avoidMemoryLeak
{
// 先对self弱引用 (必须成对使用)
// 相当于把self变成弱指针
@weakify(self);

// 双方强引用的时候,会出现内存泄露
RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
// 再在代码块内强引用 (必须成对使用)
// 对弱指针 又进行 强引用,目的是保证在代码块中不被销毁;强引用的范围仅限于代码块
@strongify(self);

// 经过上面的处理后,block中使用self已经不是一个强指针了
NSLog(@"sg__ctrl的self已经不会出现循环引用了_%@",self);
return nil;
}];

// self又对signal进行了强引用 (strong 属性的成员变量)
_signal = signal;
}

// 演示 循环引用,出现内存泄露(控制器dismiss时dealloc没被调用)
- (void)rac_define_memoryleak
{
// 双方强引用的时候,会出现内存泄露
RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {

// block中使用self,会对self进行了强引用
NSLog(@"sg__ctrl被强引用,无法被销毁_%@",self);
return nil;
}];

// self又对signal进行了强引用 (strong 属性的成员变量)
_signal = signal;
}

- (void)dealloc
{
NSLog(@"sg__dealloc__安全销毁__无内存泄漏");
}

@end
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息