iOS成员变量、实例变量、属性

前言

实例变量本质上就是成员变量,只是实例是针对类而言,属性是通过关键字@property编译器自动生成setter与getter方法和一个以下划线开头的成员变量,通过setter和getter方法可以访问成员变量,我们会发现针对成员变量的声明访问出现了一系列的关键字,例如:

  • 针对成员变量访问范围的@public、@private、@protected、@package
  • 针对通过setter与getter方法访问成员变量的@property、@sythesize、@dynamic

下面主要介绍一下这一系列关键字对成员变量的声明和访问有哪些影响。

@public、@private、@protected、@package

@public 在任何地方都能直接访问对象的成员变量
@private 只能在当前类的对象方法中直接访问,如果子类要访问需要调用父类的get/set方法
@protected 可以在当前类及其子类对象方法中直接访问(系统默认下是用它来修饰的)
@package:在同一个包下就可以直接访问,比如说在同一个框架

例如:在父类.h文件中声明如下成员变量

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface Person : NSObject{
    
    @public
    NSString *_name;
    @private
    NSInteger _age;
    @protected
    NSString *_sex;
    @package
    NSInteger _height;
}

@end

NS_ASSUME_NONNULL_END

在父类.m文件中声明如下成员变量

#import "Person.h"

@implementation Person{
    
    @public
    NSString *_job;
    @private
    NSString *_income;
    @protected
    NSString *_father;
    @package
    NSString *_mother;
}

@end
  • 创建一个Student类继承父类Person,在子类方法中调用父类的成员变量


    WX20190731-152423@2x.png

    从上图可以看出我们在.m里面声明的成员变量子类是无法访问的(即使给他@public),也会被认为是@private,所以我们的对外的成员变量都会放到.h去声明,然而由于_age变量是@private,所以子类还是无法访问的。

  • 在外部调用Person类的成员变量


    从外部调用成员变量.png

    可以看到由于我们没有在Person或它的子类里面,所以只能访问.h中@public和@package修饰的变量,也就是_name和_height,由于_sex是@ protected在外部是不能被访问的。

@property

@property的作用是定义属性,可以通过点语法来访问,当我们在@interface内部声明了一个属性,编译器会自动帮我们实现以下三件事

  1. 生成一个以下划线开头的成员变量_propertyName(默认用@private修饰)
  2. 属性setter、getter方法的声明
  3. 属性setter、getter方法的实现

我们可以看到属性是通过setter和getter方法来访问成员变量的,只不过通过@property关键字编译器自动帮我们生成了成员变量、getter和setter方法,调用点语法实质上是调用setter和getter方法访问成员变量。@property用readonly修饰时属性是只读的,也即是只会生成getter方法。

注意:在category中使用@property编译器不会生成成员变量,只会生成setter和getter方法的声明,也不会生成setter和getter方法的实现,因为category中只能添加方法,不能添加成员变量,没有成员变量就无法与外面的值产生关联,setter和getter方法就无从访问。

属性的原理就是利用getter和setter方法去访问对象中某个变量关联的值,通过属性的原理利用runtime是可以为category添加属性的。

创建一个NSObject分类,为分类添加一个name属性

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface NSObject (objc)

@property (nonatomic, copy) NSString *name;

@end

NS_ASSUME_NONNULL_END

添加getter和setter方法的实现

#import "NSObject+Objc.h"
#import <objc/message.h>

@implementation NSObject (objc)

- (void)setName:(NSString *)name
{
    // 添加属性,给某个对象产生关联
    // object:给哪个对象添加属性
    // key:属性名,根据key去获取关联的对象 ,void * == id
    // value:关联的值
    // policy:策越
    objc_setAssociatedObject(self, @"name", name, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (NSString *)name
{
    return objc_getAssociatedObject(self, @"name");
}

@end

@sythesize

如果想要修改@property自动生成的_propertyName成员变量名,我们就可以通过@synthesize propertyName = newName;


新变量名.png

当同时重写getter和setter两个方法的时候,实现了完全的自定义实现,无法对应到默认的变量_propertyName,_propertyName就无效了,需要手动定义一个变量或者使用@synthesize指定一个变量来绑定到属性上。

方式一:手动定义一个变量

//方式一
#import "ViewController.h"

@interface ViewController ()

@property (nonatomic, copy) NSString *name;

@end

@implementation ViewController{
    //手动添加一个变量与属性绑定
    NSString *_name;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
}

-(NSString *)name {
    return _name;
}

- (void)setName:(NSString *)name {
    
    if (_name != name) {
        _name = name;
    }
}

@end

方式二:使用@synthesize指定一个变量来绑定到属性

//方式二
#import "ViewController.h"

@interface ViewController ()

@property (nonatomic, copy) NSString *name;

@end

@implementation ViewController
//使用@synthesize指定一个变量来绑定到属性
@synthesize name = newName;

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
}

-(NSString *)name {
    return newName;
}

- (void)setName:(NSString *)name {
    
    if (newName != name) {
        newName = name;
    }
}

@end

同样的,如果你的属性是只读属性,但是你重写了getter方法,编译器不会为你自动生成成员变量,也需要手动定义一个变量或者使用@synthesize指定一个变量来绑定到属性上。
注意:在@protocol中使用@property只会生成getter和setter方法的声明,这样就跟在协议中添加普通的方法声明一样,如果希望遵守该协议的类能像使用属性那样使用它,那就需要遵守该协议的类不仅实现getter和setter方法,还要手动定义一个变量或者使用@synthesize指定一个变量来绑定到属性上。
例如:

#import <Foundation/Foundation.h>

@protocol personDelegate <NSObject>

@property (nonatomic, copy) NSString *name;

@end

@interface Person : NSObject

@end


#import <Foundation/Foundation.h>
#import "Person.h"

@interface Student : NSObject <personDelegate>
{
    NSString *_name;
}

@end


#import "Student.h"

@implementation Student

- (void)setName:(NSString *)name
{
    _name = name;
}

- (NSString *)name
{
    return _name;
}
@end


- (void)viewDidLoad {
    [super viewDidLoad];
    
    Student *stu = [[Student alloc]init];
    stu.name = @"abc";
    NSLog(@"%@", stu.name);
}

@dynamic

  • @dynamic 告诉编译器:属性的 setter 与 getter 方法由用户自己实现,不自动生成。(当然对于 readonly 的属性只需提供 getter 即可)。
  • 假如一个属性被声明为 @dynamic var,然后你没有提供 @setter方法和 @getter 方法。编译的时候没问题,但是当程序运行到 instance.var = someVar,由于缺 setter 方法会导致程序崩溃。
  • 或者当运行到 someVar = var 时,由于缺 getter 方法同样会导致崩溃。
  • 编译时没问题,运行时才执行相应的方法,这就是所谓的动态绑定。


    没有实现setter和getter方法.png

写在最后

由于技术有限,文中若有错误之处请留言指正。

参考链接
http://08643.cn/p/0695ecbe9e06
https://www.cnblogs.com/Jenaral/p/5970393.html
https://my.oschina.net/Jerod/blog/1935419

最后编辑于
?著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,128评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,316评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事?!?“怎么了?”我有些...
    开封第一讲书人阅读 159,737评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,283评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,384评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,458评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,467评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,251评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,688评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,980评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,155评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,818评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,492评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,142评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,382评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,020评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,044评论 2 352

推荐阅读更多精彩内容