第一部分
- 类的@interface、@implementation描述
- 类方法和实例方法
- 初步理解alloc和init
- 初步理解声明一个对象(实例)
- setter方法和getter方法
- 基本数据类型
- 限定词:long、long long、short、unsigned、signed
- #import <>和""的区别
类的@interface、@implementation描述
@interface接口部分用于描述类和类的方法;
@implementation实现部分用于描述数据(类对象的实例变量存储的数据),并实现在接口中声明方法的实际代码。也可以在interface(接口)部分为类声明实例变量。
类方法和实例方法
开头的负号(-)表示该方法是一个实例方法;实例方法能够对类的实例执行一些操作,例如,设置值、检索值和显示值等。
开头的正号(+)表示这是一个类方法;类方法是对类本身执行某些操作的方法,例如,创建类的新实例。
实例方法总是可以直接访问它的实例变量。然而,类方法不能,因为它只处理本身,并不处理任何实例。
初步理解alloc和init
alloc是allocate(vt.分配,分派; 把…拨给)的缩写。作用是为新创建的对象(实例)分配内存。alloc方法保证对象的所有实例变量都变成初始状态。然而,这并不意味着该对象已经进行了适当的初始化,从而可以使用。在创建对象之后,还必须对它初始化。
init方法用于初始化类的实例变量。
理解:alloc可以使实例变量指定为0或者是nil,但是对象还是没有被正确的初始化,所以必须使用init来初始化新创建的对象。
初步理解声明一个对象(实例)
声明一个对象:Fraction *myFraction;
myFraction前的星号()表明myFraction是Fraction对象的引用(或指针)。变量myFraction实际上并不储存Fraction的数据,而是存储了一个引用(其实是内存地址),表明对象数据在内存中的位置。在声明myFraction时,它的值时未定义的,它没有被设定为任何值,并且没有默认值*。在概念上,我们可以认为myFraction是一个能够容纳值的盒子。在初始化盒子时包含了一些未定义的值,它并没有被指定任何值。
如果你创建一个新对象(例如,使用alloc),就会在内存中为它保留足够的空间用于存储对象数据,这包括它的实例变量空间,再另外预留了一些。通常,alloc会返回存储数据的位置(对数据的引用),并赋给变量myFraction。
setter方法和getter方法
设值方法和取值方法通常称为访问器(accessor)方法。
基本数据类型
oc中的基本数据类型有:int、float、double、char、long、double。
在oc中,任何数字、单个字符或者字符串通常称为常量。例如,数字58是一个常量整数值。字符串@"aaaaaa"表示一个常量字符串对象。表达式 @5 表示一个常量数字对象。
每个值无论是字符、整数还是浮点数字,都有其对应的值域。这个值域与系统为特定类型的值分配的内存量有关。一般来说,在语言中没有规定这个量,它通常依赖于所运行的计算机。因此,叫做设备或机器相关量。例如,一个整数可以在计算机上占用32位。
要区分浮点常量,可以看它是否包含小数点。float类型和double类型(浮点常量),可以存储科学计数法数字。例如,1.7e4、17e-1就是使用这种计数法来表示的浮点数,它表示值1.710^4、1710^-1。
在计算机内存中,不能精确的表示一些浮点值。
限定词:long、long long、short、unsigned、signed
- 如果直接把long放在int前面,那么所声明的整型变量在某些计算机上具有扩展的值域。long变量的具体范围也是有具体的计算机系统决定。
long int a;long long int;long double;
- 把限定词short放在int前,表示要声明的整型变量用来存储相当小的整数。之所以使用short变量,主要是出于节约内存空间的考虑。
short int;
- unsigned放在int前,表示变量只能存储正值。
unsigned int a;
#import <>和""的区别
双引号""适用于本地文件,即你自己创建的文件,不是系统文件;尖括号<>导入的是系统文件。使用双引号时,编译器一般会首先在项目目录中寻找指定文件,然后转到其它位置寻找。
- 理解@property和@synthesize
- iOS中属性与成员(实例)量的区别
- 属性修饰符
- 深拷贝和浅拷贝
- 重写NSString的setter方法
第二部分
- 理解@property和@synthesize
- iOS中属性与成员(实例)量的区别
- 属性修饰符
- 深拷贝和浅拷贝
- 重写NSString的setter方法
理解@property和@synthesize
@proterty @synthesize详解
@synthesize 的作用
@property,iOS6以后出来的关键字:编译器会自动给你生成setter(setter方法名即setName)和getter(而getter方法名即name)方法的声明,以及实现一个以_name 的成员变量(name是你属性定义的变量名字)。
-
@synthesize ,简单来说,就是为属性声明实例变量
- @synthesize name = _name;将实例变量name名称换成_name。这样写是为了区分成员变量(实例变量)_name和属性name,在.m文件里面使用的时候见到_name就知道是成员(实例)变量了,见到self.name就知道是属性了。另外,系统库中的所有类的声明部分都是这样写的, 总之一句话:区分成员变量名称和属性名称。使用@synthesize可以改变_name名称
- @synthesize name = custom_name:将实例变量_name名称换成custom_name
@synthesize 还有一个作用,可以指定与属性对应的实例变量,例如@synthesize myButton = xxx;那么self.myButton其实是操作的实例变量xxx,而不是_myButton了。
使用@property,编译器会自动生成@synthesize name = _name;
如果只写@synthesize name;那么成员变量的名称会是name,而不是_name.
注意:@synthesize不会影响@property产生的setter和getter方法的名称,@property和@synthesize不必成对出现。
禁止@synthesize:如果某属性已经在某处实现了自己的 setter/getter ,可以使用 @dynamic 来阻止 @synthesize 自动生成新的 setter/getter 覆盖。
注意:如果你setter和getter两个方法都重写了,那么@property是不会自动生成带有下划线的成员变量的,需要手动使用synthesize生成。 @synthesize name = _name
iOS中属性与成员(实例)量的区别
iOS中属性与成员变量的区别
解惑——iOS中成员变量和属性区别
MyViewController.h文件
@interface MyViewController :UIViewController
@property (nonatomic, retain) UIButton *myButton;
@end
- 在MyViewController.m文件中,编译器也会自动的生成一个实例变量_myButton。那么在.m文件中可以直接的使用_myButton实例变量,也可以通过属性self.myButton.都是一样的。注意这里的self.myButton其实是调用的myButton属性的getter/setter方法。
@interface MyViewController :UIViewController
{
NSString *name;
}
@end
- .m文件中,self.name 这样的表达式是错误的。xcode会提示你使用->,改成self->name就可以了。因为oc中点表达式是表示调用方法,而上面的代码中没有name这个方法。
@interface MyViewController :UIViewController
@property (nonatomic, retain) UIButton *myButton;
@end
因为编译器会自动为你生成以下划线开头的实例变量_myButton,不需要自己手动再去写实例变量。而且也不需要在.m文件中写@synthesize myButton;也会自动为你生成setter,getter方法。@synthesize的作用就是让编译器为你自动生成setter与getter方法。
@synthesize 还有一个作用,可以指定与属性对应的实例变量,例如@synthesize myButton = xxx;那么self.myButton其实是操作的实例变量xxx,而不是_myButton了。
成员变量用于类内部,无需与外界接触的变量。
根据成员变量的私有性,为了方便访问,所以就有了属性变量。属性变量的好处就是允许让其他对象访问到该变量。当然,你可以设置只读或者可写等,设置方法也可自定义。所以,属性变量是用于与其他对象交互的变量。
属性修饰符
@property后面的关键字
@property 后面可以有哪些修饰符
@property都有哪些修饰词?以及作用总结
- 线程安全的: atomic(原子性), nonatomic(非原子性)
- 指定方法名称: setter= getter=
- 访问权限的: readonly(只读), readwrite (读写)
- 内存管理(ARC) assign,strong,weak,copy
- 内存管理(MRC)assign, retain,copy
1. 线程安全(系统默认是atomic)
-
nonatomic(英['nɒnə'tɒmɪk]): 禁止多线程,变量保护,提高性能。
- 不对set、get方法加同步锁
- 性能好
- 线程不安全
-
atomic(英[əˈtɒmɪk]): Objc使用的一种线程保护技术,基本上来讲,是防止在写未完成的时候被另外一个线程读取,造成数据错误。而这种机制是耗费系统资源的,所以在iPhone这种小型设备上,如果没有使用多线程间的通讯编程,那么nonatomic是一个非常好的选择。
- 原子属性就是对生成的set方法加互斥锁(互斥锁是一种同步锁,互斥锁:如果共享数据已经有其他线程加锁了,线程会进入休眠状态等待锁,一旦被访问的资源解锁,则等待访问资源的线程会被唤醒。自旋锁:如果共享的数据已经有其他线程加锁了,线程会以死循环的方式等待锁,一旦被访问的资源被解锁,则等待资源的线程会立即执行。自旋锁的效率高于互斥锁)
- 需要消耗系统资源
- 互斥锁是用线程同步实现的,意在保证同一时间只有一个线程调用get、set方法。
简单来讲:一般单线程声明nonatomic,考虑到速度问题。多线程程序就不要使用nonatomic。
OC在定义属性时有nonatomic和atomic两种选择。
atomic:原子属性,为setter方法加锁(默认就是atomic)。
nonatomic:非原子属性,不会为setter方法加锁。
atomic加锁原理:
@property (assign, atomic) int age;
- (void)setAge:(int)age
{
@synchronized(self) {
_age = age;
}
}
互斥锁
// 注意:锁定1份代码只用1把锁,用多把锁是无效的
@synchronized(锁对象) { // 需要锁定的代码 }
使用互斥锁,在同一个时间,只允许一条线程执行锁中的代码。因为互斥锁的代价非常昂贵,所以锁定的代码范围应该尽可能小,只要锁住资源读写部分的代码即可。使用互斥锁也会影响并发的目的。
1. nonatomic和atomic的介绍和区别
当使用atomic时,虽然对属性的读和写是原子性的,但是仍然可能出现线程错误:当线程A进行写操作,这时其他线程的读或者写操作会因为等该操作而等待。当A线程的写操作结束后,B线程进行写操作,所有这些不同线程上的操作都将依次顺序执行——也就是说,如果一个线程正在执行 getter/setter,其他线程就得等待。如果有线程C在A线程读操作之前release了该属性,那么还会导致程序崩溃。所以仅仅使用atomic并不会使得线程安全,我们还要为线程添加lock来确保线程的安全。
2. 访问权限(默认为readwrite)
- readonly,只生成getter方法,没有setter方法
- readwrite,是默认的
3. 指定方法名称: setter= getter=
- getter=newGetterName,指定新的getter方法名,一般重新改写BOOL实例变量的getter名。例如
@property (getter=isFinished) BOOL finished;
- setter=,指定新的setter方法名。
4. **retain(MRC,对象)和strong(ARC,对象)
retain(MRC)
1.release旧对象(旧对象引用计数-1),retain新对象(新对象引用计数+1),然后指向新对象
2.在set方法里面是这样写的:
if(_dog!=nil){
[_dog release];//先release
}
_dog = [dog retain];//再retain新对象
strong(ARC)(对象)
1.直接赋值,并且对象的引用计数器加1;
2.在ARC中替代了retain的作用。
相同点 :
- strong 和 retain 都是针对对象类型进行内存管理。如果去修饰基本数据类型,Xcode会直接报错。
- 当给对象类型使用此修饰符时,setter方法会先将旧的对象属性release掉,再将新的对象赋值给属性并对该对象进行一次retain操作。
- 两者都会增加对象的引用计数。
不同点: strong一般用于ARC环境, retain用于MRC环境.
5. assign(ARC/MRC,对象/基本数据类型)
1.这个修饰词是直接赋值的意思,整型、浮点型等基础数据类型都用该修饰词;
3.assign不会牵扯到内存管理,不会增加引用计数(与weak相同);
2.如果没有使用weak、strong、retain、copy等词修饰,默认就用assign修饰(它们之间是有你没我的关系,一般的指针类型都用strong修饰)
3.当然其实对象也可以用assign修饰,只是对象的计数器不会加1(与strong的区别)
4.如果assign用来修饰对象属性,那么当对象被销毁后指针是不会指向nil的,必须手动将其置为nil,否则会出现野指针错误(如果weak指向的对象消失了,那么它会自动置为nil,不会产生野指针;weak只能修饰对象),如果还通过此指针操作那块内存,便会导致EXC_BAD_ACCESS错误,调用了已经释放的内存空间。
6. weak(ARC,对象)
1.weak弱指针,修饰对象的修饰词,也就是说它不能修饰基本数据类型(int、float)
2.weak修饰的对象引用计数器不会加1,也就是直接赋值;(和assign相同,引用计数都不会增加)
3.弱引用是为打破引用循环而生的
4.它最被人所喜欢的原因是 它所指向的对象如果被销毁 , 它会指向nil . 从而不会出现野指针错误 。
7. weak和assign的区别
assign和weak,他们都是弱引用类型,但是他们有区别的:
1.用weak声明的变量在栈中会被自动清空,赋值为nil;
2.用assign声明的变量在栈中可能不会被赋值为nil,就会造成野指针错误;
以delegate为例,在MRC中delegate被声明为assign,这是为了不造成循环引用,这时我们需要在dealloc中写上self.delegate = nil;
以免造成delegate的野指针错误,当然在ARC中只需要用weak声明delegate就可以自动释放了。
8. copy(ARC/MRC)
必须要使用copy的情况:(1.属性是一个不可变对象,如NSString,NSArray,NSDictionry;2.需要把一个可变对象赋值给属性,如把一个NSMutableString赋值给属性NSString,除此之外的情况的使用copy和strong是没区别的);
1.copy在MRC中时是这样做的:release旧对象,旧对象引用计数器减1,copy新对象,新对象的引用计数器加1,然后指向新对象(新对象是最终指向的那个对象,不管是深拷贝还是浅拷贝)。
在set方法里这样写的
- (void)setName:(NSString *)name {
if(_name){
[_name release];
}
_name = [name copy];
}
2.copy在ARC中是这样干的,copy新对象,新对象的引用计数器加1,然后指向新对象。
在set方法里是这样写的:
- (void)setName:(NSString *)theName {
if(_name != theName){
_name = [theName copy];
}
}
3.使用注意:
- 修饰的属性本身必须是不可变的。例如:NSMutableArray采用copy修饰,在addObject时会出现Crash,因为NSMutableArray的对象在copy时就会变成NSArray,如需要拷贝NSMutableArray对象用mutableCopy。
- 遵守NSCopying协议的对象使用
copy 和 strong 都可修饰不可变类型,但一般用copy
一般用copy修饰不可变的, 因为安全, 可以保证其封闭性.
因为用copy修饰,setter方法中会自动判断如果来源,如果是不可变的,那和strong一样,进行浅拷贝,会增加其引用计数,如果是可变的那么就深拷贝,不会增加其引用计数。 所以如果如果项目中这样的不可变对象(比如NSString)多的话,当一定数量if判断消耗的时间累加起来就会影响性能.
所以,只需要记住一点,当你给你的不可变对象赋值时, 如果来源是可变的,那么就用copy,如果来源是不可变类型的,就用strong。(这句话存疑,因为我理解为:希望不跟随源对象变化时才用copy,希望跟随源对象变化而变化时用strong)
注:如果当strong修饰不可变的, 如果来源是不可变得,那么同上,没有问题。如果来源是可变的时, 那么当源对象变化时,我们的不可变属性也会跟着变化,那么就破坏了其封闭性, 就不安全了。如果用 copy 修饰 可变类型 会出现什么问题?
copy修饰可变的对象的话, 会生成一个不可变的NSCFConstantSting对象,赋值给可变属性,编译没问题,方法修改其内容时崩溃:unrecognized selector sent to instance。
总结
- copy 修饰不可变的 要看赋值来源
- 来源是可变的话, 会自动进行深拷贝, 来源对象的变化不会影响我们的不可变属性
- 来源是不可变的话,那么就和strong一样大胆的指针拷贝,反正都不会改变.
- copy 修饰可变的.
- 那么会生成一个不可变对象,赋值给可变属性,编译没问题,调方法修改其内容时会崩溃unrecognized selector sent to instance
- strong修饰不可变的 也要看来源
- 如果来源是不可变的, 那就没什么问题
- 如果来源是可变的, 那么当源对象的内容发生改变时,我们的不可变属性的值也会发生改变,那么就破坏的其封闭性, 不安全.
- strong修饰可变的 也要看来源
- 如果来源是不可变的, 那么会直接报警告运行出错 unrecognized selector sent to instance
- 如果来源是可变的,那么没问题.
重写NSString的setter方法
重写NSString的setter方法
copy是复制一个对象,当我们不希望新对象随着旧对象变化而变化时,就要使用copy。
copy修饰的是不可变的对象。
因为NSString是一般用copy来修饰。
所以在MRC中,setter方法这样写
- (void)setName:(NSString *)name {
if(_name){
[_name release];
}
_name = [name copy];
}
在ARC中,不用release,直接copy。ARC会自动管理内存。
- (void)setName:(NSString *)name {
_name = [name copy];
}
注意,不要去加什么判断比如下面这样的。
if(_name != name){
_name = [name copy];
}
每次赋值都去判断一下会更耗时间,这个东西纯粹是多余的。
深拷贝和浅拷贝
浅拷贝:指针拷贝
深拷贝:内容拷贝(开辟内存)
浅拷贝就是对内存地址的复制,让目标对象指针和源对象指向同一片内存空间。
深拷贝是指拷贝对象的具体内容,而内存地址是自主分配的,拷贝结束之后,两个对象虽然存的值是相同的,但是内存地址不一样,两个对象也互不影响,互不干涉。