yaofly2012 / note

Personal blog
https://github.com/yaofly2012/note/issues
44 stars 5 forks source link

Objective-C: @property, @synthesize, @dynamic #271

Open yaofly2012 opened 1 year ago

yaofly2012 commented 1 year ago

@property

引入背景

先说结论:OC中约定俗成地利用getter/setter方法获取/设置类成员变量值。为了便于声明和定义Getter/Setter方法才引入了@property

类成员变量声明和访问

@interface Girl : NSObject {
@public // publick成员变量
    NSString *name;
@private // private成员变量
    int age;
}
@end

@implementation Girl

- (instancetype)init {
    if(self = [super init]) {
        self->name = @"Default"; // 利用`->`直接访问成员变量
        self->age = 20;
    }
    return self;
}

@end

Girl *girl = [[Girl alloc] init];
NSLog(@"The girl's name is %@", girl->name);

OC里利用->直接访问成员变量,那为啥要用getter/setter方式呢?

为啥要用getter/setter方法获取/设置类成员变量值 TODO

Explain Getter and Setters in Objective C :

  1. 可以在getter和setter中添加额外的代码,实现特定的目的。比如赋值前(set)需要实现一些特定的内部计算,或者更新状态,缓存数据等等。
  2. KVC(Key-Value Coding)和KVO(Key-Value Observing)都是基于此实现的。
  3. 在非ARC时代,可以在在getter和setter中进行内存管理。

基本语法

@property(特性1, 特性2 , 特性3)varType varName;

将上面的Demo改用@property:

// 在@interface里使用@property 声明成员变量
@interface Girl : NSObject

@property (copy) NSString *name; // 头文件里利用@property声明

@property int age;

@end

// 实现
@implementation Girl

- (instancetype)init {
    if(self = [super init]) {
        self.name = @"Default"; // 利用点`.`操作符访问setter,或者直接调用`setName`方法:` [self setName:@"Default"];`
        self.age = 20; // 利用点`.`操作符访问setter,或者直接调用`setAge`方法:` [self setAge:20];`
    }
    return self;
}

@end

// 使用
Girl *girl = [[Girl alloc] init];
NSLog(@"girl's name is %@", girl.name); // 利用点`.`操作符访问getter,或者`[girl name]`

@property做了3件事情:

  1. @interface里声明成员变量,方法名字格式是:
    • getter方法跟变量名一致
    • setter方法则是setXXX
  2. @interface里声明声明getter/setter方法;
  3. @implementation定义getter/setter方法。

注意:

  1. @property须在@interface使用,不可以在@implementation里使用。
  2. 利用点.操作符访问既可以访问getter,也可以访问setter。如果采用[]方式就比较繁琐些。

特性

可使用的修饰关键字根据功能可分为5类(主要用到的前3类):

  1. 原子特性:atomic, nonatomic,默认值atomic
  2. 访问特性性:readonly, readwrite,默认值readwrite
  3. 内存管理特性:assign, retain, copy默认值assgin ARC之后针对对象类型的变量增加了weakstrong特性,默认值strong

assign/retain/copyweak/strong是互斥的,不可同时使用。

  1. getter=/ setter=自定义存取函数名称
  2. 可空特性Nullability:
    • nullable:可空
    • nonnull:不可控
    • null_unspecified:未指定
    • null_resettable:调用setter去reset属性时,可以传入nil,但是getter返回值,不为空。UIView下面的tintColor,就是null_resettable。这样就保证,即使赋值为nil,也会返回一个非空的值

最佳实践

Assigning retained object to weak property; object will be released after assignment Property attributes 'assign' and 'strong' are mutually exclusive

参考

  1. Objective-C属性(property)的特性(attribute)
  2. 详解 OC 中的 property
  3. iOS OC @property的属性理解
  4. 巧用 Objective-C Class Properties 解耦
yaofly2012 commented 1 year ago

@synthesize

protocol里使用@property

除了可以在@interface使用@propertyprotocol里也使用@property

@protocol Person

@property (copy) NSString *name;

@property int age;

@end

@interface Girl : NSObject<Person>
@end

此时XCode会有个告警:

Auto property synthesis will not synthesize property 'age' declared in protocol 'Person'

Auto property synthesis will not synthesize property 'name' declared in protocol 'Person'

协议里只有声明并没有实现,所以得需要实现类去实现。实现类正确的做法是用@synthesize声明实现:

@implementation Girl

@synthesize name;

@synthesize age;

- (instancetype)init {
    if(self = [super init]) {
        self.name = @"Default";
        [self setAge:20];
    }
    return self;
}

@end

@synthesize只能在实现类的@implementation里使用,不可以在@interface里使用。

综上:

  1. 协议里的@property的作用是声明成员变量,声明getter/setter方法;
  2. @synthesize的作用是实现属性的getter/setter方法

如果覆盖@protocol里的@property会怎样?

不要这样做。可参考OC 中,覆盖属性会有怎么样的化学反应?

参考

  1. OC 中 覆盖父类属性会有Auto property synthesis will not synthesize property 'xxx'的警告
  2. OC关于重写属性的setter和getter的问题
  3. OC 中,覆盖属性会有怎么样的化学反应?
yaofly2012 commented 1 year ago

@dynamic TODO

参考

  1. @synthesize @dynamic详解