编程实现描述方式编程里面的问题

1字符串的末尾是’\0’,不是’/0’否者下列程序运行时会字符串越界

2题目是牛客网上剑指offer的第二题

请实现一个函数,将一个字符串中的每个空格替换成“%20”例如,当芓符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy

因为操作的是字符串不是字符

C++中的string类型结尾没有’\0’,而char数组的结尾都是:’\0’,所以char类型数组的长度為看的长度+1,最后一个字符为’\0’

}

Objective-C将很多静态语言在编译和链接时莋的事放到了运行时来处理只要有可能,它做了一些动态这意味着该语言需要不只是一个编译器,但也是一个运行时系统来执行的编譯代码运行时系统作为一种Objective-C操作系统的; 是什么使该语言工作。

本文着眼于NSObject类以及Objective-C程序的运行时系统交互尤其是,在运行时动态??加載新类和转发消息到其它的对象。它还提供了有关如何可以找到有关对象的信息当你的程序运行的信息。

你应该阅读这个文件来获得嘚Objective-C运行系统是如何工作的理解以及如何利用它通常情况下,你写一个Cocoa 应用程序很少需要知道和理解这些

在legacy runtime模式下,如果你在类中的布局改变实例变量你必须从它继承的类重新编译

在modern runtime模式下,如果你在类中的布局改变实例变量 你不必从它继承的类重新编译

另外,modern runtime 支持實例变量合成为声明的属性

Objective-C 程序的运行时交互在三个不同层次:通过Objective-C源码;通过Foundation框架NSObject类中定义的方法;通过直接调用运行时的函数

在大哆数情况下,运行时系统自动的工作你可以仅仅通过编写和编译Objective-C源码

当你编译的代码中包含Objective-C类和方法,编译器会创建实现动态语言特性嘚数据结构和函数调用数据结构获取类和类别的定义以及协议声明中找到的信息,它们包括在Objective-C语言中定义一个类和协议的对象以及方法selectors, 实例变量模板,并且从源代码中提取其他信息主要的运行时方法是发送消息,描述方式编程在由源代码消息表达式调用。

在Cocoa中的大哆数对象是NSObject的子类所以大多数对象继承了它所定义的方法。(值得注意的是NSProxy类 从查看更多信息)。因此它的方法建立在每个固有的实例和對象 然而,在少数情况下NSObject类仅仅定了如何去做的模板,它不提供是有必要的代码本身

例如,NSObject类定义了一个返回类内容描述方式编程芓符串的实例方法description这主要用于调试——GDB打印方法命令打印方法返回的字符串。NSObject对方法的实现并不知道类包含因此他返回一个描述方式編程对象的名称和地址的字符串。NSObject的子类可以此实现方法的更多细节例如,Foundation的类NSArray返回它所包含对象列表的描述方式编程

一些NSObject方法简单哋查询runtime system 的信息。这些方法允许对象进行自我检查这类方法的实例是类方法,他要求一个对象以确定它的类 和 ,它测试了类在继承层次對象的位置;其指示对象是否接受一个特定的消息;,其指示对象是否声明实现一个特定协议中实现的方法;其提供了一种方法的实现嘚地址这样的方法提供了对象內省得能力。

runtime system 是一个公共接口包含一组函数和数据结构的动态分享库头文件目录位置:/usr/include/objc。许多函数允许鼡纯C去复制Objective-C 代码的编译器实现其他基础功能可以通过Objective-C 类的方法来导出。这些功能使得能够在runtime系统中开发其他接口并且增加开发环境的笁具;在Objective-C编程时他们不需要。然而当编写一个Objective-C 程序时的runtime 函数可能是有用的。所有这些功能文档在.

这章描述方式编程消息表达式转换为的函数调用以及如何使用方法名来调用方法。它然后解释了如何使用objc_msgSend优势,以及如何绕过动态绑定---绕过需要的话 

在Objective-C, 消息知道运行时才绑萣方法的实现编译器转换消息表达式:

调用一个发送消息函数,该函数需要接收器和在消息中提到的函数名, 这个方法选择器——两個主要参数:

在消息中你也可以传递任何参数给:

消息传递函数做必要的动态绑定的一切:

它首先找到selector指向的程序(方法实现)由于同样地方法能够通过单独类的不同方式实现,这个精确的过程依赖于类的接收器

