当前位置: 首页 > article >正文

【iOS】Effective Objective-C第三章

【iOS】Effective Objective-C第三章前言用前缀避免命名空间冲突提供“全能初始化方法”实现description方法尽量使用不可变对象使用清晰而协调的命名方式为私有方法名加前缀理解Objective-C错误模型理解NSCopying协议NSCopying协议NSMutableCopying协议深浅拷贝前言我们在iOS开发中构建应用程序时经常会在开发自己的应用程序时将其中的部分代码用于后续项目或供他人使用。这需要用到OC中常见的编程范式同时还需了解各种可能碰到的陷阱。用前缀避免命名空间冲突OC没有内置的命名空间机制因此我们在起名时要设法避免潜在的命名冲突否则很容易重名。这样会出错发生命名冲突应用程序链接过程出错无法链接。在运行期载入了含有重名类的程序库动态加载器遭遇重名符号错误整个程序崩溃。避免此问题唯一的办法就是变相实现命名空间为所有名称都加上适当前缀。所选前缀可以是与公司、应用程序或二者皆有关联之名。Apple宣称其保留使用所有“两字母前缀”的权利所以我们自己选用的前缀应该是三个字母的。容易忽视的类的实现文件中所用的纯C函数及全局变量也应加上前缀。这样做有两个好处避免在链接时发生重复符号错误。若此符号在栈回溯信息中则很容易判明问题来自哪块代码。自己开发的程序库中用到的第三方库也应为其中的名称加上前缀。提供“全能初始化方法”所有对象均要初始化。一般情况下开发者需向对象提供额外信息。通常情况下对象若不知道必要的信息则无法完成其工作。例如初始化UITableViewCell类对象时需要指明其样式及标识符用来区分不同类型的单元格。我们把为对象提供必要信息以便其完成工作的初始化方法叫做“全能初始化方法“。创建类实例的方法不止一种我们要在其中选定一个作为全能初始化方法令其他初始化方法都来调用它。只有在全能初始化方法中才会存储内部数据。当底层数据存储机制改变时只需修改此方法的代码就好无须改动其初始化方法。于是引出类继承时需要注意的一个重要问题如果子类的全能初始化方法与其超类方法的名称不同那么总应该覆写超类的全能初始化方法。例如开发者提供EOCRectangle类初始化设置属性覆写init方法来指明本类实例必须使用全能初始化方法来初始化现在创建EOCRectangle的子类EOCSquare该子类的全能初始化方法我们注意到它调用了超类的全能初始化方法。全能初始化方法的调用链一定要维系。然而调用者可能会使用init方法等来初始化EOCSquare对象为了避免出现宽高不等的正方形这种情况我们应该覆写其超类EOCRectangle的全能初始化方法如果超类的初始化方法不适用于子类那么常用的办法是覆写超类的全能初始化方法并于其中抛出异常这种方法看似突兀实则有时是必需的因为那种情况下创建出来的对象其内部数据有可能相互不一致。不过在OC程序中只有当发生严重错误时才抛出异常。因此初始化方法抛出异常是不得已之举。有时我们可能需要编写多个全能初始化方法那么每个子类的全能初始化方法都应该调用其超类的对应方法并逐层向上最后再执行与本类有关的任务。实现description方法调试程序时经常要打印并查看对象信息。最常用的做法是在构建需要打印到日志的字符串时object对象会收到description消息该方法返回的描述信息将取代格式字符串里的%。例如NSArray是系统类已经帮我们写好了description会输出那么如果在自定义类上这么做却会输出除非在自己的类里覆写description方法否则打印信息时会调用NSObject类所实现的默认方法。解决办法就是覆写description方法并将描述此对象的字符串返回即可。可以借助NSDictionary类的description方法使得在description中输出很多互不相同的信息。这样也可使得代码更易维护如果以后要向类中新增属性并在description方法中打印只需修改字典内容即可。NSObject协议中还有另一个方法值得注意那就是debugDescription此方法与description方法相似。区别在于debugDescription方法是开发者在调试器中以控制台命令打印对象时才调用的。在NSObject类的默认方法中此方法只是直接调用了description。具体演示定义一个Person类重写description和debugDescription方法interfacePerson:NSObjectproperty(nonatomic,copy)NSString*name;end-(NSString*)description{return[NSString stringWithFormat:description: %,self.name];}-(NSString*)debugDescription{return[NSString stringWithFormat:debug: %,self.name];}Person*p[Person new];p.nameJack;NSLog(%,p);运行可以看出调用description方法设置断点再运行可以看出调用debugDescription方法。控制器输入po p尽量使用不可变对象设立类时充分运用属性来封装数据。默认情况下属性是既可读也可写的这样设计出来的类都是可变的。笔者建议大家尽量减少对象中的可变对象。原因在于一般情况下我们要建模的数据未必需要改变。例如某数据所表示的对象源自一项只读的网络服务其中可能包含一系列需要显示在地图上的相关点像这种对象就没有必要改变其内容。即使修改新数据也不会推送回服务器。如果把可变对象放入集合之后又修改其内容那么很容易破坏set的内部数据结构使其失去固有的语义。具体到编译实践中则应尽量把对外公布出来的属性设为只读而且在有必要时才将属性对外公布。可是readonly不是不变是外部不可变。因此如果有人试着改变属性值那么编译会报错。有时我们想修改封装在对象内部的数据并不想令外人所改动通常做法是在对象内部将readonly属性重新声明为readwrite。对外可读内部可写。当然如果该属性是nonatomic的那么这样做可能会产生“竞争条件”。在对象内部写入某属性时对象外的观察者也许正读取该属性。若想避免此问题我们可以在必要时通过派发队列等手段将所有数据存取操作都设为同步操作。将属性在分类中重新声明属性的其他特质必须保持不变。这样声明的属性在对象的外部仍然能通过键值编码技术设置这些属性。[pointOfInterest setValue:abcforKey:identifier];KVC会在类中查找setIdentifier方法并借此修改属性。这样等同于绕过了本地所提供的API。在定义类的公共API时要注意对象里表示各种集合的那些属性是应该设成可变还是不可变的。如果内部有需要设置为可变类型的那么在外部调用方法返回时应该传入内部属性拷贝interfaceEOCPerson:NSObjectproperty(nonatomic,copy,readonly)NSString*firstName;property(nonatomic,copy,readonly)NSString*lastName;property(nonatomic,strong,readonly)NSSet*friends;-(id)initWithFirstName:(NSString*)firstName andLastName:(NSString*)lastName;-(void)addFriend:(EOCPerson*)person;-(void)removeFriend:(EOCPerson*)person;end#importEOCPerson.hinterfaceEOCPerson()property(nonatomic,copy,readwrite)NSString*firstName;property(nonatomic,copy,readwrite)NSString*lastName;property(nonatomic,strong)NSMutableSet*internalFriends;// 内部持有的可变集合endimplementationEOCPerson-(NSSet*)friends{return[_internalFriends copy];// 浅拷贝返回不可变类型}-(void)addFriend:(EOCPerson*)person{[_internalFriends addObject:person];}-(void)removeFriend:(EOCPerson*)person{[_internalFriends removeObject:person];}-(id)initWithFirstName:(NSString*)firstName andLastName:(NSString*)lastName{if((self[superinit])){_firstName[firstName copy];_lastName[lastName copy];_internalFriends[NSMutableSet new];}returnself;}end因此我们在编写代码时应通过提供相关方法来修改对象中的可变集合尽量不要将可变集合作为属性公开。使用清晰而协调的命名方式类、方法及变量的命名是OC编程的重要环节。类名首字母大写、通常有两三个前缀字母、方法与变量名以小写字母开头、其后每个单词首字母大写都使用驼峰式大小写命名法。给方法命名时有以下注意事项如果方法的返回值是新建的那么方法名的首个词应是返回值的类型除非前面还有修饰语。应该把表示参数类型的名词放在参数前面。如果方法要在当前对象上执行操作那么就应该包含动词若执行操作时还需要参数则应在动词后面加上一个或多个名词。不要使用str这种简称。Boolean属性应加上is前缀。如果某方法返回非属性的Boolean值那么应根据其功能选用has或is当前缀。将get前缀给那些借由“输出参数”来保存返回值的方法。类与协议的命名也应注意为类与协议名称加上前缀以避免命名空间冲突。为私有方法名加前缀为私有方法名加前缀的原因有有助于调试据此很容易能把公共方法和私有方法区别开。便于修改方法名或方法签名。前缀最好用p_p表示private下划线可把这个字母和真正的方法名区隔开下划线后面的部分按照驼峰法来命名即可。理解Objective-C错误模型自动引用计数在默认情况下不是“异常安全”的。这意味着如果抛出异常那么本应在作用域末尾释放的对象现在却不会自动释放了。如果想生成“异常安全”的代码可以通过设置编译器的标志来实现该标志叫做-fobjc-arc-exceptions。不过这将引入一些额外代码在不抛出异常时也照样要执行这部分代码。-fobjc-arc-exceptions标志的作用开启后编译器会为每个可能抛出异常的作用域生成完善的清理逻辑。当异常发生时ARC 会像正常退出一样释放所有本该在作用域末尾释放的对象。但这种机制即使在程序正常运行、没有抛出任何异常的情况下也会引入额外的运行时开销比如维护栈上的数据结构、注册清理函数等增加代码体积和运行时间。即使不用ARC也很难写出抛出异常时不会导致内存泄漏的代码。OC现在所采用的办法是只在及其罕见的情况下抛出异常抛出后无须考虑恢复问题而且应用程序此时也应退出。对于不那么严重的错误OC语言所用的编程范式是令方法返回nil/0或是使用NSError。NSError对象里封装了三条信息Error domain类型为字符串错误范围即产生错误的根源通常用一个特有的全局变量来定义。Error code类型为整数定义为枚举类型最佳错误码用以指明在某个范围内具体发生了何种词错误。User info类型为字典用户信息即有关此错误的额外信息。在设计API时NSError有以下几种常见用法通过委托协议来传递此错误。有错误发生时当前对象会把错误经由协议中的某个方法传给其委托对象。经由方法的“输出参数”返回给调用者。例如传递给方法的参数是个指针而该指针本身又指向另一个指针那个指针指向NSError对象。或者也可以把他当成一个直接指向NSError对象的指针。这样不仅能有普通的返回值也能经由输出参数把NSError对象回传给调用者。实际上在使用ARC时编译器会把方法签名中的NSError**转换成NSError*_ _autoreleasing*也就是说指针所指的对象会在方法执行完毕后自动释放。理解NSCopying协议NSCopying协议如果想让自己的类支持拷贝操作需要实现NSCopying协议。该协议只有一个方法-(id)copyWithZone:(NSZone*)zone;以前开发程序时会据此把内存分成不同的“区”zone而对象会创建在某个区里面。现在每个程序只有一个区“默认区”。尽管必须实现这个方法也不必担心其中的zone参数。copy方法由NSObject实现该方法只是以“默认区“为参数来调用copyWithZone:。我们总是想覆写copy方法其实真正需要实现的却是copyWithZone方法。若想使某个类支持拷贝功能只需声明该类遵从NSCopying协议并实现其中的方法即可。#importFoundation/Foundation.hinterfaceEOCPerson:NSObjectNSCopyingproperty(nonatomic,copy,readonly)NSString*firstName;property(nonatomic,copy,readonly)NSString*lastName;-(id)initWithFirstName:(NSString*)firstName andLastName:(NSString*)lastName;end-(id)initWithFirstName:(NSString*)firstName andLastName:(NSString*)lastName{if(self[superinit]){_firstName[firstName copy];_lastName[lastName copy];}returnself;}-(id)copyWithZone:(NSZone*)zone{EOCPerson*copy[[[selfclass]allocWithZone:zone]initWithFirstName:_firstName andLastName:_lastName];returncopy;}上述例子中我们采用全能初始化方法来初始化待拷贝对象令其执行所有初始化工作。然而有时除了要拷贝对象还要完成其他一些操作。NSMutableCopying协议该协议也只定义了一个方法-(id)mutableCopyWithZone:(NSZone*)zone;无论当前实例是否可变若需获取其可变版本的拷贝均应调用mutableCopy方法同理若需要不可变的拷贝则应通过copy方法来获取。该关系总是成立的这样就能实现可变版本与不可变版本之间的自由切换。要达到该目的另一个办法是提供三个办法copy、immutableCopy、mutableCopy。其中copy所返回的拷贝对象与当前对象的类型一致而另外两个方法则分别返回不可变版本与可变版本的拷贝。不过这种做法最好不要用在调用者并不知道其所用实例是否真的可变的情况下。深浅拷贝在编写拷贝方法时还要决定应该执行深拷贝还是浅拷贝。深拷贝在拷贝对象自身时将底层数据也一并复制过去。浅拷贝只拷贝容器对象本身而不复制其中数据集合类默认都执行浅拷贝这是因为容器内的对象未必都能拷贝而且调用者也未必想在拷贝容器时一并拷贝其中的每个对象。-(instancetype)initWithArray:(NSArrayObjectType*)array copyItems:(BOOL)flag;若copyItem参数设为YES则该方法会向数组中的每个元素发送copy消息。这个方法才是一个容器的深拷贝而采用普通的copy方式只是一个浅拷贝不会拷贝每一个set中的元素。注意不是遵从了NSCopying协议的对象都会执行深拷贝绝大多数都是浅拷贝。除非该类的文档说明它是用深拷贝来实现NSCopying协议的。

