OC总结
oc总结
1、id与instancetype
id万能指针,指向所有oc对象,可以作为方法返回值类型、函数参数使用。
instancetype只能使用在方法返回值类型处,说明返回值的类型为当前类的类型。
2、变量的作用域
public
private,私有的,只能在当前类中直接访问,在实现中声明的成员变量默认就是这种类型。
protected被保护的,可以在当前类和其子类中访问,在类声明中声明的成员变量默认就是这种类型。
3.property和synthesize
property代表set和get方法的声明与实现。
@property int age这句代码在类的实现中代表setter与getter的实现,相当于以下代码
- (void)setAge:(int)age - (void)age
{ {
_age = age; return _age;
} }
@property (retain)int book这句代码在类的实现中代表setter与getter的实现,相当于以下代码
- (void)setBook:(Book *)book
{ if(book != _book)
{ _book= book;
[_book release];
_book = [book retain];
}
}
synthesize age;表示实现成员变量名为age的getter方法
synthesize age = _age; 表示实现成员变量名为_age的getter方法
4、自定义对象方法,也就是说在初始化时给成员变量赋值。这种写法的格式为:
- (instancetype)initWithAge:(int)age
{
if (self = [super init]) { // 先初始化父类的成员变量
_age = age;
}
return self;
}
5、分类的好处:
1,可以使得原始类文件不至于过大,导致打开困难
2,可以将原始类中按照不同功能,分模块开发,降低开发的难度
3,有利于团队合作开发,缩短开发周期
6、万物皆对象,类的本质就是对象,其类型为Class
Person *p = [[Person alloc] init];
Class c1 = [p class]; // [p class] == Person
+load方法,会在程序启动时调用,来加载所有的类对象到内存,并且这个方法只会调用一次。加载的顺序:父类 --> 子类
+initialize方法,会在类对象第一次被使用(比如:利用类对象创建这种类型的对象)时调用,并且这个方法只会调用一次。
7、arc模式下导致的内存泄露(没有及时回收内存,造成内存浪费)
MRC下:循环引用的双方,一方使用retain,另一方使用assign
ARC下:循环引用的双方,一方使用strong,另一方使用weak
8、SEL:selector 选择器
是一种数据类型,是一个指针,保存是方法的地址
9、NSPredicate 过滤器:过滤掉集合中不符合条件的,返回符合条件的
10、深浅拷贝
深拷贝:拷贝出了一个新对象的拷贝,就称为深拷贝。(深拷贝一定是内容的拷贝)
浅拷贝:仅仅是指针的拷贝,一定不会创建出新对象。其实就只有不可变版本的不可变拷贝是浅拷贝
浅拷贝就好像人的影子,人完蛋了影子也没了,深拷贝就好像克隆出的一个人,人完蛋了不代表克隆人也完蛋
只要是可变字符串的拷贝【可变类型 copy或mutcopy】都是深拷贝
不可变字符串的mutcopy拷贝【不可变字符串 mutablecopy】是深拷贝
不可变字符串的copy拷贝【不可变字符串 copy】是浅拷贝,只拷贝了指针指向的地址
11、 KVC: key value coding 键值编码,其中的key表示对象的某个成员变量
提供了一种间接修改成员变量值的方式,只需要知道成员变量名对应的字符串就能够成功修改这个成员变量,而不需要有set和get方法
设置成员变量值的两种方式:
1> 键 setValue:forKey:
2> 键路径 setValue:forKeyPath: 这种方式更常用,因为包含了第一种方式的功能
setValue:forKey: 和 setValue:forKeyPath: 这两个方法执行的原理:
首先查看是否有这个成员变量的set方法,如果有,就调用来为这个成员变量赋值;如果没有,那么会直接去寻找成员变量(带有或不带有_均可)为找到的成员变量直接赋值。
访问成员变量值的两种方式:
1> 键 valueForKey:
2> 键路径 valueForKeyPath: 这种方式更常用,因为包含了第一种方式的功能
valueForKey: 和 valueForKeyPath: 这两个方法执行的原理:
首先查看是否有这个成员变量的get方法,如果有,就调用来为这个成员变量赋值;如果没有,那么会直接去寻找成员变量(带有或不带有_均可)为找到的成员变量直接赋值。
比较valueForKey和valueForKeyPath,比如人类有一个狗属性,狗又有一个name属性,使用kvc访问狗的name:
[p setValueForKey:@“dog.name”] // 报错!不能访问,因为p中并没有dog.name这个key
[p setValueForKeyPath:@“dog.name”] // path可以逐级访问,知道找到name这个属性。
12、KVO: key value observer 键值监听者,是对象之间通信的一种方式
当被监视的对象的某个属性(成员变量)值发生改变时,通知这个成员变量的监听对象
如果需要监听对象的属性值的改变,必须先注册
注意:
属性值的改变只有通过set方法,或者KVC来修改的方式,才会通知监听对象。
// 注册监听 (注册obs指向的对象成为p的_age这个成员变量的监听对象),显示属性发生改变的new值和old值(最新值、原来的值)
[p addObserver:obs forKeyPath:@"age" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:@"监听"];
当age这个成员变量的值发生改变时就后通知到监听他的对象obs,实现相应的方法:
/* 当被监听的对象的属性发生改变时,会自动调用的方法,来通知属性监听对象
* keyPath: 监听的属性名称
* object: 监听的是谁的
* change: 属性发生的改变
* context: 当初注册监听对象时,传入的参数
*/
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
调用dealloc方法,当对象销毁时应该同时移除监听者
[p removeObserve: obj forKeyPath:@“age”];
13、通知
一则通知的发布包含通知的发布者,通知的接收者和通知的名称
步骤:(案例:张三只接受腾讯发来的科技信息)
1)创建新闻发布者对象tencent
2)创建新闻接受者对象p.name = 张三
3)创建通知对象,包含通知的信息:科技类型的消息,org1机构发布的,信息的标题为iphone7,内容为。。。。
NSNotification *note = [NSNotification notificationWithName:@“tech_news” object:org1 userInfo:@{@"title" : @"iphone7", @"contents" : @"iphone7..."}];
4)创建通知中心,通知接受者要到通知中心注册他需要什么样的信息,以便消息一发布时他能第一时间接受到。
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
// p1向通知中心注册,p1接收name为科技新闻,发布机构为org1发布的信息,
// 消息传来时就调用newsProcess:方法执行相应的操作
[center addObserver:p1 selector:@selector(newsProcess:) name:@"tech_news" object:org1];
5) 由通知中心转发通知
[center postNotification:note]; // 直接将包装好的通知对象发布出去
注意:当监听对象释放时应该移除掉在通知中注册的对象,比如一个人死了,他在通知中心注册的信息就会被删除。
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self]; // 移除 调用-dealloc方法 的对象 在通知中心注册的所有通知
}
14、创建单例对象必须具备以下几点:
1)在类的实现文件中,定义一个静态全局对象,并且初始化为nil
2)声明和定义一个用来创建单例对象的类方法
3)调用的alloc方法必须是私有的
4)为了防止不合规范的做法,需要重写allocWithZone:, copyWithZone:, retain, release, retainCount, autorelease
如果按照规范,只需要提供一个类方法来创建一个单例对象。但是为了避免可能会调用其他方法来创建对象,为保证不管以任何方式创建的这个对象,在内存中只有一个,需要重写这些方法:allocWithZone:, copyWithZone:, retain, release, retainCount, autorelease
使用单例对象的好处
1)由于单利对象在内存中只创建一次,所以在某种程度上大大节省了内存的占用。
2)控制资源的使用,通过线程同步来控制资源的并发访问(还不理解)
3)作为通信媒介使用,也就是数据共享,它可以在不建立直接关联的条件下,让多个不相关的两个线程或者进程之间实现通信。比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息。这种方式简化了在复杂环境下的配置管理。
单例对象的缺点
1)不适用于变化的对象,如果同一类型的对象总是要在不同的用例场景发生变化,单例就会引起数据的错误,不能保存彼此的状态。
2)由于单利模式中没有抽象层,因此单例类的扩展有很大的困难。
3)单例类的职责过重,在一定程度上违背了“单一职责原则”。
4)滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为的单例类,可能会导 致共享连接池对象的程序过多而出现连接池溢出;如果实例化的对象长时间不被利用,系统会认为是垃圾而被回收,这将导致对象状态的丢失。
适用场景:
单例模式只允许创建一个对象,因此节省内存,加快对象访问速度,因此对象需要被公用的场合适合使用,如多个模块使用同一个数据源连接对象等等。如:
1.需要频繁实例化然后销毁的对象。
2.创建对象时耗时过多或者耗资源过多,但又经常用到的对象。
3.有状态的工具类对象。
4.频繁访问数据库或文件的对象。
以下都是单例模式的经典使用场景:
1.资源共享的情况下,避免由于资源操作时导致的性能或损耗等。如上述中的日志文件,应用配置。
2.控制资源的情况下,方便资源之间的互相通信。如线程池等。
应用场景举例:
1. Windows的Task Manager(任务管理器)就是很典型的单例模式
2. windows的Recycle Bin(回收站)也是典型的单例应用。在整个系统运行过程中,回收站一直维护着仅有的一个实例。
3. 网站的计数器,一般也是采用单例模式实现,否则难以同步。
4. 应用程序的日志应用,一般都何用单例模式实现,这一般是由于共享的日志文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加。
5. 数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库资源。数据库软件系统中使用数据库连接池,主要是节省打开或者关闭数据库连接所引起的效率损耗,这种效率上的损耗还是非常昂贵的,因为何用单例模式来维护,就可以大大降低这种损耗。
6. 多线程的线程池的设计一般也是采用单例模式,这是由于线程池要方便对池中的线程进行控制。

浙公网安备 33010602011771号