OC 学习第十天_⼀、属性的内部实现原理 ⼆、dealloc内释放实例变量 三、便利构造器⽅法的实现原理

⼀、属性的内部实现原理
下面我将通过一个例子来引导出为什么assign,retain,copy的内部实现语句为什么要那么写,那么写是为了达到什么效果.
新建一个工程,将内存管理改成手动设置.
新建一个Person类,它有属性name.

//  Person.h
#import <Foundation/Foundation.h>

@interface Person : NSObject
//这里为什么这么用assign(一般基本数据类型用),是为了复写set,get  方法,实现推导出内部实现
@property (nonatomic,assign)NSString *name;
- (instancetype)initWithName:(NSString *)name;
+ (instancetype)personWithName:(NSString *)name;

@end

//  Person.m
- (instancetype)initWithName:(NSString *)name
{
    self = [super init];
    if (self) {
        _name = name;
    }
    return self;
}
+ (instancetype)personWithName:(NSString *)name
{
    Person *p = [[Person alloc]initWithName:name];
    return p;
}

//  main.m
#import <Foundation/Foundation.h>
#import "Person.h"

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

       //  创建一个Person类的对象
        Person *p = [[Person alloc]init]; //p引用计数为1
        NSString *name = [[NSString alloc]initWithFormat:@"张三"];
        p.name = name;                 //name引用计数为1
        [name release];                //name引用计数为0
        //思考:此时的name释放会将p.name这个指针释放掉吗?
        NSLog(@"%@",p.name);
        [p release];                      //p引用计数为0
      }
    return 0;
}

**上面出现了崩溃,因为你打印的p.name的NSString 的对象已经被释放了,如果你打印就相当于产生了一个野指针.所以会崩溃.
解决野指针:
我们可以讲直接赋值改成retain,那么对应的name的计数器就会为2,release后,还是1,还是可以打印的.但是由于这是我们需要推到的内部实现,所以还是用assign直接赋值来思考,可不可以在调用setName的时候retain一下,就可以实现那句打印.

//  在Person.m中添加set get方法
-(void)setName:(NSString *)name
{
    _name = [name retain];
}
- (NSString *)name
{
    return _name;
}

**这里补充一个点:如果你同时 重新复写set get方法会默认出错,因为当你同时重写这两个方法时,你声明的属性就失效了,此时就不会生成_name .
解决方法有两个: 1.在Person.h中添加成员变量_name;
2.在Person.m中添加成员变量名的连接语句
@synthesize name = _name;**

打印结果:2015-11-07 20:01:05.473 1[3157:313893] 张三

下面我们修改main.m中的代码:

#import <Foundation/Foundation.h>
#import "Person.h"

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

       //  创建一个Person类的对象
        Person *p = [[Person alloc]init];  //p引用计数为1
        NSString *name = [[NSString alloc]initWithFormat:@"张三"];
        p.name = name;                 //name引用计数为2,因为retain了一次
        p.name = name;                 //name引用计数为3,因为retain了一次
        p.name = name;                 //name引用计数为4,因为retain了一次
        p.name = name;                 //name引用计数为5,因为retain了一次
        [name release];
        [p release];
        //  这样会产生一个问题,person类的对象释放了,但是此时的name的对象还没有被释放
        NSLog(@"%@",p.name);

      }
    return 0;
}

怎么解决这个内存泄露呢:因为赋值的相同的值,所以我们可以在set方法中加一个判断,如果新的name和已经存储的name不相同才执行retain +1

// Person.m
-(void)setName:(NSString *)name
{
    if (_name != name) {
        _name = [name retain];
    }
    _name = [name retain];
}

改变mian.m中的代码,将不同的name付给p.name又会产生什么错误呢

#import <Foundation/Foundation.h>
#import "Person.h"

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

       //  创建一个Person类的对象
        Person *p = [[Person alloc]init];  //p引用计数为1
        NSString *name = [[NSString alloc]initWithFormat:@"张三"];
        NSString *name1 = [[NSString alloc]initWithFormat:@"李四"];
        NSString *name2 = [[NSString alloc]initWithFormat:@"王五"];
        p.name = name;                 
        p.name = name1;                 
        p.name = name2;                 
        [name release];
        [name1 release];
        [name2 release];
        //  这样会也会产生一个问题,person类的对象释放了,但是此时的name的对象也是还没有被释放
        NSLog(@"%@",p.name);        
    }
    return 0;
}
//  打印结果:2015-11-07 20:21:52.097 1[3217:324727] 王五

**该问题也是内存泄露,怎么解决呢?
思路:我们可以先将之前_name里面的值release掉再重新赋值**

//  Person.m
-(void)setName:(NSString *)name
{
    if (_name != name) {
        //  如果你对一个空release 相当于你什么都没做
        //  [nil release];
        [_name release];
         _name = [name retain];
    }

**这样每个只需要release一次就可以,但是最后剩下还有一个name和p没有释放,那么怎么做可以同时释放p对象和里面的name呢?
⼆、dealloc内释放实例变量
思路:因为当某个对象的引用计数变为0 时,会自动调用dealloc方法,所以我们可以通过复写父类的方法实现**

// Person.m
-(void)dealloc
{
    [_name release];
    [super dealloc];
}

**最后是get方法的复写,如果你想还想用这个p.name,但是p已经释放了呢?
思路:可以在get方法中retain一次,但是又不知道什么时候释放,所以可以再autorelease,自然在出了释放池之后,就可以自动释放了**

//  Person.m
-(NSString *)name
{
    return [[_name retain]autorelease];
}

以上就是retain的内部实现.copy的原理是一样的.

三.便利构造器⽅法的实现原理
由于你在便利构造器中返回的对象没有释放掉,但是这生成的对象必然在程序中使用的,但是又不知道在什么时候释放,所以我们可以配合自动释放池使用,因为自动释放池会自动收集autorelease对象,生成一个栈,将该种类的对象压入,出池后会给每一个栈内对象release一次进行释放.

+ (instancetype)personWithName:(NSString *)name
{
    Person *p = [[Person alloc]initWithName:name];
    return [p autorelease];
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值