属性的内部实现(二十七)

属性的内部实现

属性的内部实现(也就是getter、setter方法的实现),主要跟属性的attribute有关。

assign

assign一般用来标记标量(基本数据类型或者没有 * 号的)和代理delegate。

用assign来标记的的属性,对应生成的getter、setter方法,没有对野指针、内存泄漏作相应的判断,因为assign标记的是标量属性,不需要对其进行指针判断。

但是,assign也可以用来标记非标量的属性。如:用assign标记NSString *类型的属性。如果这样标记的话,就必须重写getter、setter方法,因为这些属性赋值的情况,都是将地址赋给另一个指针。很容易出现野指针和内存泄漏这种状况。

理解重写的思路:

1、创建一个Person类,声明一个属性@property (nonatomic , assign)NSString *name;此时name的默认getter、setter方法是按照assign的getter、setter方法生成的,也就是没有加上指针判断的。

2、在main函数中,创建Person类的对象p1,然后alloc出来一个NSString对象str,并赋初值@“贝爷”。这里创建了一个空间,内容是“贝爷”,有一个指针str指向它,引用计数为1。

3、p1调用setter方法,参数是str。此时将p1对象的name属性指向了“贝爷”所在的空间。

4、str使用完毕,将str释放release。此时引用计数 -1后变为0 ,此时系统已经检测到引用计数为0 ,将“贝爷”所在的内存回收。那么name就变成了野指针。

5、为了避免上面的状况,在setter方法中,将_name = [name retain],把引用计数与指针数目对应。

6、在main函数中,再alloc一个NSString的对象str2,并赋初值@“六娃”,则这里开辟了另一个空间,有一个指针str2指向它,引用计数为1。

7、p1再调setter方法,str2作为参数,此时,将p1对象的name属性指向了“六娃”所在的内存,并把引用计数+1,为2。

8、str2用完了,就将str2释放release。此时,“六娃”所在的空间有一个p1的name属性指向,引用计数为1。但是,“贝爷”所在的内存被泄漏了。

所以,在setter方法中,在[name retain]之前,应该把属性_name释放release掉。

9、此时,p1再次调用setter方法,参数是[p1 name],也就是name所指向的内存。那么在setter方法中,由于先对_name释放release了,此时引用计数为0 。那么,这块内存“六娃”就被系统回收,那么,接下来的[name retain]已经无权再对这块内存进行操作。

10、所以,setter方法中,在[_name release]之前应该加一个判断,判断赋给_name的 name是否相等,如果不相等,才release掉_name,然后retain。

代码如下:

Person.h


#import <Foundation/Foundation.h>

@interface Person : NSObject

@property (nonatomic , assign)NSString *name;

@end

Person.m


#import "Person.h"

@implementation Person

//用assign标记,如果不重写,就会直接用assign的getter、setter方法

//重写retain,则就不会用自动生成的getter,setter方法。而是用重写以后的。

@synthesize name = _name;

- (void)setName:(NSString *)name{

if (_name != name) {

[_name release];

_name = [name retain];

}

}

- (NSString *)name{

//苹果建议这样写,减少程序崩溃的几率

//如果写出野指针,只要在出自动释放池后,让其释放。一般自动释放池套在main函数里。

return [[_name retain] autorelease];

}

@end

main.m


#import <Foundation/Foundation.h>

#import "Person.h"

