一、介绍

在OC中我们可以给任意的一个类以@property的格式声明属性,当然对于这个属性也会采用某一些属性关键字进行修饰,那么属性的真正的面目是啥样子的呢?其实,runtime源码中可以看到,property是一个结构,如下所示,只不过苹果为这个结构体另外定义了一个结构体指针。

//属性结构体指针
typedef struct objc_property *objc_property_t;

二、函数

正如我们所知,MJExtension是一个非常流行的json解析框架,其内部对对象的每一个属性进行解析的方式其实就是通过runtime来实现的。runtime源码中提供了属性的相关API函数。通过这些函数,我们可以轻松的得到每一个属性名和类型以及特性,相关函数如下:

//获取一个属性
objc_property_t _Nullable class_getProperty(Class _Nullable cls, const char * _Nonnull name); //添加一个属性
class_addProperty(Class _Nullable cls, const char * _Nonnull name, const objc_property_attribute_t * _Nullable attributes,unsigned int attributeCount); //替换一个属性
class_replaceProperty(Class _Nullable cls, const char * _Nonnull name, const objc_property_attribute_t * _Nullable attributes,unsigned int attributeCount) //获取属性数组
objc_property_t _Nonnull * _Nullable class_copyPropertyList(Class _Nullable cls, unsigned int * _Nullable outCount); //获取属性名称
const char * _Nonnull property_getName(objc_property_t _Nonnull property) ; //获取属性描述名称
const char * _Nullable property_getAttributes(objc_property_t _Nonnull property); //获取属性描述数组
objc_property_attribute_t * _Nullable property_copyAttributeList(objc_property_t _Nonnull property,unsigned int * _Nullable outCount); //通过属性描述的name获取属性描述的value
property_copyAttributeValue(objc_property_t _Nonnull property,const char * _Nonnull attributeName); //属性描述结构体
typedef struct {
const char * _Nonnull name; /**< The name of the attribute */
const char * _Nonnull value; /**< The value of the attribute (usually empty) */
} objc_property_attribute_t;

三、案例

了解了基本函数后,现在来上手,获取并打印看看property到底是个啥样,代码如下:

