iOS @property、@synthesize和@dynamic

@property的本质

@property = ivar(实例变量) + getter/setter(存取方法);
  • @property有两个对应的词,一个是@synthesize,一个是@dynamic。如果@synthesize和@dynamic都没写,那么默认的就是@syntheszie var = _var;
  • @synthesize的语义是如果你没有手动实现setter方法和getter方法,那么编译器会自动为你加上这两个方法
  • @dynamic告诉编译器:属性的setter与getter方法由用户自己实现,不自动生成。(当然对于readonly的属性只需提供getter即可)。假如一个属性被声明为@dynamic var,然后你没有提供setter方法和getter方法,编译的时候没问题,但是当程序运行到 instance.var = someVar, 由于缺setter方法会导致程序崩溃; 或者当运行到 someVar = var 时, 由于缺 getter 方法同样会导致崩溃. 编译时没问题, 运行时才执行相应的方法, 这就是所谓的动态绑定.

演示使用:

#import 

NS_ASSUME_NONNULL_BEGIN
@interface Person : NSObject

@property (nonatomic, copy) NSString *name;

@end
NS_ASSUME_NONNULL_END

------------------------------------------------

#import "Person.h"

@implementation Person

//@synthesize name = _name;//默认
//@synthesize name = _name123;//生成_name123成员变量;在自己的.m中使用需要写_name123或者self.name,在其他类中调用只能写self.name,只两个是同一个东西
//@synthesize name;//生成name成员变量
//@dynamic name;//没有了_name这个成员变量

@end
(lldb) image lookup -t Person
0 match found in /Users/dengyazhouPro/Library/Developer/Xcode/DerivedData/DemoOC-aqhzxuxjddtuwnfvagfffmcnhzmb/Build/Products/Debug-iphoneos/DemoOC.app/DemoOC:
id = {0x100000002b}, name = "Person", byte-size = 16, decl = Person.h:14, compiler_type = "@interface Person : NSObject{
    NSString * _name;
}
@property(nonatomic, copy, readwrite, getter = name, setter = setName:) NSString *name;
@end"

添加,@synthesize name = _name123;,没有了_name这个成员变量,而是把_name换成了_name123

(lldb) image lookup -t Person
0 match found in /Users/dengyazhouPro/Library/Developer/Xcode/DerivedData/DemoOC-aqhzxuxjddtuwnfvagfffmcnhzmb/Build/Products/Debug-iphoneos/DemoOC.app/DemoOC:
id = {0x100000002b}, name = "Person", byte-size = 16, decl = Person.h:14, compiler_type = "@interface Person : NSObject{
    NSString * _name123;
}
@property(nonatomic, copy, readwrite, getter = name, setter = setName:) NSString *name;
@end"

添加,@dynamic name;,没有了_name这个成员变量

(lldb) image lookup -t Person
0 match found in /Users/dengyazhouPro/Library/Developer/Xcode/DerivedData/DemoOC-aqhzxuxjddtuwnfvagfffmcnhzmb/Build/Products/Debug-iphoneos/DemoOC.app/DemoOC:
id = {0x100000001f}, name = "Person", byte-size = 8, decl = Person.h:14, compiler_type = "@interface Person : NSObject
@property(nonatomic, copy, readwrite, getter = name, setter = setName:) NSString *name;
@end"

例如:

#import 

NS_ASSUME_NONNULL_BEGIN

@interface Person : NSObject

@property (nonatomic, copy) NSString *name;

@end

NS_ASSUME_NONNULL_END

------------------------------------------------

#import "Person.h"

@implementation Person

@dynamic name;

@end

当执行到:

    Person *person = [[Person alloc] init];
    person.name = @"dengyazhou";

程序就会crash,原因是“[People setName:]: unrecognized selector sent to instance”。

解决这种奔溃的方法有三种:

方法一:

最简单粗暴,但是还是挺管用,直接注释掉@dynamic name这行代码即可,由编译器自动添加。

但是如果我们不想让编译器自动添加,那么我们可以手动添加或则在运行时添加都可以。

方法二:

手动添加,由于@dynamic不能像@synthesize那样向实现文件(.m)提供实例变量,所以我们需要在类中显式提供实例变量。

#import 

NS_ASSUME_NONNULL_BEGIN

@interface Person : NSObject

@property (nonatomic, copy) NSString *name;

@end

NS_ASSUME_NONNULL_END

------------------------------------------------

#import "Person.h"

@interface Person () {
    NSString *_name;
}

@end

@implementation Person

@dynamic name;

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

- (NSString *)name {
    return _name;
}

@end
方法三:

通过runtime机制在运行时添加属性的存取方法。

在C函数中不能直接使用实例变量,需要将Objc对象self转成C中的结构体,因此在Person类同样需要显式声明实例变量而且访问级别是@public

#import 

NS_ASSUME_NONNULL_BEGIN

@interface Person : NSObject

@property (nonatomic, copy) NSString *name;

@end

NS_ASSUME_NONNULL_END

------------------------------------------------

#import "Person.h"
#include 

@interface Person () {
    @public
    NSString *_name;
}

@end

@implementation Person

@dynamic name;

+ (BOOL)resolveInstanceMethod:(SEL)sel {
    if (sel == @selector(setName:)) {
        class_addMethod([self class], sel, (IMP)setName, "v@:@");
        return YES;
    } else if (sel == @selector(name)) {
        class_addMethod([self class], sel, (IMP)getName, "@@:");
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}

void setName(id self, SEL _cmd, NSString* name) {
    if (((Person*)self)->_name != name) {
        ((Person*)self)->_name = [name copy];
    }
}

NSString* getName(id self, SEL _cmd) {
    return ((Person*)self)->_name;
}

@end

你可能感兴趣的:(iOS @property、@synthesize和@dynamic)