OC知识点碎片
- #import 有两个用途,第一个用途:与 C 语言中的 include 完全一样。第二个用途:可以自动防止文件内容被 重复拷贝,也就是说多次书写#import <Foundation/NSObjCRuntim.h>就只拷贝一次,相当于只写了一次。
- OC 与 C 语言完全兼容,其入口也为 main 函数,定义方法与 C 语言完全一样。
-
Object C 的代码中可以混入 C 语言代码,Object C 文 件后缀名为.m,把其后缀名改为.mm 还可以混入 C++代码
- 1、对象方法
(1) 减号 - 开头
(2)只能由对象来调用
(3)对象方法中能访问当前对象的成员变量(实例变量)
2、类方法
(1) 加号 + 开头
(2) 只能由类(名)来调用
(3) 类方法中不能访问成员变量(实例变量)
3、类方法的好处和使用场合
(1) 不依赖于对象,执行效率高
(2) 能用类方法,尽量用类方法
(3) 场合:当方法内部不需要使用到成员变量时,就可以改为类方法
注:可以允许类方法和对象方法同名,不能使用指针去访问类方法,会报错。类方法和成员(对象)方法可以同名
- self 的用途:
(1) 谁调用了当前方法,self 就代表谁
* self 出现在对象方法中,self 就代表对象
* self 出现在类方法中,self 就代表类
(2) 在对象方法利用"self->成员变量名"访问当前对象内部的成员变量
(3) [self 方法名]可以调用其他对象方法\类方法
- 使用 self 与 super 调用方法的区别:
使用 self 调用方法时,self 先从当前类中寻找方法,如果没有寻找到再去父类中寻找。而 super 直接在父类中 寻找方法。
注:super指向的是self,比如 [self class] 和 [super class]获取的class是同一个
- 继承的好处:
(1) 抽取重复代码
(2) 建立了类之间的关系
(3) 子类可以拥有父类中的所有成员变量和方法
2、注意点
注: 基本上所有类的根类是 NSObject
- 多态
多态就是用父类类型的指针创建子类的对象,比如 Dog 类继承了 Animal 类,下面都是多态: Animal *aa = [Dog new];
Dog *dd = (Dog *)aa;
1.没有继承就没有多态
2.代码的体现:父类类型的指针指向子类对象
3.好处:如果函数\方法参数中使用的是父类类型,可以传入父类、子类对象
4.局限性:
父类类型的变量不能直接调用子类特有的方法。必须强转为子类类型变量后,才能直接调用子类特有的方法
- @property 的参数:
(1) set 方法内存管理相关的参数
(a) retain:release 旧值,retain 新值(适用于 OC 对象类型)
(b) assign:直接赋值(默认,适用于非 OC 对象类型),常用在 BOOL、int 等基本数据类型。
(c) copy:release 旧值,copy 新值,常用在 NSString 对象
(2) 是否要生成 set 方法
(a) readwriter:同时生成 set 和 get 方法的声明和实现
(b) readonly:只生成 get 的声明和实现
(3) 多线程管理
(a) nonatomic:性能高(一般都是使用这个)
(b) atomic:性能低(默认)
(4) set 和 get 方法的名称
(a) setter:决定了 set 方法的名称,一定要加个冒号“:“
(b) getter:决定了 get 方法的名称,不要加冒号
(5) strong 和 weak
(a) strong:用于一般对象
(b) weak:用于 UI 控件
- 内存管理
在内存中,分为堆和栈,栈中主要存放变量,堆中主要存放对象。栈中的东西是系统自动回收的,当一个变 量使用完毕后,
存放在栈中的东西会立刻被回收。但堆中存储的东西是不会随便回收的。
每个 OC 对象里都有一个引用计数器,是一个整数,用来统计正在被引用多少次,每个引用计数器占用 4 个字 节,对象刚刚创建时引用计数器默认为 1。
如果 OC 对象引用计数器为 0 时,系统就可以回收这个对象了。
引用计数器的操作:
(1) 给对象发送一条 retain 消息,可以使引用计数器值+1(retain 方法返回对象本身)
(2) 给对象发送一条 release 消息,可以使引用计数器值-1
(3) 给对象发送一条 retainCount 消息,可以获得当前的引用计数器值
对象的销毁:
(1) 当一个对象的引用计数器的值为 0 时,那么它将被销毁,其占用的内存会被系统回收。
(2) 当一个对象被销毁时,系统会自动向对象发送一条 dealloc 消息
(3) 一般会重写 dealloc 方法,在这里释放相关资源,dealloc 就像对象的遗言。
(4) 一旦重写了 dealloc 方法,就必须调用[super dealloc],并且放在最后面调用。
(5) 不要直接调用 dealloc 方法。
概念:
僵尸对象:所占用的内存已经被回收的对象,僵尸对象不能再使用
野指针:指向僵尸对象的指针,给野指针发送消息会报错
空指针:没有指向任何东西的指针(存储的东西是 nil、null、0),给空指针发送消息不会报错。
【备注】nil 与 null 不同,null 是一个宏定义,值为 0,nil 表示无值,任何变量在没有赋值之前都是 nil,对于
真假判断,只有 nil 和 false 表示假,其他均为真。
内存管理原则:
(1)谁创建,谁 release:
如果你通过 alloc、new 或[mutable]copy 来创建一个对象,那么必须调用 release 或 autorelease
(2)谁 retain,谁 release:
只要你调用了 retain,无论这个对象如何生成的,你都要调用 release。
综上:有始有终,有加就有减。曾经让对象计数器+1,就必须在最后让对象计数器-1
- ARC
1、ARC 判断准则: 只要没有强指针指向对象,就会释放对象。就算此时仍有弱指针指向该对象或者该对象还指向其他对象,只 要没有强指针指向它就会释放对象。系统还会根据弱指针的情况及时释放弱指针对象,避免野指针的产生。 指针分两种:
(1)强指针:默认情况下,所有指针都是强指针 (_strong), 也可将 strong 作为参数传给@property
(2)弱指针:定义一个指针是弱指针,只需在定义时用_weak 声明即可,比如:_weak Person *p = [[Person alloc]
init];也可将 weak 作为参数传给@property
2、ARC 特点:
(1) 不允许调用 release、retain、retainCount
(2) 允许重写 dealloc,但是不允许调用[super dealloc]
(3) @property 的参数
* strong :成员变量是强指针(适用于 OC 对象类型)
* weak :成员变量是弱指针(适用于 OC 对象类型)
* assign : 适用于非 OC 对象类型
(4) 以前的 retain 改为用 strong
【备注】在实际项目中有这种需求,某些文件需要用到 release、retain 方法,比如下载的第三方框架中如果 有 release 等方法,放到支持 ARC 的编译环境中肯定会报错。这时我们可以设置这些文件不使用 ARC 而项目中 其他文件继续使用 ARC,方法很简单,选择项目的 Bulild phases,在 Compile Sources 中选择不需要 ARC 的文 件,双击或按回车,在弹出的窗口中输入-fno-objc-arc 即可。同样也可以在非 ARC 的环境中输入-f-objc-arc 使 相关文件支持 ARC。如下图:
- Block
语法格式如下:
返回值类型 (^Block 名称)()= ^{ //代码块部分 };
比如定义一个无参数、无返回值、名称为 myblock 的 Block:
void (^myblock)()= ^{ NSLog(@”-------------”); };
等价于:
void (^myblock)(); myblock = ^{ NSLog(@”-------------”); };
调用代码: myblock();
- Block 和方法很像,Block 特点:
(1)可以保存代码
(2)可以有返回值
(3)可以有形参
(4)调用方式和调用方法一样
(5)可以像方法一样调用外面的变量
(6)默认条件下,Block 不能修改外面的局部变量,但是在变量名前加上_block 就可以使 Block 可以修改这
个变量。比如:
_block int b = 20;
但是 Block 可以修改全局变量。
(7)实际开发中,常用 typedef 定义 Block 以减少代码量,比如:
typedef int (*MyBlock)(int, int); 以后再定义 Block 就可以:
MyBlock sumBlock = ^(int a,intb){ return a + b;
}
【备注】当 block 用到外面的东西时候,要把用的的外面的那个东西声明为弱指针,否则会发生循环引用的
问题。声明方法:
_weak ViewController *vc = self;//可以避免野指针,推荐使用
或者:
_unsafe_unretained ViewController *vc = self; 以后在代码块里调用的 vc 就以弱指针的方式调用。
-
protocol(协议)
1、基本用途:
(1) 可以用来声明一大堆方法(不能声明成员变量)(2) 只要某个类遵守了这个协议,就相当于拥有这个协议中的所有方法声明
(3) 只要父类遵守了某个协议,就相当于子类也遵守了
(4) 和 java 中的接口很相似,但比接口功能更丰富
2、格式(1)协议的编写
@protocol 协议名称
//方法声明列表
#end
(3) 某个类遵守协议
@interface 类名:父类 <协议名称>
@end
3、遵守协议:
首先要#import “协议文件名.h 或者@protocol 协议名称,但是使用@protocol 协议名称时只是告诉下面代码这 是一个协议,但不知道协议里面有什么东西,一般只在用到协议内容的时候在#import “协议文件名.h。在实际 开发中常用@protocol 协议名称。
(1) 类遵守协议@interface 类名 : 父类名 <协议名称 1, 协议名称 2>
@end
(2) 协议遵守协议
@protocol 协议名称 <其他协议名称 1, 其他协议名称 2>@end
4、协议中方法声明的关键字
(1) @required (默认)
要求实现,如果没有实现,会发出警告,但不报错
(2) @optional
不要求实现,不实现也不会有警告 例如:
@require
- (void)test;@optional
- (void)test2;
9、基协议
(1)NSObject 是一个基类,是最根本最基本的类,任何其他类最终都要继承它
(2)其实还有一个协议,名字也叫 NSObject,它是一个基协议,最根本最基本的协议
(3)NSObject 中声明了很多最基本的方法,比如 description、retain、release 等
(4)建议每个新协议都要遵守 NSObject 协议
-
Category(分类)
Category 又叫分类、类别、类目。
分类的作用: 在不改变原来类内容的基础上,可以为类增加一些方法 使用方法://声明
@interface 类名(分类名称)@end
//实现
@implementation 类名(分类名称)@end
使用注意:
1.分类只能增加方法,不能增加成员变量
2.分类方法实现中可以访问原来类中声明的成员变量3.分类可以重新实现原来类中的方法,但是会覆盖掉原来的方法,会导致原来的方法没法再使用
4.当分类、原来类、原来类的父类中有相同方法时,方法调用的优先级:分类(最后参与编译的分类优先) --> 原
来类 --> 父类,即先去调用分类中的方法,分类中没这个方法再去原来类中找,原来类中没有再去父类中找。
- Foundation 之 NSRange
NSRange 原型:
typedef struct _NSRange{ unsigned int location; unsigned int length; }NSRange;
NSRange 含义:
(1)一个结构体,表示相关事物的范围
(2)location 字段为该范围的起始位置
(3)length 字段为该范围内所含元素的个数
创建方式:
1)直接给字段赋值
(2)应用 C 语言的聚合结构赋值机制
(3)使用 cocoa 提供的快捷函数 NSMakeRange()
比如,从一段字符串中截取 LFF 并输出:
NSString *test = @”My name is LFF”;
NSRange range = NSMakeRange(12,3);
NSLog(@”截取的字符为:%@”,
[test substringWithRange:range]);
上述代码 NSRange range = NSMakeRange(12,3)还有很多等价写法: 常用等价写法 1—直接给字段赋值:
NSRange range; range.location = 12;
range.length = 3;
(4)常用等价写法 2—C 语言的聚合结构赋值机制:
NSRange range = {12,3};
【备注】 NSStringFromRange 可以将上面的结构体转化成字符串,例如:
NSString *test2 = NSStringFromRange(range);
- Foundation 之 NSPoint/NSRect/NSSize
NSPoint/NSRect/NSSize 的用法和 NSRange 都一样。
NSPoint 实际上就是 CGPoint
NSRect 实际上就是 CGRect
NSSize 实际上就是 CGSize
- 程序启动的完整过程:
- (1)main 函数
(2)UIApplicationMain
* 创建 UIApplication 对象
* 创建 UIApplication 的 delegate 对象
(3)delegate 对象开始处理(监听)系统事件(没有 storyboard)
* 程序启动完毕的时候, 就会调用代理的 application:didFinishLaunchingWithOptions:方法
* 在 application:didFinishLaunchingWithOptions:中创建 UIWindow
* 创建和设置 UIWindow 的 rootViewController
* 显示窗口
(3)根据 Info.plist 获得最主要 storyboard 的文件名,加载最主要的 storyboard(有 storyboard) * 创建 UIWindow
* 创建和设置 UIWindow 的 rootViewController
* 显示窗口
(4)UIApplicationMain
main 函数中执行了一个 UIApplicationMain 这个函数:
int UIApplicationMain(int argc, char *argv[], NSString
*principalClassName, NSString *delegateClassName);
各参数含义:
argc、argv:直接传递给 UIApplicationMain 进行相关处理即可。
principalClassName:指定应用程序类名(app 的象征),该类必须是 UIApplication(或子类)。如果为 nil,则用
UIApplication 类作为默认值。
delegateClassName:指定应用程序的代理类,该类必须遵守 UIApplicationDelegate 协议。
UIApplicationMain 函数会根据 principalClassName 创建 UIApplication 对象,根据 delegateClassName 创建一个
delegate 对象,并将该 delegate 对象赋值给 UIApplication 对象中的 delegate 属性。
接着会建立应用程序的 Main Runloop(事件循环),进行事件的处理(首先会在程序完毕后调用 delegate 对象的
application:didFinishLaunchingWithOptions:方法)。
程序正常退出时 UIApplicationMain 函数才返回。