《Objective-C 2.0编写高质量iOS与OS X代码的52个有效方法》---学习笔记(21-30)

21. 理解OC错误模型

自动引用计数ARC在默认情况下不是“异常安全的”(exception safe)
具体来说,结果就是:如果抛出异常,本应该在作用域末尾释放的对象现在却不会自动释放了。
OC现在的办法是:只有在极其罕见的情况下抛出异常,抛出异常之后,无须考虑恢复问题,而且应用程序此时也应该推出。而不编写复杂的“异常安全代码”

非致命错误,一般是令方法返回nil/0,或者使用NSError。

NSError

NSError对象里封装了三条信息:

  • Error domain(错误范围,其类型为字符串)
    也就是错误的根源,通常用一个特有的全局变量来定义。
    比如NSURLErrorDomain
  • Error Code(错误码,其类型为整数)
  • User info(用户信息,其类型为字典)
    有关此错误的额外信息,其中或许包含一段“本地化的描述”,或者还包含导致该错误发送的原因,经由此种信息,可将相关错误串成一条“错误链”。

在设计API时,NSError的第一种常见用法是通过委托协议来传递此错误。
有错误发生时,当前对象会把错误信息经由协议中的某个方法传给其委托对象(delegate)

另外一种常见用法是:经由方法的”输出参数“返回给调用者。比如:
-(BOOL)doSomething:(NSError **)error
注意,传递给方法的参数是个指针(A),而该指针本身又指向另外一个指针(B),那个指针(B)指向NSError对象。
此方法不仅能有普通的返回值,而且可以把NSError对象回传给调用者。

NSError *error = nil;
BOOL ret = [object doSomething: &error];
if(error){
	//There was an error
}

22. 理解NSCopying协议

如果想令自己的类支持拷贝操作,那就要实现NSCopying协议,该协议只有一个方法:
-(id)copyWithZone:(NSZone *)zone

NSZone是啥?

在以前开发程序时,会把内存分成不同的”区“(zone),而对象会创建在某个区里面。
现在不用了,每个程序只有一个区:”默认区(default zone)“

  • copy方法由NSObject实现,该方法只是以”默认区“为参数来调用"copyWithZone:"
    我们想覆写copy方法,而copy方法的真正实现是"copyWithZone:"

  • 若某个类想支持拷贝功能,只需要声明该类遵从NSCopying协议,并实现其中的那个方法即可。

  • 如果自定义的对象分为可变版本,与不可变版本,那么就要同时实现NSCopying与NSMutableCopying协议

  • mutableCopy是来自另一个叫做NSMutableCopying的协议。该协议与NSCopying类似,也只定义了一个方法,方法名为:
    - (id)mutableCopyWithZone:(NSZone *)zone

  • 深拷贝(deep copy):在拷贝对象自身时,将其底层数据也一并复制过去

  • 浅拷贝(shallow copy):只拷贝指针
    Foundation框架中的collection类,默认情况下都是浅拷贝,只拷贝容器对象本身,不复制其中数据

  • 复制对象时需要决定采用浅拷贝还是深拷贝,一般情况下应该尽量执行浅拷贝。


第四章:协议与分类

OC有一项特性叫做:”协议“(protocol),它与Java的”接口“(interface)类似
OC不支持多重继承,因而我们把某个类应该实现的一系列方法定义在协议里面
协议最为常见的用途是实现 委托模式。

”分类“(Category)也是OC的一项重要语言特性。
利用分类机制,我们无须继承子类,即可直接为当前类添加方法

23. 通过委托与数据源协议进行对象间通信

问:对象之间如何通信?

OC广泛使用”委托模式“(Delegate pattern)的编程设计模式来实现对象间的通信

对象间通信的方法之一

定义一套接口,某对象(B)若想接受另一个对象(A)的委托,则需遵从此接口,以便成为其”委托对象“(delegate)。

委托对象:遵从接口协议的对象

而这”另一个对象(A)“则可以给其委托对象回传一些信息,也可以在发生相关事件时通知委托对象。

此模式可将 数据业务逻辑 解耦

假设有一个 一些列数据的视图
那么,图只应该包含显示数据所需要的代码,而不应决定要显示何种数据以及数据之间的交互问题。
视图对象的属性中,可以包含负责 数据事件处理 的对象
这两种对象分别称为:”数据源“(data source)与”委托“(delegate)

在OC中,一般通过”协议“这项语言特性来实现此模式

通过@protocol,来实现委托模式

委托协议名通常是在相关类名后面加上Delegate,整个类名采用”驼峰法“来写

delegete作为属性,其内存管理语言,需定义为weak

委托协议中的方法一般都是“可选的”(optional)

如果要在委托对象上调用可选方法,那么必须提前使用类型信息查询方法,判断这个委托对象能否响应相关选择子。

if ([_delegate respondsToSelector:@selector(xxx)]){
	[_delegate xxx];
}

respondsToSelector:来判断委托对象是否实现了相关方法。如果实现了就调用,如果没实现,就不执行任何操作。

  • 委托模式为对象提供了一套接口,使其可由此将相关事件告知其他对象
  • 将委托对象应该支持的接口定义成协议,在协议中把可能需要处理的事件定义成方法
  • 将某对象需要从另外一个对象中获取数据时,可以使用委托模式。这种情景下,该模式亦称“数据源协议”(data source protocal)

24. 将类的实现代码分散到便于管理的几个分类之中

在某些条件下,多用分类

