1、在编译一个使用了CPerson类的文件时,如果不需要知道该类的全部细节,只需要知道有一个类名叫CPerson,最好“向前声明”该类(同理于对一个协议的引用),这样做也可以避免“循环引用”问题:
例:
@class CPerson
例:
#import "CShape.h"
#import "CDrawable.h"
@interface CRectangle : CShape <CDrawable>
2、多用字面量语法,少用与之等价的方法 (NSString/NSNumber/NSArray/NSDictionary)
字面量语法的限制:除了字符串以外,所创建出来的对象必须属于Foundation框架才行。如果自定义了这些类的之类,则无法用字面量语法创建其对象。
例:
NSString *someString = @"Effective Objective-C 2.0";
NSNumber *someNumber = @1 ;
NSNumber *floatNumber = @2.5f;
NSNumber *boolNumber = @YES;
NSNumber *charNumber = @'a';
NSArray *animals = @[@"cat", @"dog",@"pig"];//若@“dog”为空,程序会抛出异常令应用程序终止执行,达到检查是否含空字符串的目的
NSArray *dog = animals[1];
NSDictionary *personData = @{@"firstName" : @"Matt", @"lastName" : @"Galloway", @"age" : @28};//一旦有值为nil也会抛出异常
NSDictionary *firstName = personData[@"firstName"];
3、多用类型常量const static,少用#define预处理指令,这样创建出来的常量有类型信息,便于差错。
1)若不打算公开某个常量,则应将其定义在使用该常量的实现文件里;
2)在头文件中使用extern来声明全局常量,并在相关实现文件中定义其值。
4、用枚举表示状态、选项、状态码
enum Week {Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday};
enum Week day = Monday;
5、理解“属性” (属性用于封装对象中的数据,保存着数据的实例变量一般通过“存取方法”来访问)
1)atomic(原子性),如果属性具备nonatomic特质,则不使用同步锁;(开发iOS程序中所有的属性都声明为nonatomic,原因是:在iOS中使用同步锁的开销较大,这会带来性能问题;但是在开发Mac OS X程序时,使用atomic属性通常都不会有性能瓶颈。所以在iPhone这种小型设备上,如果没有使用多线程间的通讯编程,那么nonatomic是一个非常好的选择。)
2)readwrite(读写),拥有“获取方法”和“设置方法”,由@synthesize实现;
3)readonly(只读),仅拥有“获取方法”,由@synthesize实现;
4)assign,“设置方法”只会执行针对“纯量类型”(CGFloat、NSInteger等)的简单赋值操作,不更改索引计数,即setter方法直接赋值,不进行任何retain操作(对基础数据类型 (NSInteger)和C数据类型(int, float, double, char, 等)使用,否则可能导致内存泄露)
5)strong,设置方法会保留新值,并释放旧值,然后再将新值设置上去;使用ARC机制时,等同于retain
6)weak,设置方法既不保留新值,也不释放旧值。此特质同assign类似;使用ARC机制时,等同于assign,对象被释放时,属性的值会被设置为nil
7)copy,设置方法并不保留新值,而是将其“拷贝”,建立一个索引计数为1的对象,然后释放旧对象,即setter方法进行Copy操作,与retain处理流程一样,先旧值release,再Copy出新的对象,retainCount为1,为了减少对上下文的依赖而引入的机制(对NSString )
例:-(void)setOne:(NSObject *) other
{
if(one != other)
{
[one release];
one = [other copy];
}
}
8)retain,释放旧的对象,将旧对象的值赋予输入对象,再提高输入对象的索引计数为1,即setter方法对参数进行release旧值再retain新值,为了解决原类型与环循引用问题(对其他NSObject和其子类)
例:-(void)setOne:(NSObject *) other
{
if(one != other)
{
[one release];
one = [other retain];
}
}
(copy与retain的区别:
copy是创建一个新对象,retain是创建一个指针,引用对象计数加1
copy 到另外一个NSString 之后,地址为不同 ,内容相同,新的对象retain为1 ,旧有对象没有变化
retain 到另外一个NSString 之后,地址相同(建立一个指针,指针拷贝),内容当然相同,这个对象的retain值+1)
a、property,他可以提供的功能有:提供成员变量的访问方法的声明、控制成员变量的访问权限、控制多线程时成员变量的访问环境
b、synthesize的理解是:实现property所声明的方法的定义
c、使用属性的话,编译器会自动生成存取方法,并自动向类中添加适当类型的实例变量,并在属性名前面加下划线。
d、可用@synthesize语法来指定实例变量的名字;用@dynamic关键字则会告诉编译器:不要自动创建实现属性所用的实例变量,也不要为其创建存取方法。
6、在对象内部尽量直接访问实例变量,在对象之外访问实例变量时总是通过属性来做
(笔者强烈建议在读取实例变量的时候采用直接访问的形式,而在设置实例变量的时候通过属性来做)
(在初始化方法及dealloc方法中,总是应该直接通过实例变量来读写数据)
直接访问(_name) 与 属性访问(self.name) 的区别:
1)直接访问速度快,编译器所生成的代码会直接访问保存对象实例变量的那块内存;
2)直接访问不会调用“设置方法”,这就绕过了为相关属性所定义的“内存管理语义”;
3)直接访问实例变量不会触发“键值观测”(KVO)通知;
4)通过属性来访问有助于排查与之相关的错误,因为可以给“设置方法”设置断点。
7、理解“对象等同性”这一概念
1)按照 == 操作符比较的是两个指针本身,而不是其所指的对象;
2)应该使用NSObject协议中声明的“isEqual”:方法来判断两个对象的等同性。(“isEqualToString:”、“isEqualToArray:”、“isEqualToDictionary:”)
3)相同的对象必须具有相同的哈希码,但是两个哈希码相同的对象却未必相同。
8、instancetype和id的异同
1)相同点:
都可以作为方法的返回类型
2)不同点:
instancetype可以返回和方法所在类相同类型的对象,id只能返回未知类型的对象;
instancetype只能作为返回值,不能像id那样作为参数。
9、@selector
1)一种类型 SEL
2)代表你要发送的消息(方法), 跟字符串有点像, 也可以互转.: NSSelectorFromString() / NSSelectorFromString()
3)可以理解为类似函数指针的东西–是能让Objective-C动态调用方法的玩意.–是 object-c 的动态后绑定技术 可以通过字符串 访问的函数指针
4)其实就是消息响应函数—选一个消息响应的函数地址给你的action
5)@selector(function_name) 即取得一个function的id
10、理解objc_msgSend的作用(“传递消息”)
1)消息由接收者、选择子(方法)及参数构成。给某对象“发送消息”也就是相当于在该对象上“调用方法”;
2)发给某对象的全部消息都要由“动态消息派发系统”来处理,该系统会查出对应的方法,并执行其代码;
3)C语言使用“静态绑定”(编译期就能决定运行时所应调用的函数),而objective-c则使用“动态绑定”(所要调用的函数直到运行期才能确定)
4)编译器看到消息后,会将其转换为一条标准的C语言函数调用,这个核心函数叫做:objc_msgSend:
id returnValue = [someObject messageName:parameter];
==> id returnValue = objc_msgSend(someObject, @selector(messageName:), parameter);
(该方法需要在接收者所属的类中搜寻其“方法列表”,如果能找到与选择子名称相符的方法,就跳至其实现代码。若是找不到,那就沿着继承体系继续向上查找,等找到合适的方法之后再跳转。如果最终还是找不到相符的方法,那就执行“消息转发”操作。)
11、@synthesize和 @dynamic
@property有两个对应的词,一个是@synthesize,一个是@dynamic。如果@synthesize和@dynamic都没写,那么默认的就是@syntheszie var = _var;
@synthesize的语义是如果你没有手动实现setter方法和getter方法,那么编译器会自动为你加上这两个方法。
@dynamic告诉编译器,属性的setter与getter方法由用户自己实现,不自动生成。(当然对于readonly的属性只需提供getter即可)。假如一个属性被声明为@dynamic var,然后你没有提供@setter方法和@getter方法,编译的时候没问题,但是当程序运行到instance.var =someVar,由于缺setter方法会导致程序崩溃;或者当运行到 someVar = var时,由于缺getter方法同样会导致崩溃。编译时没问题,运行时才执行相应的方法,这就是所谓的动态绑定。
12、全局变量,和局部变量再内存占用上有什么区别?
变量可以分为:全局变量、静态全局变量、静态局部变量和局部变量。 按存储区域分,全局变量、静态全局变量和静态局部变量都存放在内存的静态存储区域,局部变量存放在内存的栈区。
按作用域分,全局变量在整个工程文件内都有效;静态全局变量只在定义它的文件内有效;静态局部变量只在定义它的函数内有效,只是程序仅分配一次内存,函数返回后,该变量不会消失;局部变量在定义它的函数内有效,但是函数返回后失效。
全局变量和静态变量如果没有手工初始化,则由编译器初始化为0。局部变量的值不可知。
静态全局变量,只本文件可以用。
全局变量是没有定义存储类型的外部变量,其作用域是从定义点到程序结束.省略了存储类型符,系统将默认为是自动型.
静态全局变量是定义存储类型为静态型的外部变量,其作用域是从定义点到程序结束,所不同的是存储类型决定了存储地点,静态型变量是存放在内存的数据区中的,它们在程序开始运行前就分配了固定的字节,在程序运行过程中被分配的字节大小是不改变的.只有程序运行结束后,才释放所占用的内存.
自动型变量存放在堆栈区中.堆栈区也是内存中一部分,该部分内存在程序运行中是重复使用的.