Programming with Objective-C

Posted 题材新颖

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Programming with Objective-C相关的知识,希望对你有一定的参考价值。

有段时间没有看ios开发相关的知识了,现在来看看官方文档,写写相关博客。

现在正在看的是 Programming with Objective-C,当前看到了 Working with Objects。

说到对象的话,其实就涉及到了面向的对象的概念,而 OC 作为一门面向对象的语言,OC 的开发自然和面向对象脱不了关系。在面向对象的世界中,可以说,万物皆为对象,对象之间通过消息来沟通。所以对于 OC 而言,所谓的函数其实就是消息,函数的调用,就是发送消息,函数的实现,就是接受消息。这一块相关的语法其实很简单,所以不多做讨论。

谈到对象,就避免不了指针,因为对象存储了,总得有个东西来记录,让我们可以追溯到它。在 OC 中,就采用指针来追溯对象,这一点和 C 是差不多的。但是这里有个需要提到的地方,那就是对象的生存周期。对于局部变量,我们都知道,它的生存周期取决于代码块,一旦超出代码块就会被销毁,但是对象有所不同,它的声明周期会稍微长一些。比如我们在一个代码块中声明了一个指针,当代码块执行结束的时候,这个指针就被销毁了,但是需要注意的是,该指针指向的对象,其实是还存在着的。这里可能会有一点疑惑,主要就是要区分清楚指针和对象,指针是对象,在分配的时候是存在于栈中的(当然,这里指的是局部),创建的时候只要声明就可以了。但是对象是不同的,对象的创建,涉及到内存的分配,属性的初始化,然后我们才得到对象,而这个过程,是在堆上面完成的。指针和对象之间的关联,只是说指针存放了对象所对应的地址。所以当代码块执行完了,指针被释放掉了,但是对象还存在。这么做的意义就在于,当代码块执行完之后,对象可能还需要起到一定的作用,比如说,函数会返回一个对象,将这个对象的属性,复制给另一个对象,也就是采取深复制的时候。在这种情况下,局部产生的对象,就不能让它随着代码块来释放了。那么对象到底怎么释放?这个就可以交给 ARC 来解决了。那么根据前面提到的,对象和指针的关系,因为指针是一种变量,所以如果在函数内部需要从外部传进来一个对象,我们就可以通过指针来完成。

考虑到函数内部的对象的话,其实函数内部还有两个隐含的对象。首先就是 self,这个对象所指向的,是接受到信息的对象。也正是基于这一点,在编写某个类的时候,我们可以考虑到把某些相同的功能给抽出来,比如下面这样:

@implementation

- (void)sayHello() 
    [self saySomething:@"Hello, world!"];


- (void)saySomething() 
    NSLog("%@", greeting);

像这样去写代码,我们可能会有很多不同的方法要实现,比如 sayHello, 比如 sayGoodbye,等等,但是实质上他们只是输出字符串而已,只是字符串的内容不同,那我们就可以把输出的功能单独抽出来,然后在其他消息内部,去写上不同的字符串传过来就可以了,万一对于这些方法的功能有所变更,那我们也只需要变更 saySomething 就可以了。

另外一个隐含的指针则是 super,这个指针用于指向当前类的父类。当引入 super 类之后,意味着子类和父类中的某些属性,乃至函数,都是可以共用的。这里的共用,就不只是类内部的共用了,可以直接跨两个类来使用,还是用上面的代码作为例子。上面的代码我们当做是 Person 类的方法,接下来我们需要另一个类,比如 Woman 类,然后在 saySomething 中,我们把所有的输出,都变成大写的。那么当我们对 woman 对象发送 sayHello 消息的时候,输出的就是大写的字符串了。当然,这里还有个麻烦的地方,如果我们对于输出的文字,需要修改,那么,两个类都要修改。那么有了 super 关键字,我可以确定我即将发送的消息将发送给父类,我也就可以把基础功能交给父类来实现。比如父类的 saySomething 实现的就是单纯的字符串的输出,那么子类对字符串的附加处理,比如转成大写,我转换之后,通过 super 关键字调用父类的 saySomething 就可以了。这样以后如果对于整个 Person 的输出有什么改动,我们需要的就只是改动 Person 的saySomething,并且能够保证子类功能不受影响。

谈到了 this 和 super,就有必要提到 OC 中的对象。OC 中的对象是动态创建的,并且它的创建流程基本和 C 一致。也就是说,OC 中的对象,是先分配内存,然后在初始化,之后得到的才是一个可以放心操作的对象。在这个过程中, OC 提供了两个函数,alloc 和 init 让我们来完成相应的操作。alloc 的作用有两个:一个是内存的分配;另外一个就是对内存的清理。谈到内存清理的时候可能有人会产生疑惑,alloc 对内存的清理就是置0,那么为什么我们还需要 init 呢?这个地方的原因在于,alloc 的内存置0,针对的是内存,它所做的实际操作,是把内存中已经存放着的,上一次使用该内存的对象的相关内容清空,主要目的是保证你当前分配的对象,其中的一些属性,不会受到之前分配在这个位置的值的影响。从这里也可以看到,alloc 并没有考虑到初始化的问题,而是把初始化的工作交给了 init,这么设计的好处我也并不是很明白,但是无疑这是很符合函数的耦合性要求的。接下来说说 init,init 的主要作用就是初始化,那么对于从 C/C++ 过来的人,init 给人的感觉就是不可思议,因为在 C/C++ 的世界中,如果你要初始化,特别是当类中含有指针的时候,一定要在构造函数中搞定它的初始化。但是在 OC 中你完全不用考虑,直接 init 就可以了。这里的话,因为 OC 中的 nil 存在特殊性。对于指针,init 默认初始化成 nil,而在 OC 中,nil 虽然是空指针,但是是可以接受消息的,并且不会出现异常,所以在 OC 中就没有这么多顾虑了。

alloc 也好,init 也好,它们的返回类型都是 id,id 在 OC 中也是具有特殊地位的。首先,id 本身就是一个指针类型,这句话可能有点绕口,主要意思就是,通常我们声明指针,都需要加上一个星号,但是 id 类型等于是自带星号,我们不用再考虑星号的问题。其次,id 类型它可以指向任何类型的对象,并且对于 id 类型的对象在实际发消息的时候,在运行时是可以找到它真正的对象的。只不过消息要是发错了,那么程序就会报错。这里需要注意的是,init 所返回的 id 类型,并不能确保就是原来的对象,因为初始化可能会返回一个新创建的对象,所以如果在初始化的时候不去用一个指针保存新创建的对象,那么很有可能你所获得对象其实并没有初始化完成。比如下面这个代码就是有问题的。

NSObject *someObject = [NSObject alloc];
[someObject init];

关于类和对象其实也就只剩下一点了,那就是 OC 为我们封装好的,一系列工厂方法,我们经常可以用到一些方法名称带个 with 的函数,调用这类函数我们可以直接获得对象,这些函数其实就是已经封装好的工厂方法。关于工厂方法可以自行了解一下,其实也没有太多的内容。

以上是关于Programming with Objective-C的主要内容,如果未能解决你的问题,请参考以下文章

Aspect-Oriented Programming : Aspect-Oriented Programming with the RealProxy Class

System and device programming——R&W with semaphore

PyQt5 GUI Programming With Python 3.6

[RxJS] Reactive Programming - Using cached network data with RxJS -- withLatestFrom()

[Functional Programming] propSatisfies with implies

iOS 10 Programming Fundamentals with Swift 学习笔记 0