iOS 开发 -《Effective Objective-C 2.0:编写高质量 iOS 与 OS X 代码的 52 个有效方法》读书笔记(2)

文章共分为三篇:

第一篇:iOS 开发 -《Effective Objective-C 2.0:编写高质量 iOS 与 OS X 代码的 52 个有效方法》读书笔记(1)
第二篇:iOS 开发 -《Effective Objective-C 2.0:编写高质量 iOS 与 OS X 代码的 52 个有效方法》读书笔记(2)
第三篇:iOS 开发 -《Effective Objective-C 2.0:编写高质量 iOS 与 OS X 代码的 52 个有效方法》读书笔记(3)

接上篇:iOS 开发 -《Effective Objective-C 2.0:编写高质量 iOS 与 OS X 代码的 52 个有效方法》读书笔记(1)


第 3 章:接口与 API 设计

第 13 条:用前缀避免命名空间冲突

OC没有其他语言那种内置的命名空间机制。因此我们起名时要设法避免潜在的命名冲突,否则很容易重名,引发命名冲突(naming clash)。避免此问题的唯一方法就是变相实现命名空间:为所有名称都加上适当前缀。所选前缀可以是与公司、应用程序或二者皆有关联之名。另外,Apple宣称其保留使用所有“两字母前缀”,所以我们一般还是三个字母开头比较好。

第 14 条:提供“全能初始化方法”

能够为对象提供必要信息以便其完成工作的初始化方法叫做“全能初始化方法”。

如果创建类实例的方式不止一种,那么这个类就会有多个初始化方法。这当然很好,不过仍然要在其中选定一个作为全能初始化方法,令其它初始化方法都来调用它。这样的话,当底层数据存储机制改变时,只需修改此方法的代码就好,无须改动其他初始化方法。

NSDate就是一个例子,其初始化方法如下:

- (instancetype)init NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithTimeIntervalSinceReferenceDate:(NSTimeInterval)ti NS_DESIGNATED_INITIALIZER;
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithTimeIntervalSinceNow:(NSTimeInterval)secs;
- (instancetype)initWithTimeIntervalSince1970:(NSTimeInterval)secs;
- (instancetype)initWithTimeInterval:(NSTimeInterval)secsToBeAdded sinceDate:(NSDate *)date;

在上面几个初始化方法中,initWithTimeIntervalSinceReferenceDate:是全能初始化方法,也就是说,其余的初始化方法都要调用它。只有在全能初始化方法中,才会存储内部数据。

第 15 条:实现 description 方法

调试程序时,经常需要打印并查看对象信息。一种办法是编写代码把对象的全部属性都输出到日志中。不过最常用的做法还是像下面这样:

NSLog(@"object = %@", object);

这样直接打印我们的对象它会输出 <Object:0x*****>,这并不是我们想要的,所以当我们重写description的时候才可能满足我们调试的需求。

- (NSString *)description {
    return [NSString stringWithFormat:@"%@ : %@, %@", [self class], self, @"你需要的属性"];
}

若想在调试时打印出更详尽的对象描述信息,则应实现 - (NSString *)debugDescription;方法,再与po命令一起使用配合调试。

第 16 条:尽量使用不可变对象

这条主要讲的是,尽量使用不可变的对象。简单来说,我们设计出来一个类,其实是不希望别人更改其属性的,因此应该尽量把对外公布出来的属性设置为只读,而且只在确有必要时才将属性对外公布。

第 17 条:使用清晰而协调的命名方式

类、方法及变量的命名是 OC 编程的重要环节。这点就不用多说了,具体可以看下官方的命名规则:Coding Guidelines for Cocoa

第 18 条:为私有方法名加前缀

这样有助于调试,也很容易让我们将公共方法和私有方法区分开,另外便于修改方法名。但是要注意不要但用一个下划线做私有方法的前缀,因为这种做法是预留给苹果公司的。

第 19 条:理解 Objective-C 错误模型