int main(int argc, const char * argv[]) {

@autoreleasepool {

Person *p1 = [[Person alloc]init];

[p1 setAge:10];

NSString *nameStr = [[NSString alloc]initWithFormat:@"贝爷"];

[p1 setName:nameStr];

NSLog(@"%@",[p1 name]);

//安全释放

[nameStr release];

nameStr = nil;

//释放了nameStr,此时属性name的指针还指向nameStr原来所指向的值,(由于[p1 setName:nameStr];时,没有使引用计数+1,)在nameStr释放后,引用计数为0,此时“贝爷”所在的内存已经被回收,导致name属性存得地址成为野指针。所以在.m中name的_name = [name retain],使引用计数和所指向的指针个数一致。

//        NSString *nameStr2 = [[NSString alloc]initWithFormat:@"六娃"];

//        [p1 setName:nameStr2];

//        NSLog(@"%@",p1.name);

//

//        [nameStr2 release];

//        nameStr2 = nil;

//        //上面的name先指向“贝爷”,然后nameStr被释放,引用计数 -1,此时,重新开辟空间,放入“六娃”,nameStr2指向“六娃”。[p1 setName:nameStr2];把nameStr2的地址赋给name,name就指向了“六娃”,此时,“贝爷”所在的内存就泄漏了。所以在.m中在retain之前,应该将_name release。“贝爷”被回收,name重新指向“六娃”

}

return 0;

}

retain

以上就是用assign来展示retain的内部getter、setter方法实现。

也就是说,声明一个属性,attribute设置为retain。那么这个属性的内部实现,就是上面的10点。

@property (nonatomic , retain)NSString *name;

copy

copy的内部实现,与retain的一样,只是将内容copy到另一个空间之前,判断copy内容过去的地址是否与现在的地址相同,避免野指针;再把原来的指针release掉,避免内存泄漏。注意的是,拷贝过去的那个空间引用计数为0。

Person.h


#import <Foundation/Foundation.h>

@interface Person : NSObject

@property (nonatomic , copy)NSString *sex;

- (instancetype)initWithName:(NSString *)name age:(NSInteger)age sex:(NSString *)sex;

@end

Person.m

copy语义的内部实现


#import "Person.h"

@implementation Person

//copy

//与retain的情况差不多。

@synthesize sex =_sex;

- (void)setSex:(NSString *)sex{

if (_sex != sex) {

[_sex release];

_sex = [sex copy];

}

}

- (NSString *)sex{

return [[_sex retain]autorelease];

}

- (void)dealloc{

[_name release];

[_sex release];

[super dealloc];

}

- (instancetype)initWithName:(NSString *)name age:(NSInteger)age sex:(NSString *)sex{

if (self = [super init]) {

self.name = name;

self.age = age;

self.sex = sex;

}

return self;

}

@end

main.m


#import <Foundation/Foundation.h>

#import "Person.h"

int main(int argc, const char * argv[]) {

@autoreleasepool {

Person *p2 = [[Person alloc]init];

NSString *str1 = [[NSString alloc]initWithFormat:@"男"];

[p2 setSex:str1];

[str1 release];

str1 = nil;

NSString *str2 = [[NSString alloc]initWithFormat:@"女"];

[p2 setSex:str2];

[str2 release];

str2 = nil;

[p2 setSex:[p2 sex]];

}

return 0;

}

初始化方法

Person.h


#import <Foundation/Foundation.h>

@interface Person : NSObject

@property (nonatomic , copy)NSString *sex;

@property (nonatomic,assign)NSInteger age;

@property (nonatomic , copy)NSString *sex;

- (instancetype)initWithName:(NSString *)name age:(NSInteger)age sex:(NSString *)sex;

@end

Person.m


#import "Person.h"

@implementation Person

- (void)dealloc{

[_name release];

[_sex release];

[super dealloc];

}

- (instancetype)initWithName:(NSString *)name age:(NSInteger)age sex:(NSString *)sex{

if (self = [super init]) {

self.name = name;

self.age = age;

self.sex = sex;

}

return self;

}

@end

用self.name(调自己的setter方法,在传进来的参数name地址给自己之前,做了判断,retain等操作。避免野指针和内存泄漏)

main.m


#import <Foundation/Foundation.h>

#import "Person.h"

int main(int argc, const char * argv[]) {

@autoreleasepool {

Person *p4 = [[Person alloc]initWithName:@"白雪公主" age:18 sex:@"男"];    }

return 0;

}

