浅析objective-c中的strong和weak
2016-03-09 23:42
525 查看
在才开始学习oc时,搞不懂什么时候用strong,什么时候用weak,经过一段时间的学习,我谈谈我对strong和weak的理解。
首先strong和weak这两个关键字是用来修饰变量,表示这个变量是强(strong)引用和弱(weak)引用
我们在程序中经常会用到“[[class alloc]init]” 这样的代码,我想你对它已经很熟。这是在开辟一块内存,并初始化。那么系统开辟了这块内存,我们怎么去拿到它呢?
显然是将刚分配好的内存赋值到一个变量,以后我们就可以利用这个变量直接操作这块内存了。那么把刚分配的内存赋值给一个strong变量和weak变量是有区别的:
赋值给weak变量后这块内存会马上被释放。而分配给strong变量的会等到这个变量的生命周期结束后,这块内存才被释放(不用关键字weak修饰的变量默认为strong变量)。
看下面的例子:
添加一个Person类,只有一个name属性
main函数中定义了一个weak的zhangSan和一个strong的李四,很明显zhangSan指定的内存在“zhangSan=[[Person alloc]init]”执行后就立即被释放了。我们分别打印出两个变量的地址和name属性,可以看到zhangSan的确被释放了,而liSi一直到程序的结尾。
我们可以这样理解,分配出来的内存像一头牛,得用一条结实、强壮(strong)的绳子才能把它牵住,用纤细、弱小(weak)的绳子的话,这头牛随时会把绳子挣断逃脱。
而绳子的另一端是被固定到我们能够看得见够得着的物体(就是我们的变量)上面,我们顺着这个物体上面的绳子摸索过去,你的那头牛还在不在就看你用的上面绳子了。
那既然weak类型的变量内存分配出来就被释放了,它还有什么用呢?我们再看下面的例子
先将分配好的内存赋值给一个strong变量,然后再将这个strong变量赋值给一个weak变量,这样两个Person的地址都一样,显然name属性也一样。这样就好比先用结实的绳子拴住牛,这样牛就不会跑了,然后在用一根弱小的绳子拴住这头牛,这样顺着这根弱小的绳子也能找到这头牛。很明显如果当我们把结实的绳子弄断时,弱小的绳子自然也拉不住这头牛了。比如下面的例子:
“liSi”声明在一对大括号内,表明它只在大括号内有效,除了大括号,这个拴绳子的物体就不在了,那么牛自然挣脱弱小绳子的束缚跑掉,所以你再拿到“wangWu”想找到那头牛(内存及内存中的值)就不可能了。那如果有多条结实的绳子拴住牛,想必你也知道是怎么回事了。
用两条结实的绳子拴住一头牛,即使一根不在了,利用另一根还是能找到这头牛。
说到这里其实也没有说到weak类型的变量这种机制到底有什么用,我们看下面的例子
1.自定义一个VIew继承自UIView,重写dealloc方法,查看对象什么时候被销毁
2.我们在程序要主视图的时候,将自定义视图添加到主视图上,并且添加按钮,监听点击事件。
- (void)viewDidLoad {
[super viewDidLoad];
//创建自定义view
myView *view=[[myView alloc]init];
view.frame=CGRectMake(50, 50, 200, 200);
view.backgroundColor=[UIColor redColor];
UILabel *msgLabel=[[UILabel alloc]initWithFrame:CGRectMake(0, 0, 200, 20)];
msgLabel.text=@"这是一个视图";
[view addSubview:msgLabel];
//添加到视图
[self.view addSubview:view];
//添加按钮
UIButton *btn=[[UIButton alloc]initWithFrame:CGRectMake(100, 260, 100, 80)];
[btn setTitle:@"移除子视图" forState:UIControlStateNormal];
[btn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[btn addTarget:self action:@selector(btnClick) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:btn];
}
-(void)btnClick
{
NSLog(@"点击了按钮");
}
我想要的效果是:点击按钮移除 子视图 ,并且程序以后的运行永远也不会用到这个view。
这里我想到有两种方式拿到这个view,然后从父控件中移除它,
第一种:这种方式显然能实现这样的要求,我们能看到打印结果,在子视图被移除父控制器之后对象也被销毁了。然而这不是我们最常用的方式,有可能父控件上有很多子视图,这样效率很低,而且代码不简洁。
-(void)btnClick
{
for (UIView *subView in self.view.subviews) {
if ([subView isKindOfClass:[myView class]]) {
[subView removeFromSuperview];
}
}
NSLog(@"点击了按钮");
}
第二种:我们给控制器增加一个属性,指向我们的子视图。这个属性有两种可能,一种是strong,一种是weak。我们先来试试strong。
从结果可以看出,点击按钮子视图是移除了,但是对象没有被销毁。它仍然在内存中(你可以再添加一个按钮打印一下self.testView试试),这不是我们想要的效果。
我们在来试试weak,只需要将声明变量的地方的strong改为weak即可,其他地方不变。从运行结果可以看出:子视图被移除了,且变量被销毁了。
为什么我们没有添加strong属性的时候分配出来的内存没有被释放,仍然能通过for循环找到它?需要注意的是:当一个视图A被添加到另一个视图B时,A就被B的subViews强引用了(有一个结实的绳子拉着它了),所以我们再用一个强属性去拉着它的话,自然要两条绳子都断了,它才会被释放。
也许现在你对strong和weak又对一点了解了
首先strong和weak这两个关键字是用来修饰变量,表示这个变量是强(strong)引用和弱(weak)引用
我们在程序中经常会用到“[[class alloc]init]” 这样的代码,我想你对它已经很熟。这是在开辟一块内存,并初始化。那么系统开辟了这块内存,我们怎么去拿到它呢?
显然是将刚分配好的内存赋值到一个变量,以后我们就可以利用这个变量直接操作这块内存了。那么把刚分配的内存赋值给一个strong变量和weak变量是有区别的:
赋值给weak变量后这块内存会马上被释放。而分配给strong变量的会等到这个变量的生命周期结束后,这块内存才被释放(不用关键字weak修饰的变量默认为strong变量)。
看下面的例子:
添加一个Person类,只有一个name属性
@interface Person : NSObject @property(nonatomic,copy) NSString *name; @end
main函数中定义了一个weak的zhangSan和一个strong的李四,很明显zhangSan指定的内存在“zhangSan=[[Person alloc]init]”执行后就立即被释放了。我们分别打印出两个变量的地址和name属性,可以看到zhangSan的确被释放了,而liSi一直到程序的结尾。
int main(int argc, const char * argv[]) { @autoreleasepool { __weak Person* zhangSan=[[Person alloc]init]; zhangSan.name=@"张三"; Person *liSi=[[Person alloc]init]; liSi.name=@"李四"; NSLog(@"%p----%p",zhangSan,liSi); NSLog(@"%@----%@",zhangSan.name,liSi.name); } return 0; }
我们可以这样理解,分配出来的内存像一头牛,得用一条结实、强壮(strong)的绳子才能把它牵住,用纤细、弱小(weak)的绳子的话,这头牛随时会把绳子挣断逃脱。
而绳子的另一端是被固定到我们能够看得见够得着的物体(就是我们的变量)上面,我们顺着这个物体上面的绳子摸索过去,你的那头牛还在不在就看你用的上面绳子了。
那既然weak类型的变量内存分配出来就被释放了,它还有什么用呢?我们再看下面的例子
先将分配好的内存赋值给一个strong变量,然后再将这个strong变量赋值给一个weak变量,这样两个Person的地址都一样,显然name属性也一样。这样就好比先用结实的绳子拴住牛,这样牛就不会跑了,然后在用一根弱小的绳子拴住这头牛,这样顺着这根弱小的绳子也能找到这头牛。很明显如果当我们把结实的绳子弄断时,弱小的绳子自然也拉不住这头牛了。比如下面的例子:
“liSi”声明在一对大括号内,表明它只在大括号内有效,除了大括号,这个拴绳子的物体就不在了,那么牛自然挣脱弱小绳子的束缚跑掉,所以你再拿到“wangWu”想找到那头牛(内存及内存中的值)就不可能了。那如果有多条结实的绳子拴住牛,想必你也知道是怎么回事了。
用两条结实的绳子拴住一头牛,即使一根不在了,利用另一根还是能找到这头牛。
说到这里其实也没有说到weak类型的变量这种机制到底有什么用,我们看下面的例子
1.自定义一个VIew继承自UIView,重写dealloc方法,查看对象什么时候被销毁
#import "myView.h" @implementation myView -(void)dealloc { NSLog(@"对象被销毁"); } @end
2.我们在程序要主视图的时候,将自定义视图添加到主视图上,并且添加按钮,监听点击事件。
- (void)viewDidLoad {
[super viewDidLoad];
//创建自定义view
myView *view=[[myView alloc]init];
view.frame=CGRectMake(50, 50, 200, 200);
view.backgroundColor=[UIColor redColor];
UILabel *msgLabel=[[UILabel alloc]initWithFrame:CGRectMake(0, 0, 200, 20)];
msgLabel.text=@"这是一个视图";
[view addSubview:msgLabel];
//添加到视图
[self.view addSubview:view];
//添加按钮
UIButton *btn=[[UIButton alloc]initWithFrame:CGRectMake(100, 260, 100, 80)];
[btn setTitle:@"移除子视图" forState:UIControlStateNormal];
[btn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[btn addTarget:self action:@selector(btnClick) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:btn];
}
-(void)btnClick
{
NSLog(@"点击了按钮");
}
我想要的效果是:点击按钮移除 子视图 ,并且程序以后的运行永远也不会用到这个view。
这里我想到有两种方式拿到这个view,然后从父控件中移除它,
第一种:这种方式显然能实现这样的要求,我们能看到打印结果,在子视图被移除父控制器之后对象也被销毁了。然而这不是我们最常用的方式,有可能父控件上有很多子视图,这样效率很低,而且代码不简洁。
-(void)btnClick
{
for (UIView *subView in self.view.subviews) {
if ([subView isKindOfClass:[myView class]]) {
[subView removeFromSuperview];
}
}
NSLog(@"点击了按钮");
}
第二种:我们给控制器增加一个属性,指向我们的子视图。这个属性有两种可能,一种是strong,一种是weak。我们先来试试strong。
从结果可以看出,点击按钮子视图是移除了,但是对象没有被销毁。它仍然在内存中(你可以再添加一个按钮打印一下self.testView试试),这不是我们想要的效果。
我们在来试试weak,只需要将声明变量的地方的strong改为weak即可,其他地方不变。从运行结果可以看出:子视图被移除了,且变量被销毁了。
为什么我们没有添加strong属性的时候分配出来的内存没有被释放,仍然能通过for循环找到它?需要注意的是:当一个视图A被添加到另一个视图B时,A就被B的subViews强引用了(有一个结实的绳子拉着它了),所以我们再用一个强属性去拉着它的话,自然要两条绳子都断了,它才会被释放。
也许现在你对strong和weak又对一点了解了
相关文章推荐
- 峰回路转,Firefox 浏览器即将重返 iOS 平台
- 峰回路转,Firefox 浏览器即将重返 iOS 平台
- 不可修补的 iOS 漏洞可能导致 iPhone 4s 到 iPhone X 永久越狱
- iOS 12.4 系统遭黑客破解,漏洞危及数百万用户
- 每日安全资讯:NSO,一家专业入侵 iPhone 的神秘公司
- [转][源代码]Comex公布JailbreakMe 3.0源代码
- Ruby中require、load、include、extend的区别介绍
- Ruby中的p和puts的使用区别浅析
- Ruby中的block、proc、lambda区别总结
- Redis和Memcached的区别详解
- Lua中调用函数使用点号和冒号的区别
- Lua中关于求模与求余的区别介绍
- TMP、TEMP和TMP文件区别解析
- C#基础语法:结构和类区别详解
- 深入c# 类和结构的区别总结详解
- C#中string.Empty和null的区别详解
- sqlserver和oracle中对datetime进行条件查询的一点区别小结
- 网页中Span和Div的区别
- 大家看了就明白了css样式中类class与标识id选择符的区别小结