iOS成员属性和成员变量的区别

作者: 朝雨晚风 | 来源:发表于2016-08-12 11:16 被阅读3248次

    一、@property 和@synthesizer

    在objective-c 1.0中,我们为interface同时声明了属性和底层实例变量,那时,属性是oc语言的一个新的机制,并且要求你必须声明与之对应的实例变量,例如:

    @interface MyViewController :UIViewController
    {
        UIButton *myButton;
    }
    @property (nonatomic, retain) UIButton *myButton;
    @end
    

    在objective-c 2.0中,@property它将自动创建一个以下划线开头的实例变量。因此,在这个版本中,我们不再为interface声明实例变量。变成我们常见的形式

    @interface MyViewController :UIViewController
    @property (nonatomic, retain) UIButton *myButton;
    @end
    

    在MyViewController.m文件中,编译器也会自动的生成一个实例变量_myButton。那么在.m文件中可以直接的使用_myButton实例变量,也可以通过属性self.myButton.都是一样的。

    注意这里的self.myButton其实是调用的myButton属性的getter/setter方法。这与C++中点的使用是有区别的,C++中的点可以直接访问成员变量(也就是实例变量)。

    例如在oc的.h文件中有如下代码

    @interface MyViewController :UIViewController
    {
        NSString *name;
    }
    

    .m文件中,self.name 这样的表达式是错误的。xcode会提示你使用->,改成self->name就可以了。因为oc中点表达式是表示调用方法,而上面的代码中没有name这个方法。所以在oc中点表达式其实就是调用对象的setter和getter方法的一种快捷方式。

    你可能还见过这种写法

    #import "ViewController.h"
    
    @interface ViewController ()
    @property (nonatomic, strong) UIButton *myButton;
    @end
    
    @implementation ViewController
    @synthesize myButton;
    

    @synthesize 语句只能被用在 @implementation 代码段中,@synthesize的作用就是让编译器为你自动生成setter与getter方法,@synthesize 还有一个作用,可以指定与属性对应的实例变量,例如@synthesize myButton = xxx;那么self.myButton其实是操作的实例变量xxx,而不是_myButton了。

    如果.m文件中写了@synthesize myButton;那么生成的实例变量就是myButton;如果没写@synthesize myButton;那么生成的实例变量就是_myButton。所以跟以前的用法还是有点细微的区别。

    二、类别中的属性property

    类与类别中添加的属性要区分开来,因为类别中只能添加方法,不能添加实例变量。经常会在ios的代码中看到在类别中添加属性,这种情况下,是不会自动生成实例变量的。比如在:UINavigationController.h文件中会对UIViewController类进行扩展

    @interface UIViewController (UINavigationControllerItem)
    @property(nonatomic,readonly,retain) UINavigationItem *navigationItem;
    @property(nonatomic) BOOL hidesBottomBarWhenPushed;
    @property(nonatomic,readonly,retain) UINavigationController *navigationController;
    @end
    

    这里添加的属性,不会自动生成实例变量,这里添加的属性其实是添加的getter与setter方法。注意一点,匿名类别(匿名扩展)是可以添加实例变量的,非匿名类别是不能添加实例变量的,只能添加方法,或者属性(其实也是方法),常用的扩展是在.m文件中声明私有属性和方法。 Category理论上不能添加变量,但是可以使用rRuntime机制来弥补这种不足。

    #import
    static const void * externVariableKey =&externVariableKey;
    @implementation NSObject (Category)
    @dynamic variable;
    - (id) variable
    {
           return objc_getAssociatedObject(self, externVariableKey);
    }
    - (void)setVariable:(id) variable
    {
        objc_setAssociatedObject(self, externVariableKey, variable, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    } 
    

    三、@private、@protect、@public

    @protected是受保护的,只能在本类及其子类中访问,在{}声明的变量默认是@protect
    @private是私有的,只能在本类访问
    @public公开的,可以被在任何地方访问。
    在头文件.h中:

    @interface ViewController : UIViewController
    {
    // 成员变量
            @public
                NSString* publicString;
           
            @protected
                NSString* protectedString;
           
            @private
                NSString* privateString;
    }
    //属性变量
    @property (nonatomic,strong) NSArray *propertyString;
    @end
    
    • 成员变量用于类内部,无需与外界接触的变量。
    • 根据成员变量的私有性,为了方便访问,所以就有了属性变量。属性变量是用于与其他对象交互的变量。(属性变量的好处就是允许让其他对象访问到该变量。当然,你可以设置只读或者可写等,设置方法也可自定义。)

    一些建议:
    1.如果只是单纯的private变量,最好声明在implementation里.
    2.如果是类的public属性,就用property写在.h文件里
    3.如果自己内部需要setter和getter来实现一些东西,就在.m文件的类目里用property来声明

    .h中的interface的大括号{}之间的实例(成员)变量,.m中可以直接使用;
    .h中的property(属性)变量,.m中需要使用self.propertyVariable的方式使用propertyVariable变量

    四、成员变量和成员属性的关系

    1. 属性对成员变量扩充了存取方法 .
    2. 属性默认会生成带下划线的成员变量 .
    3. 但只声明了变量,是不会有属性的,可以通过以下代码证明
      在Person.h 头文件中
    @interface Person : NSObject {
        @private
        //name为私有成员变量
        NSString *name;
    }
     // age 为成员属性
    @property (nonatomic ,copy) NSString *age;
    

    在viewController.m 中,通过RunTime机制获得对象的所有成员变量和成员属性。

        Person *p = [Person new];
        unsigned int count = 0; //count记录变量的数量
        
        // 获取类的所有成员变量
        Ivar *members = class_copyIvarList([Person class], &count);
        for (int i = 0; i < count; i++) {
            Ivar ivar = members[i];
            // 取得变量名并转成字符串类型
            const char *memberName = ivar_getName(ivar);
            NSLog(@"变量名 = %s",memberName);
        }
        // 获取类的所有成员属性
        objc_property_t *properties =class_copyPropertyList([Person class], &count);
        for (int i = 0; i<count; i++)
        {
            objc_property_t property = properties[i];
            const char* char_f =property_getName(property);
            NSString *propertyName = [NSString stringWithUTF8String:char_f];
            NSLog(@"属性名 = %@",propertyName);
        }
    

    打印结果为

    2016-08-12 11:31:50.225 modifyPrivate[777:143231] 变量名 = name
    2016-08-12 11:31:50.226 modifyPrivate[777:143231] 变量名 = _age
    2016-08-12 11:31:50.226 modifyPrivate[777:143231] 属性名 = age
    

    相关文章

      网友评论

      本文标题:iOS成员属性和成员变量的区别

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