iOS 属性关键字剖析

@property
  • @property 其实就是在编译阶段由编译器自动帮我们生成ivar成员变量getter方法,setter方法。

使用“自动合成”( auto synthesis)这个过程由编译器在编译阶段执行自动合成,所以编译器里看不到这些“合成方法”(synthesized method)的源代码。除了生成getter、setter方法之外,编译器还要自动向类中添加成员变量(在属性名前面加下划线,以此作为实例变量的名字)

每次增加一个属性,系统都会在 ivar_list 中添加一个成员变量的描述,在 method_list 中增加 setter 与 getter 方法的描述,在 prop_list 中增加一个属性的描述,计算该属性在对象中的偏移量,然后给出 setter 与 getter 方法对应的实现。

在 setter 方法中从偏移量的位置开始赋值,在 getter 方法中从偏移量开始取值,为了能够读取正确字节数,系统对象偏移量的指针类型进行了类型强转。

下面看看内部代码:

@property(nonatomic,strong)UIButton * testBtn;
@property(nonatomic,strong)NSMutableArray * testArray;

@end

@implementation testBaseViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor redColor];
    unsigned int count , methodC;
    Ivar * ivars = class_copyIvarList([self class], &count);
    for (int i =0 ; i < count; i++) {
        Ivar ivar = ivars[i];
        const char * name = ivar_getName(ivar);
        NSLog(@"成员变量:%s",name);
    }
    free(ivars);
    Method * methods = class_copyMethodList([self class], &methodC);
    for (int i =0 ; i < methodC; i++) {
        Method ivar = methods[i];
        struct objc_method_description * des = method_getDescription(ivar);
        NSLog(@"%@",NSStringFromSelector(des->name));
    }
    
}

2017-07-17 10:41:16.639 LeeSDWebImageLearn[1143:59316] 成员变量:_testBtn
2017-07-17 10:41:16.639 LeeSDWebImageLearn[1143:59316] 成员变量:_testArray
2017-07-17 10:41:16.640 LeeSDWebImageLearn[1143:59316] testBtn
2017-07-17 10:41:16.640 LeeSDWebImageLearn[1143:59316] setTestBtn:
2017-07-17 10:41:16.640 LeeSDWebImageLearn[1143:59316] testArray
2017-07-17 10:41:16.640 LeeSDWebImageLearn[1143:59316] setTestArray:
2017-07-17 10:41:16.640 LeeSDWebImageLearn[1143:59316] .cxx_destruct
2017-07-17 10:41:16.641 LeeSDWebImageLearn[1143:59316] viewDidLoad

注意:

在声明property属性后,有2种实现选择

  • @synthesize (Xcode6以后省略这个了, 默认在 @implementation .m中添加这个@synthesize xxx = _xxx; )
    编译器期间,让编译器自动生成getter/setter方法。
    但是当有自定义的存或取方法时,自定义会屏蔽自动生成该方法

  • @dynamic (Xcode6以后省略这个了, 默认在 @implementation .m中添加这个@synthesize xxx; )
    就是要来告诉编译器,代码中用@dynamic修饰的属性,其getter和setter方法会在程序运行的时候或者用其他方式动态绑定,以便让编译器通过编译。

使用:

@interface testBaseViewController (){

    UIButton * _heBtn;//使用@dynamic 要自己声明一个成员变量自己实现setter/getter 方法
}
@property(nonatomic,copy)NSString * name;
@property(strong)UIButton * clickBtn;
@property(nonatomic,strong)UIButton * heBtn;

@end

@implementation testBaseViewController
@synthesize  name = _name;
@synthesize clickBtn = _clickBtn;
@dynamic heBtn ;

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

    if (_name != name) {
        _name = name;
    }
}
-(NSString*)name{
    return _name;
}

