iOS 5之后:
@property声明的属性默认会生成一个_类型的成员变量,同时也会生成setter/getter方法。
iOS 5之前:
1.一个大括号里面定义了成员变量
2.@property声明
3.@implementation中使用@synthesize方法。
Demo
iOS5 之前是这样的
@interface ViewController ()
{
// 1.声明成员变量
NSString *myString;
}
//2.在用@property
@property(nonatomic, copy) NSString *myString;
@end
@implementation ViewController
//3.最后在@implementation中用synthesize生成set方法
@synthesize myString;
@end
其实,发生这种状况根本原因是苹果将默认编译器从GCC转换为LLVM(low level virtual machine),才不再需要为属性声明实例变量了。
在没有更改之前,属性的正常写法需要成员变量+ @property + @synthesize 成员变量三个步骤。
如果我们只写成员变量+ @property:
@interface GBViewController :UIViewController
{
NSString *myString;
}
@property (nonatomic, strong) NSString *myString;
@end
编译时会报警告:
Autosynthesized property ‘myString’ will use synthesized instance variable ‘_myString’, not existing instance variable ‘myString’
iOS5 之后:
更换为LLVM之后,编译器在编译过程中发现没有新的实例变量后,就会生成一个下划线开头的实例变量。因此现在我们不必在声明一个实例变量。(注意:==是不必要,不是不可以==)
当然我们也熟知,@property声明的属性不仅仅默认给我们生成一个_类型的成员变量,同时也会生成setter/getter方法。
在.m文件中,编译器也会自动的生成一个实例变量_myString。那么在.m文件中可以直接的使用_myString实例变量,也可以通过属性self.myString.都是一样的。
注意这里的self.myString其实是调用的myString属性的setter/getter方法。这与C++中点的使用是有区别的,C++中的点可以直接访问成员变量(也就是实例变量)。
例如在oc中有如下代码
@interface MyViewController :UIViewController
{
NSString *name;
}
@end
在这段代码里面只是声明了一个成员变量,并没有setter/getter方法。所以访问成员变量时,可以直接访问name,也可以像C++一样用self->name来访问,但绝对不能用self.name来访问
首先我们要明白,@synthesize 生成了setter/getter方法。
虽然现在直接使用@property时,编译器会自动为你生成以下划线开头的实例变量_myString,不需要自己手动再去写实例变量。而且也不在.m文件中通过@synthesize myString;生成setter/getter方法。但在看老代码的时候,我们依旧可以看到有人使用成员变量+ @synthesize 成员变量的形式。
那么问题来了:
我们能否认为新编译器LLVM下的@property == 老编译器GCC的 成员变量+ @property + @synthesize 成员变量呢?
答案是否定的。
因为成员变量+ @property + @synthesize 成员变量的形式,编译器不会帮我们生成成员变量,因此不会操作成员变量了;
同时@synthesize 还有一个作用,可以指定与属性对应的实例变量,
例如@synthesize myString = xxx;
那么self.myString其实是操作的实例变量xxx,而非_String了。
1.## 成员变量作用范围 ##
@public:在任何地方都能直接访问对象的成员变量
@private:只能在当前类的对象方法中直接访问,如果子类要访问需要调用父类的get/set方法
@protected:可以在当前类及其子类对象方法中直接访问(系统默认下是用它来修饰的)
@package:在同一个包下就可以直接访问,比如说在同一个框架
2.## 注意 ##
无论父类是在@interface还是@implementation声明的成员变量子类都能拥有;但是子类能不能直接通过变量名来访问父类中定义的成员变量是需要看父类中定义的成员变量是由什么修饰符来修饰的。 (修饰符在1中变量的作用范围中可以得到)
3.## 默认修饰 ##
在.m中成员变量的修饰符为@private.
在.h中成员变量的修饰符@protected.
创建一个学生类,然后分别在.h跟.m中声明4中不同的类型的变量,创建一个MidStudent来继承Student
我们在MidStudent中来调用下看看这些变量哪些可以访问哪些不可以:
有图可以看出,我们在.m里面声明的变量子类是无法访问的(即使给他@public),也会被认为是@private,所以我们的对外属性都会放到.h去声明,然而由于six变量是@private,所以子类还是无法访问的
我们再去外部去调用下MidStudent 对象 看看是什么效果:
由于我们没有在Student或他的子类里面,所以只能访问.h中@public修饰的变量,也就是name,由于age是@protrcted在外部是不能被访问的;_age是protected修饰,可以在当前类及其子类对象方法中直接访问(系统默认下是用它来修饰的);_six是private修饰:只能在当前类的对象方法中直接访问,如果子类要访问需要调用父类的get/set方法
属性的声明,还有一些修饰词的含义Objective-C 编程语言官网文档(五)-属性的声明
readwirte
如果没有声明成 readonly ,那就 默认是 readwrite 。可以用来赋值,执行set方法
readonly
不可以被赋值
assgin
通常用于标量(简单变量 int , float , CGRect 等)
一种典型情况是用在对对象没有所有权的时候,通常是 delegate ,避免造成死循环(如果用 retain 的话会死循环)
strong
释放旧对象 将旧对象的值赋予输入对象 ,再提高输入对象的索引计数为 1 ,常使用在继承自 NSObject 的类。(MRC为retain)
weak
weak 不增加对对象的引用计数,也不持有对象,因此不能决定对象的释放。它比 assign 多了一个功能,当对象消失后自动把指针变成 nil
strong
释放旧对象 将旧对象的值赋予输入对象 ,再提高输入对象的索引计数为 1 ,常使用在继承自 NSObject 的类。(MRC为retain)
weak
weak 不增加对对象的引用计数,也不持有对象,因此不能决定对象的释放。它比 assign 多了一个功能,当对象消失后自动把指针变成 nil
copy
建立一个索引计数为 1 的对象 然后释放旧对象 对 NSString 它指出 ,在赋值时使用传入值的一份拷贝 ,拷贝工作由 copy 方法执行,此属性只对那些实行了 NSCopying 协议的对象类型有效。