#import与@class
与C语言一样,ObjecTive-C使用头文件来包含结构体、符号常量和函数原型等元素的声明。在C语言中使用#include语句来通知编译器查询头文件中相应的定义代码。在ObjecTive-C也可以使用#include来达到同样的目的。不过通常情况下在ObjecTive-C使用#import语句来完成。#import是由Xcode编译器提供的,Xcode在编译ObjecTive-C、C和C++程序时都会使用它。#import可以保证头文件只被包含一次,无论此命令在该文件中出现了多少次。
例如通知编译器查找Foundation框架中的Foundation.h头文件:
#import <Foundation/Foundation.h>
与#import会包含这个类的所有信息,包括实体变量和方法不同,@class只告知编译器其后面声明的名称是类的名称,但不考虑这个类是如何定义的,一般来说,@class是放在interface中的,只是为了在interface中引用这个类,把这个类作为一个类型来用的。
NS前缀
Cocoa为其所有函数、常量、和类型名称都添加了NS前缀,这个前缀告诉我们函数来自Cocoa而不是其它的工具包。使用前缀可以避免命名冲突(即两个不同的事物使用相同的标识符会引发错误)。NS前缀的来历要追溯自此工具包还被称为NextSTEP的时候,当时它是NeXT Software公司(前NeXT公司,与1996年被苹果公司收购)的产品。苹果公司没有破坏为NextSTEP编写的代码的兼容性,继续使用NS前缀。
interface与implementation
ObjecTive-C中的类必须包括两部分,interface部分和implementation部分,这才是ObjecTive-C中的一个类的完整声明。ObjecTive-C中将成员变量和成员方法的声明部分放置在interface部分中,包括继承关系,protocal实现关系,都在interface里面的头部进行声明,然后将实现部分放置在implementation部分中,相当于是将类拆分成声明和实现两部分,这两部分缺一不可。
@property与@synthesize:
@property与@synthesize是成对出对出现的,可以自动生成某个类成员变量的存取方法。在Xcode4.5以及以后的版本,@synthesize可以省略。
atomic与nonatomic:
是原子性的,系统生成的 getter/setter 会保证 get、set 操作的完整性,可以保证读/写安全,但不能保证线程安全,因为其他线程还可以进行读写之外的其他操作。默认属性。
非原子的,相比atomic有更快的读写速度,但多线程下不保证安全,非默认属性。
由于atomic的机制比较耗费系统资源,所以在iPhone这种小型设备上,如果没有使用多线程间的通讯编程,那么nonatomic是一个非常好的选择。
readwrite与readonly:
这个属性是默认的情况,会自动为你生成存取器。
只读的,只生成getter不会有setter方法。
这两个属性不仅提供成员变量访问接口,而且控制成员变量的访问权限。
copy和mutableCopy
浅拷贝,不拷贝对象本身,仅仅是拷贝指向对象的指针。但修改源对象或修改copy后得到副本对象的值,两者不会互相影响。因为当修改源对象或修改copy后得到副本对象的值时,系统会从新开辟一块内存空间。用copy修饰的或者赋值的,变量都会变为是不可变的。即使声明为可变的。用copy赋值,如果源对象是可变的,拷贝指针与对象到另一块内存空间,如果源对象是不可变的,只拷贝指针。
深拷贝,是直接拷贝整个对象内存到另一块内存中。mutableCopy只能用于赋值,无法修饰属性。
strong与weak:
强引用,在ARC环境下,只要某一对象被一个strong指针指向,该对象就不会被销毁。如果对象没有被任何strong指针指向,那么就会被销毁。在默认情况下,所有的实例变量和局部变量都是strong类型的。
弱引用,所指向的对象销毁后,weak型指针会自动变成nil指针(空指针),不再指向已经销毁的对象。weak只能用于修饰对象类型,基本数据类型不能使用。一般用于处理循环引用、Deleagte。
assign
assign : 简单赋值,不更改索引计数,可修饰基本数据类型和对象,如果修饰对象,会产生野指针问题;如果修饰基本数据类型则是安全的。修饰的对象释放后,指针不会自动被置空,此时向对象发消息会崩溃。
SEl与IMP与Method
定义为 typedef struct objc_selector *SEL,代表方法的名称。仅以名字来识别。翻译成中文叫做选择子或者选择器,选择子代表方法在 Runtime期间的标识符。为 SEL类型,虽然 SEL是 objc_selector 结构体指针,但实际上它只是一个 C 字符串。在类加载的时候,编译器会生成与方法相对应的选择子,并注册到 Objective-C的 Runtime 运行系统。不论两个类是否存在依存关系,只要他们拥有相同的方法名,那么他们的SEL都是相同的。比如,有n个viewcontroller页面,每个页面都有一个viewdidload,每个页面的载入,肯定都是不尽相同的。但是我们可以通过打印,观察发现,这些viewdidload的SEL都是同一个,因此类方法定义时,尽量不要用相同的名字,就算是变量类型不同也不行,否则会引起重复。例如:
// 方法名字
SEL sel = @selector(methodName);
// log输出为 address = 0x1df807e29
NSLog(@"address = %p",sel);
定义为typedef id (*IMP)(id, SEL, ...),代表函数指针,保存了方法的地址,即函数执行的入口。该函数使用标准的 C调用。第一个参数指向 self(它代表当前类实例的地址,如果是类则指向的是它的元类),作为消息的接受者;第二个参数代表方法的选择子;... 代表可选参数,前面的 id 代表返回值。
每一个继承于NSObject的类都能自动获得runtime的支持。在这样的一个类中,有一个isa指针,指向该类定义的数据结构体,这个结构体是由编译器编译时为类(需继承于NSObject)创建的.在这个结构体中有包括了指向其父类类定义的指针以及 Dispatch table. Dispatch table是一张SEL和IMP的对应表。(http://blog.csdn.net/fengsh998/article/details/8614486)
也就是说方法编号SEL最后还是要通过Dispatch table表寻找到对应的IMP,IMP就是一个函数指针,然后执行这个方法
定义为typedef struct objc_method *Method,Method对开发者来说是一种不透明的类型,被隐藏在我们平时书写的类或对象的方法背后。它是一个objc_method结构体指针,我们可以看到该结构体中包含一个SEL和IMP,实际上相当于在SEL和IMP之间作了一个映射。有了SEL,我们便可以找到对应的IMP,从而调用方法的实现代码。 objc_method的定义为:
/// Method
struct objc_method {
//方法名 method_name 类型为 SEL,相同名字的方法即使在不同类中定义,它们的方法选择器也相同。
SEL method_name;
//方法类型 method_types 是个 char 指针,其实存储着方法的参数类型和返回值类型,即是 Type Encoding 编码。
char *method_types;
//method_imp 指向方法的实现,本质上是一个函数的指针
IMP method_imp;
};
_cmd
_cmd在Objective-C的方法中表示当前方法的selector,正如同self表示当前方法调用的对象实例。例如:
///是否允许交互式弹出
- (BOOL)fd_interactivePopDisabled
{
//加此行代码是为了防止有输入框并且输入框为第一响应者的情况下,侧滑或者左上角返回上一页面当前页面UI下移动问题(lyutn)
[self.view endEditing:YES];
//_cmd即代表名为fd_interactivePopDisabled的selector
return [objc_getAssociatedObject(self, _cmd) boolValue];
}
///设置是否允许交互式弹出
- (void)setFd_interactivePopDisabled:(BOOL)disabled
{
objc_setAssociatedObject(self, @selector(fd_interactivePopDisabled), @(disabled), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
nullable和nonnull
表示修饰的属性或参数可以为空。
非空,表示修饰的属性或参数不能为空。
id 和 instancetype
id 和 instancetype 都是万能指针,都能指向一个对象。区别在于id 在编译时候不能判断对象的真实类型,而在运行时检查类型。instancetype 编译的时候可以判断对象真实类型,如果类型指错,instancetype可以告警,我们应该尽量把告警暴露在编译的时候。id可以作为方法的参数,但instancetype不可以,instancetype只适用于初始化方法和便利构造器的返回值类型。
release
release是NSobject类的一个方法。它不会立即释放内存,它只是减少对象的一个引用计数。一直到它检测到0,然后调用-dealloc()。
+load()与+initialize()两者的区别?
方法会在main()函数调用前就调用,而+initialize()是在类第一次使用时才会调用。+load方法的调用优先级: 父类 > 子类 > 分类,并且不会被覆盖,均会调用。+load方法是在main() 函数之前调用,所有的类文件都会加载,包括分类也会加载。
方法的调用优先级:分类 > 子类,父类 > 子类。(父类的分类重写了+initialize方法会覆盖父类的+initialize方法)
什么是类?什么是对象?
是一种抽象的概念,代表着一种类型/类别,代表着一类个体。基于多个对象抽出,类是对象的模版。
对象是类的具体实现,真实存在的单个个体,执行着具体的行为 。
一个NSObject对象占有多少内存?
系统分配了16个字节给NSObject对象(通过malloc_size函数获得),但是NSObject对象内部只使用了8个字节的空间(64bit环境下,可以通过class_getInstanceSize函数获得)。
对象的isa指针的指向哪里?
instance对象的isa指向class对象。
class对象的isa指向meta-class对象。
meta-class对象的isa指向基类的meta-class对象。
OC的类信息存放在哪里?
对象方法,属性,成员变量,协议信息,存放在class对象中。
类方法存放在meta-class对象中。
成员变量的具体值存放在instance对象中。
pt 与px
IOS开发中用到的抽象单位,即point(点),是独立像素的意思,它是绝对长度,不随屏幕像素密度变化而变化。例如iPhone 4中 1pt 对应 2px,iPhone 6Plus为3px=1pt。
像素,物理屏幕显示的基本单位,即使在程序中使用的不是px,但最后都会转化为px,显示在手机上。在不同像素密度的屏幕里面,像素本身大小是不一样的。
循环中的continue 与 break 与return
结束本次循环,进入下次循环。循环完毕后执行循环体之后的代码。
结束循环体,执行循环体之后的代码。
结束循环体,并结束包含循环体的函数,循环体之后的代码不会执行。
- (void)viewDidLoad {
[super viewDidLoad];
for (int i = 0; i < 10; i++) {
if (i == 5) {
continue;
}
NSLog(@"i = %d",i);
};
NSLog(@"不return我就会输出");
}
//continue : 5之后的数字仍会输出
//break : 数字只会输出到4
//return :数字只会输出到4,并且循环体之后的代码也不会执行
预处理指令
预处理指令是以#开头的代码行,#后是指令关键字,在关键字和#号之间允许存在任意个数的空白字符,这行语句构成了一条预处理指令。该指令将在编译器进行编译之前对源代码进行某些转换。
常用的预处理指令:
- #include 指令: 包含一个源代码文件。
- #define 指令: 定义宏。
- #undef 指令: 取消定义宏。
- #if 指令: 如果条件为真,则编译下面的代码。
- #elif 指令:如果前面的#if不为真,则编译下面的代码。
- #else 指令: 任意数量的 #elif 指令可以出现在 #if 和 #endif 指令之间,但最多允许一个 #else 指令。 #else 指令(如果有)必须是 #endif 之前的最后一个指令 。
- #endif 指令:结束一个#if...#elif条件编译块。
- #ifdef 指令:如果已经定义了某个宏,则编译下面的代码。
- #ifndef 指令:如果没有定义某个宏,则编译下面的代码。
- #error 指令:停止编译并显示错误信息。
- # 指令: 空指令,没有任何效果。
#define
宏定义,宏可以分为两类,一类称为对象宏,一类称为函数宏。通常可以使用的一些简单的宏定义去定义一些常量或者一个函数。
宏定义中的#、##、@#、\操作符的作用:
“#”:字符串化操作符,作用是:将宏定义中的传入参数名转换成用一对双引号括起来参数名字符串。使用条件:只能用于有传入参数的宏定义中,且必须置于宏定义体中的参数名前 。例如:
“##”:符号连接操作符,作用:将宏定义的多个形参名连接成一个实际参数名,使用条件:只能用于有传入参数的宏定义中,且必须置于宏定义体中的参数名前。例如:
@#:名称字符化操作符 作用:将传入的单字符参数名转换成字符,以一对单引用括起来。使用条件:只能用于有传入参数的宏定义中,且必须置于宏定义体中的参数名前。例如:
\ : 换行操作符,当定义的宏不能用一行表达完整时,可以用”\”表示下一行继续此宏的定义。注意:换行不能切断单词,只能在空格的地方进行。例如:
_OBJC_宏定义作用
在.pch 文件中一般都会自动加上这句宏定义,表示宏内引用的文件确保只被使用Objective-C语言的文件所引用,保证引用关系的清晰。因为在一个OC工程中,可能包含.m、.mm、.c、.cpp四类编译文件,这四类文件均会引用.pch预编译头。在编译.c、.cpp时,因为语法不兼容OC,所以预编译头中不能包含objc代码。因为.pch是2类源文件共用的,所以在pch中,oc头文件要用_OBJC_包含起来。
宏(Macro)
宏(Macro),是一种批量处理的称谓。计算机科学里的宏是一种抽象(Abstraction),它根据一系列预定义的规则替换一定的文本模式。解释器或编译器在遇到宏时会自动进行这一模式替换。对于编译语言,宏展开在编译时发生,进行宏展开的工具常被称为宏展开器。
extern
extern是计算机语言中的一个关键字,可置于变量或者函数前,以表示变量或者函数的定义在别的文件中。提示编译器遇到此变量或函数时,在其它模块中寻找其定义,另外,extern也可用来进行链接指定。
&
1.逻辑上表示and (和)的意思。A & B表示A、B两种元素缺一不可。
2.&还可以用作位运算符,当&操作符两边的表达式不是boolean类型时,&表示按位与操作,我们通常使用0x0f来与一个整数进行&运算,来获取该整数的最低4个bit位,例如,0x31 & 0x0f的结果为0x01。
&是二进制“与”运算时,参加运算的两个数的二进制按位进行运算,运算的规律是:
0 & 0=0
0 & 1=0
1 & 0=0
1 & 1=1
对于参加运算的数要换算为二进制进行运算,例如7 & 2的结果是2,过程如下:
7 & 2
=0111 & 0010
=0010
=2
即 &:按位与运算,两个当且仅当都为1的时候结果才为1,即1&1==1,1&0==0&1==0&0==0
typedef
typedef为C语言的关键字,作用是为一种数据类型定义一个新名字。这里的数据类型包括内部数据类型(int,char等)和自定义的数据类型(struct等)。通常用于给类型起别名(给已知的类型起别名)。常用于简化复杂类型,变量类型意义化等。
例如:
typedef double NSTimeInterval;//给double取别名为NSTimeInterval(变量类型意义化)
typedef struct FSCalendarCoordinate{
NSInteger row;
}MyFSCalendarCoordinate;//给FSCalendarCoordinate结构体取别名为MyFSCalendarCoordinate。
size_t
size_t是标准C库中头文件stddef.h定义的,size_t是针对系统定制的一种数据类型,一般是整型,里面保存的是一个整数,就像int, long那样。这种整数用来记录一个大小(size)。size_t的全称应该是size type,就是说“一种用来记录大小的数据类型”。
free()
free()是C标准库里面的,是一个函数。它调用malloc(),可以立即释放内存。因此它必须有malloc()传递一个指针,不然会引起异常。
double sqrt(double)
定义于C 标准库math.h 头文件中,计算一个非负实数的平方根。然后转换为指定的类型。
double pow(double, double)
定义于C 标准库math.h 头文件中,计算x的y次幂。如果x为负数且y为小数,返回nan(Not a Number,非数),或者x为0且y小于等于0,返回inf(infinity的简写,意义是无穷大),正常计算返回幂指数的结果。然后转换为指定的类型。
float roundf(float)
定义于C 标准库math.h 头文件中,对数值进行四舍五入取整。然后转换为指定的类型。
double ceil(double)
定义于C 标准库math.h 头文件中,返回不小于参数的最小整数值,即向上取整。然后转换为指定的类型。
double floor(double)
定义于C 标准库math.h 头文件中,返回不小于参数的最小整数值,即向下取整。然后转换为指定的类型。