然后,它调用程序通过它的接受对象(它的数据指针),以忣用于该方法指定的任何参数

最后,它通过程序的返回值作为自身的返回值

发送消息的关键在于编译器生成每个类和对象的结构体每個类结构体包含 两个基本元素:

一个调度表。这个表具有特定类对该方法标识的地址及相关联的方法选择器条目setOrigin::方法选择器关联setOrigin::方法地址(实现地址),选择器的显示方法与显示的地址相关联等等。

当创建一个对象时内存被分配,并且它的实例变量被初始化首先在類结构中对象的变量实际上是一个指针。这个指针叫做isa给了类的对象访问权限,并通过类来找到所有他继承的类。

当一个消息被发送給你一个对象这个消息传递函数遵循对象的isa指针的类结构,其查找调度表中的方法选择器如果它不能在哪里找到方法选择器,objc_msgSend找到父類的指针并试图找到其在调度表中的方法选择器。连续的失败引起objc_msgSend追溯类结构至到NSObject类一旦定位到选择器,函数调用调度表中的方法並传递结束对象的数据结构。

这种方法实现在运行时被选中或者在面向对象编程里,即方法动态绑定到消息为了加速消息处理过程,runtime system緩存了选择器与方法地址当被调用过对于每一个类有一个单独的缓存,包含选择器和继承方法以及在类中定义的方法在搜索调度表前,消息例程首先检查接受对象类的缓存(理论上一个方法一旦被调用后很大可能被再次调用)如果在缓存中存在这个方法选择器,消息发送呮比函数调用稍微慢一点一旦一个程序以及运行足够长的时候去"唤醒"它的缓存,几乎所有发送的消息能够找到缓存的方法缓存的动态增长以适应程勋运行的新信息。

当找到方法实现的过程中他调用程序并且传递在消息中所有的参数。它还传递两个隐藏的参数

这些参数給每个方法实现关于方法表达式调用的准确信息他们说的"hidden",因为他们没有定义在方法的源码中而是代码编译时被插入进来的。

尽管这些参数没有被明确声明源码任然可以指向他们(正如它可以指向接受对象的实例变量)。一个方法可以指向接收对象的本身并且它自己的選择器_cmd。在下面的例子中_cmd指向strange方法的选择器并且self接收strange消息。

self是两个参数中比较有用的信息实际上,这样接收对象实例变量使变量在方法中更精确

避免动态绑定的唯一方式是获取函数地址并且直接调用它,就像他是一个函数在一个特定方法罕见的被多次执行的情况下昰释放的,这样就避免了消息调用的每一次开销

在NSObject类中定义的methodForSelector:方法,你可以获取一个指向实现方法的指针然后用这个指针来调用程序。methodForSelector: 方法返回的指针必须强制转换为正确类型无论在返回值还是在参数类型中都应包括。

下面的例子展示了thesetFilled:方法实现是怎么被调用的:

传遞给该过程的前两个参数接受对象(self)和方法选择器(_cmd)这些参数在方法语法中被隐藏但是方法做为一个函数调用时必须明确。

使用methodForSelector:来规避动态綁定大部分有消息通知所需的时间然而,这种节省只有在一个特定消息被重复使用多次如上面for 循环展示的。

本章描述方式编程了如何提供一个方法的动态实现

某些情况下可能需要提供一个方法的动态实现。例如Objective-C的属性声明功能(参见Objective-C语言的属性声明)包括 @dynamic 指令:

它告诉编譯器,与属性关联的方法是动态的提供的

你可以实现方法和 去为实例和类方法动态提供一个给定的程序实现。

一个Objective-C方法根本是至少有两個参数(self和_cmd)的C函数你可以用方法给类方法添加一个功能,因此给出以下功能:

转发方法(例如消息转发)和动态方法解析,在很大程度上正茭一个类在转发生效前有机会动态解决一个方法。如果 或者被调用时, 动态的解析时首先提供一个选择器的IMP的选择如果要实现但要对于特殊选择器去转发,你返回No对于这些

一个Objective-C在运行时可以载人并链接一个新类。新的代码并入程序和相同的处理已在开始时加载雷和类別。

动态loading可以用来做很多不同的事情例如在系统预置应用程序中的各种模块是动态加载。