//定义一个对象,声明各种属性property
@interface SubObject : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) void (^Block)(void);
@property (nonatomic, weak) id<NSObject> delegate;
@property (nonatomic, assign) int age;
@property (nonatomic, assign) float height;
@property (nonatomic, assign) double weight;
@property (nonatomic, strong) NSNumber *num;
@property (nonatomic, strong) NSDate *date;
@property (nonatomic, strong) NSData *data;
@property (nonatomic, strong) NSArray *array;
@property (nonatomic, strong) NSDictionary *dic;
@property (nonatomic, strong) MyObject *myObject;
@end
//获取属性列表
unsigned int count;
objc_property_t *properties = class_copyPropertyList([subObject class], &count);
for (int i=; i<count; i++) { //获取属性
objc_property_t property = properties[i]; //属性名称
const char *propertyName = property_getName(property);
NSLog(@"------propertyName = %s-------",propertyName); //属性描述名称
const char * property_attr = property_getAttributes(property);
NSLog(@"------property_attr = %s-------",property_attr); //属性类型
const char *propertyType = property_copyAttributeValue(property, "T");
NSLog(@"------propertyType = %s-------",propertyType); //属性变量名称
const char *property_Value = property_copyAttributeValue(property, "V");
NSLog(@"------property_Value = %s-------",property_Value); //属性描述列表
unsigned int outCount;
objc_property_attribute_t *attributeList = property_copyAttributeList(property, &outCount);
for (int j=; j<outCount; j++) {
objc_property_attribute_t attribute = attributeList[j];
NSLog(@"property_attribute_t_name:%s-----property_attribute_t_value:%s",attribute.name,attribute.value);
}
NSLog(@"");
}
-- ::10.335830+ 运行时[:] ------propertyName = name-------
-- ::10.336009+ 运行时[:] ------property_attr = T@"NSString",C,N,V_name-------
-- ::10.336152+ 运行时[:] ------propertyType = @"NSString"-------
-- ::10.336261+ 运行时[:] ------property_Value = _name-------
-- ::10.336405+ 运行时[:] property_attribute_t_name:T-----property_attribute_t_value:@"NSString"
-- ::10.336532+ 运行时[:] property_attribute_t_name:C-----property_attribute_t_value:
-- ::10.336648+ 运行时[:] property_attribute_t_name:N-----property_attribute_t_value:
-- ::10.336770+ 运行时[:] property_attribute_t_name:V-----property_attribute_t_value:_name
-- ::10.336877+ 运行时[:]
-- ::10.336974+ 运行时[:] ------propertyName = Block-------
-- ::10.337256+ 运行时[:] ------property_attr = T@?,C,N,V_Block-------
-- ::10.337568+ 运行时[:] ------propertyType = @?-------
-- ::10.337972+ 运行时[:] ------property_Value = _Block-------
-- ::10.338296+ 运行时[:] property_attribute_t_name:T-----property_attribute_t_value:@?
-- ::10.338638+ 运行时[:] property_attribute_t_name:C-----property_attribute_t_value:
-- ::10.341894+ 运行时[:] property_attribute_t_name:N-----property_attribute_t_value:
-- ::10.342363+ 运行时[:] property_attribute_t_name:V-----property_attribute_t_value:_Block
-- ::10.342769+ 运行时[:]
-- ::10.343143+ 运行时[:] ------propertyName = delegate-------
-- ::10.343664+ 运行时[:] ------property_attr = T@"<NSObject>",W,N,V_delegate-------
-- ::10.343779+ 运行时[:] ------propertyType = @"<NSObject>"-------
-- ::10.344314+ 运行时[:] ------property_Value = _delegate-------
-- ::10.344746+ 运行时[:] property_attribute_t_name:T-----property_attribute_t_value:@"<NSObject>"
-- ::10.345172+ 运行时[:] property_attribute_t_name:W-----property_attribute_t_value:
-- ::10.345485+ 运行时[:] property_attribute_t_name:N-----property_attribute_t_value:
-- ::10.345773+ 运行时[:] property_attribute_t_name:V-----property_attribute_t_value:_delegate
-- ::10.346062+ 运行时[:]
-- ::10.346475+ 运行时[:] ------propertyName = age-------
-- ::10.346859+ 运行时[:] ------property_attr = Ti,N,V_age-------
-- ::10.347278+ 运行时[:] ------propertyType = i-------
-- ::10.347784+ 运行时[:] ------property_Value = _age-------
-- ::10.348131+ 运行时[:] property_attribute_t_name:T-----property_attribute_t_value:i
-- ::10.348551+ 运行时[:] property_attribute_t_name:N-----property_attribute_t_value:
-- ::10.348980+ 运行时[:] property_attribute_t_name:V-----property_attribute_t_value:_age
-- ::10.349373+ 运行时[:]
-- ::10.349737+ 运行时[:] ------propertyName = height-------
-- ::10.350087+ 运行时[:] ------property_attr = Tf,N,V_height-------
-- ::10.350393+ 运行时[:] ------propertyType = f-------
-- ::10.350763+ 运行时[:] ------property_Value = _height-------
-- ::10.351191+ 运行时[:] property_attribute_t_name:T-----property_attribute_t_value:f
-- ::10.351596+ 运行时[:] property_attribute_t_name:N-----property_attribute_t_value:
-- ::10.351971+ 运行时[:] property_attribute_t_name:V-----property_attribute_t_value:_height
-- ::10.352397+ 运行时[:]
-- ::10.352694+ 运行时[:] ------propertyName = weight-------
-- ::10.353102+ 运行时[:] ------property_attr = Td,N,V_weight-------
-- ::10.353529+ 运行时[:] ------propertyType = d-------
-- ::10.353791+ 运行时[:] ------property_Value = _weight-------
-- ::10.354096+ 运行时[:] property_attribute_t_name:T-----property_attribute_t_value:d
-- ::10.354411+ 运行时[:] property_attribute_t_name:N-----property_attribute_t_value:
-- ::10.354693+ 运行时[:] property_attribute_t_name:V-----property_attribute_t_value:_weight
-- ::10.355023+ 运行时[:]
-- ::10.355303+ 运行时[:] ------propertyName = num-------
-- ::10.355567+ 运行时[:] ------property_attr = T@"NSNumber",&,N,V_num-------
-- ::10.355887+ 运行时[:] ------propertyType = @"NSNumber"-------
-- ::10.356349+ 运行时[:] ------property_Value = _num-------
-- ::10.357784+ 运行时[:] property_attribute_t_name:T-----property_attribute_t_value:@"NSNumber"
-- ::10.358246+ 运行时[:] property_attribute_t_name:&-----property_attribute_t_value:
-- ::10.358968+ 运行时[:] property_attribute_t_name:N-----property_attribute_t_value:
-- ::10.359325+ 运行时[:] property_attribute_t_name:V-----property_attribute_t_value:_num
-- ::10.359998+ 运行时[:]
-- ::10.360152+ 运行时[:] ------propertyName = date-------
-- ::10.360572+ 运行时[:] ------property_attr = T@"NSDate",&,N,V_date-------
-- ::10.360834+ 运行时[:] ------propertyType = @"NSDate"-------
-- ::10.361263+ 运行时[:] ------property_Value = _date-------
-- ::10.361579+ 运行时[:] property_attribute_t_name:T-----property_attribute_t_value:@"NSDate"
-- ::10.361951+ 运行时[:] property_attribute_t_name:&-----property_attribute_t_value:
-- ::10.362362+ 运行时[:] property_attribute_t_name:N-----property_attribute_t_value:
-- ::10.362838+ 运行时[:] property_attribute_t_name:V-----property_attribute_t_value:_date
-- ::10.363256+ 运行时[:]
-- ::10.363500+ 运行时[:] ------propertyName = data-------
-- ::10.363788+ 运行时[:] ------property_attr = T@"NSData",&,N,V_data-------
-- ::10.364093+ 运行时[:] ------propertyType = @"NSData"-------
-- ::10.364430+ 运行时[:] ------property_Value = _data-------
-- ::10.364925+ 运行时[:] property_attribute_t_name:T-----property_attribute_t_value:@"NSData"
-- ::10.365110+ 运行时[:] property_attribute_t_name:&-----property_attribute_t_value:
-- ::10.365525+ 运行时[:] property_attribute_t_name:N-----property_attribute_t_value:
-- ::10.365928+ 运行时[:] property_attribute_t_name:V-----property_attribute_t_value:_data
-- ::10.366541+ 运行时[:]
-- ::10.366648+ 运行时[:] ------propertyName = array-------
-- ::10.366929+ 运行时[:] ------property_attr = T@"NSArray",&,N,V_array-------
-- ::10.367389+ 运行时[:] ------propertyType = @"NSArray"-------
-- ::10.367686+ 运行时[:] ------property_Value = _array-------
-- ::10.368112+ 运行时[:] property_attribute_t_name:T-----property_attribute_t_value:@"NSArray"
-- ::10.368357+ 运行时[:] property_attribute_t_name:&-----property_attribute_t_value:
-- ::10.368664+ 运行时[:] property_attribute_t_name:N-----property_attribute_t_value:
-- ::10.368905+ 运行时[:] property_attribute_t_name:V-----property_attribute_t_value:_array
-- ::10.369409+ 运行时[:]
-- ::10.369565+ 运行时[:] ------propertyName = dic-------
-- ::10.369810+ 运行时[:] ------property_attr = T@"NSDictionary",&,N,V_dic-------
-- ::10.370092+ 运行时[:] ------propertyType = @"NSDictionary"-------
-- ::10.370366+ 运行时[:] ------property_Value = _dic-------
-- ::10.370706+ 运行时[:] property_attribute_t_name:T-----property_attribute_t_value:@"NSDictionary"
-- ::10.371128+ 运行时[:] property_attribute_t_name:&-----property_attribute_t_value:
-- ::10.371537+ 运行时[:] property_attribute_t_name:N-----property_attribute_t_value:
-- ::10.371911+ 运行时[:] property_attribute_t_name:V-----property_attribute_t_value:_dic
-- ::10.372340+ 运行时[:]
-- ::10.372762+ 运行时[:] ------propertyName = myObject-------
-- ::10.373469+ 运行时[:] ------property_attr = T@"MyObject",&,N,V_myObject-------
-- ::10.373751+ 运行时[:] ------propertyType = @"MyObject"-------
-- ::10.374167+ 运行时[:] ------property_Value = _myObject-------
-- ::10.374515+ 运行时[:] property_attribute_t_name:T-----property_attribute_t_value:@"MyObject"
-- ::10.374742+ 运行时[:] property_attribute_t_name:&-----property_attribute_t_value:
-- ::10.375014+ 运行时[:] property_attribute_t_name:N-----property_attribute_t_value:
-- ::10.375595+ 运行时[:] property_attribute_t_name:V-----property_attribute_t_value:_myObject
-- ::10.375718+ 运行时[:]

