【Effective Objective - C 2.0】——读书笔记(三)

文章目录

  • 十五、用前缀避免命名空间冲突
  • 十六、提供全能初始化方法
  • 十七、实现description方法
  • 十八、尽量使用不可变对象
  • 十九、使用清晰而协调的命名方式
  • 二十、为私有方法名加前缀
  • 二十一、理解Objective-C错误模型
  • 二十二、理解NSCopying协议



十五、用前缀避免命名空间冲突

  • OC语言中没有其他语言那种内置的命名空间机制,所以我们在对文件命名时要十分的注意,若是发生重名冲突,那么应用程序相应的链接过程就会出错,导致运行文件不知道究竟该调用那个文件,因为其中出现了重复的符号。
  • 也就是理解成命名的时候需要加上前缀,比如一个项目里存在很多歌ViewController的子类,那么在命名的时候需要分清楚子类的名称,加上前缀总是好的

使用Cocoa 创建应用程序时一定要注意,Apple 宣称其保留使用所有“两字母前缀” (two-letter prefix)的权利,所以你自己选用的前缀应该是三个字母的。

// 公司/应用程序相关的类名前缀
// 公司前缀
@interface ABCCompanyPerson : NSObject
@property (nonatomic, copy) NSString *name;
- (void)work;
@end// 应用程序前缀
@interface MyAppViewController : UIViewController
@property (nonatomic, strong) ABCCompanyPerson *person;
@end// 第三方库的类名前缀
// 第三方库前缀
@interface TPThirdPartyManager : NSObject
+ (instancetype)sharedManager;
- (void)performTask;
@end// 你的程序库,使用了第三方库
@interface YourLibraryClass : NSObject
@property (nonatomic, strong) TPThirdPartyManager *manager;
- (void)doSomething;
@end

十六、提供全能初始化方法

