include程序员
include <> :用于对系统文件的引用,编译器会在系统文件目录下去查找该文件。web
#include "xx.h":用于对用户自定义的文件的引用,编译器首先会去用户目录下查找,而后去安装目录,最后去系统目录查找。
注:使用include要注意重复引用的问题:服务器
class A,class B都引用了class C,class D若引用class A与class B,就会报重复引用的错误。网络
功能与include基本相同,不过它避免了重复引用的问题。因此在OC中咱们基本用的都是import。session
@class
@class就是告诉编译器有这个类存在,可是类是如何实现的不用告诉编译器.若.m文件用到了这个类,仍是要在.m文件汇总import这个类的。
既然这样,为何不直接在头文件中import呢,举个例子:
class A引用了class B,class B引用了class C…. , class A,B,C…的头文件又import了不少文件,那么 import了A的话,编译器就须要编译大量的文件,编译时间就会增长。并发
难道头文件中都是用@class吗?固然不是,有时也是须要#import的,那么何时该用什么呢?
(1)通常若是有继承关系的用#import,如B是A的子类那么在B中声明A时用#import;异步
(2) 另外就是若是有循环依赖关系,如:A->B,B->A这样相互依赖时,若是在两个文件的头文件中用#import分别声明对方,那么就会出现头文件循环利用的错误,这时在头文件中用@class声明就不会出错;async
(3)还有就是自定义代理的时候,若是在头文件中想声明代理的话如@interface SecondViewController:UIViewController时应用#import否则的话会出错误,注意XXXXDelegate是自定义的。编辑器
区别:模块化
当咱们在代码中使用两次#include的时候会报错:由于#include至关于拷贝头文件中的声明内容,因此会报重复定义的错误
可是使用两次#import的话,不会报错,因此他能够解决重复导入的问题,他会作一次判断,若是已经导入一次就不导入了
2、#import与@class的区别
1.import会包含这个类的全部信息,包括实体变量和方法,而@class只是告诉编译器,其后面声明的名称是类的名称,至于这些类是如何定义的,暂时不用考虑,后面会再告诉你。
2.在头文件中, 通常只须要知道被引用的类的名称就能够了。 不须要知道其内部的实体变量和方法,因此在头文件中通常使用@class来声明这个名称是类的名称。 而在实现类里面,由于会用到这个引用类的内部的实体变量和方法,因此须要使用#import来包含这个被引用类的头文件。
3.在编译效率方面考虑,若是你有100个头文件都#import了同一个头文件,或者这些文件是依次引用的,如A–>B, B–>C, C–>D这样的引用关系。当最开始的那个头文件有变化的话,后面全部引用它的类都须要从新编译,若是你的类有不少的话,这将耗费大量的时间。而是用@class则不会。
4.若是有循环依赖关系,如:A–>B, B–>A这样的相互依赖关系,若是使用#import来相互包含,那么就会出现编译错误,若是使用@class在两个类的头文件中相互声明,则不会有编译错误出现。
因此,通常来讲,@class是放在interface中的,只是为了在interface中引用这个类,把这个类做为一个类型来用的。 在实现这个接口的实现类中,若是须要引用这个类的实体变量或者方法之类的,仍是须要import在@class中声明的类进来.
举个例子说明一下。
在ClassA.h中
若是
只是 ClassB 那就没有include ClassB.h。仅须要在须要用到ClassB的.m文件中 #import ClassB.h
那么何时能够用呢?
若是ClassA.h中仅须要声明一个ClassB的指针,那么就能够在ClassA.h中声明
@ClassB
…
ClassB *pointer;
假设,有两个类:ClassA和ClassB,两个之间相互使用到,即构成了circular dependency(循环依赖)。若是在头文件里面只用#import把对方的头文件包含进来(构成circular inclusions,循环包含),则编译器会报错:
Expected specifier-qualifier-list before ‘ClassA’
或者
Expected specifier-qualifier-list before ‘ClassB’
为了不循环包含,在ClassA.h文件里面用@class classB把classB包含进来,一样,在ClassB.h文件里面用@class ClassA把ClassA包含进来。@class指令只是告诉编译器,这是个类,保留个空间来存放指针就能够了。
接下来,极可能在ClassA.m和ClassB.m中会有访问包含进来对象的成员的状况,这时必须让编译器知道更多信息,好比那个类有些什么方法能够调用,就必须用#import,再次把用到的类包含进来,告诉编译器所须要的额外信息。
不然,编译器会警告:
warning: receiver ‘ClassA’ is a forward class and corresponding @interface may not exist
还有另外一种状况,使用有Categories的类,要在.h头文件里用#import把Categories包含进来。
总之,使用原则是:
头文件里面只#import超类 消息文件里面#import须要发消息过去的类 其余地方就用@class转向声明
import会包含这个类的全部信息,包括实体变量和方法(.h文件中),而@class只是告诉编译器,其后面声明的名称是类的名称,至于这些类是如何定义的,后面会再告诉你。
在头文件中, 通常只须要知道被引用的类的名称就能够了。 不须要知道其内部的实体变量和方法,因此在头文件中通常使用@class来声明这个名称是类的名称。 而在实现类里面,由于会用到这个引用类的内部的实体变量和方法,因此须要使用#import来包含这个被引用类的头文件。
一.通俗的说:
copy, mutableCopy
@interface A {
B *b;
}
浅拷贝只是拷贝对象自己,不会对里面的子对象进一步拷贝
深拷贝会对子对象以及子对象的子对象进一步拷贝
二.深刻理解:
浅拷贝并不复制数据,只复制指向数据的指针,所以是多个指针指向同一份数据。 深拷贝会复制原始数据,每一个指针指向一份独立的数据。经过下面的代码, 能够清楚地看出它们的区别:
struct Test{
char ptr;
};
void shallow_copy(Test &src, Test &dest){
dest.ptr = src.ptr;
}
void deep_copy(Test &src, Test &dest){
dest.ptr = (char)malloc(strlen(src.ptr) + 1);
memcpy(dest.ptr, src.ptr);
}
浅拷贝在构造和删除对象时容易产生问题,所以使用时要十分当心。如无必要, 尽可能不用浅拷贝。当咱们要传递复杂结构的信息,而又不想产生另外一份数据时, 可使用浅拷贝,好比引用传参。浅拷贝特别须要注意的就是析构时的问题, 当多个指针指向同一分内存时,删除这些指针将致使屡次释放同一内存而出错。
实际状况下是不多使用浅拷贝的,而智能指针是浅拷贝概念的加强。 好比智能指针能够维护一个引用计数来代表指向某块内存的指针数量, 只有当引用计数减至0时,才真正释放内存。
大部分时候,咱们用的是深拷贝,特别是当拷贝的结构不大的时候。
1)在ARC中,在有可能出现循环引用的时候,每每要经过让其中一端使用weak来解决,好比:delegate代理属性
2)自身已经对它进行一次强引用,没有必要再强引用一次,此时也会使用weak,自定义IBOutlet控件属性通常也使用weak;固然,也可使用strong。在下文也有论述:《IBOutlet连出来的视图属性为何能够被设置成weak?》
二.不一样点:
1)weak 此特质代表该属性定义了一种“非拥有关系” (nonowning relationship)。为这种属性设置新值时,设置方法既不保留新值,也不释放旧值。此特质同assign相似, 然而在属性所指的对象遭到摧毁时,属性值也会清空(nil out)。 而 assign 的“设置方法”只会执行针对“纯量类型” (scalar type,例如 CGFloat 或 NSlnteger 等)的简单赋值操做。
2)assigin 能够用非OC对象,而weak必须用于OC对象
@property 的本质是什么?
@property = ivar + getter + setter;
下面解释下:
“属性” (property)有两大概念:ivar(实例变量)、存取方法(access method = getter + setter)。
“属性” (property)做为 Objective-C 的一项特性,主要的做用就在于封装对象中的数据。 Objective-C 对象一般会把其所须要的数据保存为各类实例变量。实例变量通常经过“存取方法”(access method)来访问。其中,“获取方法” (getter)用于读取变量值,而“设置方法” (setter)用于写入变量值。这个概念已经定型,而且经由“属性”这一特性而成为Objective-C 2.0的一部分。 而在正规的 Objective-C 编码风格中,存取方法有着严格的命名规范。 正由于有了这种严格的命名规范,因此 Objective-C 这门语言才能根据名称自动建立出存取方法。其实也能够把属性当作一种关键字,其表示:
编译器会自动写出一套存取方法,用以访问给定类型中具备给定名称的变量。 因此你也能够这么说:
@property = getter + setter;
例以下面这个类:
@interface Person : NSObject
@property NSString firstName;
@property NSString lastName;
@end
上述代码写出来的类与下面这种写法等效:
@interface Person : NSObject
(NSString )firstName;
(void)setFirstName:(NSString )firstName;
(NSString )lastName;
(void)setLastName:(NSString )lastName; @end ivar、getter、setter 是如何生成并添加到这个类中的?
“自动合成”( autosynthesis)
完成属性定义后,编译器会自动编写访问这些属性所需的方法,此过程叫作“自动合成”( autosynthesis)。须要强调的是,这个过程由编译 器在编译期执行,因此编辑器里看不到这些“合成方法”(synthesized method)的源代码。除了生成方法代码 getter、setter 以外,编译器还要自动向类中添加适当类型的实例变量,而且在属性名前面加下划线,以此做为实例变量的名字。在前例中,会生成两个实例变量,其名称分别为 _firstName与_lastName。也能够在类的实现代码里经过 @synthesize语法来指定实例变量的名字.
@implementation Person
@synthesize firstName = _myFirstName;
@synthesize lastName = myLastName;
@end
我为了搞清属性是怎么实现的,曾经反编译过相关的代码,大体生成了五个东西:
1)OBJCIVAR$类名$属性名称 :该属性的“偏移量” (offset),这个偏移量是“硬编码” (hardcode),表示该变量距离存放对象的内存区域的起始地址有多远。
2)setter与getter方法对应的实现函数
3)ivar_list :成员变量列表
4)method_list :方法列表
5)prop_list :属性列表
也就是说咱们每次在增长一个属性,系统都会在ivar_list中添加一个成员变量的描述,在method_list中增长setter与getter方法的描述,在属性列表中增长一个属性的描述,而后计算该属性在对象中的偏移量,而后给出setter与getter方法对应的实现,在setter方法中从偏移量的位置开始赋值,在getter方法中从偏移量开始取值,为了可以读取正确字节数,系统对象偏移量的指针类型进行了类型强转.
补充:
Objective-C运行时定义了几种重要的类型。
Class:定义Objective-C类
Ivar:定义对象的实例变量,包括类型和名字。
Protocol:定义正式协议。
objc_property_t:定义属性。叫这个名字多是为了防止和Objective-C 1.0中的用户类型冲突,那时候尚未属性。
Method:定义对象方法或类方法。这个类型提供了方法的名字(就是选择器)、参数数量和类型,以及返回值(这些信息合起来称为方法的签名),还有一个指向代码的函数指针(也就是方法的实现)。
SEL:定义选择器。选择器是方法名的惟一标识符。
IMP:定义方法实现。这只是一个指向某个函数的指针,该函数接受一个对象、一个选择器和一个可变长参数列表(varargs),返回一个对象
dispatch_queue_t queue = dispatch_get_main_queue(); for (int i =0;i<10;i++){ dispatch_async(queue,^{ [self longtimeOperation:i]; }); }