版本记录
版本号 | 时间 |
---|---|
V1.0 | 2019.05.02 星期四 |
前言
OC是运行时的语言,底层就是运行时,可以说runtime是OC的底层,很多事情也都可以用运行时解决,下面就讲述一下运行时runtime的知识以及它的妙用。感兴趣的可以看上面几篇。
1. 运行时runtime深度解析(一)—— API
2. 运行时runtime深度解析(二)—— Method Swizzling在页面统计上的应用
3. 运行时runtime深度解析(三)—— Method Swizzling在数组越界上的应用
4. 运行时runtime深度解析(四)—— 获取类的属性和方法列表
获取属性和成员变量列表
下面我们直接看示例代码,获取属性和成员变量列表:
#import "ViewController.h"
#import <objc/runtime.h>
@interface ViewController ()
{
NSString *name;
}
@property (nonatomic, copy) NSString *age;
@end
@implementation ViewController
#pragma mark - Override Base Function
- (void)viewDidLoad
{
[super viewDidLoad];
self.view.backgroundColor = [UIColor greenColor];
[self setupUI];
//获取属性列表
NSArray *propertyArr = [[self class] loadAllProperties];
NSLog(@"属性列表 = %@", propertyArr);
//获取成员列表
NSArray *ivarsArr = [[self class] loadAllIvars];
NSLog(@"成员列表 = %@", ivarsArr);
}
#pragma mark - Object Private Function
- (void)setupUI
{
}
#pragma mark - Class Public Function
//获取对象的所有属性
+ (NSArray *)loadAllProperties
{
u_int count;
//将count的地址传递过去
objc_property_t *properties = class_copyPropertyList([self class], &count);
NSMutableArray *propertyListArrM = [NSMutableArray arrayWithCapacity:count];
for (int i = 0; i < count; i++) {
//得到propertyName为c语言的字符串
const char *propertyName = property_getName(properties[i]);
//c字符串转化为oc字符串
[propertyListArrM addObject:[NSString stringWithCString:propertyName encoding:NSUTF8StringEncoding]];
}
//class_copyPropertyList为底层c语言,我们使用完一定要记得释放properties
free(properties);
return [propertyListArrM copy];
}
//获取对象的所有方法
+ (NSArray *)loadAllIvars
{
u_int count;
//将count的地址传递过去
Ivar *ivars = class_copyIvarList([self class], &count);
NSMutableArray *ivarListArrM = [NSMutableArray arrayWithCapacity:count];
for (int i = 0; i < count; i++) {
//得到propertyName为c语言的字符串
const char *ivarName = ivar_getName(ivars[i]);
//c字符串转化为oc字符串
[ivarListArrM addObject:[NSString stringWithCString:ivarName encoding:NSUTF8StringEncoding]];
}
//class_copyPropertyList为底层c语言,我们使用完一定要记得释放properties
free(ivars);
return [ivarListArrM copy];
}
@end
下面看一下输出结果
2019-05-02 14:10:47.324313+0800 JJRuntime[1452:55332] 属性列表 = (
age
)
2019-05-02 14:10:47.324602+0800 JJRuntime[1452:55332] 成员列表 = (
name,
"_age"
)
大家可以看出来,属性列表可以看出来是age
,但是成员列表相对就不一样了。
原因是苹果将默认编译器从GCC
转换为LLVM(low level virtual machine)
之后,直接使用@property时,编译器会自动为你生成以下划线开头的实例变量,所以成员变量列表里面有_age
这一项。
区别和联系
1. 成员变量
作用范围
-
@public
:在任何地方都能直接访问对象的成员变量。 -
@private
:只能在当前类的对象方法中直接访问,如果子类要访问需要调用父类的getter/setter
方法。 -
@protected
:可以在当前类及其子类对象方法中直接访问(系统默认下是用它来修饰的)。 -
@package
:在同一个包下就可以直接访问,比如说在同一个框架。
无论父类是在@interface
还是@implementation
声明的成员变量子类都能拥有;但是子类能不能直接通过变量名来访问父类中定义的成员变量是需要看父类中定义的成员变量是由什么修饰符来修饰的。
一种老的写法
下面是关于成员变量的一种比较老的写法,现在不用了。
@interface ViewController ()
{
NSString *_name;
}
@property (nonatomic, copy) NSString *name;
@end
@implementation ViewController
@synthesize name = _name;
@end
但是很多新的项目不会这么写了,一般都是写成下面这个样子了。
@interface ViewController ()
@property (nonatomic, copy) NSString *name;
@end
@implementation ViewController
@end
其实,发生这种状况的根本原因是苹果将默认编译器从GCC
转换为了LLVM(low level virtual machine)
,才不再需要为属性声明实例变量了,在没有更改之前,属性的正常写法需要三个步骤:
成员变量 + @property + @synthesize 成员变量
如果只写成下面这个样子
// 成员变量 + @property
@interface ViewController ()
{
NSString *myString;
}
@property (nonatomic, copy) NSString * myString;
@end
编译器会警告
Autosynthesized property '�myString' will use synthesized instance variable '_myString', not existing instance variable 'myString'
1) 但更换为LLVM之后,编译器会在编译过程中检查有没有相应的实例变量,发现没有相应的实例变量就会自动生成一个带下划线开头的实例变量。因此,现在我们不必再声明一个实例变量。(注意:是不必要,不是不可以)
2)@synthesize
语句只能被用在implementation
代码段中,@synthesize的作用就是让编译器为你自动生成setter
与getter
方法,@synthesize
还有一个作用,可以指定与属性对应的实例变量,例如@synthesize myButton = xxx
;那么self.myButton
其实是操作的实例变量xxx
,而不是_myButton
了。
3) 如果.m文件中写了@synthesize myButton
;那么生成的实例变量就是myButton
;如果没写@synthesize myButton
,那么生成的实例变量就是_myButton
。所以跟以前的用法还是有点细微的区别。
上面部分是别人总结的,这里用了下,文章后面已经给出出处和链接。
下面还要多说下:
-
@property
的作用是定义属性,声明getter、setter
方法。(注意:属性不是变量) -
@synthesize
的作用是实现属性的,如getter,setter方法 -
@synthesize
声明的属性 = 变量。意思是:将属性的getter、setter
方法,作用于这个变量。 -
Xocde4.5
以后,编译器会为你自动实现setter及getter方法,如果他找不到_name
,会为你自动创建一个_name
的变量。
默认
- 在.m中成员变量的修饰符为
@private
- 在.h中成员变量的修饰符
@protected
2. 属性
- 关于属性可以参考文档Objective-C 编程语言官网文档(五)-属性的声明
- 对于属性重点看那几个属性修饰符的使用,这个就不赘述了,早就已经分篇进行了说明。
- 属性声明以关键词
@property
开头。@property 可以出现在类的 @interface 块中方法声明的任何地方。 -
@property
还可以出现在protocol 或者 category声明中。 -
@property
声明的属性不仅仅默认给我们生成一个_
类型的成员变量,同时也会生成setter/getter
方法。
3. 区别联系
成员变量用于类内部,无需与外界接触的变量。因为成员变量不会生成set、get方法,所以外界无法与成员变量接触。根据成员变量的私有性,为了方便访问,所以就有了属性变量。属性变量的好处就是允许让其他对象访问到该变量(因为属性创建过程中自动产生了set 和get方法)。当然,你可以设置只读或者可写等,设置方法也可自定义。所以,属性变量是用于与其他对象交互的变量。
综上所述可知:成员变量是定义在{}号中的变量,如果变量的数据类型是一个类则称这个变量为实例变量。因为实例变量是成员变量的一种特殊情况,所以实例变量也是类内部使用的,无需与外部接触的变量,这个也就是所谓的类私有变量。而属性变量是用于与其他对象交互的变量。可以这么说:实例变量 + 基本数据类型变量 = 成员变量。
参考文章
1. 黄增松的技术博客
2. iOS 成员变量和属性的区别
3. 成员变量和属性
4. 类的成员变量和属性
后记
本篇讲述了属性和成员变量区别,暂时先写这么多,感兴趣的给个赞或者关注~~~