便利构造器

便利构造器,在实现.m文件中,return 出来的地址,要用[ p autorelease ]来释放。符合谁污染谁治理原则。

便利构造器创建的对象不需要进行释放,否则会出现过度释放。

Person.h声明便利构造器


#import <Foundation/Foundation.h>

@interface Person : NSObject

@property (nonatomic , assign)NSString *name;

@property (nonatomic,assign)NSInteger age;

@property (nonatomic , copy)NSString *sex;

- (instancetype)initWithName:(NSString *)name age:(NSInteger)age sex:(NSString *)sex;

//便利构造器

+ (instancetype)personWithName:(NSString *)name age:(NSInteger)age sex:(NSString *)sex;

@end

person.m


#import "Person.h"

@implementation Person

- (instancetype)initWithName:(NSString *)name age:(NSInteger)age sex:(NSString *)sex{

if (self = [super init]) {

self.name = name;

self.age = age;

self.sex = sex;

}

return self;

}

//便利构造器

+ (instancetype)personWithName:(NSString *)name age:(NSInteger)age sex:(NSString *)sex{

Person *p = [[Person alloc]initWithName:name age:age sex:sex];

//出了自动释放池再让其释放

return [p autorelease];

}

@end

main.m


#import <Foundation/Foundation.h>

#import "Person.h"

int main(int argc, const char * argv[]) {

@autoreleasepool {

//便利构造器

//便利构造器创建的对象不用release,否则会过度释放

//因为便利构造器内部已经用了autorelease。

Person *p3 = [Person personWithName:@"小金刚" age:19 sex:@"男"];

//过度释放

//        [p3 release];

Person *p4 = [[Person alloc]initWithName:@"白雪公主" age:18 sex:@"男"];

NSLog(@"%ld",[p4 retainCount]);//1

NSMutableArray *arr = [NSMutableArray array];

//装入容器(不管是数组,字典,集)

//对象装入容器的时候,引用计数 +1。

[arr addObject:p4];

NSLog(@"%ld",[p4 retainCount]);//2

//对象移出容器的时候,引用计数 -1

[arr removeObject:p4];

NSLog(@"%ld",[p4 retainCount]);//1.

[p4 release];

p4 = nil;

}

return 0;

}

集合内存管理

main.m


Person *p4 = [[Person alloc]initWithName:@"白雪公主" age:18 sex:@"男"];

NSLog(@"%ld",[p4 retainCount]);//1

NSMutableArray *arr = [NSMutableArray array];

//装入容器(不管是数组,字典,集)

//对象装入容器的时候,引用计数 +1。

[arr addObject:p4];

NSLog(@"%ld",[p4 retainCount]);//2

//对象移出容器的时候,引用计数 -1

[arr removeObject:p4];

NSLog(@"%ld",[p4 retainCount]);//1.

[p4 release];

p4 = nil;

时间: 2024-11-08 17:23:11

属性的内部实现(二十七)的相关文章

QT开发(二十七)——QT常用类(一)

QT开发(二十七)--QT常用类(一) 一.QString 1.QString简介 QString提供了Unicode编码的字符串,使用隐式共享技术来节省内存和不必要的数据拷贝,不必考虑跨平台的兼容性. QString类成员函数中除了 ascii().latin1().utf8().local8Bit()函数,其他所有的函数都是可重入的. 2.QString成员函数 QString::QString ( const QChar * unicode, int size ) QString::QSt

企业搜索引擎开发之连接器connector(二十七)