在Cocoa环境下动态载入通常运行应用程序进行定淛。其他人的写的模块可以在运行时载入你的程序——就像Interface Builder载入自定义调色板和OS X系统加载自定义的喜好模块可扩展模块可以扩展你的应鼡程序功能。它们有助于在你允许胆没有预料或自定义的方式 你提供框架,其他人提供代码

定义)文件有运行时函数来执行Objective-C模块的动态加载,Cocoa的NSBundle类提供了一个更方便的接口用于动态加载——一个面向对象并集成了相关服务在Foundation framework 参考中NSBundle类规范中关于一个NSBundle类及其使用参考。 

发送一个消息给你个对象却不处理消息是错误的然而,在公布错误前runtime system系统给接受对象第二次机会来处理消息。

如果你发送一个消息到一個不处理消息的对象在公布错误前,runtime发送这个对象一个NSInvocation对象做文艺参数的forwardInvocation:消息 ?该NSInvocation对象封装原始消息和它已通过的参数。

你可以实现這个forwardInvocation:方法得到一个消息的默认反馈,去避免某些其他方式的错误正如名字所暗示的,forwardInvocation:通常用于转发消息到另一个对象

要查看转发的范围和目的,想想一下以下场景:假设首先你正设计一个对象,能对一个所谓谈判的消息做出反应并且希望包含对另一个对象的反馈。你可以在negotiate方法的实现很容易的发送anegotiate消息给其他对象

更进一步,假设你希望你对anegotiate消息的响应在另一个类中实现响应你的类继承自其他類会很容易坐到这一点。然而它可能无法以这种方式安排事情。可能有好的理由为什么你的类和实现negotiate的类在继承层次的不同分支。

即使你的类不能继承negotiate方法你仍然可以"借用"它,通过实现一个简单地将消息传递到其他类的一个实例方法来实现:

这种做事方式可能变得有點麻烦尤其你有一些消息需要从你的对象传递给其他对象。你必须实现一个方法来覆盖每一个你想从其他类借用的方法此外,你在写玳码时处理你你想要转发的全套消息是不可能的这组依赖于runtime时的事件,当一个新的类或者方法被实现时可能会改变

由forwardInvocation:提供第二次机会:消息提供了对于这个问题的解决,?而另一个是动态的而不是静态的它的工作原理是这样的:当一个对象不能对消息做出响应因为在消息中选择器没有匹配到方法, runtime

被转发消息的返回值被返回给原始的发送者所有类型的返回值可以被传递给发送者,包括idsstructures和双精度浮點数。

一个forwardInvocation:方法可以作为未识别消息的集散地分发他们到不同的接收器。或者它可以作为一个传输站发送所有消息到同一个目的地。咜可以转发一个消息到另一个或者干脆"吞"一些消息,所以没有返回值也没有错误一个forwardInvocation:可以合并多个消息到一个当以响应。forwardInvocation:做的是实现但是,它提供了一种为转发链接对象开辟程序设计的可能性

转发模仿了多继承,可以向在Objective-C程序中的对多继承的一些效果如图5-1所示,┅个对象响应一个消息听过借用或继承在其他类中实现的方法

在这个例子中,一个Warrior类的实例变量转发一个anegotiate消息给一个Diplomat类的实例Warrior会出现潒Diplomat类的negotiate。它将返回一个反馈给negotiate消息并且为所有实际目的做出回应(尽管做出反馈的是Diplomat类)。

转发消息的对象从两个继承体系"继承"——它自己嘚分支和响应消息的对象在上面的例子中,它看起来像Warrior继承自Diplomat类和他的父类

转发提供了大多数希望从多重继承中想要的功能。然而兩者之间有一个重要的区别:多重继承在单一对象中集成不同能力。它趋向于大型多层面的对象。而另一方面转发分配不同的职责给鈈同的对象。它分解问题为更小的对象但在某种程度上是透明的的消息转发到相关联对象。

转发不仅模仿多重继承这也使你开发出轻量级的对象,代表或"掩盖"较大幅度的对象其他对象的替代对象和漏斗消息。

在Objective-C编程语言中"Remote Messaging"代理是这样一个替代品代理转发消息到一个遠程接收器,确保参数值被复制并通过连接检索等等。但它不会尝试做出更多地行为它不会复制远程对象的功能而只是简单地给出远程对象的地址,可以在另一个应用程序接受对象的地方

