iOS

iOS:@property和@synthesize

2019-02-20  本文已影响19人  HoooChan

定义一个很简单的类:

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

@interface SimpleClass : NSObject {
    NSString *simpleName;
}
@end

// .m
#import "SimpleClass.h"

@implementation SimpleClass
@end

运行clang -rewrite-objc SimpleClass.m查看生成的SimpleClass.cpp文件:

struct SimpleClass_IMPL {
    struct NSObject_IMPL NSObject_IVARS;
    NSString *simpleName;
};

/* @end */


// @implementation SimpleClass

// @end

SimpleClass_IMPL是SimpleClass类的实现,里面保存了simpleName变量,但这个时候没有getter和setter方法。

我们把{NSString *simpleName;}改成@property (nonatomic, copy) NSString *simpleName;再重新编译:

struct SimpleClass_IMPL {
    struct NSObject_IMPL NSObject_IVARS;
    NSString *_simpleName;
};

// @property (nonatomic, copy) NSString *simpleName;
/* @end */


// @implementation SimpleClass


static NSString * _I_SimpleClass_simpleName(SimpleClass * self, SEL _cmd) {
    return (*(NSString **)((char *)self + OBJC_IVAR_$_SimpleClass$_simpleName));
}

...

static void _I_SimpleClass_setSimpleName_(SimpleClass * self, SEL _cmd, NSString *simpleName) {
    objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct SimpleClass, _simpleName), (id)simpleName, 0, 1);
}
// @end

可以看到编译器帮我们生成了名为_simpleName的成员变量,同时生成了对应的getter/setter方法。

getter方法里面利用OBJC_IVAR_$_SimpleClass$_simpleName偏移量来计算_simpleName的位置,来看看OBJC_IVAR_$_SimpleClass$_simpleName的定义:

extern "C" unsigned long int OBJC_IVAR_$_SimpleClass$_simpleName __attribute__ ((used, section ("__DATA,__objc_ivar"))) = __OFFSETOFIVAR__(struct SimpleClass, _simpleName);

是通过__OFFSETOFIVAR__来计算偏移量的。

setter方法里面,通过调用objc_setProperty方法来设置值。如果我们把simpleName的属性类型改为strong再重新编译:

static void _I_SimpleClass_setSimpleName_(SimpleClass * self, SEL _cmd, NSString *simpleName) {
    (*(NSString **)((char *)self + OBJC_IVAR_$_SimpleClass$_simpleName)) = simpleName;
}

可以看到setter函数的实现也改变了。

我们在@Implementation里面添加一行代码:

@implementation SimpleClass

@synthesize simpleName = newSimpleName;

@end

再重新编译,则看到原本的_simpleName改为了newSimpleName:

extern "C" unsigned long OBJC_IVAR_$_SimpleClass$newSimpleName;
struct SimpleClass_IMPL {
    struct NSObject_IMPL NSObject_IVARS;
    NSString *newSimpleName;
};

// @property (nonatomic, copy) NSString *simpleName;
/* @end */


// @implementation SimpleClass

// @synthesize simpleName = newSimpleName;
static NSString * _I_SimpleClass_simpleName(SimpleClass * self, SEL _cmd) {
    return (*(NSString **)((char *)self + OBJC_IVAR_$_SimpleClass$newSimpleName));
}
extern "C" __declspec(dllimport) void objc_setProperty (id, SEL, long, id, bool, bool);

static void _I_SimpleClass_setSimpleName_(SimpleClass * self, SEL _cmd, NSString *simpleName) {
    objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct SimpleClass, newSimpleName), (id)simpleName, 0, 1);
}

所以可以用@synthesize来修改生成的成员变量的名字

如果我们定义的属性可读或可写而同时又自己复写了getter或setter方法,这时编译器不会自动帮我们生成成员变量,这时我们需要自己通过@synthesize来添加成员变量。所以当我们手动添加了getter/setter方法时,我们需要利用@synthesize来自动生成成员变量

在协议和category里面实现了property属性时也不会自动生成成员变量。如果一个类实现了带有property的协议,那它必须自己通过@synthesize来自动生成成员变量

所以什么时候会用到@synthesize
1、需要修改成员变量的命名
2、无法自动生成成员变量

  • 2.1、复写了可读或可写属性的getter或setter方法;
  • 2.2、属性是协议里面定义的

当我们在子类和父类里面定义相同的属性时,看看会发生什么:

@interface SimpleClass : NSObject
@property (nonatomic, copy) NSString *simpleName;
@end

@interface SubSimpleClass : SimpleClass
@property (nonatomic, copy) NSString *simpleName;
@end

编译后的代码:

struct SimpleClass_IMPL {
    struct NSObject_IMPL NSObject_IVARS;
    NSString *_simpleName;
};

// @property (nonatomic, copy) NSString *simpleName;
/* @end */
...
struct SubSimpleClass_IMPL {
    struct SimpleClass_IMPL SimpleClass_IVARS;
};

// @property (nonatomic, copy) NSString *simpleName;
/* @end */


// @implementation SimpleClass


static NSString * _I_SimpleClass_simpleName(SimpleClass * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_SimpleClass$_simpleName)); }
extern "C" __declspec(dllimport) void objc_setProperty (id, SEL, long, id, bool, bool);

static void _I_SimpleClass_setSimpleName_(SimpleClass * self, SEL _cmd, NSString *simpleName) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct SimpleClass, _simpleName), (id)simpleName, 0, 1); }
// @end

// @implementation SubSimpleClass

// @end

可以看到子类中并没有自动生成成员变量,也没有生成getter/setter方法。

上一篇下一篇

猜你喜欢

热点阅读