界面通信

属性传值、协议传值、Block传值
 
⼀、属性传值
 
/**
 *  属性传值
    1、属性传值用于第一个界面向第二个界面传送值
    2、明确二者联系的桥梁,也就是触发跳转的地方
    3、明确传输的值 类型是什么
    4、在第二个视图控制器内部声明相对应类型的属性,来接收传输的值
    5、在第二个界面使用传入的值
 */
 
//第⼀步:在SecondViewController.h⾥定义⼀个contents字符串属性来保存由第一个界面传过来的字符串内容
@interface SecondViewController :
UIViewController
@property(nonatomic,copy)NSString *contents;
@end
 
//第⼆步:在点击FirstViewController按钮的⽅法⾥给SecondViewController的contents属性赋值
-(void)buttonAction:(UIButton *)button
{
    NSLog(@"进⼊第⼆⻚");
    SecondViewController *secondVC =
    [[SecondViewController alloc] init];
    secondVC.contents = self.label.text;
    [self.navigationController
     pushViewController:secondVC animated:YES];
    [secondVC release];
}
 
//第三步:在SecondViewController使⽤contents属性给textField赋值
@implementation SecondViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    self.view.backgroundColor = [UIColor whiteColor];
    self.navigationItem.title = @"第⼆⻚";
    self.textField = [[UITextField alloc] initWithFrame:CGRectMake(85, 200, 200, 40)];
    self.textField.placeholder = @"请输⼊内容";
    self.textField.text = self.contents;
    self.textField.borderStyle =
    UITextBorderStyleRoundedRect;
    [self.view addSubview:self.textField];
    [_textField release];
}
 
⼆、协议传值
 
第⼀步:声明协议
第⼆步:声明代理⼈
第三步:执⾏协议⽅法
第四步:签订协议
第五步:指定代理⼈
第六步:实现协议⽅法
 
 
//1、在 FourthViewController.h⾥声明协议
//UI中的协议名称为 当前类名 + Delegate
@protocol FourthViewControllerDelegate <NSObject>
//声明协议方法
@required //必须要实现的⽅法,默认是必须实现
- (void)pushValue:(NSString *)text;
@optional //可选实现的协议⽅法
- (void)pushColor:(UIColor *)color;

@end
 
//2、声明代理   必须是assign,使⽤retain,copy会导致循环引⽤问题
@property (nonatomic, assign) id<FourthViewControllerDelegate>delegate;
 
//3、执⾏协议⽅法
- (void)back {
   
    if (self.delegate != nil && [self.delegate respondsToSelector:@selector(pushValue:)]) {
        [self.delegate pushValue:self.textField.text];
    }
   
    if (self.delegate != nil && [self.delegate respondsToSelector:@selector(pushColor:)]) {
        [self.delegate pushColor:self.view.backgroundColor];
    }
    [self.navigationController popViewControllerAnimated:YES];
}
 
//4、接收(签订)协议
@interface ThirdViewController : UIViewController<FourthViewControllerDelegate>
 
 
//5、指定当前对象为代理人
- (void)push {
    FourthViewController *fourthVC = [[FourthViewController alloc] init];
    //指定第二个界面的代理对象为第一个视图控制器
    fourthVC.delegate = self;
    [self.navigationController pushViewController:fourthVC animated:YES];
    [fourthVC release];
}
 
//6、实现协议方法,并接收传过来的值
- (void)pushValue:(NSString *)text {
    self.label.text = text;
}
- (void)pushColor:(UIColor *)color {
    self.view.backgroundColor = color;
}
 
三、Block传值
 
  • block是匿名函数,能够实现函数回调功能
  • ⽤于页⾯之间通信,同时可以进⾏传值
 
 
/**
     *  1、Block是一种数据类型,并且是一种自定义的数据类型
        2、Block的标志是^(托字符)
        3、Block是匿名函数,TA与函数最主要的区别在于,函数在编译期就已经知道封装了什么功能,但是Block只有当执行时才知道内部封装的功能,所以说Block更加灵活多变
        4、Block的作用也是封装代码段来实现具体的功能
        5、既然Block是匿名函数,所以赋值时,不能将函数名直接赋值,初值为函数的实现体。
     */
 
 
void(^block)(void) = ^(void)
{
};
其中:
1.void(^block)(void)是类型
2.block是变量名
3.^(void){};是block实现
 
//1.⽆参⽆返回值类型的block
__block int a = 0;
void(^block1)(void) = ^(void)
{
    //在block内部不能直接修改局部变量的值,
    如果想修改必须声明成__block类型的变量
    a ++;
    NSLog(@"block1 %d",a);
};
block1();//执⾏block
 
//2.有参⽆返回值
void(^block2)(int age, NSString *string) = ^(int
                                             age, NSString *string)
{
    NSLog(@"age = %d, text = %@", age, string);
};
block2(20,@"⼩明");
 
