写了一段时间的iOS,把iOS和javascript在各方面做一下对比,记录一些比较基础的东西。

iOS开发包含哪些东西?

  • 二门语言:Objective-C,Swift
  • 一个基础框架:Cocoa Touch
  • 一个IDE:XCode

我需要准备哪些东西?

  • Mac电脑,身残志坚的朋友可以选择黑苹果。
  • 最好有一台iPhone,身残志坚的朋友没得选了,通常在模拟器里面没问题的东西在真机里面会有一些诡异的问题,特别是涉及到摄像头以及麦克风等等硬件的时候。

Objective-c

从名字上可以见,Objective-c就是对象化的C语言,所以支持C语言,同时扩展了一些面向对象的内容,比如:Class

Objective-c与类C类的语言的一个比较大的区别是,Objective-c使用消息传递模型,从如下代码可以看出:

假设我要调用car实例的move方法,

C++

1
car.move();

Objective-c

1
[car move];

从语法上可以看出一二,C++中确实是调用了car实例的move方法,如果car实例没有这个方法,那么编译期间就会报错,但是Objective-c仅仅是向car实例发送了一个move消息,代码只会在运行时去检查car实例到底有没有能力对move消息进行回应,从中可以看出Objective-c是一门动态语言。

新建一个iOS应用

打开xcode,选择红框中的项目。

创建项目

然后选择Single View Application,顾名思义:单页应用。

取个名字:IOS-dev-demo,然后选择保存的地方,一个新的iOS应用就创建好了。

创建文件夹

xcode会自动帮我们创建四个group,分别是IOS-dev-demoIOS-dev-demoTestsIOS-dev-demoUITestsproducts,作用如下:

  • IOS-dev-demo 项目的主目录,所有的项目业务代码都保存在这个group下。
  • IOS-dev-demoTests 单元测试目录。
  • IOS-dev-demoUITests UI测试目录。
  • Products 相关编译文件保存目录。

基本我们用到最多的就是我们的项目主目录了。

运行我们的项目

运行按钮

CMD+R 或者点击左上方三角形的运行按钮,即可在模拟器上运行,刚创建好只是一个空的应用。

搭建我们的应用

为什么用搭建这个词呢,因为Cocoa Touch已经帮助我们封装好了很多内部组件,和安卓不同,Cocoa Touch的内部组件通常都有一个比较好的交互和外观,典型应用是微信,微信基本都使用了内部组件,内部组件也最符合用户使用习惯。

搭建界面有两种方式:

  1. storyboard,可视化组件拖拉,类似于Dreamweaver,这种方式不做介绍了,快速搭建应用首选,也存在些许弊端,有兴趣的可以看看这篇文章
  2. 纯代码,类似于手写HTML代码。

如何添加组件?

拿一个button组件来说,在ViewController类中的viewDidLoad方法中添加如下代码:

1
UIButton *button = [[UIButton alloc] init];
button.frame     = CGRectMake(100, 100, 100, 50);
[button setTitle:@"按钮测试" forState:UIControlStateNormal];
[button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];

[self.view addSubview:button];

重新编译运行一下我们的代码,就可以看到一个黑色的按钮在界面上了。

下面用浏览器的方式展示如何添加一个按钮,相信大家一定都非常熟悉了。

1
const button = document.createElement( 'button' );
button.textContent  = '按钮测试'
button.style.width  = '100px';
button.style.height = '50px';
button.style[ 'margin-left' ]   = '100px';
button.style[ 'margin-top'  ]   = '100px';
button.style.color  = 'rgb(0, 0, 0)'

document.body.appendChild( button );

对比一下两段代码:

1
// 创建一个组件
UIButton *button = [[UIButton alloc] init];                                ||| let button = document.createElement( 'button' );

// 设置组件的left, top, width, height属性
button.frame     = CGRectMake(100, 100, 100, 50);                          ||| button.style.width  = '100px';
                                                                           ||| button.style.height = '50px';
                                                                           ||| button.style.margin-left   = '100px';
                                                                           ||| button.style.margin-top    = '100px';

