美文网首页iOS基础iOS扩展
iOS @property、@synthesize和@dynam

iOS @property、@synthesize和@dynam

作者: 心至靜行至遠 | 来源:发表于2017-02-15 12:27 被阅读3195次

    @property

    @property的本质:

    @property = ivar(实例变量) + getter/setter(存取方法);
    

    在正规的 Objective-C 编码风格中,存取方法有着严格的命名规范。 正因为有了这种严格的命名规范,所以 Objective-C 这门语言才能根据名称自动创建出存取方法。

    完成属性定义后,编译器会自动编写访问这些属性所需的方法,此过程叫做“自动合成”(autosynthesis)。需要强调的是,这个过程由编译 器在编译期执行,所以编辑器里看不到这些“合成方法”(synthesized method)的源代码。除了生成方法代码 getter、setter 之外,编译器还要自动向类中添加适当类型的实例变量,并且在属性名前面加下划线,以此作为实例变量的名字。

    我们每次在增加一个属性,系统都会在 ivar_list 中添加一个成员变量的描述,在 method_list 中增加 setter 与 getter 方法的描述,在属性列表中增加一个属性的描述,然后计算该属性在对象中的偏移量,然后给出 setter 与 getter 方法对应的实现,在 setter 方法中从偏移量的位置开始赋值,在 getter 方法中从偏移量开始取值,为了能够读取正确字节数,系统对象偏移量的指针类型进行了类型强转.

    @property有两个对应的词,一个是 @synthesize,一个是 @dynamic。如果 @synthesize和 @dynamic都没写,那么默认的就是@syntheszie var = _var;

    @synthesize

    @synthesize表示如果属性没有手动实现setter和getter方法,编译器会自动加上这两个方法。
    @dynamic

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

    例如:

    #import <Foundation/Foundation.h>
    
    @interface People : NSObject
    
    @property (nonatomic, copy) NSString *name;
    
    @end
    
    #import "People.h"
    
    @implementation People
    
    @dynamic name;
    
    @end
    

    当执行到:

       People *p= [People new];
       p.name = @"Peter";
    

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

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

    方法一:

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

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

    方法二:

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

    #import <Foundation/Foundation.h>
    
    @interface People : NSObject
    
    @property (nonatomic, copy) NSString *name;
    
    @end
    
    #import "People.h"
    
    @interface People ()
    {
        NSString *_name;
    }
    @implementation People
    
    @dynamic name;
    
    - (void)setName:(NSString *)name {
    
        _name = [name copy];
    }
    
    - (NSString *)name {
    
        return _name;
    }
    
    @end
    

    方法三:

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

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

    #import <Foundation/Foundation.h>
    
    @interface People : NSObject
    
    @property (nonatomic, copy) NSString *name;
    
    @end
    
    #import "People.h"
    #import <objc/objc-runtime.h>
    
    @interface People ()
    {
        @public
        NSString *_name;
    }
    @end
    
    @implementation People
    
    @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 (((People*)self)->_name != name) {
            ((People *)self)->_name = [name copy];
        }
    }
    
    NSString* getName(id self, SEL _cmd)
    {
        return ((People *)self)->_name;
    }
    @end
    
    

    相关文章

      网友评论

        本文标题:iOS @property、@synthesize和@dynam

        本文链接:https://www.haomeiwen.com/subject/sjqqwttx.html