四、分析

看到上述打印,发现一些特性,可以获取到属性名称、属性类型,属性原子性、属性引用情况等,这些基本都在属性描述property_attr中,并使用一些特别的字符表示,例如T、@,&,N,V等,每一个属性描述都是以字符“T”开头,以字符“V下划线变量名 V_xxx”结尾 (结尾不固定,对于其他属性)。

例如属性name的属性描述打印:

//T:  Type(@encode) 属性类型
//@: 对象类型, NSString类型
//C: Copy 拷贝特性
//N: nonatomic 非原子性,默认为atomic,atomic为空
//V:variable 变量 _name , 使用@property修饰的属性系统默认@synthesize定义下划线变量,_name
property_attr = T@"NSString",C,N,V_name

例如属性age的属性描述打印:

//T:  Type(@encode) 属性类型
//i: 基本数据类型, int类型//N: nonatomic 非原子性,默认为atomic,atomic为空
//V:variable 变量 _age , 使用@property修饰的属性系统默认@synthesize定义下划线变量,_age
property_attr = Ti,N,V_age-------

例如属性myObject的属性描述打印:

//T:  Type(@encode) 属性类型
//@: 对象类型, 自定义类MyObject类型
//&: 引用特性, 强引用
//N: nonatomic 非原子性,默认为atomic,atomic为空
//V:variable 变量 _myObject-- , 使用@property修饰的属性系统默认@synthesize定义下划线变量,_myObject--
property_attr = T@"MyObject",&,N,V_myObject-------