-(void)setClickBtn:(UIButton *)clickBtn{

    @synchronized (self) {
        if (_clickBtn != clickBtn) {
            _clickBtn = clickBtn;
        }
    }
}
-(UIButton*)clickBtn{

    @synchronized (self) {
        return _clickBtn;
    }
}
-(void)setHeBtn:(UIButton *)heBtn{
   
    if (_heBtn != heBtn) {
        _heBtn = heBtn;
    }
}
-(UIButton*)heBtn{
    return _heBtn;
}
readwrite,readonly,assign,retain,copy,nonatomic,atomic,strong,weak属性的作用分别是什么。
  • readwrite
    此标记说明属性会被当成读写的,这也是默认属性。
  • readonly
    此标记说明属性只可以读,也就是不能设置,可以获取。
  • assign
    不会使引用计数加1,也就是直接赋值,一般用于基本数据类型。
  • retain
    会使引用计数加1,先release 旧值,在retain新值。mrc 下使用。
  • copy
    建立一个索引计数为1的对象,在赋值时使用传入值的一份拷贝。
  • nonatomic
    非原子性访问,多线程并发访问会提高性能。
  • atomic
    原子性访问。资源加锁,线程同步访问。
  • strong
    打开ARC时才会使用,相当于retain。
  • weak
    打开ARC时才会使用,相当于assign,可以把对应的指针变量置为nil。

分析:

atomic

系统生成的getter/setter方法会进行加锁操作,注意:这个锁仅仅保证了getter和setter存取方法的线程安全.
因为getter/setter方法有加锁的缘故,故在别的线程来读写这个属性之前,会先执行完当前操作.

atomic是线程安全的吗?

不是,
很多文章谈到atomic和nonatomic的区别时,都说atomic是线程安全,其实这个说法是不准确的.
atomic只是对属性的getter/setter方法进行了加锁操作,这种安全仅仅是set/get 的读写安全,并非真正意义上的线程安全,因为线程安全还有读写之外的其他操作(比如:如果当一个线程正在get或set时,又有另一个线程同时在进行release操作,可能会直接crash)

nonatomic

系统生成的getter/setter方法没有加锁,线程不安全,但更快
当多个线程同时访问同一个属性,会出现无法预料的结果

下面看演示:

@property(nonatomic,copy)NSString * name;
@property(strong)UIButton * clickBtn;
@end
@implementation testBaseViewController
@synthesize  name = _name;
@synthesize clickBtn = _clickBtn;
  • nonatomic对象setter和getter方法的实现
-(void)setName:(NSString *)name{

    if (_name != name) {
        _name = name;
    }
}
-(NSString*)name{
    return _name;
}

  • atomic对象setter和getter方法的实现
-(void)setClickBtn:(UIButton *)clickBtn{

    @synchronized (self) {
        if (_clickBtn != clickBtn) {
            _clickBtn = clickBtn;
        }
    }
}
-(UIButton*)clickBtn{

    @synchronized (self) {
        return _clickBtn;
    }
}

weak assign

3.引用计数的工作原理:

  • 当我们创建(alloc)一个新对象A的时候,它的引用计数从零变为 1;
  • 当有一个指针指向这个对象A,也就是某对象想通过引用保留(retain)该对象A时,引用计数加 1;
  • 当某个指针/对象不再指向这个对象A,也就是释放(release)该引用后,我们将其引用计数减 1;
  • 当对象A的引用计数变为 0 时,说明这个对象不再被任何指针指向(引用)了,这个时候我们就可以将对象A销毁,所占内存将被回收,且所有指向该对象的引用也都变得无效了。系统也会将其占用的内存标记为“可重用”(reuse);

在 ARC 中,在有可能出现循环引用的时候,往往要通过让其中一端使用 weak 来解决,比如: delegate 代理属性
自身已经对它进行一次强引用,没有必要再强引用一次,此时也会使用 weak,自定义 IBOutlet 控件属性一般也使用 weak;当然,也可以使用strong

