@property (strong ,nonatomic) NSString *strongStr;
@property (copy ,nonatomic) NSString *copyyyStr;
@property (strong ,nonatomic) NSMutableString *strongMStr;
@property (copy ,nonatomic) NSMutableString *copyyyMStr;
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
NSString *originStr = [NSString stringWithFormat:@"hello,originStr"];
_strongStr = originStr;
_copyyyStr = originStr;
NSLog(@"originStr 对象地址: %p ,对象指针地址:%p ,对象的值:%@", originStr, &originStr, originStr);
NSLog(@"strongStr 对象地址: %p ,对象指针地址:%p ,对象的值:%@", _strongStr, &_strongStr, _strongStr);
NSLog(@"copyyyStr 对象地址: %p ,对象指针地址:%p ,对象的值:%@", _copyyyStr, &_copyyyStr, _copyyyStr);
}
originStr 对象地址: 0x6000010fd350 ,对象指针地址:0x7ffee9bd1a78 ,对象的值:hello,originStr
strongStr 对象地址: 0x6000010fd350 ,对象指针地址:0x7f9212d07918 ,对象的值:hello,originStr
copyyyStr 对象地址: 0x6000010fd350 ,对象指针地址:0x7f9212d07920 ,对象的值:hello,originStr
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
NSString *originStr = [NSString stringWithFormat:@"hello,originStr"];
self.strongStr = originStr;
self.copyyyStr = originStr;
NSLog(@"originStr 对象地址: %p ,对象指针地址:%p ,对象的值:%@", originStr, &originStr, originStr);
NSLog(@"strongStr 对象地址: %p ,对象指针地址:%p ,对象的值:%@", _strongStr, &_strongStr, _strongStr);
NSLog(@"copyyyStr 对象地址: %p ,对象指针地址:%p ,对象的值:%@", _copyyyStr, &_copyyyStr, _copyyyStr);
}
originStr 对象地址: 0x60000099e370 ,对象指针地址:0x7ffee031ea78 ,对象的值:hello,originStr
strongStr 对象地址: 0x60000099e370 ,对象指针地址:0x7fba4420e438 ,对象的值:hello,originStr
copyyyStr 对象地址: 0x60000099e370 ,对象指针地址:0x7fba4420e440 ,对象的值:hello,originStr
----------------------------------------------------------------------------
总结:对于NSString,不管是copy还是strong,两种方式赋值(_property|self.property),其指向的地址都是originStr的地址。
----------------------------------------------------------------------------
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
NSMutableString *originMStr = [NSMutableString stringWithFormat:@"hello,originStr"];
_strongMStr = originMStr;
_copyyyMStr = originMStr;
[originMStr setString:@"hello,I changed"];
NSLog(@"originMStr 对象地址: %p ,对象指针地址:%p ,对象的值:%@", originMStr, &originMStr, originMStr);
NSLog(@"strongMStr 对象地址: %p ,对象指针地址:%p ,对象的值:%@", _strongMStr, &_strongMStr, _strongMStr);
NSLog(@"copyyyMStr 对象地址: %p ,对象指针地址:%p ,对象的值:%@", _copyyyMStr, &_copyyyMStr, _copyyyMStr);
}
originMStr 对象地址: 0x600002d0c6c0 ,对象指针地址:0x7ffeef04ca78 ,对象的值:hello,I changed
strongMStr 对象地址: 0x600002d0c6c0 ,对象指针地址:0x7fd23060cff8 ,对象的值:hello,I changed
copyyyMStr 对象地址: 0x600002d0c6c0 ,对象指针地址:0x7fd23060d000 ,对象的值:hello,I changed
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
NSMutableString *originMStr = [NSMutableString stringWithFormat:@"hello,originStr"];
self.strongMStr = originMStr;
self.copyyyMStr = originMStr;
[originMStr setString:@"hello,I changed"];
NSLog(@"originMStr 对象地址: %p ,对象指针地址:%p ,对象的值:%@", originMStr, &originMStr, originMStr);
NSLog(@"strongMStr 对象地址: %p ,对象指针地址:%p ,对象的值:%@", _strongMStr, &_strongMStr, _strongMStr);
NSLog(@"copyyyMStr 对象地址: %p ,对象指针地址:%p ,对象的值:%@", _copyyyMStr, &_copyyyMStr, _copyyyMStr);
}
originMStr 对象地址: 0x600002ef9950 ,对象指针地址:0x7ffee0063a78 ,对象的值:hello,I changed
strongMStr 对象地址: 0x600002ef9950 ,对象指针地址:0x7fb52a705238 ,对象的值:hello,I changed
copyyyMStr 对象地址: 0x600002ef9860 ,对象指针地址:0x7fb52a705240 ,对象的值:hello,originStr
----------------------------------------------------------------------------
总结:对于NSMutableString
_property : 不管是copy还是strong,其指向的地址都是originMStr的地址。
self.property:strong指向的地址是originMStr的地址,copy是新地址,只有此时为深拷贝,以上的其他情况均为浅拷贝
用@property来声明属性变量时,编译器会自动为我们生成一个以下划线加属性名命名的实例变量(@synthesize copyyyMStr = _copyyyMStr),并且生成其对应的getter、setter方法。
1.self.copyyyMStr = originMStr赋值时,会调用copyyyMStr的setter方法
2._copyyyMStr = originMStr 赋值时给_copyyyMStr实例变量直接赋值,并不会调用copyyyMStr的setter方法
而在setter方法中有一个非常关键的语句:_copyyyMStr = [copyyyMStr copy];
----------------------------------------------------------------------------
属性本质
property = ivar + setter + getter
即:成员变量 + setter函数 + getter函数 就构成了属性。
synthesize语义是指:当我们没有手动为成员变量添加setter、getter函数时,编译器会自动为我们添加上这两个函数。
如果我们既没有写synthesize也没有写dynamic,那编译器默认会为我们添加:
@synthesize property = _property; //这里的_property是编译器默认为我们添加成员变量。
因此在类内部我们可以使用 _property 来进行赋值、取值操作。
现在我们大多数情况下都是直接创建一个property来使用,背后由编译器来为我们自动添加上面的synthesize,
进而读取synthezise语义添加 setter和getter函数。以至于我们都快忘记了synthesize到起到什么作用。
手动添加synthesize
我们手动添加synthesize后,有两种方式如下:
a、@synthesize xxx;
这种方式:为类生成一个和xxx同名的实例变量。我们在类文件中赋值取值是可以直接用xxx。
b、@synthesize xxx = _newName
这种方式:如果不存在_newName变量,则会创建一个_newName成员变量,如果存在则不会添加_newName成员变量。
在类中可以使用_newName来存取值。newName可以跟propertyName不同名。
@synthesize基本使用
* 在@implementation中, 用来自动生成setter和getter的实现
@synthesize编写步骤
1.在@implementation和@end之间写上@synthesize
2.在@synthesize后面写上和@property中一样的属性名称, 这样@synthesize就会将@property生成的什么拷贝到@implementation中
3.由于getter/setter方法实现是要将传入的形参 给属性和获取属性的值,所以在@synthesize的属性后面写上要将传入的值赋值给谁和要返回哪个属性的值, 并用等号连接
@synthesize注意点
* @synthesize age = _age;
* setter和getter实现中会访问成员变量_age
* 如果成员变量_age不存在,就会自动生成一个@private的成员变量_age
* @synthesize age;
* setter和getter实现中会访问@synthesize后同名成员变量age
* 如果成员变量age不存在,就会自动生成一个@private的成员变量age
* 多个属性可以通过一行@synthesize搞定,多个属性之间用逗号连接
@synthesize age = _age, number = _number, name = _name;
@synthesize age = _age; 在给age赋值时,编译器做了哪些事?
答:
@synthesize age = _age;
1.在@synthesize后面的age,告诉编译器, 需要实现哪个@property生成的声明
2.告诉@synthesize, 需要将传入的值赋值给谁和返回谁的值给调用者
如果在@synthesize后面没有告诉系统将传入的值赋值给谁, 系统默认会赋值给和@synthesize后面写得名称相同的成员变量 @synthesize age;
@synthesize的语义是如果你没有手动实现setter方法和getter方法,那么编译器会自动为你加上这两个方法。
@dynamic告诉编译器,属性的setter与getter方法由用户自己实现,不自动生成。
@interface RJTextTwoViewController ()
@property (copy ,nonatomic) NSString *name;
@end
@implementation RJTextTwoViewController
@synthesize name = _myName;
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
_myName = @"诸葛亮";
NSLog(@"name = %@",self.name);
}
@end
name = 诸葛亮
@dynamic使用如果不实现setter与getter则报错
image.png
.h
@interface RJDragSortCollectionView : UICollectionView
@property (nonatomic, assign) id<RJDragSortCollectionViewDelegate> delegate;
@property (nonatomic, assign) id<RJDragSortCollectionViewDataSource> dataSource;
@end
.m
@implementation RJDragSortCollectionView
@dynamic delegate;
@dynamic dataSource;
@end
/*
如果@implementation中不写@dynamic delegate;则.h中delegate会报警告
Auto property synthesis will not synthesize property 'delegate'; it will be implemented by its superclass, use @dynamic to acknowledge intention
翻译:自动属性合成不会合成属性“delegate”;它将由其超类实现,使用@dynamic确认意图
@dynamic A 相当于告诉编译器:"参数A的getter和setter方法并不在此处,
而在其他地方实现了或者生成了,当你程序运行的时候你就知道了,
所以别警告我了” 这样程序在运行的时候,
对应参数的getter和setter方法就会在其他地方去寻找,比如父类
*/
@interface RJTextTwoViewController ()
{
// must provide a ivar for our setter and getter
NSString *_userName;
NSString *_cName;
}
@property (copy ,nonatomic) NSString *name;
@property (copy ,nonatomic) NSString *userName;
@property (copy ,nonatomic) NSString *cName;
@end
@implementation RJTextTwoViewController
@synthesize name = _myName;
@dynamic userName,cName;
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = UIColor.whiteColor;
}
-(void)dealloc{
NSLog(@"%s",__func__);
}
// OC语言实现方式 ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
-(void)setUserName:(NSString *)userName{
if (_userName != userName) {
_userName = [userName copy];
}
}
-(NSString *)userName{
return _userName;
}
// ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
// C语言实现方式 ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
+(BOOL)resolveInstanceMethod:(SEL)sel{
// Capture setName: and name method
if (sel == @selector(setCName:)) {
class_addMethod([self class], sel, (IMP)setCName, "v@:@");
return YES;
}
else if (sel == @selector(cName)) {
class_addMethod([self class], sel, (IMP)getCName, "@@:");
return YES;
}
return [super resolveInstanceMethod:sel];
}
void setCName(id self, SEL _cmd, NSString *cName){
if (((RJTextTwoViewController *)self)->_cName != cName) {
((RJTextTwoViewController *)self)->_cName = [cName copy];
}
}
NSString* getCName(id self, SEL _cmd){
return ((RJTextTwoViewController *)self)->_cName;
}
// ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
_myName = @"诸葛亮";
NSLog(@"name = %@",self.name);
self.userName = @"刘备";
NSLog(@"userName = %@",self.userName);
self.cName = @"曹操";
NSLog(@"cName = %@",self.cName);
}
@end
name = 诸葛亮
userName = 刘备
cName = 曹操
实例变量与属性
@interface MyViewController :UIViewController
{
UIButton *myButton;
}
@property (nonatomic, retain) UIButton *myButton;
@end
苹果将默认编译器从GCC转换为LLVM(low level virtual machine),从此不再需要为属性声明实例变量了。如果LLVM发现一个没有匹配实例变量的属性,它将自动创建一个以下划线开头的实例变量。因此,我们不再为输出口声明实例变量。
而且也不需要在.m文件中写@synthesize myButton;也会自动为你生成setter,getter方法。@synthesize的作用就是让编译器为你自动生成setter与getter方法。
@synthesize 还有一个作用,可以指定与属性对应的实例变量,例如@synthesize myButton = xxx;那么self.myButton其实是操作的实例变量xxx,而不是_myButton了。
例如:MyViewController.h文件
@interface MyViewController :UIViewController
@property (nonatomic, retain) UIButton *myButton;
@end
在MyViewController.m文件中,编译器也会自动的生成一个实例变量_myButton。那么在.m文件中可以直接的使用_myButton实例变量,也可以通过属性self.myButton.都是一样的。
注意这里的self.myButton其实是调用的myButton属性的getter/setter方法。这与C++中点的使用是有区别的,C++中的点可以直接访问成员变量(也就是实例变量)。
例如在oc中有如下代码
.h文件
@interface MyViewController :UIViewController
{
NSString *name;
}
@end
.m文件中,self.name 这样的表达式是错误的。xcode会提示你使用->,改成self->name就可以了。因为oc中点表达式是表示调用方法,而上面的代码中没有name这个方法。
oc语法关于点表达式的说明:"点表达式(.)看起来与C语言中的结构体访问以及Java语言汇总的对象访问有点类似,其实这是oc的设计人员有意为之。如果点表达式出现在等号 = 左边,该属性名称的setter方法将被调用。如果点表达式出现在右边,该属性名称的getter方法将被调用。"
网友评论