当前很多种编程语言都有异常处理机制,OC也不例外。在OC中异常只用于处理严重错误(fatal error),出现“不那么严重的错误(nonfatal error)”时,OC语言的处理方式是:令方法返回 nil/0,或者使用NSError,以表明有错误发生。

第 20 条:理解 NSCopying 协议

OC 中,拷贝对象通过copy方法完成,如果想令自己的类支持拷贝操作,那就要实现NSCopying协议,该协议只有一个方法:

- (id)copyWithZone:(NSZone*)zone

当然如果要求返回对象是可变的类型就要用到NSMutableCopying协议,相应方法:

- (id)mutableCopyWithZone:(NSZone *)zone
  • 在拷贝对象时,需要注意拷贝执行的是深拷贝还是浅拷贝。

    • 深拷贝的意思就是:在拷贝对象自身时,会将对象的底层数据也进行复制。
    • 浅拷贝是只拷贝容器对象本身,而不拷贝其中数据。Foundation 框架中所有的collection 类在默认情况下都执行浅拷贝。
  • 要点:

    • 若想令自己所写的对象具有拷贝功能,则需实现NSCopying协议。
    • 如果自定义的对象分为可变版本与不可变版本,那么就要同时实现NSCopyingNSMutableCopying协议。
    • 复制对象时需决定采用浅拷贝还是深拷贝,一般情况下应该尽量执行浅拷贝。
    • 如果你所写的对象需要深拷贝,那么可考虑新增一个专门执行深拷贝的方法。

第 4 章:协议与分类

第 21 条:通过委托与数据源协议进行对象间通信

简单来说,这条讲的是使用delegateprotocal进行数据传递。此模式可将数据与业务逻辑解耦。需要注意的是:修饰delegate的属性一定要定义成weak,而非strong,因为两者之间必须为“非拥有关系”。

第 22 条:将类的实现代码分散到便于管理的数个分类之中

这条讲的是使用分类。类中经常容易填满各种方法,而这些方法的代码则全部堆在一个巨大的实现文件里。有时这么做是合理的,因为即便通过重构把这个类打散,效果也不会更好。在此情况下,可以通过OC的“分类”机制,将类代码按逻辑划入几个分区中,这对开发与调试都有好处。

第 23 条:总是为第三方类的分类名称加前缀

这条也很容易理解,就是给分类名加前缀,比如NSString分类,

@interface NSString (HTTP)

@end

// 加前缀
@interface NSString (ABC_HTTP)

@end

给分类加上前缀,易于区分,并且能避免不必要的错误。

第 24 条:勿在分类中声明属性

除了 Class-continuation分类,其他分类无法向类中新增实例变量,这样就无法把实现属性所需要的实例变量合成出来,即无法生成实例变量,如下图:

第 25 条:使用 “Class-continuation分类” 隐藏实现细节

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

#import "NNTestClass.h"

@interface NNTestClass ()
// 写你所需要的私有变量或方法
@end

@implementation NNTestClass
// 实现
@end

这条和之前的描述也有点类似,总之就是在说尽量不要在公共接口中暴露太多内容,隐藏其实现细节。

第 26 条:通过协议提供匿名对象

  • 协议可以在某种程度上提供匿名对象,例如id<someProtocal> objectobject对象的类型不限,只要能遵从这个协议即可,在这个协议里面定义了这个对象所应该实现的方法。
  • 使用匿名对象来隐藏类型名称(或类名)。
  • 如果具体类型不重要,重要的是对象能否处理好一些特定的方法,那么就可以使用这种协议匿名对象来完成。

第 5 章:内存管理

第 27 条:理解引用计数

OC 使用引用计数器来管理内存,每个对象都有个可以递增或递减的计数器。对象创建好之后,其保留计数至少为1,若保留计数为正,则对象继续存活,当保留计数降为0时,对象被销毁。
在对象的生命周期中,其余对象通过引用来保留或释放此对象。保留或释放操作分别会递增或递减保留计数。