weak 和 assign 和 strong 的不同点:

  • assign:用于非指针变量,一般用于基础类型和C数据类型,这些类型不是对象,统一由系统栈进行内存管理。
  • weak:对对象的弱引用,不增加对象的引用计数,也不持有对象,当对象消失后指针自动指向nil,所以这里也就防止了野指针的存在。
  • strong:对对象的强引用,增加对象的引用计数,持有对象。
  • weak 策略在属性所指的对象遭到摧毁时,系统会将 weak 修饰的属性对象的指针指向 nil,在 OC 给 nil 指针对象发消息是不会有什么问题的;如果使用 assign 策略在属性所指的对象遭到摧毁时,对象的内存区块被擦写,原有的各种方法的内存指向没有了,属性对象指针还指向原来的对象,由于对象已经被销毁,这时候就产生了野指针,如果这时候在给此对象发送消息,当前指针所指向的内存区块就没有那些方法了,很容造成程序奔溃,assigin 可以用于修饰非 OC 对象,而 weak 必须用于 OC 对象。
  • Delegate基本总是使用weak,以防止循环引用。特殊情况是,希望在dealloc中调用delegate的某些方法进行释放,此时如果使用weak将引起异常,因为此时已经是nil了,那么采用assign更为合适。
    UIButton * testBtn = [UIButton new];
    NSLog(@"引用计数:%ld",CFGetRetainCount((__bridge CFTypeRef)testBtn));
    self.strongBtn = testBtn;
    NSLog(@"引用计数:%ld",CFGetRetainCount((__bridge CFTypeRef)testBtn));
    self.strongBtn1 = testBtn;
    NSLog(@"引用计数:%ld",CFGetRetainCount((__bridge CFTypeRef)testBtn));
    self.assignBtn  = testBtn;
    NSLog(@"引用计数:%ld",CFGetRetainCount((__bridge CFTypeRef)testBtn));
    self.weakBtn   = testBtn;
    NSLog(@"引用计数:%ld",CFGetRetainCount((__bridge CFTypeRef)testBtn));
    [self.view addSubview:testBtn];
    NSLog(@"引用计数:%ld",CFGetRetainCount((__bridge CFTypeRef)testBtn));
    self.strongBtn   = nil;
    self.strongBtn1 = nil;
    self.assignBtn = nil;
    self.weakBtn  = nil;
    NSLog(@"引用计数:%ld",CFGetRetainCount((__bridge CFTypeRef)testBtn));

上面分别使用strong assign weak 引用btn对象结果只有strong 和 addsubview 导致原始对象的引用计数增加了,其他的没变。

2017-07-17 16:19:02.024 LeeSDWebImageLearn[2302:176043] 引用计数:1
2017-07-17 16:19:02.025 LeeSDWebImageLearn[2302:176043] 引用计数:2
2017-07-17 16:19:02.025 LeeSDWebImageLearn[2302:176043] 引用计数:3
2017-07-17 16:19:02.025 LeeSDWebImageLearn[2302:176043] 引用计数:3
2017-07-17 16:19:02.025 LeeSDWebImageLearn[2302:176043] 引用计数:3
2017-07-17 16:19:02.026 LeeSDWebImageLearn[2302:176043] 引用计数:4
2017-07-17 16:19:02.026 LeeSDWebImageLearn[2302:176043] 引用计数:2

weak 实现原理的概括

Runtime维护了一个weak表,用于存储指向某个对象的所有weak指针。weak表其实是一个hash(哈希)表,Key是所指对象的地址,Value是weak指针的地址(这个地址的值是所指对象的地址)数组。

  • weak 的实现原理可以概括一下三步:

1、初始化时:runtime会调用objc_initWeak函数,初始化一个新的weak指针指向对象的地址。

2、添加引用时:objc_initWeak函数会调用 objc_storeWeak() 函数, objc_storeWeak() 的作用是更新指针指向,创建对应的弱引用表。

