美文网首页
iOS strong & copy @synthes

iOS strong & copy @synthes

作者: 青椒辣不辣 | 来源:发表于2020-12-21 18:44 被阅读0次

iOS Runtime 简单使用

class_addMethod第四个参数含义官网链接

@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];

----------------------------------------------------------------------------

\color{rgb(0 ,139, 139)}{@synthesize}

属性本质
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;

\color{rgb(0 ,139, 139)}{@dynamic}

@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方法将被调用。"

相关文章

网友评论

      本文标题:iOS strong & copy @synthes

      本文链接:https://www.haomeiwen.com/subject/hsxdnktx.html