iOS自学笔记-Objective-C编程BNR(第2版)

今天对Objective-C编程(第2版)这本书做一个简短的学习总结。

如何阅读本书

本书第1章就谈了这个问题,按照作者说的做吧。

《iOS自学笔记-Objective-C编程BNR(第2版)》

内容编排(全书分6大部分)

第1部分:入门

俗话说:良好的开始是成功的一半。这部分内容帮我树立了正确的编程学习观念,如果不下水去游,学再多的理论知识也不会游泳,编程就跟学游泳一样,只看理论是学不好的,只有多敲代码,多犯错并修正错误,才能熟练掌握语言。

本部分简单介绍了一个绝大部分编程入门书都会举的“Hello World!”程序例子,并简单解释了这个程序是如何运行的。就是这个小小的程序背后也涉及了很多知识点尤其是底层方面的,但在目前阶段不建议去深究,我以前就因为想多了解,看了著名的深入理解计算机系统–>计算机系统概论–>高质量程序设计指南:C++、C语言–>C程序设计语言–>C Primer Plus(第5版)(中文版)–>C和指针–>C语言程序设计:现代方法(第2版),这些都是很好的书,而且我都购买了(虽然这些书网上都有电子扫描版本的,但是为了提高自己的学习主动性也方便自己学习,我还是花钱购买了,提高了我的学习经济成本,那我必须好好学一学,赚钱不容易啊!)但我这样就偏离了我的主要目的学习掌握Objective-C语言,而且战线拉长了,学习很曲折,很打击我的学习积极性,这些书我也看的很浅,收获甚微,毕竟这里的每一本书都可以上一学期的课了,所以我后来回归主线,回到这本书的学习了。

顺便说一下关于C语言入门的学习心得吧,这个说起来都是泪&累。不多说了,我就推荐下自己学过觉得还不错的资料吧。对于没什么计算机基础知识的朋友推荐(百度云分享)C语言自学教程-郝斌,讲解很详细,有利于掌握知识点,内容不复杂,有利于学习积极性。主要学习书籍推荐C语言程序设计:现代方法(第2版),关于这本书豆瓣里有很好比较中肯的评价了,可以参考下。这只是C语言的入门学习资料,我目前还比较菜,但学好C语言我觉得还是有利于以后的编程之路。毕竟怎么强调基础知识都不过分!

第2部分:如何编程

本部分讲述了学习Objective-C语言之前需要了解的C语言内容,重点讲述了基本数据类型,控制结构,函数,指针,结构。

函数

变量是“与某块数据相关联的名称”,函数是“与某块代码相关联的名称”。程序可以向函数传递信息,要求函数执行代码并返回信息。当需要某个功能时,就可以通过创建函数来实现。程序是函数的集合,当程序运行起来时,计算机会将这些函数从硬盘拷贝到内存里,然后找到”main” 函数并执行。函数里可以有局部变量,保存在函数的帧(frame)中,栈(stack)是程序员用来描述这些帧在内存中存储的地点。栈是后进先出的,执行函数时,函数的帧会在栈的顶部被创建出来,函数执行结束时,其帧会退出栈,也就是函数返回了,等待下次被调用。

指针

内存是有数字编号的,通常用地址(address)来指代某个特定字节的数据。当程序在计算机上执行时,相关一切数据(变量,函数等)都会保存在内存里,并有各自的地址。指针就是用来保存这些地址的,本身不保存这些值,而是指向某个地址,而这个地址就是保存了值的内存地址。为什么需要指针呢?因为程序不一定通过拷贝来传递数据,但是一定能够直接传递或者通过拷贝来传递数据的起始地址。一旦有了数据的起始地址,就能很容易地存取相应数据了。

通过引用传递

C函数只能返回一个值,那怎么做到返回两个或更多的值呢?可以通过引用传递来实现:调用函数时传入某个地址(也称为引用),然后由函数将数据存入该地址指向的内存。书中是这样比喻的:假设你负责为间谍委派任务。一天,你通知某个间谍:“想办法拿到财政部长和他女友在一起的照片。我已经在公园的天使雕像下藏了一根短钢管,得到照片后,卷起来放在钢管里,我会在周二午饭后取走。”用间谍行话讲,称为情报传递点(dead drop)。调用函数时要传入某个地址,类似公园的短钢管,这样函数就可以将计算结果存放在指定位置,以便调用方取用。

结构和堆

编写程序时,可能要使用一个变量来保存多个相关的数据。C语言中,我们可以使用结构(structure)来实现这个功能。每份数据称为结构的成员(memeber)。

