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

 

属性的内部实现

属性的内部实现(也就是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;

 

posted @ 2016-01-10 16:13  恒远也  阅读(217)  评论(0编辑  收藏  举报