简述
通常在开发中,大多数人关注的往往都是技术,只是希望学习更多新的技术。但是,我认为每一个新的技术内部用到的都是很多基本内容。
因此,本文着重通过一个基本的 顺传
来了解其中遇到的问题和相应的分析方法。
看过本文之后,能够学习到的内容:
- 顺传的原则
- 对于控制器view 的懒加载的理解
- 如何进行调试
- viewDidLoad 和viewWillAppear的使用及调用顺序
- 实现顺传的三种方式
效果图如下
其中,window的根控制器是rootViewController是导航控制器。
如上图所示,实现起来很简答。当点击按钮“点击跳转”时,就会跳转到另一个View上。同时,将显示在该View上的label上的内容传递到第二个控制器的页面的label上。
实现的条件
本文为了方便解释,在跳转时展示,使用的是storyboard
进行搭建的页面。
顺传的原则
基本上,如果通过顺传,则需要遵循如下两个原则即可。
1. 目标控制器得有属性进行接收
2. 要拿到目标控制器
这两个原则就是就是将数据从一个view传递到另一个view时需要遵循的。
通过在storyboard中,进行脱线从而实现跳转的功能。因此当调用performSegueWithIdentifier
方法时候,内部会自动调用方法:prepareForSegue
相应的方法。因此可以在该方法内部做跳转前的一些配置信息。
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
NSLog(@"%@",segue.destinationViewController);
ToViewController *toVC = segue.destinationViewController;
//由于控制器的 View 是懒加载的.因此只有加载了,才可以分配内存空间.
//方法一:提前加载 view.
toVC.view.backgroundColor = [UIColor redColor];
toVC.toLabel.text = self.fromLabel.text;
}
对于控制器view 的懒加载的理解
为了方便解释控制器的跳转的逻辑,在此处用如下的方式进行表述:`A控制器跳转到B控制器`
正如上部分代码展示的一样,其中展示的是方法一:
由于控制器的view是懒加载的。故首先将B控制器的view提前加载后,这时候才能加载B控制器,然后分配相应的内存空间。这时候B内的属性才能有地方存放接收到的数据,而不会被释放。
通过添加断点,然后进行打印,结果如下图所示:
分析调试结果:
当执行如下的语句时
toVC.view.backgroundColor = [UIColor redColor];
toVC.toLabel.text = self.fromLabel.text;
首先,由于当执行第一条语句的时候,会加载toVC的view,因此内部具体的操作是:
- 先调用toVC的viewDidLoad方法,然后分配相应的内存空间;
- 接着给toVC的属性也分配相应的内存空间
- 由于toVC.toLabel该UI控件直接从storyboard进行脱线。
- 故该控件不仅分配响应的内存控件,并且进行相应的初始化操作(
就是将一个UILabel拖到storyboard中的时候,label的默认text值为@“label”
)。
- 从上图中
the debug result
可以看到-
当执行完第一行代码后,显示的结果为
(lldb) p toVC.toLabel.text (NSTaggedPointerString *) $0 = 0xa00006c6562614c5 @"Label"
- 因此,可以看出来,此时toVC.toLabel的值是原来storyboard中的初始化的值
Label
- 因此,可以看出来,此时toVC.toLabel的值是原来storyboard中的初始化的值
-
当执行完第二条语句,从该图
the debug result
可以看到(lldb) p toVC.toLabel.text (__NSCFConstantString *) $1 = 0x000000010e5840a0 @"顺传,,出去"
- 其内部具体做的操作就是:在堆内存中将toVC.toLabel.text指向self.fromLabel.text.我们知道,text是字符串,而在OC中字符串是一个对象,因此他们转为C语言的变化是:
将NSString -> NSCFString
而在C语言中,字符串 是一个指针,具体是指向一个字符的指针
因此传递的是地址。而当类型char *c = "fdsfsd"时候,也就是在一个缓冲队列中的保存一堆字符。
- 其内部具体做的操作就是:在堆内存中将toVC.toLabel.text指向self.fromLabel.text.我们知道,text是字符串,而在OC中字符串是一个对象,因此他们转为C语言的变化是:
-
如何进行调试
从the debug result
图片中,可以看到。使用的命令有p
,相应的还有其他的命令po
。他们两者的主要区别是,p同时能够打印两该变量在内存中的存储位置信息。这两种在调试过程中最常用。
使用前提:
1. 程序中有断点;
2. 只能查看当前{}内的相应的变量的信息。
当查看{}外部的信息的时候,可能出现呢error。
3. 当查看具体{}内响应对象等信息的时候,一定要加上完整的前缀。
针对上述的内容the debug result
,例如查看toLabel的信息,不能直接输入p toLabel
,这样会报错的。因为找不到。而应该输入:p toVC.toLabel
就可以查看相应的信息。
viewDidLoad 和viewWillAppear的使用及调用顺序
- 调用顺序
这个目前毋庸置疑的是,他们两个的调用的顺序为:viewDidLoad -> viewWillAppear . - 通常情况下,进行相应的初始化操作信息,是通过在view加载完成(
viewDidLoad
),view显示之前(viewWillAppear
)进行的初始化操作。而viewWillAppear的使用,有时候是为了覆盖之前在viewDidLoad中的某些操作,或者通过重新赋值来改变相应值操作。在代码中的在下一板块中体现。
实现顺传的三种方式
1. 方式一:通过B控制器的view提前进行加载的方式进行处理(上述已经展示过)
2. 方式二:通过将要传递的内容封装成模型,然后传递过去
3. 方式三:通过额外增加一个UILabel,使其作为B控制器属性,手动为其分配存储空间。
注意点:以上三种方式都是通过给新的对象分配相应的内存空间的方式进行处理的。只不过有些是通过隐式的方式,进行分配而已。其本质都是一样的。在swift
中,会对其有更好的理解。
方式一上述已经讨论过
-
方式二:代码如下
- 模型代码
@interface Content : NSObject //声明模型属性 @property (nonatomic, copy) NSString *name; @end
- 控制器的代码
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender: (id)sender { NSLog(@"%@",segue.destinationViewController); ToViewController *toVC = segue.destinationViewController; //方法三:通国模型进行传递 toVC.cont.name = self.fromLabel.text; }
- 懒加载模型的代码(实质是
分配内存空间
进行保存)
- (Content *)cont { if (!_cont) { _cont = [[Content alloc]init]; } return _cont; }
通过对此三小份额代码的分析。其思想是:定义模型进行保存将要传递的name。然后拿到B控制器的模型属性进行赋值,由于赋值的时候,为了让con
能够保存传递的信息,因此要给其分配内存空间进行保存将要传递的数据。故可以通过懒加载的方式,给其分配相应的存储空间。
-
方式三:代码如下
- 懒加载额外的label(otherLabel)代码
- (UILabel *)OtherLabel { if (!_OtherLabel) { UILabel *otherLabel = [[UILabel alloc]init]; /* 若不想要该控件进行显示,因此可以不设置尺寸,从而不会显示出来. UILabel *otherLabel = [[UILabel alloc]initWithFrame:CGRectMake(50, 50, 100, 50)]; */ otherLabel.backgroundColor = [UIColor redColor]; self.OtherLabel = otherLabel; // 只有将 weak 的属性加载到 self.view 上之后,才会进行强引用,而不会导致释放. [self.view addSubview:_OtherLabel]; } /** 若没有强指针指向 _OtherLabel,当从上一个`}`出来之后,就会自动释放. NSLog(@"--%@", _OtherLabel);// nil */ return _OtherLabel; }
- 重新赋值toLabel的内容
- (void)viewWillAppear:(BOOL)animated{ self.toLabel.backgroundColor = [UIColor greenColor]; self.toLabel.text = self.OtherLabel.text; }
- 定义新的属性label
#import <UIKit/UIKit.h> @class Content; @interface ToViewController : UIViewController //不能以关键字作为命名的开头 @property (weak, nonatomic) UILabel *OtherLabel; @end
此份代码的思想不是为了提供一种新的方法,而是为了说明一个道理: 实现某一种需求的方法有很多,只不过思考一个问题:为什么最后的最后只留下一个成果。因为那是最推荐的方法,而不推荐的方法,也自然有不推荐的理由。
在该份代码中,就是不推荐的方法,因为它里边有很多需要注意,容易犯错误的地方。而这些坑就是我们在日常的代码中,经常会遇到的小bug。
- 例如:在该份代码中,若该属性为weak,假如不将其进行强引用,则在其从懒加载的代码出去之前就已经释放。因为是局部变量,因此出来{}之后,就直接释放了。
//不能以关键字作为命名的开头
@property (weak, nonatomic) UILabel *OtherLabel;
因此,为了解决上述的问题。需要将其进行强引用,因此需要将其添加到控制器的view上。从而不会进行释放。
故,另一种方式是,直接将属性定义为strong的。