注意retain,release,autorelease,这三个方法操作计数器。

  • retain递增保留计数。
  • release递减保留计数。
  • autorelease待稍后清理“自动释放池”时,再递减保留计数。

第 28 条:以 ARC 简化引用计数

ARC,即自动管理计数器,理解起来很容易。
ARC会自动执行retain,release,autorelease等操作,所以直接在ARC中调用这些内存管理方法是非法的。具体来说,ARC不能调用以下方法:

  • retain
  • release
  • autorelease
  • dealloc

ARC中,变量的内存管理语义可以通过修饰符指明,而原来则需手工执行”保留“及释放操作;(__strong,__weak,一个保留值,一个不保留值)。
注意的是ARC只负责管理OC对象的内存。注意:CoreFoundation对象不归ARC管理,必须适时调用CFRetain/CFRelease

第 29 条:在 dealloc 方法中只释放引用并解除监听

不要在delloc方法中调用其它方法,尤其是需要异步执行某些任务又要回调的方法,这样是很危险的行为,很可能异步执行完回调的时候该对象已经被销毁了,crash了。

delloc方法里,应该做的事情就是释放指向其他对象的引用,并取消原来订阅的键值观测(KVO)或NSNotificationCenter等通知,不要做其它事情。

- (void)dealloc {
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

第 30 条:编写 “异常安全代码” 时留意内存管理问题

这条也和上面说过的类似,有两点需要注意:

  • 捕获异常时,一定要注意将try块内所创立的对象清理干净。
    @try { // 可能会出现崩溃的代码
    }
    @catch (NSException *exception) { // 捕获到的异常 exception
    }
    @finally { // 结果处理,也可以去掉
    }
  • 在默认情况下,ARC 不生成安全处理异常所需的清理代码。开启编译器标志后,可生成这种代码,不过会导致应用程序变大,而且会降低运行效率。

第 31 条:以弱引用避免保留环

这个也很容易理解,保留环就是我们常说的循环引用。就是有时候我们需要将某些引用设为weak,避免出现“保留环”。

第 32 条:以“自动释放池块”降低内存峰值

合理运用自动释放池,可降低应用程序的内存峰值。

  • 请看下面几行代码:
    NSArray *dataArray = /*****/;
    NSMutableArray *testArray = [NSMutableArray array];
    for(NSDictionary *recordDic in dataArray) {
        TestPerson *person = [[TestPerson alloc] initWithRecord:recordDic];
        [testArray addObject:person];
    }

当循环长度无法预估,需要创建很多对象时,上面代码会创造数量无法预估的临时对象,即内存中会出现很多不必要的临时对象,他们本该提早回收的。这时我们可以给这段代码增加一个释放池:

    NSArray *dataArray = /*****/;
    NSMutableArray *testArray = [NSMutableArray array];
    for(NSDictionary *recordDic in dataArray) {
        @autoreleasepool {
        TestPerson *person = [[TestPerson alloc] initWithRecord:recordDic];
        [testArray addObject:person];
        }
    }

这样临时对象就可以及时回收了,有一点需要注意:不要把for循环放到释放池里面,特别是循环长度特别长的时候。

第 33 条:用“僵尸对象”调试内存管理问题

僵尸对象

如上图,启动这项调试功能之后,运行期系统在回收对象时,会把它的isa指针指向特殊的僵尸类,而不是真正的回收他们。系统会修改对象的isa指针,令其指向特殊的僵尸类,从而使该对象变为僵尸对象。僵尸类能够响应所有的选择子,响应方式为:打印一条包含消息内容以及其接收者的消息,然后终止应用程序。

第 34 条:不要使用 retainCount

retainCount,对象的保留计数,ARC环境下已经正式废弃,在MRC下还可以正常使用。




下一篇:iOS 开发 -《Effective Objective-C 2.0:编写高质量 iOS 与 OS X 代码的 52 个有效方法》读书笔记(3)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335

推荐阅读更多精彩内容