ChangeQueue类实现ChangeSource接口,声明了拉取下一条Change对象的方法 * A source of {@link Change} objects. * * @since 2.8 */ public interface ChangeSource { /** * @return the next change, or {@code null} if there is no change available */ public Change getNextChange();

【管理心得之二十七】管理者的开局

场景再现 ===================== Boss      :王さん,你怎么搞的?刚刚接手这个部门才3个月,部门人员就走了30%,再有一年岂不是这个部门没有人了? 王さん    :Boss,您是不知道具体怎么回事,这个部门原来烂到什么程度,再给我2个月时间,定会把它调整过来,保你满意. Boss      :王さん,我让你是接手该部门,并不是大刀阔斧地搞改革.裁员.整顿. 王さん    :我这么做也是为了这个部门,把人得罪了不说,现在还要受组织埋怨? Boss      :王さん,

angular学习笔记(二十七)-$http(5)-使用$http构建RESTful架构

在angular中有一个特别为RESTful架构而定制的服务,是在$http的基础上进行了封装. 但是为了学习,我们先看看用直接$http是如何构建RESTful架构的: 假设有一个银行卡的列表.需要的功能有: 可以通过id来获取用户123的指定id的卡     'GET'  'card/user/123/id' 可以获取用户123的所有的银行卡             'GET'  'card/user/123' 可以更新用户123的指定id的卡                'POST' '

二十七、Linux下常用的shell命令记录

本文章记录我在linux系统下常用或有用的系统级命令,包括软硬件查看.修改命令,有CPU.内存.硬盘.网络.系统管理等命令.但本文不打算介绍生僻命令,也不介绍各个linux发行版下的特有命令,且以后会持续更新. 说明,我是在一个Centos 6.4 64位的虚拟机系统进行测试.本文介绍的命令都会在此Centos下运行验证(也有部分命令会在我的suse/ubuntu系统里测试的,会做特明说明),但运行结果就不再列出了. 硬件篇 CPU相关 lscpu #查看的是cpu的统计信息. cat /pro

ActionScript3游戏中的图像编程(连载二十七)

2.2 Photoshop投影样式在Flash基本滤镜中的体现 作为上帝的另一个化身,Flash在图层样式方面自然也不甘示弱,不过在Flash里面,它有另外一个名字--滤镜(仅仅从概念上说,Flash的滤镜包含了Photoshop里的图层样式和滤镜). 下面就让我们打开Flash CS6/5.5,一起在滤镜里寻找Photoshop图层样式的影子吧,同时,我们先暂时把Photoshop的图层样式清除掉(把样式前面那些复选框的勾全部去掉即可). 先新建一个文档("文件"--"新建

微信小程序把玩(二十七)audio组件

原文:微信小程序把玩(二十七)audio组件 音频播放已经封装的很好!只需配合属性设置即可! (method和data配合使用) 主要属性: wxml <audio action="{{action}}" src='http://sc1.111ttt.com/2016/1/09/26/202261732256.mp3' poster= 'http://avatar.csdn.net/E/3/9/1_u014360817.jpg' controls="true"

二十七、Java图形化界面设计——容器(JFrame)

摘自http://blog.csdn.net/liujun13579/article/details/7756729 二十七.Java图形化界面设计--容器(JFrame) 程序是为了方便用户使用的,因此实现图形化界面的程序编写是所有编程语言发展的必然趋势,在命令提示符下运行的程序可以让我们了解java程序的基本知识体系结构,现在就进入java图形化界面编程. 一.Java基本类(JFC) Java基本类("JavaFoundationClasses",JFC),由一些软件包组成.这些

[原创]ActionScript3游戏中的图像编程(连载二十七)

2.2 Photoshop投影样式在Flash基本滤镜中的体现 作为上帝的另一个化身,Flash在图层样式方面自然也不甘示弱,不过在Flash里面,它有另外一个名字——滤镜(仅仅从概念上说,Flash的滤镜包含了Photoshop里的图层样式和滤镜). 下面就让我们打开Flash CS6/5.5,一起在滤镜里寻找Photoshop图层样式的影子吧,同时,我们先暂时把Photoshop的图层样式清除掉(把样式前面那些复选框的勾全部去掉即可). 先新建一个文档(“文件”——“新建”或Ctrl+N),