//3.⽆参有返回值类型的block
NSString *(^block3)(void) = ^(void)
{
    return @"⽆参有返回值";
};
NSLog(@"block3 = %@“,block3());
 
//4.有参有返回值类型的block
NSString *(^block4)(NSString *text) =
^(NSString *string)
{
    return [string
            stringByAppendingString:@"有返回值"];
};

NSLog(@"block4 %@", block4(@"有参"));
 
 
__block int a = 6;
    void (^testBlock)() = ^{
        NSLog(@"%d",a);//Block内部可以访问局部变量的值
       
        a = 9;//Block内部如果想修改外界局部变量的值,必须对变量进行__block修饰
       
        count = 101;//Block内部可以直接修改全局变量的值,也可以直接访问全局变量的值
    };
    testBlock();
    NSLog(@"a = %d, count = %d",a, count);
 
  • 使⽤场景类似协议传值,都是解决从后⼀个页⾯往前⼀个页⾯传值问题
 
Block传值的两种方式:
  • ⽅式⼀: 使⽤block属性实现回调传值 
 
#warning 第⼀步
//在第⼆个⻚⾯⾥声明block属性
typedef void (^BaDa)(NSString *);
typedef void (^FuFu)(UIColor *);
 
//Block声明成属性,必须使⽤copy,retain⽆效
@property (nonatomic, copy) FuFu fufu;
@property (nonatomic, copy) BaDa bada;
 
#warning 第⼆步
//在第⼆个⻚⾯⾥执⾏block回调,将所要传的值传给第⼀个⻚⾯
- (void)back {
    //执行Block
    if (self.bada != nil) {
        self.bada(self.textField.text);
    }
    if (self.fufu != nil) {
        self.fufu(self.view.backgroundColor);
    }
    [self.navigationController popViewControllerAnimated:YES];
}
 
#warning 第三步
//在第⼀个⻚⾯⾥,实现block
- (void)push {   
    SecondViewController *secondVC = [[SecondViewController alloc] init];
    secondVC.bada = ^(NSString *str) {
        self.label.text = str;
    };
    secondVC.fufu = ^(UIColor *color) {
        self.view.backgroundColor = color;
    };
   
    [self.navigationController pushViewController:secondVC animated:YES];
    [secondVC release];  
}
 
#warning 第四步
//block内存管理
- (void)dealloc {
    //释放Block有专门的方法
    Block_release(_bada);
    Block_release(_fufu);
    [super dealloc];
}
 
  • ⽅式⼆: 在⽅法中定义block实现回调传值
 
#warning 第⼀步
//在AppTool.h中重定义void(^)(NSString *string)类型的别名为AppToolBlock
typedef void(^AppToolBlock)(NSString *string);
 
#warning 第⼆步
//声明⽅法,在⽅法中封装block
-(void)sendNumber:(NSInteger )number andBlock:
(AppToolBlock)block;
 
#warning 第三步
//在AppTool.m实现⽅法,并执⾏block
-(void)sendNumber:(NSInteger )number andBlock:(AppToolBlock)block;
{
    NSString *string = [NSString stringWithFormat:@"%ld",number];
    block(string);
}
 
#warning 第四步
-(void)buttonAction:(UIButton *)button
{
    AppTool *appTool = [[AppTool alloc] init];
    //执⾏⽅法,实现block并接收回传过来的string值
    [appTool sendNumber:10086 andBlock:^(NSString *string) {
        self.label.text = string;
    }];
}
 
四、Block内存管理
 
• 没有使⽤局部变量的block内存存储在全局区
void(^block)(void) = ^(void)
{
};
NSLog(@"block = %@“,block);
运⾏结果: block = <__NSGlobalBlock__: 0x107321360>
 
 
• block内部使⽤局部变量的时候内存存储在栈区
__block int a = 0;
void(^block)(void) = ^(void)
{
    a = 10;
};
NSLog(@"block = %@",block);
运⾏结果: block = <__NSStackBlock__: 0│7fff57a920c8>
 
  • 当block变量定义为属性的时候,必须使⽤copy修饰,retain ⽆效,即retain和assign会造成野指针问题.
  • 当对block进⾏copy操作的时候,此时block的内存区域为堆 区.
  • 当不使⽤block时需要使⽤Block_Release()进⾏销毁.
 
  • 注意:关于block内存管理上的三个区域,在arc和⾮arc下还是 有区别的
//block会造成self的引⽤计数+1
-(void)pushAction:(UIButton *)button
{
    self.FirstBlock([UIColor yellowColor]);
    NSLog(@"firstBlock === %@",self.FirstBlock);
}
运⾏结果:firstBlock === <__NSMallocBlock__: 0│7ff838d14b70>
 
-(void)dealloc
{
    Block_release(_FirstBlock);
    [super dealloc];
}
 
//block会造成self的引⽤计数+1,使⽤__block 修饰变量来解决block循环引⽤问题
SecondViewController *secondVC = [[SecondViewController alloc] init];
__block FirstViewController *firstVC = self;
secondVC.SecondBlock = ^(NSString *string){
    firstVC.label.text = string;
};
注意:
在⾮arc下使⽤__block修饰变量来防⽌循环引⽤
在arc下使⽤__week修饰变量来防⽌循环引⽤
原文地址:https://www.cnblogs.com/Walking-Jin/p/5210875.html