assgin适用于基本的数据类型,比如NSInteger、BOOL等。
NSString、NSArray、NSDictionary等经常使用copy关键字,是因为他们有对应的可变类型:NSMutableString、NSMutableArray、NSMutableDictionary;
其他的都要strong修饰
#import <Foundation/Foundation.h>
@interface Animals : NSObject
@property(nonatomic,strong) NSString *strongPeople;
@property(nonatomic,copy) NSString *people;
@end
- (void)viewDidLoad {
[super viewDidLoad];
NSMutableString *defaultName = [NSMutableString stringWithFormat:@"我是特种兵"];
Animals *animal = [Animals new];
animal. people = defaultName; //生成一份新的内存空间
animal. strongPeople = defaultName; //指针指向同一块内存空间
NSLog(@"%@,%@",animal. people,animal. strongPeople);
[defaultName appendString:@"我爱吃鸡"];
NSLog(@"更改后:%@,%@",animal. people,animal. strongPeople);
}
输出:我是特种兵,我是特种兵
我是特种兵,我是特种兵我爱吃鸡
@property声明 NSString、NSArray、NSDictionary 会使用到copy关键字,他们有对应的可变类型:NSMutableString、NSMutableArray、NSMutableDictionary,他们之间可能进行赋值操作,为确保对象中的字符串值不会无意间变动,应该在赋值操作时拷贝一份。
strong ---- 这个属性有可能指向一个可变对象,如果这个可变对象在外部被修改了,那么会影响该属性。(两个属性指向同一块内存空间 。在这里strong 属于浅拷贝,只赋值对象的指针)
copy --- 经常用此特质来保护其封装性。因为传递给该属性的新值有可能指向一个NSMutable类的实例,若是不拷贝,那么设置完属性之后,可变类实例的值就可能会在对象不知情的情况下遭人更改。
总结:用@property声明的NSString(或NSArray,NSDictionary)经常使用copy关键字,为什么?如果改用strong关键字,可能造成什么问题?
因为父类指针可以指向子类对象,使用copy的目的是为了让本对象的属性不受外界影响,使用copy无论给我传入是一个可变对象还是不可对象,我本身持有的就是一个不可变的副本.
如果我们使用是strong,那么这个属性就有可能指向一个可变对象,如果这个可变对象在外部被修改了,那么会影响该属性.
Block属性声明为什么用copy来修饰
首先,你必须要对(堆栈)有一定的理解,参考iOS block详解。 block声明后是放在栈里的,只有copy后才会放在堆上。当栈里的block被释放掉的时候,本地变量将变得不可访问,一旦代码执行到block这段就会导致崩溃。
C语言内存模型图:
image
栈区(stack):存放函数的参数值、局部变量的值等,由编译器自动分配释放,通常在函数执行结束后就释放了,其操作方式类似数据结构中的栈。栈内存分配运算内置于处理器的指令集,效率很高,但是分配的内存容量有限,比如iOS中栈区的大小是2M。
堆区(heap):就是通过new、malloc、realloc分配的内存块,它们的释放编译器不去管,由我们的应用程序去释放。如果应用程序没有释放掉,操作系统会自动回收。分配方式类似于链表。
静态区:全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后,由系统释放。
常量区:常量存储在这里,不允许修改的。
代码区:存放函数体的二进制代码。
栈区释放内存:
- (void)print {
int i = 10;
int j = 20;
NSLog(@"i+j = %d", (i+j));
}
在上面的代码中当程序执行到 } 的时候,变量i和j的作用域已经结束了,编译器就会自动释放掉i和j所占的内存,所以理解好作用域就理解了栈区的内存分配。
- (void)testMethod {
int anInteger = 42;
void (^testBlock)(void) = ^{
NSLog(@"Integer is : %i", anInteger);
};
anInteger = 50;
testBlock();
}
输出:Integer is : 42
- (void)testMethod {
__block int anInteger = 42;
void (^testBlock)(void) = ^{
NSLog(@"Integer is : %i", anInteger);
};
anInteger = 50;
testBlock();
}
输出:输出:Integer is : 50
在例子1中,block会把anInteger变量复制为自己私有的const变量,也就是说block会捕获栈上的变量(或指针),将其复制为自己私有的const变量。在例子1中,在进行anInteger = 50的操作的时候,block已经将其复制为自己的私有变量,所以这里的修改对block里面的anInteger不会造成任何影响。
在例子2中,anInteger是一个局部变量,存储在栈区的。给anInteger加入__block修饰符所起到的作用就是只要观察到该变量被block所持有,就将该变量在栈中的内存地址放到堆中,此时不管block外部还是内部anInterger的内存地址都是一样的,进而不管在block外部还是内部都可以修改anInterger变量的值,所以anInteger = 50之后,在block输出的值就是50了
网友评论