3、释放时,调用clearDeallocating函数。clearDeallocating函数首先根据对象地址获取所有weak指针地址的数组,然后遍历这个数组把其中的数据设为nil,最后把这个entry从weak表中删除,最后清理对象的记录。

copy
首先以一个String类型 demo分析吧:

@property(nonatomic,strong)NSString * bigName;//这里设置成strong
@property(nonatomic,strong)NSString * smallName;
@property(nonatomic,copy)NSString  * firstName;//这里设置成copy
@property(nonatomic,copy)NSString  * lastName;

调用的地方:

    NSMutableString * name1 = [NSMutableString stringWithString:@"oliverlee"];
    NSString * name2 = @"liyangyang";
    Person *p = [[Person alloc] init];
    p.bigName    = name1;
    p.smallName = name2;
    p.firstName   = name1;
    p.lastName    = name2;
    
    NSLog(@"big=%@,small=%@,first=%@,last=%@",p.bigName,p.smallName,p.firstName,p.lastName);
    [name1 appendString:@"hello world"];
     name2 = @"world hello";
    NSLog(@"big=%@,small=%@,first=%@,last=%@",p.bigName,p.smallName,p.firstName,p.lastName);

看控制台:

2017-07-17 18:29:03.759 LeeSDWebImageLearn[2896:235465] big=oliverlee,small=liyangyang,first=oliverlee,last=liyangyang
2017-07-17 18:29:03.760 LeeSDWebImageLearn[2896:235465] big=oliverleehello world,small=liyangyang,first=oliverlee,last=liyangyang

这次看出了我们平时同学乱使用的strong出问题了吧,当你用可变的字符串赋值后,在修改原来的字符串导致结果变化了,而用copy修饰的无论是可变还是不可变都不影响结果。
当我们通过一个 NSMutableString 对 NSString 变量进行赋值,如果 NSString 的 property 是 strong 类型的时候,就会随着 NSMutableString 类型的变量一起变化。

  
    NSString * string = @"test";
    NSString * stringCopy = [string copy];//内存地址没有变化,不可变。
    NSMutableString * stringMCopy = [string mutableCopy];//内存地址发生变化,拷贝了值和地址,可变的
    [stringMCopy appendString:@"hello"];
    NSLog(@"%p,%p,%p------->%@,%@,%@",string,stringCopy,stringMCopy,string,stringCopy,stringMCopy);
    
    NSMutableString * mutString = [NSMutableString stringWithString:@"test"];
    NSMutableString * m_stringCopy = [mutString copy];//内存地址发生变化,值也拷贝了,但是不可变
    NSMutableString * m_stringMutCopy = [mutString mutableCopy];//内存地址和值都拷贝了,可变的。
//    [m_stringCopy appendString:@"hello"];
    [m_stringMutCopy appendString:@"oliver"];
    NSLog(@"%p,%p,%p------->%@,%@,%@",mutString,m_stringCopy,m_stringMutCopy,mutString,m_stringCopy,m_stringMutCopy);


    NSString * str1 = @"123";
    NSString * str2 = @"123";
    NSMutableString * mstr1= [NSMutableString stringWithString:@"123"];
    NSMutableString * mstr2 = [NSMutableString stringWithString:@"123"];
    NSLog(@"%p,%p,%p,%p",str1,str2,mstr1,mstr2);
2017-07-17 21:25:56.657 LeeSDWebImageLearn[3588:303863] 0x10fc39bd0,0x10fc39bd0,0x60800007ad80,0x60800007ad40

从上面看出NSString 类型的字符串放在了常量区也就是静态区,NSMutableString是在堆区

  • NSString 类型采用copy方法,内存地址没发生变化,只是指向了原来的对象,认为不可变类型(浅拷贝)
  • NSString 类型采用mutablecopy方法,内存地址发生变化,拷贝了值换了地址,可变的操作 (深拷贝)
  • NSMutableString 类型采用copy 方法,内存地址发生变化,值也拷贝了,但是不可变操作 (浅拷贝)
  • NSMutableString 类型采用mutablecopy方法,内存地址发生改变,值也拷贝了,是可变的操作 (深拷贝)

