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

2019-07-31  本文已影响0人  小宝二代

前言

实例变量本质上就是成员变量,只是实例是针对类而言,属性是通过关键字@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

@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

写在最后

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

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

上一篇 下一篇

猜你喜欢

热点阅读