相关文章:

【iOS】Effective Objective-C第三章

【iOS】Effective Objective-C第三章前言用前缀避免命名空间冲突提供“全能初始化方法”实现description方法尽量使用不可变对象使用清晰而协调的命名方式为私有方法名加前缀理解Objective-C错误模型理解NSCopying协议NSCopying协议NSMutableCopying协议深浅拷贝前言 我们在iO…...

保姆级教程:用聆思CSK6开发板把‘小美小美’换成你自己的专属唤醒词

从“小美”到专属唤醒词:CSK6开发板个性化定制全流程解析 当你第一次唤醒CSK6开发板时,那句机械的“小美小美”是否让你觉得与精心设计的智能助手形象格格不入?作为一款支持大模型语音交互的开发板,CSK6的真正魅力在于它的高度可定…...

[具身智能-60]:具身智能的核心是让大模型替代传统的预设的规则和固化的算法,从传感器检测到的信号中提取有意义的信息、让大模型进行规划和决策,让大模型进行路径的规划,并指挥执行机构完成相应的动作控制。

用大模型替代传统规则和固化算法”是这一轮技术革命的核心分水岭。我们可以将你描述的这个过程拆解为三个维度的范式转移(Paradigm Shift),来深入理解为什么大模型能带来这种颠覆:1. 感知维度:从“特征工程”到“语义理…...

