类成员公私属性
1 | @private |
类的成员和方法使用
1 | //生成对象 |
自动生成set get函数
在ObjC中可以通过声明@property,同时通过@synthesize自动生成getter、setter方法(在新版本中甚至甚至都不用通过@synthesize只声明就可以使用)
1 | //头文件 |
1 | //m文件 |
1 | //调用 |
- 如果只声明一个属性a,不使用@synthesize实现:编译器会使用_a作为属性的成员变量(如果没有定义成员变量_a则会自动生成一个私有的成员变量_a;如果已经定义了成员变量_a则使用自定义的成员变量_a。注意:如果此时定义的成员变量不是_a而是a则此时会自动生成一个成员变量_a,它跟自定义成员变量a没有任何关系);
- 如果声明了一个属性a,使用@synthesize a进行实现,但是实现过程中没有指定使用的成员变量(例如上面birthday):则此时编译器会使用a作为属性的成员变量(如果定义了成员变量a,则使用自定义成员变量;如果此时没有定义则会自动生成一个私有的成员变量a,注意如果此时定义的是_a则它跟生成的a成员变量没有任何关系);
- 如果声明了一个属性a,使用@synthesize a=_a进行实现,这个过程已经指定了使用的成员变量:此时会使用指定的成员变量作为属性变量;
自定义构造函数
当构造函数需要初始化一些成员, 或要传输参数时需要使用自定义构造函数
1
2
3
4
5
6
7
8
9//头文件
@interface Person : NSObject
@property NSString *name;
@property int age;
-(id)initWithName:(NSString *)name andAge:(int )age;
@end1
2
3
4
5
6
7
8
9
10
11
12
13
14//实现文件
@implementation Person
//自定义构造方法
-(id)initWithName:(NSString *)name andAge:(int)age{
if(self=[super init]){ //super代表父类
self.name=name;
self.age=age;
}
return self; //返回类对象
}
@end1
2
3
4
5
6
7
8
9//调用
int main(int argc, const char * argv[]) {
Person *p=[[Person alloc]initWithName:@"Kenshin" andAge:28];
NSLog(@"name=%@,age=%i",p.name,p.age);
//结果:name=Kenshin,age=28
return 0;
}
description方法
在C#中每个类都有一个ToString()方法(java中叫做toString())用于打印一个对象的信息,在ObjC中这个方法叫description, 也就是把类名转成字符串, 当然你也可以自定义
1 | -(NSString *)description{ |
1 | Person *p=[[Person alloc]init]; |
property的一些修饰词
@property的参数分为三类,也就是说参数最多可以有三个,中间用逗号分隔,每类参数可以从上表三类参数中人选一个。如果不进行设置或者只设置其中一类参数,程序会使用三类中的各个默认参数,MRC默认参数:(atomic,readwrite,assign), ARC下基本数据类型默认的属性参数为(atomic,readwrite,assign),对象类型默认的属性参数为(atomic,readwrite,strong)
常见修饰词有:assign、weak、strong、retain、copy、nonatomic、atomic、readonly、readwrite
其中部分用在特定的内存管理中:
ARC(自动管理内存):assign、weak、strong、copy
MRC(手动管理内存):assign、retain、copy、nonatomic、atomic
assign ( ARC/MRC )
1.这个修饰词是直接赋值的意思 , 整型/浮点型等数据类型都用这个词修饰 。
2.如果没有使用 weak、strong、 retain、 copy 等修饰 , 那么默认就是使用 assign 了 ( 它们之间是有你没我的关系 ,一般的指针变量是strong修饰)。
3.当然其实对象也可以用 assign 修饰 , 只是对象的计数器不会+1 . ( 与 strong 的区别 )
4.如果用来修饰对象属性 , 那么当对象被销毁后指针是不会指向 nil 的 . 所以会出现野指针错误 ( 与weak的区别 )。weak ( ARC )(对象)
1.弱指针是针对对象的修饰词 , 就是说它不能修饰基本数据类型(int float) 。
2.weak 修饰的引用计数器不会+1 , 也就是直接赋值 。
3.弱引用是为打破循环引用而生的,比如在Block中,block在copy的时候,会对内部使用到的对象的引用技术+1,如果使用[self 方法名],那么就会有一个强指针指向self所在的对象的内存地址,对象的引用计数会+1,这样一来会导致对象所在的内存地址无法被释放,造成内存泄漏 。
4.它最被人所喜欢的原因是 它所指向的对象如果被销毁 , 它会指向 nil . 从而不会出现野指针错误 。
weak 和 assign的区别
assign与weak,它们都是弱引用声明类型,但是他们是有区别的。
1.用weak声明的变量在栈中就会自动清空,赋值为nil。
2.用assign声明的变量在栈中可能不会自动赋值为nil,就会造成野指针错误!
以delegate的声明为例,在MRC中多delegate声明使用的是assign,这是为了不造成循环引用,这时,我们需要在-dealloc方法中写上 self.delegate = nil,以免造成delegate的野指针错误。当然,在ARC中,只需要用weak声明delgate,就会自动释放了。strong ( ARC )(对象)
1.直接赋值并且对象的引用计数器 +1 。
2.在 ARC 里替代了 retain 的作用 。retain ( MRC )
1.release 旧对象( 旧对象计数器 -1 ) , retain 新对象( 新对象计数器 +1 ) , 然后指向新对象 。
copy ( ARC/MRC )
1.copy 在 MRC 时是这样做的, release 旧对象( 旧对象的引用计数器 -1 ) , copy 新对象( 新对象的引用计数器 +1 ) , 然后指向新对象 .(新对象是指最终指向的那个对象,不管深拷贝还是浅拷贝)
nonatomic ( ARC/MRC )
1.不对set方法加同步锁 。
2.性能好。
3.线程不安全。atomic ( ARC/MRC )
1.原子属性就是对生成的 set 方法加互斥锁 (互斥锁 是一种同步锁,
互斥锁:如果共享数据已经有其他线程加锁了,线程会进入休眠状态等待锁。一旦被访问的资源被解锁,则等待资源的线程会被唤醒。
自旋锁:如果共享数据已经有其他线程加锁了,线程会以死循环的方式等待锁,一旦被访问的资源被解锁,则等待资源的线程会立即执行。自旋锁的效率高于互斥锁 )
@synchronized(锁对象) 。
@synchronized(self) { _delegate = delegate;}
2.需要消耗系统资源 。
3.互斥锁是利用线程同步实现的 , 意在保证同一时间只有一个线程调用 set 方法 。
4.其实还有 get 方法 , 要是同时 set 和 get 一起调用还是会有问题的 . 所以即使用了 atomic 修饰 还是不够安全 。
readonly (只读)
1.让 Xcode 只生成get方法 。
2.不想把暴露的属性被人随便替换时 , 可以使用 。readwrite (读写)(默认)
1.让 Xcode 生成get/set方法 。
2.不用 readonly 修饰时 , 默认就是 readwrite 。
nonatomic 和 atomic 的介绍和区别
什么是atomicity(原子性)?
atomicity(原子性):我把原子性理解成线程对属性的单一执行。
例如,当两条线程同时执行一个属性的set方法的时候,如果不具有原子性(也就是声明属性时使用 了nonatimic),那么就可能出现当A线程正在改写某属性值的时候,B线程也许会突然闯入,把尚未修改好的属性值读取出来。发生这种情况时,线程读取到的属性值肯定不对。
保证atomicity真的就线程安全了吗?为什么日常声明都用的是nonatomic呢?
1.保证atomicity并非也是线程安全的,如果需要保证安全,需要跟深层次的线程锁定机制。
2.使用同步锁在iOS中开销比较大,会给程序带来性能上的问题。
- 为什么atomicity也不能保证线程安全?
例如:当使用atomic时,仍然可能出现线程错误:当线程A进行set操作,这时其他线程的get或者set操作会因为等该操作而等待。当A线程的set操作结束后,B线程进行set操作,然后当A线程需要get操作时,却获得了在B线程的值,这就破坏了线程安全,如果有C线程在A线程get操作之前release了该属性,那么还会导致程序崩溃。所以仅仅使用atomic并不会使得线程安全,我们还是要为线程添加lock来确保线程的安全。
一般情况下如果在多线程开发中一个属性可能会被两个及两个以上的线程同时访问,此时可以考虑atomic属性,否则建议使用nonatomic,不加锁,效率较高;readwirte方法会生成getter、setter两个方法,如果使用readonly则只生成getter方法;关于set方法处理需要特别说明,假设我们定义一个属性a,这里列出三种方式的生成代码:
assign,用于基本数据类型1
2
3-(void)setA:(int)a{
_a=a;
}
retain,通常用于非字符串对象1
2
3
4
5
6-(void)setA:(Car *)a{
if(_a!=a){
[_a release];
_a=[a retain];
}
}
copy,通常用于字符串对象、block、NSArray、NSDictionary1
2
3
4
5
6-(void)setA:(NSString *)a{
if(_a!=a){
[_a release];
_a=[a copy];
}
}