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


    属性的内部实现

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

  • 相关阅读:
    Spring配置多个数据源
    虚拟机内存结构
    Java中sleep,wait,yield,join的区别
    Java的四种引用方式
    Java 中的泛型详解-Java编程思想
    Java RTTI和反射
    linux 分析java 线程状态
    小容量的byteBuffer 读取大文本
    @Conditional 原理
    替换字符串占位符
  • 原文地址:https://www.cnblogs.com/DevinSMR/p/5118649.html
Copyright © 2020-2023  润新知