就不一一列举了,基本属性特性大概如下,可能不完整:

//基本字符表示
T 是固定开头,表示类型,后跟类型 @、i、f、d、#等
& 代表强引用
C 代表copy
R 代表readOnly属性,readwrite时为空
W 代表weak,assign为空,默认为assign。
N 区分的nonatomic和atomic,默认为atomic,atomic为空,’N’代表是nonatomic
D @dynamic 动态特性,动态绑定,需要手动生成setter和getter方法
V_exprice 固定结尾,V代表变量,后面紧跟着的是成员变量名,代表这个property的成员变量名为_exprice。

属性类型的表示字符特性编码苹果官方给了一套参考:特性编码解析大全

//编码值   含意
//c 代表char类型
//i 代表int类型
//s 代表short类型
//l 代表long类型,在64位处理器上也是按照32位处理
//q 代表long long类型
//C 代表unsigned char类型
//I 代表unsigned int类型
//S 代表unsigned short类型
//L 代表unsigned long类型
//Q 代表unsigned long long类型
//f 代表float类型
//d 代表double类型
//B 代表C++中的bool或者C99中的_Bool
//v 代表void类型
//* 代表char *类型
//@ 代表对象类型
//# 代表类对象 (Class)
//: 代表方法selector (SEL)
//[array type] 代表array
//{name=type…} 代表struct结构体
//(name=type…) 代表union共同体
//bnum A bit field of num bits,代表位字段
//^type A pointer to type,代表指针类型
//? An unknown type (among other things, this code is used for function pointers),代表未知类型,例如block

四、扩展

类的属性声明

//类拥有一个属性
NS_ASSUME_NONNULL_BEGIN
@interface MyObject : NSObject
@property (nonatomic, copy) NSString *name;
@end

1、获取

-(void)printPropertyWithObj:(MyObject *)myObject{
unsigned int count;
objc_property_t *properties = class_copyPropertyList([myObject class], &count);
for (int i=; i<count; i++) { //获取属性
objc_property_t property = properties[i]; //属性名称
const char *propertyName = property_getName(property);
NSLog(@"------propertyName = %s-------",propertyName); //属性变量
char *property_Value = property_copyAttributeValue(property, "V");
NSLog(@"------property_Value = %s-------",property_Value); //属性类型
char *property_Type = property_copyAttributeValue(property, "T");
NSLog(@"------property_Type = %s-------",property_Type);
}
}
-- ::24.258232+ 运行时[:] ------propertyName = name-------
-- ::24.258412+ 运行时[:] ------property_Value = _name-------
-- ::24.258541+ 运行时[:] ------property_Type = @"NSString"-------