前面的程序使用的都是栈中的内存,这类内存空间会在调用函数时由系统自动分配,并在函数结束后自动释放。这也是局部变量常被称为自动变量的原因。只有自动变量是不够的,有时还要“申请”一块连续的内存--缓冲区(buffer)。缓冲区来自特定的内存区域--堆(heap),堆和栈是分开的。

在堆上,缓冲区独立于任何函数的栈。因此它可以在多个函数中使用。例如,你可以声明一个缓冲区储存一些文本文件,然后调用某个函数将文本文件保存至缓冲区里,再调用另一个函数来统计文本中元音字母的个数,最后调用一个函数来进行校对。处理完文本文件后,就可以将缓冲区的这块内存返还给堆。

重中之重点来了

前面我们声明的结构是将其作为main()函数帧上的栈中的局部变量,也就是栈上的结构,现在我们来在堆中为结构分配一块缓冲区,声明一个堆上的结构。而这个堆上的结构就是接下来我们要学习 Objective-C对象的实现基础。Objective-C对象的本质就是结构体,而且是堆上的结构体。

想想为什么要使用在堆上的结构体,而不使用栈上的结构体呢?这个问题在内存管理里会有个答案的。带着问题学可以带动自己多思考帮助自己更好的理解概念,这样的学习也是最有效的。

简单说下我对这个问题的理解:在栈上的结构体有两个优点1.高速,栈使用的是一级缓存,所以在栈上可以迅速的分配内存,而堆使用的是二级缓存。2.简单,栈上的结构体有自己的生命周期,还记得前面的局部变量吗,栈上结构体也是一样的,当超出它的作用域时就会被计算机自动销毁了。但它的生命周期无法服从ARC,在Obiective-C里是通过自动引用计数(ARC)来管理内存的,每个对象都会对指向自己的指针(也就是引用)进行计数,当引用数为0时就会认为不需要该对象了,程序会自动销毁该对象。而栈上的结构体或者说对象,有唯一一个指向自己的拥有者即创建栈对象的函数,当你将这个对象传给其他代码使用时,你对他retain是无效的,也无法保存它,因为当创建栈结构体的函数返回时,栈结构体就被自动销毁了,这会产生一个悬垂引用,导致程序崩溃。所以Objective-C对象使用的是堆上的结构体即堆上的对象。不过也有正常使用的栈上对象,那就是Blocks对象。

第3部分Objective-C与Foundation

本部分是全书的重点内容,讲述了Objective-C的基本语法和特性,还讲解了最基本框架Foundation里的几个常用的类。

Objective-C语言是C语言的超集,它是以“从堆中创建出来的结构”为理论基础,加入了来自于smalltalk语言里的消息发送机制,形成的面向对象的语言。

对象(object)和结构类似,但对象不仅包含数据即实例变量还包含函数即方法。

类(class)负责描述某个特定类型的对象,其中包括方法和实例变量。类可以描述以下两种对象:1.抽象概念,例如数据,字符串或指令集。2.实际存在的事物,例如人,地点或储蓄账户。一个类定义了一种对象,同时它也可以用来创建这种对象。类既是对象的原型,又是生产对象的工厂。

方法和函数类似。它根据指令执行相应的功能。Objective-C语言中,如需执行方法里的代码,首先需要发送一条消息(可以带参数)给包含这个方法的对象或类。

对象只能通过指针来访问,因为对象在堆上,你要声明一个指针变量来保存对象在内存中的地址。

栈是由有序的帧构成的,函数的帧在函数结束后会自动释放,而堆里的对象不会自动释放,需要通过ARC自动引用计数来管理了。

内存管理的思考方式:

1.自己生成的对象,自己持有。

2.非自己生成的对象,自己也能持有。

3.不再需要自己持有的对象时释放。

4.非自己持有的对象无法释放。

摘自《Objective-C高级编程:iOS与OS X多线程和内存管理》

属性(property)

属性是Objective-C语言的一项特性,用于封装对象中的数据。属性简化了存取方法,对象会把所需数据保存为实例变量,实例变量一般通过存取方法来访问。属性拥有4类特性,这些特性会影响编译器所生成的存取方法。这4类特性分别是:

1.原子性(nonatomic)默认情况都是原子性的,属性要么是原子性的要么是非原子性的,这个区别与多线程运行相关。具体内容我还没了解。目前一般都是采用非原子性的。

2.读/写权限(readwrite和readonly)默认是读写权限,表示该实例变量拥有存方法和取方法。属性要么读写性药么只读性,只读性就是实例变量只拥有取方法。

3.内存管理语义(assign,strong,weak,unsafe_unretained,copy)因为数据有“具体的所有权语义”。这组特性仅会影响“设置方法”。例如用“设置方法”设定一个新值时,它是应该“保留”此值呢?还是只将其赋给底层实例变量就好?

