工欲善其事必先利其器,在做iOS开发前要了解iOS的编程语言和基础语法,了解iOS的编程思想和相关语法基础,这样才能在之后的开发中举一反三,事半功倍。
一、编程语言。
Objective-C
Objective-C是一种在C的基础上加入面向对象特性扩充而成的编程语言,通常称为jObC和较少用的 Objective C或ObjC。在一定程度上,可以把 Objective-C看成是ANSI版本C语言的一个超集,它支持相同的C语言基本语法,同时它还扩展了标准的 ANSI C语言的语法,包括定义类、方法和属性。当然还有其他一些结构的完善和拓展,如类别(Category)的出现。
OC语言有众多优点和特点:
1、程序更小。
Objective-C写成的程序通常不会比其源代码和函式库(通常无须包含在软件发行版本中)大太多,不会像Smalltalk系统,即使只是打开一个窗口也需要占用极大的内存。由于Objective-C的动态类型特征,Objective-C不能对方法进行内联(inline)一类的优化,使得Objective.C的应用程序一般比类似的C或C++程序更小。
2、适应性好、门槛低。
Objective-C较强的编译器适应性。Objective-C可以在现存C编译器基础上实现,而不需要编写一个全新的编译器。这个特性使得Objective-C能利用大量现存的C代码、库、工具和编程思想等资源。现存C库可以用Objective-C包装器来提供一个Objective-C使用的00风格界面包装,这些特性极大地降低了进入Objective-C的门槛。
3、内存管理(垃圾回收)。
Objective-C垃圾回收机制的引进。最初Objective-C不支持垃圾回收机制。在Apple发布的Xcode4中已经支持自动释放功能,严格而言,应该称为“垃圾回收”。因为两者机制不同,在Xcode4中的自动释放,也就是ARC(Automatic Reference Counting)机制,是不需要用户手动去释放一个对象的,而是在编译期间,编译器会自动帮助人们添加那些以前经常写的[NSObject release]。苹果公司在其Mac OS X 10.5中也提供了这种实现。
4、继承特性(OC没有多重继承)
Objective-C独有个性。虽然Objective-C是C的严格超集,但它也不视C的基本类型为第一级的对象。和C++不同,Objective-C不支援运算子多载(它不支持ad-hoc多型)。亦与C++不同,但和Java相同,Objective-C只容许物件继承一个类别(不设多重继承)。Categories和protocols不但可以提供很多多重继承的好处,而且没有很多缺点,如额外执行时间过重和二进制不兼容。
5、动态语言。
由于Objective-C使用动态运行时类型,而且所有的方法都是函数调用(有时甚至连系统调用如syscalls也如此),很多常见的编译时的性能优化方法都不能应用于Obj ective-C(例如,内联函数、常数传播、交互式优化、纯量取代与聚集等),这使得Objective-C性能劣于类似的对象抽象语言(如CH)。不过Objective-C的拥护者认为既然Objective-C运行时消耗较大,Objective-C不该应用于C++或Java常见的底层抽象。
Swift
Swift,苹果于2014年WWDC苹果开发者大会发布的新开发语言,可与Objective-C共同运行于macOS和iOS平台,用于搭建基于苹果平台的应用程序。Swift是一款易学易用的编程语言,而且它还是第一套具有与脚本语言同样的表现力和趣味性的系统编程语言。Swift的设计以安全为出发点,以避免各种常见的编程错误类别。
Objective-C开发者对Swift并不会感到陌生。它采用了Objective-C的命名参数以及动态对象模型,可以无缝对接到现有的Cocoa框架,并且可以兼容Objective-C代码。在此基础之上,Swift还有许多新特性并且支持过程式编程和面向对象编程。
Swift 对于初学者来说也很友好。它是第一个既满足工业标准又像脚本语言一样充满表现力和趣味的编程语言。它支持代码预览,这个革命性的特性可以允许程序员在不编译和运行应用程序的前提下运行Swift代码并实时查看结果。
Swift作为全新的语言,它的优势也是非常明显的。语法表达十分简洁,很容易上手
1、基础语法
//基础语法
//常量:let 常量名:类型=初值
let myName:String = "theonelgq"
//变量
var intro
//基本类型
Int32 Int16 Int64 Int8
Double Character Float Bool
Swift 的字符是一个单一的字符字符串字面量,数据类型为 Character
let char1: Character = "A"
如果你想在 Character(字符) 类型的常量中存储更多的字符,则程序执行会报错,如下所示:
//Swift 中以下赋值会报错
let char: Character = "AB"
2、字符串和数组
let a:Int = 5
let b:Int = 8
//嵌入值
print("\(a+b)")
//可变字符串
var myName:String
//定长数组
let myToys:[String] = ["car","phone","watch"]
//变长数组
var myWathes:[String]
swift4有新的特性,需要以后慢慢学习。
二、面向对象的思想。
首先说一下编程范式。编程范式Programming paradigm是指计算机中编程的典范模式或方法,也就是编程的风格,常见的编程方法有:函数式编程、事件驱动编程、面向对象编程、过程话(命令话)编程等。
过程化(命令化)编程
过程化编程,也被称为命令式编程。它应该是最原始的、也是我们最熟悉的一种传统的编程方式之一。
尽管现存的计算机编程语言种类很多,但是人们把所有支持过程化编程范式的编程语言都被归纳为过程化编程语言。例如,机器语言、汇编语言、BASIC、COBOL、C 、FORTRAN、语言等等许多编程语言都被归纳为过程化语言。
过程化语言特别适合解决线性(或者说按部就班)的算法问题。它强调“自上而下(自顶向下)精益求精的设计方式。这种方式非常类似我们的工作和生活方式,因为我们的日常活动都是按部就班的顺序进行的。
事件驱动编程
事件驱动常常用于用户与程序的交互,通过图形用户接口(鼠标、键盘、触摸板)进行交互式的互动。当然,也可以用于异常的处理和响应用户自定义的事件等等。
事件驱动不仅仅局限在GUI编程应用。但是实现事件驱动我们还需要考虑更多的实际问题,如:事件定义、事件触发、事件转化、事件合并、事件排队、事件分派、事件处理、事件连带等等。
其实,到目前为止,我们还没有找到有关纯事件驱动编程的语言和类似的开发环境。所有关于事件驱动的资料都是基于GUI事件响应的。
属于事件驱动的编程语言有:VB、C#、Java(Java Swing的GUI)等。它们所涉及的事件绝大多数都是GUI事件。
面向对象编程(OOP)
面向对象的程序设计模式已经出现二十多年,经过这些年的发展,它的设计思想和设计模式已经稳定的进入编程语言的主流。来自TIOBE Programming Community2010年11月份编程语言排名的前三名Java、C、C++中,Java和C++都是面向对象的编程语言。
-
对象
在面向对象的语言中,世间万事万物都可以看作是对象。
面向对象的程序设计的抽象机制是将待解问题抽象为面向对象的程序中的对象。利用封装使每个对象都拥有个体的身份。程序便是成堆的对象,彼此通过消息的传递,请求其它对象进行工作。 -
类
在面向对象语言中,每个对象都是其类中的一个实体。
物以类聚——就是在说明:类是相似对象的集合。类中的对象可以接受相同的消息。换句话说:类包含和描述了“具有共同特性(数据元素)和共同行为(功能)”的一组对象。
比如:苹果、梨、橘子等等对象都属于水果类。
面向对象的程序设计包括了三个基本概念:封装性、继承性、多态性。面向对象的程序语言通过类、方法、对象和消息传递,来支持面向对象的程序设计范式。
-
封装
封装(有时也被称为信息隐藏)就是把数据和行为结合在一个包中,并对对象的使用者隐藏数据的实现过程。信息隐藏是面向对象编程的基本原则,而封装是实现这一原则的一种方式。
封装使对象呈现出“黑盒子”特性,这是对象再利用和实现可靠性的关键步骤。 -
接口
每个对象都有接口。接口不是类,而是对符合接口需求的类所作的一套规范。接口说明类应该做什么但不指定如何作的方法。一个类可以有一个或多个接口。 -
方法
方法决定了某个对象究竟能够接受什么样的消息。面向对象的设计有时也会简单地归纳为“将消息发送给对象”。 -
继承
继承的思想就是允许在已存在类的基础上构建新的类。一个子类能够继承父类的所有成员,包括属性和方法。
继承的主要作用:通过实现继承完成代码重用;通过接口继承完成代码被重用。继承是一种规范的技巧,而不是一种实现的技巧。 -
多态
多态提供了“接口与实现分离”。多态不但能改善程序的组织架构及可读性,更利于开发出“可扩充”的程序。
继承是多态的基础。多态是继承的目的。
合理的运用基于类继承的多态、基于接口继承的多态和基于模版的多态,能增强程序的简洁性、灵活性、可维护性、可重用性和可扩展性。
主要功能就是能够使子类在覆盖或者重载父亲方法的同时产生不同结果。
函数编程
- 代码简洁,开发快速
函数式编程大量使用函数,减少了代码的重复,因此程序比较短,开发速度较快。
- 接近自然语言,易于理解
函数式编程的自由度很高,可以写出很接近自然语言的代码。
- 更方便的代码管理
函数式编程不依赖、也不会改变外界的状态,只要给定输入参数,返回的结果必定相同。因此,每一个函数都可以被看做独立单元,很有利于进行单元测试(unit testing)和除错(debugging),以及模块化组合。
- 易于"并发编程"
函数式编程不需要考虑"死锁"(deadlock),因为它不修改变量,所以根本不存在"锁"线程的问题。不必担心一个线程的数据,被另一个线程修改,所以可以很放心地把工作分摊到多个线程,部署"并发编程"(concurrency)。
请看下面的代码:
var s1 = Op1();
var s2 = Op2();
var s3 = concat(s1, s2);
由于s1和s2互不干扰,不会修改变量,谁先执行是无所谓的,所以可以放心地增加线程,把它们分配在两个线程上完成。其他类型的语言就做不到这一点,因为s1可能会修改系统状态,而s2可能会用到这些状态,所以必须保证s2在s1之后运行,自然也就不能部署到其他线程上了。
多核CPU是将来的潮流,所以函数式编程的这个特性非常重要。
- 代码的热升级
函数式编程没有副作用,只要保证接口不变,内部实现是外部无关的。所以,可以在运行状态下直接升级代码,不需要重启,也不需要停机。
三、线程和进程。
进程和线程(thread)是操作系统的基本概念,但是它们比较抽象,不容易掌握。
如果把计算机比喻成一个工厂,那么每一个车间就是一个进程,而线程就是就好比是车间中的工人。一个进程包括多个线程,线程共享内存,通过互斥锁(Mute)来防止多个线程同时读写某一块内存区域,通过“信号量”(Semaphore)来保证多个线程不会相互冲突。
线程是运行时执行的一组指令序列
每个进程至少包含一个线程,在iOS中,进程启动时的主要线程是主线程。所有的UI要在主线程中创建和管理,与用户交互的所有中断最终都会分发到UI线程,IBAction方法的代码都会在主线程中执行。
Cocoa不允许其他线程进行UI的更新操作,只能放在主线程中进行,在其他线程执行的耗时操作,比如网络请求或其他的处理,代码都必须进行上下文切换到主线程进行UI的更新。
线程和进程也可以这样描述:进程和线程都是一个时间段的描述,是CPU工作时间段的描述。
CPU看来所有的任务都是一个一个的轮流执行的,具体的轮流方法就是:先加载程序A的上下文,然后开始执行A,保存程序A的上下文,调入下一个要执行的程序B的程序上下文,然后开始执行B,保存程序B的上下文。。。。
-
进程就是包换上下文切换的程序执行时间总和 = CPU加载上下文+CPU执行+CPU保存上下文
CPU加载上下文,开始执行程序A的a小段,然后执行A的b小段,然后再执行A的c小段,最后CPU保存A的上下文。
多线程
多线程就是利用CPU同时处理多个任务来提高软件工作效率和资源利用率的方法。线程过多的时候,会消耗大量的CPU资源。
线程开销
虽然多线程很赞,能够提供资源利用率,但是每一个线程都有一定的开销,从而影响整个应用的性能。线程不仅仅有创建时间的开销,还会消耗内核的内存,即应用的内存空间。
内核中每个线程大约消耗1KB的内核内存空间。这块内存用于存储与线程有关的数据结构和属性,这块是联动内存,所以无法被分页。
栈空间
主线程的栈空间大小为1M,而且无法被修改。所有的二级线程分配的栈内存大小为512KB。在线程创建之前,栈空间的大小是可以改变的,栈空间的最小值是16KB,其大小为4KB的整数倍。
线程创建耗时
创建线程的耗时大概在4-5毫秒。
创建线程后启动线程的耗时区间为5-100毫秒,平均大约在29毫秒。这是很大的时间消耗,若在应用启动时开启多个线程,则尤为明显。线程的启动时间之所以如此之长,是因为多次的上下文切换所带来的开销。
四、反射、注解与泛型
反射、注解与泛型是APP开发中大量使用的几个概念。
反射
反射(Reflection)是在程序运行状态中动态监测、访问或者修改类型的行为特性,具体表现为以下两个方面:
- 对于任意一个类,都能知道这个类的所有属性和方法。
- 对于任意一个类,都能够随意调用它的任何一个方法和属性。
反射可以让我们在程序运行时获取类的属性和方法、构造方法、父类、接口等信息,也可以在运行期实例化(构造和生成)类的属性和调用方法等。
注解
一种注射机制,类、方法、变量、参数和包等都可以被标注,代码级别的说明。
@Override - 检查该方法是否是重写方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误。
@Deprecated - 标记过时方法。如果使用该方法,会报编译警告。
@SuppressWarnings - 指示编译器去忽略注解中声明的警告。
注解源自于java,可以理解为给代码打标记。Objective-C 本身是没有支持注解功能的,但有时使用注解将大幅提高效率,同时让代码更简单易懂。我们可以通过模拟的形式实现注解的功能。
要模拟注解的过程,需要解决,1.不影响以前有的业务。 2.在被注解的源代码实现里面能方便的获取注解内容,可以理解为被注解的代码,在编译期间能自动生成一段代码在被注解类里面,或者我们需要建立一个“被注解者”与“注解代码”的对应关系。
注解的基本格式为 @annotation(attr)
@page(@"main")
- (void)dispatch {
// Do stuff...
}
泛型
泛型是程序设计语言的一种特性。允许程序员在强类型程序设计语言中编写代码时定义一些可变部分,那些部分在使用前必须作出指明。可以把类型明确的工作推迟到创建对象或调用方法的时候才去明确的特殊的类型
-
使用场景
1.在集合(数组NSArray、字典NSDictionary、集合NSSet)中使用泛型比较常见。
2.当声明一个类,但是类里面的某些属性的类型不确定的时候,我们才使用泛型。 -
书写规范
在类型后面定义泛型:NSMutableArray<UITouch *> dataArray -
泛型的好处
1.提高开发的规范,减少程序员之间的交流。
2.通过集合取出来的对象,可以直接当做泛型对象使用。这样我们就可以直接使用.点语法。 -
声明一个泛型为NSString的数组(例子)
//具体做法就是在 NSMutableArray 后带一个 <NSString *> ,尖括号内部即为泛型类型
@property (nonatomic, strong, nullable) NSMutableArray<NSString *> *dataArray;
- 泛型的自定义
id:任何对象都能传进来 而且不确定是否为空
@property (nonatomic, strong, null_unspecified) id language;
知识扩展
iOS9的几个新关键字(nonnull、nullable、null_resettable、__null_unspecified)
1、nonnull:字面意思就能知道:不能为空(用来修饰属性,或者方法的参数,方法的返回值)
//三种使用方式都可以
@property (nonatomic, copy, nonnull) NSString *name;
@property (nonatomic, copy) NSString * _Nonnull name;
@property (nonatomic, copy) NSString * __nonnull name;
//补充(不适用于assign属性,因为它是专门用来修饰指针的)
@property (nonatomic, assign) NSUInteger age;
//补充(用下面宏包裹起来的属性全部都具nonnull特征,当然,如果其中某个属性你不希望有这个特征,也可以自己定义,比如加个nullable) 12 //在NS_ASSUME_NONNULL_BEGIN和NS_ASSUME_NONNULL_END之间,定义的所有对象属性和方法默认都是nonnull
2、nullable:表示可以为空(使用方法和上面几乎一样,但是没有发现和上面类似的宏)
//三种使用方式
// 方式一:
@property (nonatomic, copy, nullable) NSString *name;
// 方式二:
@property (nonatomic, copy) NSString *_Nullable name;
// 方式三:
@property (nonatomic, copy) NSString *__nullable name;
3、null_resettable: get:不能返回空, set可以为空(注意:如果使用null_resettable,必须 重写get方法或者set方法,处理传递的值为空的情况)
@property (nonatomic, copy, null_resettable) NSString *name;
4、_Null_unspecified:不确定是否为空
@property (nonatomic, strong) NSString *_Null_unspecified name;
网友评论