2、添加

//添加属性
//* @param cls The class to modify.
//* @param name The name of the property.
//* @param attributes An array of property attributes.
//* @param attributeCount The number of attributes in \e attributes.
//class_addProperty(Classcls, const char *name, const objc_property_attribute_t *attributes,unsigned int attributeCount)
const char * propertyName = "age";
objc_property_attribute_t type = { "T", "i" }; //int
objc_property_attribute_t ownership = { "N" }; //nonatomic
objc_property_attribute_t variable = { "V", "_age" }; //_age
objc_property_attribute_t attrs[] = { type, ownership, variable };
BOOL addSuccess = class_addProperty([myObject class], propertyName, attrs, );
if (addSuccess) {
[self printPropertyWithObj:myObject];
}
-- ::12.120742+ 运行时[:] 默认属性如下:
-- ::12.120941+ 运行时[:] ------propertyName = name-------
-- ::12.121073+ 运行时[:] ------property_Value = _name-------
-- ::12.121199+ 运行时[:] ------property_Type = @"NSString"-------
-- ::12.121295+ 运行时[:]
-- ::12.121405+ 运行时[:] 添加属性如下:
-- ::12.121617+ 运行时[:] ------propertyName = age-------
-- ::12.121731+ 运行时[:] ------property_Value = _age-------
-- ::12.121886+ 运行时[:] ------property_Type = i-------
-- ::12.122279+ 运行时[:] ------propertyName = name-------
-- ::12.122780+ 运行时[:] ------property_Value = _name-------
-- ::12.123222+ 运行时[:] ------property_Type = @"NSString"-------

3、替换

//替换属性【注意:无法改变属性名称,只是修改生成的下划线变量,_variable】
//* @param cls The class to modify.
//* @param name The name of the property.
//* @param attributes An array of property attributes.
//* @param attributeCount The number of attributes in \e attributes.
//class_replaceProperty(Class cls, const char *name, const objc_property_attribute_t * _Nullable attributes, unsigned int attributeCount)
const char * propertyName = "name";
objc_property_attribute_t type = { "T", "@\"NSString\"" };//NSString
objc_property_attribute_t ownership = { "C", "N" }; // copy、nonatomic
objc_property_attribute_t variable = { "V", "_newName" }; //_newName
objc_property_attribute_t attrs[] = { type, ownership, variable };
class_replaceProperty([myObject class], propertyName, attrs, );
[self printPropertyWithObj:myObject];
-- ::52.448875+ 运行时[:] 默认属性如下:
-- ::52.450451+ 运行时[:] ------propertyName = name-------
-- ::52.452714+ 运行时[:] ------property_Value = _name-------
-- ::52.453001+ 运行时[:] ------property_Type = @"NSString"-------
-- ::52.453125+ 运行时[:]
-- ::52.458433+ 运行时[:] 替换属性如下:
-- ::52.458658+ 运行时[:] ------propertyName = name-------
-- ::52.463156+ 运行时[:] ------property_Value = _newName-------
-- ::52.463973+ 运行时[:] ------property_Type = @"NSString"-------