还在用4G“小灵通”?别慌,网速不够,“骚操作”来凑!

看着身边人的手机顶着个闪亮的“5G”标志,下载速度像坐火箭,而你的手机却固执地显示着“4G”,甚至偶尔还退化成“E”网,是不是感觉被时代抛弃在了石器时代?先别急着砸手机换新款,设备不支持5G虽然是个硬伤&…...

医院HIS系统集成umeditor时如何解决长文档粘贴卡顿问题?

程序员的外包奇遇记:Word一键粘贴大作战 大家好,我就是那个在安徽码PHP的"秃"出程序员!最近接了个CMS企业官网的活儿,客户爸爸突然甩来一个需求… 需求来了! “小张啊,我们这个新闻发布系统啊…...

揭露降重套路:免费降AI工具真的存在吗?2026届毕业生必看的70%→10%避坑指南

眼瞅着毕业答辩的日子一天天逼近,大家手里的论文查重报告是不是还红得刺眼? 说实话,这届毕业生真的太难了。以前的学长学姐只用担心查重率,现在倒好,不仅要查重,还得面对那个神出鬼没的AIGC检测。 刚开始看…...

农业大数据平台如何利用umeditor插件实现Excel动态图表粘贴?

各位道友,且听我这个江西老表用带着辣椒味的普通话,讲讲如何在99元预算下,给CMS系统加上Word一键粘贴功能,顺便还能防黑客、防白嫖、防导师催稿! 一、技术方案(白嫖防身版) 前端篇(…...

LeetCode 热题-矩阵置零 螺旋矩阵 旋转图像

矩阵置零 73. 矩阵置零https://leetcode.cn/problems/set-matrix-zeroes/ 给定一个 m x n 的矩阵,如果一个元素为 0 ,则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。 示例 1: 输入:matrix [[1,1,1],[1,0,1],[1,1,1]…...

查重70%急疯了?2026届降AI避坑指南:扒一扒从70%降到10%的免费工具内幕!

眼瞅着毕业答辩的日子一天天逼近,大家手里的论文查重报告是不是还红得刺眼? 说实话,这届毕业生真的太难了。以前的学长学姐只用担心查重率,现在倒好,不仅要查重,还得面对那个神出鬼没的AIGC检测。 刚开始看…...

基于STM32的指纹刷卡开锁签到考勤系统

一、系统介绍 本文详细介绍了一种基于 STM32 单片机的指纹 RFID 刷卡开锁签到考勤系统。该系统以功能强大的 STM32F103C8T6 单片机核心板为控制核心,集成了多个关键模块。用户可以根据需求选择 2.4 寸或 1.44 寸的 TFT 彩屏,用于清晰地显示系统信息和操作…...

从CAD到HMI:如何利用DXF2SVG工具实现可视化组件自动化转换

从CAD到HMI:如何利用DXF2SVG工具实现可视化组件自动化转换 在工业自动化领域,人机界面(HMI)的设计效率直接影响着整个系统的开发周期。传统HMI开发中,工程师往往需要手动重绘CAD设计图纸,既耗时又容易引入误…...

别再只会conda update了!这5个隐藏命令帮你搞定90%的环境管理难题

解锁Conda高阶玩法:5个被低估的环境管理神技 当你已经能熟练使用conda create和conda install时,是否觉得环境管理依然充满各种"玄学"问题?比如团队协作时环境复现总出岔子,或者明明删除了环境却还占用着几个G的磁盘空间…...

2025年文本分类技术全景:从模型演进到工业落地指南

1. 文本分类技术的2025年全景图 记得2018年第一次用BERT做情感分析时,那种"原来还能这样"的震撼感至今难忘。七年过去,文本分类技术已经从实验室里的玩具变成了工业流水线上的标准工具。2025年的文本分类技术栈已经形成了清晰的层级结构&#…...

Vue学习 —— 计算属性

1、计算属性介绍一句话介绍:计算属性本质就是基于已有数据做 “加工”,结果会缓存,只有依赖数据变了才重新算。更简单易懂的例子:用大白话讲:就像你有苹果和梨(原始数据),计算属性就…...

机器视觉零基础入门:(三)图像上采样实战:从原理到代码的像素填充艺术

1. 图像上采样:给照片"无中生有"的艺术 第一次接触图像上采样这个概念时,我脑海里浮现的是科幻电影里那些神奇的画面放大场景——特工们随意放大监控画面,模糊的车牌瞬间变得清晰可见。虽然现实中的技术没那么神奇,但上…...

拆解50kW光伏逆变器的硬件代码实战

三相光伏并网逆变器方案 资料 50kw组串式 主控芯片TMS32F2808,提供pcb,原理图,代码。 组成如下: 1.主控DSP板,芯片型号TMS32F2808,负责逆变器的逆变及保护控制。 原理图为pdf,pcb为AD文件,有P…...

星穹铁道革新性自动化工具:三月七小助手技术解析与应用指南

星穹铁道革新性自动化工具:三月七小助手技术解析与应用指南 【免费下载链接】March7thAssistant 🎉 崩坏:星穹铁道全自动 Honkai Star Rail 🎉 项目地址: https://gitcode.com/gh_mirrors/ma/March7thAssistant 在《崩坏&a…...

MT4移动止损实战:如何用300行代码实现智能追踪止盈(附ma.mq4改造指南)

MT4移动止损实战:300行代码构建智能追踪止盈系统 在趋势交易中,移动止损是保护利润的关键技术。许多交易者都遇到过这样的困境:当市场朝着有利方向运行时,静态止损点无法跟随价格移动,导致本该获得的利润大幅回撤。本文…...

芯片可靠性标准解析:从商规到车规的实战指南

1. 芯片可靠性标准入门:为什么商规、工规、车规差异这么大? 刚入行时,我总纳闷为什么同样功能的芯片,车规级价格能比商规贵5倍。直到有次亲眼目睹某新能源汽车因一颗电源管理芯片失效导致整车趴窝,才明白可靠性标准背后…...

告别‘从入门到放弃’:ESP32+MicroPython项目实战,用OLED做个物联网温湿度计

从零打造物联网温湿度计:ESP32MicroPython实战指南 项目背景与核心价值 在物联网技术快速普及的今天,掌握嵌入式开发已成为开发者的一项重要技能。ESP32作为一款功能强大且价格亲民的微控制器,结合MicroPython的易用性,为初学者打…...

Qt+onnxruntime实战:手把手教你部署MaskRCNN模型(附动态尺寸处理技巧)

QtONNXRuntime实战:工业级MaskRCNN模型部署全流程与动态尺寸优化 在工业检测、医疗影像和智能安防等领域,基于深度学习的实例分割技术正逐渐成为核心解决方案。本文将深入探讨如何将PyTorch训练的MaskRCNN模型高效部署到Qt应用中,特别针对实际…...

【人工智能】中国大模型“六小虎”:百模大战突围者,引领国产AI商业化新征程

中国大模型“六小虎”:百模大战突围者,引领国产AI商业化新征程 2023年被称作中国通用大模型的产业爆发元年,一场席卷科技行业的“百模大战”骤然打响。上百家企业扎堆入局,从参数竞赛到能力比拼,行业在极速爆发中迎来残酷洗牌。当潮水渐退,六家高估值AI创业独角兽从混战…...

Transformer模型探秘03-QKV矩阵在Self-Attention中的核心作用

1. QKV矩阵的物理意义揭秘 第一次接触Transformer模型时,最让我困惑的就是这三个神秘的字母:Q、K、V。它们看起来像某种密码,但实际上却是自注意力机制的核心组件。经过多次实践,我发现理解它们的物理意义比死记公式更重要。 想象…...

Meta关闭Horizon Worlds VR版

就在几年前,马克扎克伯格还激情澎湃地描绘着“元宇宙”作为下一代“具身互联网”的宏伟蓝图,甚至不惜将公司更名为“Meta”以表决心。然而,本周的一则公告标志着其战略发生了戏剧性转变:Meta宣布将关闭其面向Quest虚拟现实头显用户…...

20年如一日,坐标唐山瑞德数码城一层最北排,我们还在坚持

> 从大头显示器到现在的华为、戴尔超薄笔记本、游戏本,从路北的老街坊到全市的企业客户。很多人问:老孙,现在网上买电脑多方便,你这实体店怎么还这么忙?我说:因为服务带不走,信任买不到。电脑…...

论文免费降低ai率:DeepSeek降AI指令实测+3款工具深度测评(2026最新)

知网AIGC检测算法又更新了,很多人的检测报告一片红。不用急着慌,其实只要用对方法,就能降低ai率。 今天这篇,我整理了3组我亲测过的DeepSeek降ai指令,能把有AI味儿的文章改成符合学术规范的人话。最后还有我自己真金白…...

华为交换机策略路由实战:基于流分类的多路径智能选路

1. 为什么企业需要策略路由 想象一下你每天开车上班的场景:平时走高速路只要20分钟,但遇到早高峰时,导航会自动推荐绕城快速路避开拥堵。网络世界里的策略路由就像这个智能导航系统,它能根据数据流的"身份特征"自动选择…...

Node Js 配置环境步骤

下载Node Js 1. 浏览器搜索Node Js中文网 下载 | Node.js 中文网 2. 如果是Windows就选Windows,其他就选其他即可 3. 更改安装路径,啥都不用勾选,一直Next就可以了,安装完成后选择Finish后,删除页面 4. 安装后可以…...

日本汽车又赢了,增程连连下跌,国产车下注节油混动了

2025年增程车只卖了123.5万辆,增速大幅下降,甚至从2025年下半年起连连下滑,占电车销量的比例跌穿了一成,这让国产车大失所望,面对这样的现实部分国产车企迅速转向,开年以来多家国产车企都发布了小容量电池的…...

C++ 实战:STL List 容器自定义排序深度解析

在 C STL 中, 是一个双向循环链表。与 不同,由于 的内存空间是不连续的,它不能使用系统提供的标准算法 std::sort,而是内置了一个成员函数 。std::liststd::vectorlistsort()今天我们就通过一个“人员排序”…...