实例变量跟属性
概要
在开发IOS中,发现自己对属性和实例变量存在一定的困惑,所以今天花了时间查资料以及做实验,顺便记录一下自己的体会。
属性和实例变量都是类的对象实例所有,属性是方法,可使用点(.)调用也可以按照OC的正常方法调用,对于一般的属性赋值其实就是调用属性的设置方法;实例变量如果可以访问,需要使用指针访问形式(当然因为访问的实例是一个指针)。正常情况下,属性和实例变量没有直接的关联,但是通过@synthesize指定以及LLVM的名字规则可以把实例变量和属性关联起来。
可以使用@synthesize直接给属性指定其对应的实例变量(@synthesize per =_author; 实例变量_author和属性per关联上了,和名字无关),如果没有指定,但是符合LLVM的命名规范,也可以把实例变量和属性关联起来。实际上,LLVM会将属性per和成员变量_per关联起来,而且如果没有_per实例变量定义,LLVM会自动合成该私有的实例变量,供类正常使用。
结果展示
2014-12-17 11:07:03.208 MemberProperty[43430:2367258] name : Book. author : Author. publisher : Publisher. align name : NameAlign. 2014-12-17 11:07:03.209 MemberProperty[43430:2367258] name : main_set_name author : main_set_author publisher : main_set_publisher align name : ok? 2014-12-17 11:07:03.209 MemberProperty[43430:2367258] name : set_name author : setsetAuthor publisher : setPublisher align name : setNameAlign
流程概要
定义一个类校验各种猜想
总结:实例变量和属性的关系
最新的编译器的LLVM支持自动给属性(eg.myproperty)合成属性方法并关联实例变量(_myproperty),如果该实例变量存在,直接关联。如果该实例变量不存在,则自动给类添加该实例变量,该实例变量是私有类型(private)。这样的话,只需要在头文件使用@property声明一个属性,我们就可以正常使用该属性,而且在类里面能直接使用对应的私有变量。如果你想修改自动合成的_myproperty访问类型,需要在类的头文件定义所需的访问类型的实例变量。当然,以前的用_var定义变量然后使用synthesize指定var=_var的方法也值得推荐的,不过相对上述方法而言,我还是喜欢现在说的这种。
主要代码
h文件
//
// Book.h
// MemberProperty
//
// Created by arbboter on 14/12/17.
// Copyright (c) 2014年 arbboter. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface Book : NSObject
{
// 默认protect成员变量
NSString* _name;
NSString* _author;
@public
NSString* _nameAlign;
@private
NSString* _nameIn;
@protected
NSString* _protectName;
}
/* 属性声明 头文件中的public.protect.private成员变量都可以指定为属性 */
// 声明属性_name,由m文件对应的synthesize自动合成_name和set_name方法(合称属性方法)
// (不建议使用)
@property (nonatomic, retain) NSString* _name;
// 声明属性,使用LLVM自动添加该属性的合成方法(因为m文件没有@synthesize指定该属性)
// 该属性自动和成员变量_nameAlign关联
@property (nonatomic, retain) NSString* nameAlign;
// 声明属性,自动合成属性方法,m文件中使用synthesize指定和成员变量_author关联
// (以前比较规范额使用方法,现在可以直接利用LLVM省去,详见publisher的使用)
@property (nonatomic, retain) NSString* author;
// 声明属性,由LLVM自动生成成员变量_publisher和属性方法,合成的成员变量是private类型
// (私有变量推荐使用这种方法)
@property (nonatomic, retain) NSString* publisher;
// 声明属性,由LLVM自动合成属性方法,并且由LLVM指定其和成员变量_nameIn关联
// (私有成员变量不建议使用,因为会自动合成私有的成员变量,所以可以去掉成员变量的声明)
@property (nonatomic, retain) NSString* nameIn;
// 声明属性,由LLVM自动合成属性方法,并且由LLVM指定其和保护成员变量_protectName关联
// (保护变量使用这种类型声明)
@property (nonatomic, retain) NSString* protectName;
@end
/* 总结:实例变量和属性的关系
* 最新的编译器的LLVM支持自动给属性(eg.myproperty)合成属性方法并关联实例变量(_myproperty),如果该实例变量存在,直接关联。
* 如果该实例变量不存在,则自动给类添加该实例变量,该实例变量是私有类型(private)。这样的话,只需要在头文件使用@property声
* 明一个属性,我们就可以正常使用该属性,而且在类里面能直接使用对应的私有变量。如果你想修改自动合成的_myproperty访问类型,需要
* 在类的头文件定义所需的访问类型的实例变量。当然,以前的用_var定义变量然后使用synthesize指定var=_var的方法也值得推荐的,不过相对上述方法而言,我还是喜欢现在说的这种。
*/
m文件
//
// Book.m
// MemberProperty
//
// Created by arbboter on 14/12/17.
// Copyright (c) 2014年 arbboter. All rights reserved.
//
#import "Book.h"
@implementation Book
{
// 声明有一个私有变量,不能使用指定为属性
NSString* _no;
}
@synthesize _name;
@synthesize author = _author;
- (NSString*)description
{
NSString* str = [NSString stringWithFormat:@"\nname : %@\nauthor : %@\npublisher : %@\nalign name : %@\n",
_name, _author, _publisher, _nameAlign];
return str;
}
- (id)init
{
self = [super init];
if(self)
{
_name = [[NSString alloc] initWithString:@"Book."];
_author = [[NSString alloc] initWithString:@"Author."];
_publisher = [[NSString alloc] initWithString:@"Publisher."];
_nameAlign = [[NSString alloc] initWithString:@"NameAlign."];
}
return self;
}
- (void)dealloc
{
[_name release];
[_author release];
[_publisher release];
[_nameAlign release];
[super dealloc];
}
@end
main文件
//
// main.m
// MemberProperty
//
// Created by arbboter on 14/12/17.
// Copyright (c) 2014年 arbboter. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "Book.h"
int main(int argc, const char * argv[])
{
Book* b = [[Book alloc] init];
NSLog(@"%@", [b description]);
/*
* 点语法的本质是方法的调用,而不是访问实例变量,当使用点语法时,编译器会自动展开成相应的方法。
* 点语法的本质是转换成相应的set和get方法,如果没有set和get方法,则不能使用点语法。
*/
// 直接赋值型 实际上调用的还是set方法 (隐式调用set方法)
b._name = @"main_set_name";
b.author = @"main_set_author";
b.publisher = @"main_set_publisher";
b.nameIn = @"private member set ok?";
b.nameAlign = @"main_set_align_name";
// _nameAlign是pulich实例变量,所以可直接访问变量赋值
b->_nameAlign = @"ok?";
NSLog(@"%@", [b description]);
// 显示调用set方法
[b setAuthor:@"setsetAuthor"];
[b setPublisher:@"setPublisher"];
[b set_name:@"set_name"];
[b setNameAlign:@"setNameAlign"];
NSLog(@"%@", [b description]);
// 错误用法
/* 默认私有变量不能访问 */
// b->_author;
[b release];
return 0;
}