分析Runtime的属性Property的更多相关文章

  1. iOS runtime探究(三): 从runtime開始理解OC的属性property

    你要知道的runtime都在这里 转载请注明出处 http://blog.csdn.net/u014205968/article/details/67639303 本文主要解说runtime相关知识, ...

  2. Yii2基本概念之——属性(property)

    学习任何一门学问,往往都是从起基本的概念学起.万丈高楼平地起,这些基本概念就是高楼的基石,必须做详尽的分析.我们知道,Yii2是一款脉络清晰的框架,理顺了基础的概念和基本功能,学习更高级和复杂的功能就 ...

  3. 区分元素特性attribute和对象属性property

    × 目录 [1]定义 [2]共有 [3]例外[4]特殊[5]自定义[6]混淆[7]总结 前面的话 其实attribute和property两个单词,翻译出来都是属性,但是<javascript高 ...

  4. 属性(@property)、@synthesize

    先前我们学的实例变量是这样的 { int _age; int _height; int age; } 后来学属性 @property int age; 看到@property 会自动编译生成某个成员变 ...

  5. Object的属性property详细解释(自动生成成员变量)

    类Class中的属性property: 在ios第一版中,我们为输出口同时声明了属性和底层实例变量,那时,属性是oc语言的一个新的机制,并且要求你必须声明与之对应的实例变量,例如: @interfac ...

  6. OC 实例变量(instance var)与属性(@property)的关系 isa指针

    实例变量(instance var)与属性(@property)的关系 Objective-C 2.0之后,声明一个@property name自动产生一个实例变量,名为_name,因此省去实例变量和 ...

  7. iOS中属性Property的常用关键字的使用说明

    属性关键字的作用 现在我们iOS开发中,基本都是使用ARC(自动引用计数)技术,来编写我们的代码.因此在属性property中我们经常使用的关键字有strong,weak,assign,copy,no ...

  8. Effective C# 学习笔记(原则一:始终能的使用属性(property),而不是可直接访问的Data Member)

    原则一:始终能的使用属性(property),而不是可直接访问的Data Member    Always use properties instead of accessible data memb ...

  9. 对用户控件(ascx)属性(property)赋值

    对用户控件(ascx)属性(property)赋值 Insus.NET写此博文,是对用户控件(ASCX)的属性赋值经验与技巧分享.是这样子的,在做新闻站点时,一般都会有分很多类别. 在站点首页会显示最 ...

随机推荐

  1. V4 Reduce Transportable Tablespace Downtime using Cross Platform Incremental Backup (Doc ID 2471245.1)

    V4 Reduce Transportable Tablespace Downtime using Cross Platform Incremental Backup (Doc ID 2471245. ...

  2. [Go] 实现面向对象中的继承和覆盖方法

    go中的继承是使用结构体嵌套实现的,可以继承父类的方法 覆盖和其他面向对象的语言是一样的,函数名,参数,返回类型一致,就可以覆盖父类的方法 package main import "log& ...

  3. robotframework框架 - 利用RequestsLibrary关键字轻松实现接口自动化!

    robotframework(后续简称为robot)是一款自动化测试框架,可能做各种类型的自动化测试. 本文介绍通过robotframework来做接口测试. 第一步:安装第三方库,提供接口测试的关键 ...

  4. 解决tail命令提示“tail: inotify 资源耗尽,无法使用 inotify 机制,回归为 polling 机制”

    报错的原因是 inotify 跟踪的文件数量超出了系统设置的上限值,要是这个问题不经常出现可以使用临时解决方法,或者写入配置文件来永久解决. 临时解决方法: # 查看 inotify 的相关配置 $ ...

  5. 通过jgit一次性升级fastjson版本

    背景:笔者所在公司经历了三次fastjson的升级,由于集群,工程数量众多,每次升级都很麻烦.因此开发了一个java的升级工具. 功能介绍: 功能介绍:一个jar文件,通过java -jar命令,输入 ...

  6. PHP实现微信提现功能

    提现必须得用双向证书.所以大家一定要在微信的商户平台找到相应的地方去设置.因为做这个提现已经有一段时间了.所以设置微信商户平台的那几个地方没有图的情况.也说不清楚.下次再做提现的时候.给大家分享如何设 ...

  7. 聊聊 Java8 以后各个版本的新特性

    作者:ZY5A59 juejin.im/post/5d5950806fb9a06b0a277412 某天在网上闲逛,突然看到有篇介绍 Java 11 新特性的文章,顿时心里一惊,毕竟我对于 Java ...

  8. BeautifulSoup的重要操作

    BeautifulSoup相关概念总结:https://www.cnblogs.com/pythonywy/p/11134481.html css基础以及选择器基础:https://www.cnblo ...

  9. SpringBoot+MyBatisPlus整合时提示:Invalid bound statement(not found):**.dao.UserDao.queryById

    场景 在使用SpringBoot+MyBatisPlus搭建后台启动项目时,使用EasyCode自动生成代码. 在访问后台接口时提示: Invilid bound statement (not fou ...

  10. PHP fnmatch 文件系统函数

    定义和用法 fnmatch - 用模式匹配文件名 目前该函数无法在 Windows 或其它非 POSIX 兼容的系统上使用. 版本支持 PHP4 PHP5 PHP7 4.3.0(含)+支持 支持 支持 ...