**2. 系统的容器类对象 **

指NSArray,NSMutableArray,NSDictionary,NSMutableDictionary等。对于容器类本身,上面讨论的结论也是适用的,需要探讨的是复制后容器内对象的变化。

  • 看下NSArray 和 NSDictioary 的copy 和 mutablecopy
    NSArray * array = @[@"hello"];
    NSMutableArray * copyArray = array.copy;
    NSLog(@"%@",[copyArray class]);
    NSLog(@"引用计数:%ld",CFGetRetainCount((__bridge CFTypeRef)array));
    NSMutableArray * muCopyArray = array.mutableCopy;
    [muCopyArray addObject:@"world"];
    NSLog(@"element::::::%p----%p----%p",array[0],muCopyArray[0],copyArray[0]);
    NSLog(@"%p,%p,%p------->%@,%@,%@",array,copyArray,muCopyArray,array,copyArray,muCopyArray);
    copyArray = nil;
    NSLog(@"引用计数:%ld",CFGetRetainCount((__bridge CFTypeRef)array));
    NSDictionary * dic = @{@"key":@"hello"};
    NSMutableDictionary * copyDic = dic.copy;
    NSLog(@"%@",[copyDic class]);
    NSLog(@"引用计数:%ld",CFGetRetainCount((__bridge CFTypeRef)dic));
    NSMutableDictionary * mutDic = dic.mutableCopy;
    [mutDic setValue:@"oliver" forKey:@"name"];
    NSLog(@"element::::::%p----%p----%p",dic[@"key"],mutDic[@"key"],copyDic[@"key"]);
    NSLog(@"%p,%p,%p------->%@,%@,%@",dic,copyDic,mutDic,dic,copyDic,mutDic);
    copyDic = nil;
    NSLog(@"引用计数:%ld",CFGetRetainCount((__bridge CFTypeRef)dic));

控制台输出:

2017-07-17 23:19:39.853 LeeSDWebImageLearn[4068:350869] __NSSingleObjectArrayI
2017-07-17 23:19:39.854 LeeSDWebImageLearn[4068:350869] 引用计数:2
2017-07-17 23:19:39.854 LeeSDWebImageLearn[4068:350869] element::::::0x10e431650----0x10e431650----0x10e431650
2017-07-17 23:19:39.854 LeeSDWebImageLearn[4068:350869] 0x608000019cb0,0x608000019cb0,0x60000004cf30------->(
    hello
),(
    hello
),(
    hello,
    world
)
2017-07-17 23:19:39.854 LeeSDWebImageLearn[4068:350869] 引用计数:1
2017-07-17 23:19:39.855 LeeSDWebImageLearn[4068:350869] __NSSingleEntryDictionaryI
2017-07-17 23:19:39.855 LeeSDWebImageLearn[4068:350869] 引用计数:2
2017-07-17 23:19:39.855 LeeSDWebImageLearn[4068:350869] element::::::0x10e431650----0x10e431650----0x10e431650
2017-07-17 23:19:39.856 LeeSDWebImageLearn[4068:350869] 0x6080000362c0,0x6080000362c0,0x60800004acb0------->{
    key = hello;
},{
    key = hello;
},{
    key = hello;
    name = oliver;
}
2017-07-17 23:19:39.856 LeeSDWebImageLearn[4068:350869] 引用计数:1

  • 看下NSMutableArray 和 NSMutableDictionary 的copy 和 mutablecopy
    NSMutableArray * array = @[@"hello"].mutableCopy;
    NSMutableArray * copyArray = array.copy;
    NSLog(@"%@",[copyArray class]);
    NSLog(@"引用计数:%ld",CFGetRetainCount((__bridge CFTypeRef)array));
    NSMutableArray * muCopyArray = array.mutableCopy;
    [muCopyArray addObject:@"world"];
    NSLog(@"element::::::%p----%p----%p",array[0],muCopyArray[0],copyArray[0]);
    NSLog(@"%p,%p,%p------->%@,%@,%@",array,copyArray,muCopyArray,array,copyArray,muCopyArray);
    copyArray = nil;
    NSLog(@"引用计数:%ld",CFGetRetainCount((__bridge CFTypeRef)array));

    
    
    NSMutableDictionary * dic = @{@"key":@"hello"}.mutableCopy;
    NSMutableDictionary * copyDic = dic.copy;
    NSLog(@"%@",[copyDic class]);
    NSLog(@"引用计数:%ld",CFGetRetainCount((__bridge CFTypeRef)dic));
    NSMutableDictionary * mutDic = dic.mutableCopy;
    [mutDic setValue:@"oliver" forKey:@"name"];
    NSLog(@"element::::::%p----%p----%p",dic[@"key"],mutDic[@"key"],copyDic[@"key"]);
    NSLog(@"%p,%p,%p------->%@,%@,%@",dic,copyDic,mutDic,dic,copyDic,mutDic);
    copyDic = nil;
    NSLog(@"引用计数:%ld",CFGetRetainCount((__bridge CFTypeRef)dic));