4.方法名(getter=<name>和setter=<name>) 这个可以用来指定存取方法的方法名。

摘自《Effective Objective C 2.0:编写高质量iOS与OS X代码的52个有效方法》

继承(inheritance)

继承也是Objective-C语言的一项特性,子类的实例可以继承父类的所有方法。通常子类和父类有所不同,可以通过覆盖(override)来改变继承自父类的方法,分为全部改变和部分改变(super)。

根类NSObject拥有很多方法,但只有一个实例变量: isa指针。任何一个对象的isa指针都会指向创建该对象的类。给对象发送消息的时候,对象就会查询是否有该消息名的方法。搜索会通过isa指针找到该对象的类并查询“是否有名为消息名的实例方法?”如果没有,就会继续查询它的父类。依此类推,对象会沿着继承链向上查询,直到找到名为消息的方法,或者到达继承链的顶端(NSObject)为止。

对象实例变量及属性

实例变量可以是指针,指向其他对象。以下是对象实例变量的三种常见用途。

1.对象属性:指针,指向某个单一的,代表某个值的对象。

2.一对一关系:指针,指向单个复杂的对象。

3.一对多关系:指针,指向某个colllection类的实例。

强引用和弱引用

对象所有权:当某个对象(A)拥有某个对象实例变量(B)时,称A拥有B指向的对象。

事物间的关系时双向的,所以对象间“相互拥有”的所有权关系导致相关的对象都无法被释放,这种情况就称为强引用循环。这是导致内存泄露的常见原因。通过弱引用可以解决强引用循环问题。弱引用时不说明所有权的指针,也就是将属性里内存管理语义类设为 weak。如果对象间是父-子关系,父对象拥有子对象,但是子对象不拥有父对象。

强引用会保留对象的拥有方,使其不被释放。而弱引用则不会保留,因此标为弱引用的实例变量与属性指向的对象可能会消失。如果发生这种情况,那么这个实例变量或属性会被设为nil,而不失继续指向曾经指向的对象。

回调(callback)

回调就是将一段可执行的代码和一个特定的事件绑定起来。当待定的事件发生时,就会执行这段代码。

在Objective-C语言中,有四种途径可以实现回调。

1.目标-动作对(target-action):在程序开始等待前,要求“当事件发生时,向指定的对象发送某个特定的消息”。这里接收消息的对象是目标(target) ,消息的选择器(selector)是动作(action)。

2.辅助对象(helper objects):在程序开始等待前,要求“当事件发生时,向遵守相应协议的辅助对象发送消息”。委托对象(delegate)和数据源(data source)是常见的辅助对象。

3.通知(notification):在程序开始等待前,可以告知通知中心“某个对象正在等待某些特定的通知。当其中的某个通知出现时,向指定的对象发送特定的消息”。当事件发生时,相关的对象会向通知中心发布通知,然后再由通知中心将通知转发给正在等待该通知的对象。

4.Block对象(blocks):Block是一段可执行的代码。还记得前面说的栈上的对象吗?它就是的了。在程序开始等待前,声明一个Block对象,当事件发生时,执行这段Block对象。

协议(protocols)

你是谁和你做什么是不同的概念,对象也是这样的:在某个工作系统中,对象的类和对象要扮演的角色是不同的。协议可以为一个对象指定角色。协议是一组方法的声明,其中部分是必需的,另一些是可选的。如果对象要扮演特定的角色,就一定要实现相应的必需方法,并选择实现部分可选方法。

第4部分 由事件驱动的应用

这部分实现了两个简单的应用分别是iOS应用和Cocoa应用。本书用的Xcode版本是5.0的,最新的7.3版本对比5.0版本有较大变化,不过也不大影响按照书中来学习这两个案例。

第5部分 Objective-C高级主题

这部分讲了五个知识点:init方法,进一步讲解属性,KVC(key-value coding),KVO(key-value observing),范畴(Categories)。本书简单讲解了一下,还需要在接下来的实例学习中加深理解。

第6部分 C语言高级主题

这部分也讲了五个知识点:位运算,C字符串,C数组,从命令行运行,Switch语句。

结束语

至此,这本书就学完了,但这还只是开始,后面要走的路还很长,一定要学好这个语言基础,后面的路才好走,所以这本书我还要时常查阅。

《iOS自学笔记-Objective-C编程BNR(第2版)》

此文写了一点自己关于Objective-C还比较肤浅的理解,如有不对的地方,欢迎指正!谢谢!

最后送上这本书的(百度云)英文版吧,英语不怎么好的还是推荐看中文版。

浪沙淘金!加油!

    原文作者:斯道名城
    原文地址: https://www.jianshu.com/p/69161ba546d7
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