ios 私有变量的发展历史以及self. 和 _的区别

来源:互联网 发布:视频特效制作软件哪些 编辑:程序博客网 时间:2024/06/06 17:33

在 Objective-C 的语言的早期,类的私有成员变量是只能定义在 .h 的头文件里面的。像如下这样:

  1. @interface ViewController : UIViewController { 
  2. @private 
  3. NSInteger _value; 

之后,苹果改进了 Objective-C,允许在 .m 里面添加一个特殊的匿名 Category(扩展),即没有名字的 Category,来实现增加类的成员变量。像如下这样:

  1. @interface ViewController () 
  2. @property (nonatomic) NSInteger value; 
  3. @end 

这样的好处是,这些变量在头文件中被彻底隐藏起来了,不用暴露给使用者。

接着,在 2013 年的 WWDC 中,苹果进一步改进了 Objective-C,允许在 .m 的 @implementation 中直接添加类的私有成员变量。像如下这样:

  1. @implementation ViewController { 
  2. NSInteger _value; 

  1. }
其中最常用的方式是第二种。

首先通过self.xxx 访问属性的方法包含了set和get方法。而通过下划线是获取自己的实例变量,不包含set和get的方法。 
例如: 
@property (nonatomic,copy) NSString *propertyName;

self.propertyName 是对属性的访问;

_propertyName 是对局部变量的访问。

@property的声明中,编译器在生成getter,setter方法时是有优先级的,它首先查找当前的类中用户是否已定义属性的getter,setter方法,如果有,则编译器会跳过,不会再生成,使用用户定义的方法。 无论怎样你在使用self.propertyName 时是都在调用一个getter方法。self.propertyName 会让计数器+1;

而_propertyName,是直接调用变量,不通过getter方法。

_propertyName是类似于self->_propertyName。

所以使用self.xxx是更好的选择,因为这样可以兼容懒加载,同时也避免了使用下滑线的时候忽略了self这个指针,后者容易在BLock中造成循环引用。另外,使用下划线是获取不到父类的属性。

最后总结:self方法实际上是用了get和set方法间接调用,下划线方法是直接对变量操作。

下面是例子代码和注释:

#import <Foundation/Foundation.h>

@interface MethodDetailMusic : NSObject {

@public NSString *publicStr;

    

}

- (void)initPrivateString;

- (void)initPrivateString2;

- (void)initStr2;

@end

#import "MethodDetailMusic.h"

//_propertyName 是对局部变量的访问, 是直接调用变量,不通过getter方法,他的使用方法类似于 self->_propertyName. self.propertyName 是对属性的访问,在调用一个getter方法。self.propertyName 会让计数器+1;

@interface MethodDetailMusic () {

    //private string 这里有花括号,发现无法声明属性变量, 即这一作用域的声明仅仅限定于实例变量的声明,即没有settergetter方法

@private NSString *specialPrivateString;

    NSString *str ;

}

//interface中花括号外声明的只能是属性变量,即有settergetter方法,系统自动带上了,这里也可以达到私有变量的效果,也是目前最常用的一种声明所谓私有化的变量,但是实际上

//是没有所谓的私有化的变量的,因为可以利用kvo在外部进行值的改变

@property (nonatomic, strong) NSString * str2;

- (void)createPrivateFunction;

@end

@implementation MethodDetailMusic {

    //另外一种在@implementation中定义实例变量的方式,同样的不能在这里声明属性变量

    NSString *privateTestString2;

}

- (void)createPrivateFunction {

    

}

- (void)initPrivateString {

    //实例变量的访问,不能使用self.访问符号,但是可以使用_因为specialPrivateString不是属性变量而是实例变量,所以不能使用self. 也不能_ 会报错

    if (!specialPrivateString) {

        specialPrivateString = @"我是私有变量";

    }

    NSLog(@"%@", specialPrivateString);

}

- (void)initPrivateString2 {

    if (!privateTestString2) {

        privateTestString2 = @"我是私有变量2";

    }

    NSLog(@"%@", privateTestString2);

}

- (void)initStr2 {

    if (!self.str2) {

        self.str2 = @"我是私有变量3";

    }

    NSLog(@"%@", self.str2);

}

#import "ViewController.h"

#import "MethodDetailMusic.h"

#import <objc/runtime.h>

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {

    [super viewDidLoad];

    [self test];

}

- (void)didReceiveMemoryWarning {

    [super didReceiveMemoryWarning];

    // Dispose of any resources that can be recreated.

}

- (void)test {

    MethodDetailMusic *music = [[MethodDetailMusic alloc] init];

    [music initPrivateString];

    

    //kvc 键值编码可以修改私有变量

    [music setValue:@"修改私有变量" forKey:@"specialPrivateString"];

    [music initPrivateString];

    

    //定义一个实例变量 privateString 通过底层runtime 获取实例变量Ivar 对应私有值

    Ivar privateString = class_getInstanceVariable([music class], "specialPrivateString");

    NSString *privateStr = object_getIvar(music, privateString);

    NSLog(@"打印私有变量===%@",privateStr);

    

    //第二种私有变量的方式

    [music initPrivateString2];

    [music setValue:@"修改私有变量2" forKey:@"privateTestString2"];

    [music initPrivateString2];

    

    //定义一个实例变量 privateString 通过底层runtime 获取实例变量Ivar 对应私有值

    Ivar privateString2 = class_getInstanceVariable([music class], "privateTestString2");

    NSString *privateStr2 = object_getIvar(music, privateString2);

    NSLog(@"打印私有变量2===%@",privateStr2);

    

    //外部访问定义私有的属性变量

    [music initStr2];

    [music setValue:@"修改私有属性变量" forKey:@"str2"];

    [music initStr2];

    

    //定义一个实例变量 privateString 通过底层runtime 获取实例变量Ivar

    //在这里会发现打印出来的是null,因为str2不是实例变量,而是属性变量

    Ivar privateString3 = class_getInstanceVariable([music class], "str2");

    NSString *privateStr3 = object_getIvar(music, privateString3);

    NSLog(@"打印私有变量3===%@",privateStr3);

}

原创粉丝点击