在类的方法太多,导致实现文件太大,可以使用“分类”,把方法按逻辑划分到几个分区中
类的基本要素(属性与初始化方法等)都声明在“主实现”里,执行不同类型的操作所用的另外几套方法则归入各个分类中。

在编写准备分享给其他开发者使用的程序库时,可以考虑创建Private分类,以隐藏实现细节。

25. 总是为第三方类的分类名称加前缀

  • 分类中的方法是直接添加在类里面的,它们就好比这个类中的固有方法。
  • 将分类方法加入类中这一操作是运行期系统加载分类时完成的。
  • 运行期系统会把分类中所实现的每个方法都加入类的方法列表中。
  • 如果类中本来就有此方法,而分类又实现了一次,那么分类中的方法会覆盖原来那一份实现代码。
  • 实际上可能会发生很多次覆盖,比如:某个分类中的方法覆盖了“主实现”中的相关方法,而另外一个分类中的方法又覆盖了这个分类中的方法。多次覆盖的结果以最后一个分类为准。

这就会造成,你写的方法被其他分类覆盖,从而导致执行的不是你所想的那份代码

  • 为了解决这种问题,一般的做法是:以命名空间来区别各个分类的名称与其中所定义的方法。
    而OC中想实现命名空间功能,就是给相关名称加上某个公用的前缀。
    类似于:
    在这里插入图片描述

26. 勿在分类中声明属性

属性时封装数据的方式。从技术上说,分类里也可以声明属性,但这种做法要尽量避免。

关联对象能够解决在分类中不能合成实例变量的问题。
可行,但不理想。

理解:分类机制的目标是在于扩展类的功能,而非封装数据。

27. 使用“class-continuation分类”隐藏实现细节

对于不需要对外公布 但却 应该具有的方法及实例变量,可以使用“class-continuation分类”

“class-continuation分类”和普通分类不同,它必须定义在其所接续(???)的那个类的实现文件里。

“class-continuation分类”是唯一能声明实例变量的分类,而且此分类没有特定的实现文件,其中的方法都应该定义在类的主实现文件里。与其他分类不同,“class-continuation分类”没有名字

这不就是 类扩展么。。。

类扩展还有一种合理的用法,就是将public接口中声明为“只读”的属性,扩展为“可读写”,以便在类的内部设置其值。

在这里插入图片描述
这样,外界无法修改对象,而内部可以按照需要管理数据。
也可以在类扩展中声明私有方法。

在类扩展中的属性和方法,都是私有的

类扩展还有一种作用,即在类扩展中遵守协议

28. 通过协议提供匿名对象

协议定义了一些列的方法,遵从此协议的对象应该实现他们(如果是非可选的)。

@property (nonatomic, weak) id <EOCDelegate> delegate;
由于该属性的类型是id < EOCDlegate > ,所以任何类的对象都能充当这一属性。
即便该类不继承自NSObject也可以,只要遵循EOCDelegate协议就行。
对于具备此属性的类来说,delegate就是 “匿名的”(anonymous)。

第五章 内存管理

29. 理解引用计数

OC使用引用计数来管理内存,每个对象都有一个可以递增或递减的计数器

  • 在ARC中所有与引用计数有关的方法都无法编译。

引用计数工作原理

对象有一个计数器,用以表示当前有多少个事物想令此对象继续存活。
在OC中,叫做“保留计数”(retain count)或者“引用计数”(reference count)

NSObject协议声明了三个方法用于操作计数器

  • retain 递增引用计数

  • release 递减引用计数

  • autorelease 待稍后清理“自动释放池”(autorelease pool)时,再递减引用计数

  • 当对象的引用计数为0时,对象就回收了(deallocated),也就是说:系统会将其占用的内存标记为“可重用”(reuse)。此时,所有指向该对象的引用也都变得无效了。

对象与对象之间可能存在相互联系,这些相互关联的对象就构成了一张“对象图”(object graph)

  • iOS中,UIApplication是在程序一启动就创建的单例,是所有对象的“根对象”(root object)

  • 在OC中,调用alloc方法所返回的对象由调用者所拥有。

  • 指向已经释放的对象的指针,被称为“悬垂指针”

属性存取方法中的内存管理

对象A想保留对象B,通常是通过访问“属性”来实现。

自动释放池

调用release会立刻递减对象的引用计数
调用autorelease会在稍后的时候(下一次“事件循环”(event loop)),递减
不过,也可能执行得更早些(???)

MRC有autorelease,也有自动释放池autoreleasepool

30. 以ARC简化引用计数

Clang编译器项目带有一个“静态分析器”(static analyzer),用于找出程序中引用计数出问题的地方。
既然可以查明内存管理问题,那么也就能根据需要,预先加入适当的保留或释放操作,以避免这些问题。这就是自动引用计数即:自动管理引用计数

ARC中,引用计数实际上还是要执行的,只不过 保留与释放的操作 由ARC自动操作执行。

由于ARC会自动执行 retain、release、autorelease、dealloc等操作,所以直接在ARC下调用这些内存管理方法是 非法的

若方法名是以alloc、new、copy、mutableCopy开头的,则其返回的对象归 调用者 所有

在应用程序中,可用下列修饰符来改变局部变量与实例变量的语义:

  • __strong:默认语义,保留此值
  • __unsafe_unretrained:不保留此值,这么做可能不安全,因为等到再次使用变量时,其对象可能已经回收了
  • __weak:不保留此值,但是变量可以安全使用,因为如果系统把这个对象回收了,那么变量也会自动清空。
  • __autoreleasing:把对象“按引用传递”(pass by reference)给方法时,使用这个特殊的修饰符。此值在方法返回时自动释放。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值