其他种类的替代对象也是可能的。举个例子你有一个操作大量数据对象——或許创建一个复杂的图片或读取磁盘文件的内容。设置这个对象可能非常耗时所以你选择懒加载——在它需要或者系统资源闲置时。同时你需要这个对象需要至少一个占位符,以便应用中的其他对象正常工作

在这种情况下,你可以在最初创建时不全部生成而是一个轻量级的替代。这个对象做一些事情如有关数据答题,但大多数只是一个多对象的地址当时间触发转发消息给它。当代理的forwardInvocation:方法首先收箌发往另一个对象的消息确保对象存在如果不存在创建它。大对象的所有消息都通过替代这样,对于程序的其他部分而言替代和大對象是一样的。

结果是NO虽然能够无误的收到negotiate消息并回应它,从某种意义上说消息被转发给Diplomat(见图5-1)。

在很多情况下NO是正确地答案。但它吔可能不是如果你用转发去设置一个代理对象或者扩展一个类的功能,转发机制夜巡应该是透明的继承如果你转发消息的对象继承了這些行为,你需要重新实现respondsToSelector:?和 isKindOfClass:方法包括转发算法:

你可能考虑把在私有代码中考虑转发算法并拥有这些方法,包含并调用forwardInvocation:

为了帮助runtime system編译器编码了返回值和参数类型为字符串的方法,并且把字符串与方法选择器关联它使用的编码方案可以在上下文中使用编译指令@encode()是公開可用的。当给定一个类型@encode()返回一个类型的字符串编码。类型可以是基础类型如int,pointerstructure或union,或类名——实际上任何类型可以被C的sizeof()操作苻使用。

下表列出了类型编码需要注意的是其实很多时候为了存档和分发的目的编码对象,不过这里有你编写编码器时不能使用的编碼列表,并有你希望使用但不能被@encode()编码器生成



数组的类型编码是一个封闭的方括号;数组中元素的数目在数据类型前可以马上指定。 例洳一个12个指针的浮点数组可以编码为:

结构体是一个特殊的括号和括号里的unions结构体标签被列在第一位,接着是等号和在队列中的结构体芓段编码例如,这个结构体:

同样地编码结果是否已定义的类型名(Example)或者结果体标识(example)被传递给@encode()结构体指针编码对于结构体字段携带了相哃的信息:

不过,另一方面它间接消除了内部规则:

对象被当做结构对待例如NSObject类用@encode()编码得到:

NSObject类声明是一个变量实例,isa请注意尽管@encode()指囹不直接返回他们,当他们在协议中声明方法用runtime system用额外的编码如表6-2

当编译器遇到属性声明时(参见Objective-C语言属性声明),它通常会生成一个与封閉类类别或协议相关联的描述方式编程性元数据。你可以通过类或协议支持查找属性的函数来访问元数据获得属性类型作为@encode字符串,並复制属性列表的属性作为一个C字符串数组为每一个类和协议声明可使用的属性列表。

Property结构体定义了一个不透明的句柄属性描述方式编程符

例如,给出以下类声明:

你可以用property_getAttributes函数来发现名称和属性@encode类型的字符串对于编码字符串的细节,查看该字符串的详细吸吸,查看 和 

把这些一起使用起来,你可以打印一个类相关联的属性的了列表用如下代码:

你可以使用property_getAttributes 函数去发现名字属性的@encode字符串, 和属性嘚其他属性字符串在使用@encode类型是在开始使用T和一个逗号,在结束时跟实例变量的名字和一个V这些中间属性被指定使用如下描述方式编程,以逗号分隔:

}

本课程为收费课程请先购买当湔课程

本课程为会员课时,请先开通会员

本课程为会员课时您的会员账号已经过期

本课程为会员课时,您的会员账号已被禁用

章未解锁暂无观看权限

拼团未完成,暂无观看权限

购买未完成暂无观看权限

评价 好评 中评 差评

发表评价的小伙伴,每周机会获得讲师卡~~

正在打包请勿关闭和刷新页面

恭喜学完本节课程,5秒后自动切换下一节课程

下一节课程:学习的重要性 (02:59)

}

我要回帖

更多关于 描述方式编程 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信