控制台输出:

2017-07-17 22:06:19.119 LeeSDWebImageLearn[3890:322501] __NSSingleObjectArrayI
2017-07-17 22:06:19.119 LeeSDWebImageLearn[3890:322501] 引用计数:1
2017-07-17 22:06:19.119 LeeSDWebImageLearn[3890:322501] element::::::0x110036650----0x110036650----0x110036650
2017-07-17 22:06:19.119 LeeSDWebImageLearn[3890:322501] 0x600000054970,0x60000000d600,0x600000054af0------->(
    hello
),(
    hello
),(
    hello,
    world
)
2017-07-17 22:06:19.120 LeeSDWebImageLearn[3890:322501] 引用计数:1
2017-07-17 22:06:19.120 LeeSDWebImageLearn[3890:322501] __NSDictionaryI
2017-07-17 22:06:19.120 LeeSDWebImageLearn[3890:322501] 引用计数:1
2017-07-17 22:06:19.120 LeeSDWebImageLearn[3890:322501] element::::::0x110036650----0x110036650----0x110036650
2017-07-17 22:06:19.120 LeeSDWebImageLearn[3890:322501] 0x6080000510a0,0x60800007acc0,0x608000054640------->{
    key = hello;
},{
    key = hello;
},{
    key = hello;
    name = oliver;
}
2017-07-17 22:06:19.121 LeeSDWebImageLearn[3890:322501] 引用计数:1


分析如下:

  • 不可变的数组和字典进行copy 操作时候,引用计数为增加,但是内存地址不变,属于共用元素,(浅拷贝)
  • 不可变的数组和字典进行mutablecopy 操作时候,引用计数为不变,但是内存地址改变,元素的地址依然指向最初的对象的内存地址,可以改变(伪深拷贝)
  • 可变的数组和字典进行copy 操作时候,引用计数为不变,但是内存地址改变只是对象的内存地址,元素的地址依然指向最初的对象的内存地址,猜想可能是为了节省内存空间吧,不可变(伪深拷贝)
  • 可变的数组和字典进行mutablecopy 操作时候,引用计数为不变,但是内存地址改变,元素的地址依然指向最初的对象的内存地址,重点可以改变(伪深拷贝)

回过头来开属性关键字