// 设置组件的'标题'属性
[button setTitle:@"按钮测试" forState:UIControlStateNormal];                 ||| button.textContent  = '按钮测试'

// 设置组件标题的颜色
[button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; ||| button.style.color  = 'rgb(0, 0, 0)'

// 将组件添加到当前画布上
[self.view addSubview:button];                                             ||| document.body.appendChild( button );

其实从逻辑上来看两段代码非常相似,唯一不同的仅仅是语法而已。

那么从系统层面看,两者如何?

首先来看看html

htmlc

再看看iOS

cocoac

响应UI事件

通常在页面上放置了一个按钮并不是为了展示信息,而是为了能让用户进行交互,那么来看看两者如何进行UI响应。

Javascript

1
button.addEventListener( 'click', function( event ) {
  alert( 'clicked' );
} );

拿到button对象,然后给这个button注册一个名字叫click的事件,再给这个注册方法一个回调函数,然后用户在这个按钮上单击的话,就能够触发这个回调函数了。

Objective-c

1
[button addTarget:self action:@selector(clickHandler:) forControlEvents:UIControlEventTouchUpInside];

调用button对象的addTarget方法,为UIControlEventTouchUpInside事件,也就是单击事件添加self也就是当前实例下的clickHandler作为事件处理函数。这里也可以选择别的类的方法,只要selector中的方法名能在addTarget对象中找到就行。
iOS中只能使用类的方法作为事件处理函数,没法使用类似匿名函数的block来进行事件处理。

回调

Javascript中发送一个http请求

1
let xhr = new XMLHttpRequest();
xhr.open( 'GET', 'http://xxxx.com' );
xhr.send();

xhr.onreadystatechange = function(){
  // do something
}

Javascript基于事件驱动,通常使用一个匿名函数来作为请求结束时候的回调函数。

Objective-c

1
NSURL *url = [NSURL URLWithString:@"http://xxx.com"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];

NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *task = [session dataTaskWithRequest: request completionHandler: ^(NSData *data, NSURLResponse *response, NSError *error) {
    NSString *result = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
         // ...
     }];
[task resume];

Objective-c 相对来说发送一个请求的方式稍微有一些复杂,看到completionHandler这个参数,这在Objective-c中被称为block,是一种类似于Javascript中的回调函数,当然也可以使用typedef声明一个block在多个场景下使用。

关于block更多的使用奥秘,可以点击查看这篇文章

动画

数据请求回来了,那么就要开始渲染页面了,iOS有良好的动画运行机制,可以比安卓更流畅地运行动画,良好的动画效果可以带来更好的用户体验。

Javascript

JS中动画效果大致有如下几种:

纯粹的JS实现的动画效果应该就只有如下一种了

  • setTimeout,定时更新形变达到每秒24次即可实现基本的动画效果。

示例代码:

1
let width = 100;
setInterval( function() {
  width++;
  button.style.width = `${width}px`;
}, 16 );

更多更高效的动画效果可以借助HTML5CSS3来实现

  • requestAnimationFrame,保持和系统相同的刷新率,得到更好体验的同时,有更高的效率。
  • css中的transition-duration,效率非常高,可以直接调用GPU来进行渲染。

Objective-c

iOS中的动画主要是指Core Animation框架,这里是官方的文档

调用方式主要有以下三种:

  1. UIView 代码块调用
  2. UIView [begin commit]模式
  3. CoreAnimation中的类

简单贴一下最简单也最常用的 UIView 代码块调用的方式,其他方式的详细调用可以参考这里

1
CGRect newFrame = CGRect( 200, 200, 100, 50 );
[UIView animateWithDuration:1 animations:^{
  button.frame = newFrame;
}];

修改UI只能在主线程中执行,如果说要在回调函数中执行以上代码,需要嵌套在下面的代码中进行:

1
dispatch_async(dispatch_get_main_queue(), ^{
                
});

总结

有了以上这些东西,你能已经能把一个小功能呢给串起来了,但是iOS还有更加强大实用的各种组件,UINavigationControllerUITableViewControllerNSUrlSession,还有各种全局事件派发,多线程GCD,以及处理音视频的AVFoundation,更多功能,请看进阶篇。

iOS