所有对象均要初始化。初始化时,有些对象可能无须开发者向其提供额外信息,不过一般来说还是要提供的。以iOS的UI框架UIKit为例,其中有个类叫做UITableViewCell,初始化该类对象时,需要指明其样式及标识符,标识符能够区分不同类型的单元格。由于这种对象的创建成本较高,所以绘制表格时可依照标识符来复用,以提升程序效率。我们把这种可为对象提供必要信息以便其能完成工作的初始化方法叫做“全能初始化方法”。(实际上就是在初始化时直接提供需要的数据

全能初始化

  • 如果创建类实例的方式不止一种,那么这个类就会有许多个初始化方法。在这个过程里有一个方法使得其他方法初始化的时候必须调用它,那么这个方法称“全能初始化方法”。
  • 只有在全能初始化方法中,才会存储内部数据,其他的方法只是在存储内部数据后再进行了一些其他操作而已

要点

  • 在类中提供一个全能初始化方法,并与文档里指明。其他初始化方法均应调用此方法。
  • 若全能初始化方法与超类不同,则需覆写超类中的对应方法。 如果超类的初始化方法不适用于子类,那么应该覆写这个超类方法,并在其中抛出异常。
  • 一个类有多个全能初始化方法要注意,我们就是要维持原来类的调用链,每个子类的全能初始化方法都应该调用其超类的对应方法,并逐层向上。因为其父类有两个全能初始化方法,这两种初始化方法定义出来的数据可能是不同的,若是你在子类中调用了错误的父类初始化方法,它就会可能因为数据类型的问题使程序发生错误
  • 重写初始化方法也要注意如果子类的全能初始化方法与超类方法的名称不同,我们总应覆写超类的全能初始化方法,避免子类调用父类的全能初始化方法

十七、实现description方法

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

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

在构建需要打印到日志的字符串时,object对象会收到description消息,该方法所返回的描述信息将取代“格式字符串”里的%@。比方说,object是个数组,若用下列代码打印其信息:

NSArray *object = @[@"A string", @(123)];
NSLog(@"object = %@", object);

输出:

object = {"A string"123
}

如果是一个自定义的类,则输出:

object = <EOCPerson: 0x7fd9a1600600>

这时候就需要我们重写description方法

例如下:

- (NSString *) description {return [NSString stringWithFormat:@"<%@: %p, %@>",[self class],self,@{@"latitude":_title,@"latitude":@(_latitude),@"longitude":@(_longitude)}];
} 

输出:

location = <EOCLocation: 0x7f98f2e01d20, {latitude = "51.506"longitude = 0;title = London;
}>

debugDescription:

这个也是一种描述方法,和description差不多,就是描述的位置不一样,description是在函数调用类的时候触发方法才输出的,而debugDescription是在控制台中使用命令打印该对象时才调用的。当然加断点查看时也可以看到debugDescription的描述。

如果你在description不想将一些内容输出的话,你就可以将那些数据写在debugDescription中,让程序员自己调试时可以方便的看到这些数据,而description方法就输出你想要让用户看到的信息就行了。

要点:

  • 实现description方法返回一个有意义的字符串,用以描述该实例。
  • 若想在调试时打印出更详尽的对象描述信息,则应实现debugDescription方法。

十八、尽量使用不可变对象

这里的不可变对象不是使用系统提供的不可变类,而是在设置属性的时候添加上readnoly属性

默认情况下的属性是既可读又可写的,这样的类我们在这里成为可变类,但一般的数据未必需要改变。

例如:

@property (nonatomic, copy, readonly) NSString *identifier; @property (nonatomic, copy, readonly) NSString *title; @property (nonatomic, assign, readonly) float latitude;

现在,这个属性就只能用在实现代码内部设置这些属性了,但其实,在对象外部还可以通过“键值编码”技术来设置这些属性,就像“setValue:forKey:”方法。“点语法”也可以,因为点语法就是调用set方法的。这样做虽说可以改动,但是却违背了本心,还会导致数据不同而出现问题,所以不建议更改。

[pointOfInterest setValue:@"abc" forKey:@"identifier"];

这样子可以改动属性值,因为KVC会在类里查找“setIdentifier:”方法,并借此修改此属性。即使没有于公共接口中公布此方法,它也依然包含在类中。不过,这样做等于违规地绕过了本类所提供的API,要是开发者使用这种“杂技代码”的话,那么得自己开应对可能出现的问题。
还有一种可以修改数据的方法就是直接用类型信息查询功能查出属性所对应的实例变量在内存布局中的偏移量,以此来人为设置这个实例变量的值。这样做比绕过本类的公共API还要不合规范。所以不应该因为这个原因而忽视所提的建议,大家还是要尽量编写不可变的对象。

要点:

  • 尽量创建不可变的对象
  • 若某属性进可于对象内部修改,则在“分类”中将其由属性扩展为readwrite属性
  • 不要把可变的collection作为属性公开,而应提供相关方法,以此修改对象中的可变collection。

十九、使用清晰而协调的命名方式

给方法命名时的注意事项可总结成下面几条规则:

  • 如果方法的返回值是新创建的,那么方法名的首个词应是返回值的类型,除非前面还 有修饰语,例如localized String。属性的存取方法
    不遵循这种命名方式,因为一般认 为这些方法不会创建新对象,即便有时返回内部对象的一份拷贝, 我们也认为那相当
    于原有的对象。这些存取方法应该按照其所对应的属性来命名。
  • 应该把表示参数类型的名词放在参数前面。 例如:
    在这里插入图片描述其中width就是名词
  • 如果方法要在当前对象上执行操作,那么就应该包含动词;若执行操作时还需要参数, 则应该在动词后面加 上一个或多个名词。
  • 不要使用str 这种简称,应该用string 这样的全称。
  • Boolean 属性应加is 前缀。如果某方法返回非属性的Boolean 值,那么应该根据其功 能,选用has 或is 当前缀。
  • 将get 这个前缀留给那些借由“输出参数〞来保存返回值的方法,比如说,把返回值填充到〝C语言式数组” ( C - style array ) 里的那种方法就可以使用这个词做前缀 。

类与协议的命名:

应该为类与协议的名称加上前缀,以避免命名空间冲突,而且应该像给方法起名时那样把词句组织好,使其从左至右读起来较为通顺。基本命名规则就是:命名方式应该一致,如果要从其他的类中继承子类,那么就要遵守其原本的命名惯例。 例如:UIView它的子类就应该是***View,表明其来历。

要点:

  • 起名时应遵从标准的OC命名规范,这样创建出来的接口更容易为开发者所理解。
  • 方法名要言简意赅,从左至右读起来要像个日常用语中的句子才好。
  • 方法名里不要使用缩略后的类型名称。
  • 给方法名起名时的第一要务就是确保其风格与你自己的代码或所要集成的框架相符。

二十、为私有方法名加前缀

要点:

  • 给私有方法的名称加上前缀,这样可以很容易地将其同公共方法区分开。
  • 不要单用一个下划线做私有方法的前缀(_xxx),因为这种做法是预留给苹果公司用的。

二十一、理解Objective-C错误模型

当前很多编程语言都有“异常”(exception)机制,OC也不例外。
首先要注意的是,“自动引用计数”(Automatic Reference Counting, ARC)在默认情况下不是“异常安全的”(exception safe)。具体来说,这意味着:如果抛出异常,那么本应在作用域末尾释放的对象现在不会自动释放了。如果想生成“异常安全”的代码,可以通过设置编译器的标志来实现,不过这将引入一些额外代码,在不抛出异常时,也照样要执行这部分代码。需要打开的编译器标志叫做-fobjc-arc-exceptions。可是在释放资源之前如果抛出异常了,那么该资源就不会被释放了:

id someResource = /*...*/;
if (/*check for error*/) {@throw [NSException exceptionWithName:@"ExceptionName" reason:@"There was an error" userInfo:nil];
}
[someResource doSomething];
[someResource release];

在抛出异常前先释放someResource,这样做当然能解决此问题,不过要是待释放的资源有很多,而且代码的执行路径更为复杂的话,那么释放资源的代码就容易写的很乱。此外,代码中加入了新的资源之后,开发者经常会忘记在抛出异常前先把它释放掉。
OC语言现在采用的方法是:只在极其罕见的情况下抛出异常,异常抛出之后,无须考虑恢复问题,而且应用程序此时也应该退出。 也就是说,不用再编写复杂的“异常安全”的代码了。
异常只用来处理严重错误(fatal error,致命错误);对于“不那么严重的错误”(nonfatal error,非致命错误),OC语言所采用的编程范式为:令方法返回nil/0,或是使用NSError,以表明其中有错误发生。如:

 - (id)initWithValue:(id)value {if (self = [super init]) {if (/*Value means instance can't be created*/) {self = nil;} else {//Initialize instance}}return self;
}

这种情况下,如果if语句发现无法用传入的参数值来初始化当前实例,那么就把self设置成nil,这样的话,整个方法的返回值也就是nil了。调用者发现初始化方法并没有2把实例创建好,于是便可以知道其中发生了错误。

NSError的用法更加灵活,因为经由此对象,我们可以把导致错误的原因回报给调用者。NSError对象里封装了三条消息:

  • Error domain(错误范围,类型为字符串)产生错误的根源,通常用一个特有的全局变量来定义。
  • Error code(错误码,类型为整数)独有的错误代码,用以指明在某个范围内具体发生了何种错误。某个特定范围可能会发生一系列相关错误,这些错误通常采用enum定义。
  • User info(用户信息,类型为字典)有关错误的额外信息,其中或许包含一段“本地化描述”(localized description),或许还含有导致该错误发生的另外一个错误,经由此种信息,可将相关错误串成一条“错误链”(chain of errors)。

要点

  • 只有发生了可使整个应用程序崩溃的严重错误时,才应使用异常。
  • 在错误不那么严重的情况下,可以指派“委托方法”来处理错误,也可以把错误信息放在NSError对象里,经由“输出参数”返回给调用者。

二十二、理解NSCopying协议

我们经常会使用copy函数,但是若是你自定义的类,他自己就不会实现这个函数,此时就需要你自己来实现了,要实现copy函数就的实现NSCopying协议,该协议只有一个方法:

- (id)copyWithZone:(NSZone *)zone;

copy方法由NSObject实现,该方法只是以“默认区”为参数来调用“copyWithZone:”。所以要实现copy函数,他才是关键。
想要重写copy函数,要声明该类遵从NSCopying协议,并实现其中的方法。

#import <Foundation/Foundation.h>
@interface EOCPerson : NSObject <NSCopying>
@property (nonatomic, copy, readonly) NSString *firstName; @property (nonatomic, copy, readonly) NSString *lastName;
- (id) initwithFirstName: (NSString*)firstName andLastName: (NSString*) lastName;
@end

实现协议中规定的方法:

- (id) copyWithZone: (NSZone*)zone {EOCPerson *copy = [[[self class] allocWithZone: zone] initwithFirstName: firstName andLastName: lastName];return copy; 
}

在这里插入图片描述
关于深浅拷贝之前在博客中已经详细讲过,不再赘述

要点:

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/679263.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

KingSCADA实现按钮点击效果

哈喽&#xff0c;你好啊&#xff0c;我是雷工&#xff01; 在做SCADA项目的时候&#xff0c;按钮是不可缺少的功能&#xff0c;但软件自带的按钮太丑&#xff0c;已经无法满足现如今客户对界面美观度的要求。 这时候就需要UI小姐姐设计美观大气的SCADA界面&#xff0c;但UI设计…

蓝牙BLE学习

1. 简介 1.1 蓝牙发展历程 蓝牙&#xff0c;直接来自于一位国王的名字--King Harald ‘Bluetooth Gromsson。这位国王因两件事留名于史&#xff0c;其一是在公园958年统一了丹麦和挪威&#xff0c;其二是在其死后&#xff0c;其牙齿呈现出暗蓝色的颜色&#xff0c;因而得名蓝牙…

第74讲Breadcrumb 面包屑实现

Breadcrumb 面包屑实现 为了实现二级路由&#xff0c;我们搞成搞个子路由&#xff0c;对于二级菜单 const routes [{path: /,name: 首页,component: () > import(../views/layout),redirect:/home,children:[{path: /home,name: 首页,component: () > import(../views…

《UE5_C++多人TPS完整教程》学习笔记10 ——《P11 设置加入游戏会话(Setup for Joining Sessions)》

本文为B站系列教学视频 《UE5_C多人TPS完整教程》 —— 《P11 设置加入游戏会话&#xff08;Setup for Joining Sessions&#xff09;》 的学习笔记&#xff0c;该系列教学视频为 Udemy 课程 《Unreal Engine 5 C Multiplayer Shooter》 的中文字幕翻译版&#xff0c;UP主&…

JVM-类加载器 双亲委派机制

申明&#xff1a;文章内容是本人学习极客时间课程所写&#xff0c;文字和图片基本来源于课程资料&#xff0c;在某些地方会插入一点自己的理解&#xff0c;未用于商业用途&#xff0c;侵删。 什么是JVM JVM是Java Virtual Machine&#xff08;Java虚拟机&#xff09;的缩写&a…

c语言数据类型定义错误导致的数据溢出或者死循环

数据溢出问题 #include <stdio.h>/* 数据溢出 */int main() {char i; // 数据表示范围[-128,127] 0xf0 ~ 0x7ffor(i0;i<130;i) // {printf("%d ",i);}return 0; }/* 编译运行上面的程序&#xff0c;你会发现程序陷入了死循环&#xff0c;一直在不断…

第5个-模糊加载

Day 5 - Blurry Loading 1. 项目展示 2. 分析思路 变化过程 数字从 0 不断增长到 100&#xff1b;中间的百分比数字逐渐消失&#xff0c;即透明度 opacity 从 1 到 0&#xff1b;背景图片从模糊变为清晰&#xff0c;滤镜 filter.blur()的参数设置为从 30px 到 0px。 小 tips…

unity学习案例总结

动态标签 GitHub - SarahMit/DynamicLabel3D: Simple dynamic labels for a 3D Unity scene

《CSS 简易速速上手小册》第6章:高级 CSS 技巧(2024 最新版)

文章目录 6.1 使用 CSS 变量进行设计&#xff1a;魔法配方的调配6.1.1 基础知识6.1.2 重点案例&#xff1a;创建可定制的主题6.1.3 拓展案例 1&#xff1a;响应式字体大小6.1.4 拓展案例 2&#xff1a;使用 CSS 变量创建动态阴影效果 6.2 calc(), min(), max() 等函数的应用&am…

【CTFshow】VIP题目限免 通关

&#x1f36c; 博主介绍&#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 hacker-routing &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【应急响应】 【python】 【VulnHub靶场复现】【面试分析】 &#x1f389;点赞➕评论➕收藏…

我主编的电子技术实验手册(03)——电阻的识别与测量

本专栏是笔者主编教材&#xff08;图0所示&#xff09;的电子版&#xff0c;依托简易的元器件和仪表安排了30多个实验&#xff0c;主要面向经费不太充足的中高职院校。每个实验都安排了必不可少的【预习知识】&#xff0c;精心设计的【实验步骤】&#xff0c;全面丰富的【思考习…

AI大模型学习笔记之四:生成式人工智能(AIGC)是如何工作的?

OpenAI 发布 ChatGPT 已经1年多了&#xff0c;生成式人工智能&#xff08;AIGC&#xff09;也已经广为人知&#xff0c;我们常常津津乐道于 ChatGPT 和 Claude 这样的人工智能系统能够神奇地生成文本与我们对话&#xff0c;并且能够记忆上下文情境。 Midjunery和DALLE 这样的AI…

随机过程及应用学习笔记(一)概率论(概要)

概率是随机的基础&#xff0c;在【概率论&#xff08;概要&#xff09;】这个部分中仅记录学习随机过程及应用的基本定义和结果。 前言 首先&#xff0c;概率论研究的基础是概率空间。概率空间由一个样本空间和一个概率测度组成&#xff0c;样本空间包含了所有可能的结果&…

服务器解析漏洞及任意文件下载

1.服务器文件解析漏洞 文件解析漏洞,是指Web容器&#xff08;Apache、nginx、iis等&#xff09;在解析文件时出现了漏洞,以其他格式执行出脚本格式的效果。从而,黑客可以利用该漏洞实现非法文件的解析。 &#xff08;1) Apache linux系统中的apache的php配置文件在/etc/apac…

【数据结构】顺序栈和链式栈的简单实现和解析(C语言版)

数据结构——栈的简单解析和实现 一、概念二、入栈&#xff08;push&#xff09;三、出栈&#xff08;pop&#xff09;四、顺序栈简单实现 &#xff08;1&#xff09;进栈操作&#xff08;2&#xff09;出栈操作 一、概念 本篇所讲解的栈和队列属于逻辑结构上的划分。逻辑结构…

综合项目---博客

一.运行环境 192.168.32.132 Server-Web linux Web 192.168.32.133 Server-NFS-DNS linux NFS/DNS 基础配置 1.配置主机名静态ip 2.开启防火墙并配置 3.部分开启selinux并配置 4.服务器之间通过阿里云进行时间同步 5.服务器之间实现ssh免密…

SpringCloud-Ribbon:负载均衡(基于客户端)

6. Ribbon&#xff1a;负载均衡(基于客户端) 6.1 负载均衡以及Ribbon Ribbon是什么&#xff1f; Spring Cloud Ribbon 是基于Netflix Ribbon 实现的一套客户端负载均衡的工具。简单的说&#xff0c;Ribbon 是 Netflix 发布的开源项目&#xff0c;主要功能是提供客户端的软件负…

MMKV:轻巧高效的跨平台键值存储解决方案

MMKV&#xff1a;轻巧高效的跨平台键值存储解决方案 引言 在移动应用的开发中&#xff0c;数据存储是一个至关重要的环节。随着移动应用的普及和功能的增多&#xff0c;应用需要存储和管理各种类型的数据&#xff0c;包括用户配置信息、缓存数据、临时状态等。传统的数据存储…

python巧用定理判断素数

目录 判断一个数n是否是素数 求一个数的素因数个数 求大于等于指定数的最小素数 在数论中有三个非常重要的关于素数的定理 1、任何数都可以表示成若干个素数的乘积 2、任意数的一个素因子如果小于根号n&#xff0c;那么另一个与其对应的素因子必然大于根号n。 3、除了2和…

浅谈应该遵守的伦敦银交易规则

做伦敦银投资的朋友应遵守伦敦银交易规则&#xff0c;伦敦银交易规则不是指那些伦敦银交易技巧&#xff0c;而是在这个市场中要遵循的一些约定&#xff0c;下面我们就来讨论一下。 风险管理。风险管理即指投资者控制自己一笔乃至整体交易的风险&#xff0c;没有风险管理意识的投…