# 07.深入探究属性

```C++
@interface RYModel : NSObject

@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSInteger age;
@property (nonatomic, assign) float height;
@property (nonatomic, strong) RYModel *father;
@property (nonatomic, assign) BOOL isBoy;

- (void)dosomething;

- (void)dosomethingWith:(NSString *)title;

+ (void)classDoSomething;

- (NSString *)sayMyName;

@end
```

## 一、成员变量和属性

我们知道，属性会生成带下划线的成员变量和对应的set、get方法。我们将上面用到的RYModel，编译成C++看下。

```C++
static NSString * _I_RYModel_name(RYModel * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_RYModel$_name)); }
extern "C" __declspec(dllimport) void objc_setProperty (id, SEL, long, id, bool, bool);

static void _I_RYModel_setName_(RYModel * self, SEL _cmd, NSString *name) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct RYModel, _name), (id)name, 0, 1); }

static NSInteger _I_RYModel_age(RYModel * self, SEL _cmd) { return (*(NSInteger *)((char *)self + OBJC_IVAR_$_RYModel$_age)); }
static void _I_RYModel_setAge_(RYModel * self, SEL _cmd, NSInteger age) { (*(NSInteger *)((char *)self + OBJC_IVAR_$_RYModel$_age)) = age; }

static float _I_RYModel_height(RYModel * self, SEL _cmd) { return (*(float *)((char *)self + OBJC_IVAR_$_RYModel$_height)); }
static void _I_RYModel_setHeight_(RYModel * self, SEL _cmd, float height) { (*(float *)((char *)self + OBJC_IVAR_$_RYModel$_height)) = height; }

static RYModel * _I_RYModel_father(RYModel * self, SEL _cmd) { return (*(RYModel **)((char *)self + OBJC_IVAR_$_RYModel$_father)); }
static void _I_RYModel_setFather_(RYModel * self, SEL _cmd, RYModel *father) { (*(RYModel **)((char *)self + OBJC_IVAR_$_RYModel$_father)) = father; }

static BOOL _I_RYModel_isBoy(RYModel * self, SEL _cmd) { return (*(BOOL *)((char *)self + OBJC_IVAR_$_RYModel$_isBoy)); }
static void _I_RYModel_setIsBoy_(RYModel * self, SEL _cmd, BOOL isBoy) { (*(BOOL *)((char *)self + OBJC_IVAR_$_RYModel$_isBoy)) = isBoy; }
```

### 8.1 copy 修饰符

`name` 属性用的是 `copy` 来修饰的。很容易发现它的set方法明显有不同。

他用的是 `objc_setProperty` ，而其他的都是直接通过内存平移对成员变量进行赋值操作。

## 二、objc\_setProperty

我们看下 `objc_setProperty` 的源码：

```C++
/// 对象 sel 偏移量 值 原子性 是否copy
void objc_setProperty(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, signed char shouldCopy) 
{
    // 是否是 Copy
    bool copy = (shouldCopy && shouldCopy != MUTABLE_COPY);
    // 是否是 可变的Copy
    bool mutableCopy = (shouldCopy == MUTABLE_COPY);
    reallySetProperty(self, _cmd, newValue, offset, atomic, copy, mutableCopy);
}
```

发现他实际调用了 `reallySetProperty`

### 2.1 reallySetProperty

```C++
static inline void reallySetProperty(id self, SEL _cmd, id newValue, ptrdiff_t offset, bool atomic, bool copy, bool mutableCopy)
{
    if (offset == 0) { 
        // 修改 ISA
        object_setClass(self, newValue);
        return;
    }

    id oldValue;
    // 定位（首地址+偏移量）
    id *slot = (id*) ((char*)self + offset);

    // Copy相关操作
    if (copy) {
        newValue = [newValue copyWithZone:nil];
    } else if (mutableCopy) {
        newValue = [newValue mutableCopyWithZone:nil];
    } else {
        if (*slot == newValue) return;// 相同的话不做操作
        // 引用计数+1
        newValue = objc_retain(newValue); 
    }

    // 原子性相关
    if (!atomic) {
        oldValue = *slot;
        *slot = newValue;
    } else {
      // 加锁
        spinlock_t& slotlock = PropertyLocks[slot];
        slotlock.lock();
        oldValue = *slot;
        *slot = newValue;        
        slotlock.unlock();
    }

    // release 旧值 引用计数 -1
    objc_release(oldValue);
}

```

## 三、 Set 流程图

![Set方法](/files/1HUVaW7l8LG5wXOQhymu)

## 四、Type Encodings 类型编码

我们会看到这样一些 `objc_selector`，其中有 `v24@0:8@16` 类似这样的一些看不懂的字符串。他们叫类型编码

> 具体可以对照[官方文档](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html)这里就不展开聊了

以 `setName` 为例分析一次：

|      v     |    24   |   @   |    0    |  :  |    8    |   @   |    16    |
| :--------: | :-----: | :---: | :-----: | :-: | :-----: | :---: | :------: |
| 返回值类型-void | 占用大小-24 | 参数-id | 从偏移量0开始 | SEL | 从偏移量8开始 | 参数-id | 从偏移量16开始 |

```C++
static struct /*_method_list_t*/ {
	unsigned int entsize;  // sizeof(struct _objc_method)
	unsigned int method_count;
	struct _objc_method method_list[21];
} _OBJC_$_INSTANCE_METHODS_RYModel __attribute__ ((used, section ("__DATA,__objc_const"))) = {
	sizeof(_objc_method),
	21,
	{{(struct objc_selector *)"ry_protocolDoSomething", "v16@0:8", (void *)_I_RYModel_ry_protocolDoSomething},
	{(struct objc_selector *)"name", "@16@0:8", (void *)_I_RYModel_name},
	{(struct objc_selector *)"setName:", "v24@0:8@16", (void *)_I_RYModel_setName_},
	{(struct objc_selector *)"age", "q16@0:8", (void *)_I_RYModel_age},
	{(struct objc_selector *)"setAge:", "v24@0:8q16", (void *)_I_RYModel_setAge_},
	{(struct objc_selector *)"height", "f16@0:8", (void *)_I_RYModel_height},
	{(struct objc_selector *)"setHeight:", "v20@0:8f16", (void *)_I_RYModel_setHeight_},
	{(struct objc_selector *)"father", "@16@0:8", (void *)_I_RYModel_father},
	{(struct objc_selector *)"setFather:", "v24@0:8@16", (void *)_I_RYModel_setFather_},
	{(struct objc_selector *)"isBoy", "c16@0:8", (void *)_I_RYModel_isBoy},
	{(struct objc_selector *)"setIsBoy:", "v20@0:8c16", (void *)_I_RYModel_setIsBoy_},
    ...
	{(struct objc_selector *)"isBoy", "c16@0:8", (void *)_I_RYModel_isBoy},
	{(struct objc_selector *)"setIsBoy:", "v20@0:8c16", (void *)_I_RYModel_setIsBoy_}}
};

```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://ryukiedev.gitbook.io/wiki/ios/di-ceng/07.-shu-xing.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