@property(nonatomic,copy)NSString * c_str;
@property(nonatomic,strong)NSMutableString * c_mstr;
@property(nonatomic,copy)NSArray  * c_array;
@property(nonatomic,strong)NSMutableArray * c_marray;
@property(nonatomic,copy)NSDictionary * c_dic;
@property(nonatomic,strong)NSMutableDictionary * c_mdic;

    NSString * teststr = @"good";
    NSArray  * testarr = @[@"test"];
    NSDictionary * tdic = @{@"key":@"hello"};
    
    self.c_str = teststr;
    self.c_array = testarr;
    self.c_dic    = tdic;
    NSLog(@"引用计数:%ld",CFGetRetainCount((__bridge CFTypeRef)testarr));
    NSLog(@"引用计数:%ld",CFGetRetainCount((__bridge CFTypeRef)tdic));
    self.c_mdic = tdic.mutableCopy;
    self.c_mstr = teststr.mutableCopy;
    self.c_marray = testarr.mutableCopy;

    NSLog(@"%p,%p,%p",teststr,testarr,tdic);
    NSLog(@"%p,%p,%p,%p,%p%p",self.c_str,self.c_mstr,self.c_array,self.c_marray,self.c_dic,self.c_mdic);
    
    
    NSLog(@"引用计数:%ld",CFGetRetainCount((__bridge CFTypeRef)testarr));
    NSLog(@"引用计数:%ld",CFGetRetainCount((__bridge CFTypeRef)tdic));

    
    //都是不可变的字符串
    if ([self.c_mstr respondsToSelector:@selector(appendString:)]) {
        [self.c_mstr appendString:@"hello world"];
    }
    NSLog(@"%@",self.c_mstr);
    //如果可变数组用来copy 来修饰的话将会变成不可变的程序会出问题,还是用strong来修饰吧
    if ([self.c_marray respondsToSelector:@selector(removeAllObjects)]) {
        [self.c_marray removeAllObjects];
    }
    NSLog(@"%@",testarr);
    if ([self.c_mdic respondsToSelector:@selector(setValue:forKey:)]) {
        [self.c_mdic setValue:@"helllo" forKey:@"888"];
    }
    NSLog(@"%@",self.c_mdic);

看控制台:

2017-07-18 00:48:17.182 LeeSDWebImageLearn[4696:397950] 引用计数:2
2017-07-18 00:48:17.182 LeeSDWebImageLearn[4696:397950] 引用计数:2
2017-07-18 00:48:17.182 LeeSDWebImageLearn[4696:397950] 0x101fbe770,0x60800001c4f0,0x60800003bd60
2017-07-18 00:48:17.182 LeeSDWebImageLearn[4696:397950] 0x101fbe770,0x60000007ecc0,0x60800001c4f0,0x600000055db0,0x60800003bd60,0x6000000590b0
2017-07-18 00:48:17.183 LeeSDWebImageLearn[4696:397950] 引用计数:2
2017-07-18 00:48:17.183 LeeSDWebImageLearn[4696:397950] 引用计数:2
2017-07-18 00:48:17.183 LeeSDWebImageLearn[4696:397950] goodhello world
2017-07-18 00:48:17.183 LeeSDWebImageLearn[4696:397950] (
    test
)
2017-07-18 00:48:17.183 LeeSDWebImageLearn[4696:397950] {
    888 = helllo;
    key = hello;
}

从上面可以看出:

  • 当修饰可变类型的属性时,如NSMutableArray、NSMutableDictionary、NSMutableString,用strong,并且用 strong 不会导致引用计数增加,相当于内存的拷贝了。
  • 当修饰不可变类型的属性时,如NSArray、NSDictionary、NSString,用copy。会导致引用计数的增加,NSString 类型除外,这个在常量区,没有引用的书法

友情链接:
http://www.cnblogs.com/iCocos/p/4462744.html
http://www.jianshu.com/p/58c985408b75
https://dayon.gitbooks.io/-ios/content/chapter2.html

你可能感兴趣的:(iOS 属性关键字剖析)