概述
Objective-C
主要有以下三种文件类型:
扩展名 | 内容类型 |
---|---|
.h | 头文件。头文件包含类,类型,函数和常数的声明。 |
.m | 源代码文件。这是典型的源代码文件扩展名,可以包含 Objective-C 和 C 代码。 |
.mm | 源代码文件。带有这种扩展名的源代码文件,还可以包含 C++ 代码。 仅在你的 Objective-C 代码中确实需要使用 C++ 类或者特性的时候才用这种扩展名。 |
新建类文件,在 xcode 中使用快捷键 command + N
。可见它创建了一下的两个文件:
|
|
|
|
下面由这两个文件,展开介绍一下 OC 的内容:
Objective-C 的类规格说明包含了两个部分:定义(interface)与实现(implementation)。
- 定义(interface)部分包含了类声明和实例变量的定义,以及类相关的方法。
- 实现(implementation)部分包含了类方法的实际代码。
NSObject
:NSObject
是大部分 OC 类继承体系的根类。这个类提供了一些通用的方法,比如对象的创建与初始化方法。对象通过继承
NSObject
,可以从其中继承访问运行时的接口,并让对象具备 OC 对象的基本能力:1 2 3 4 5 6 7 8
@interface NSObject <NSObject> //instancetype:返回结果以方法所在的类为类型 - (instancetype)init; //初始化对象 + (instancetype)alloc; //类方法,创建与分配内存空间 + (instancetype)new; //等价于 alloc + init @end
如何创建并初始化一个对象:
1 2 3
NSObject *obj = [[NSObject alloc] init]; Person *person = [[Person alloc] init]; // 这类似于 Person.alloc().init()
成员变量
成员定义
成员定义的位置是在类定义最开始的一个花括号中:
|
|
比如下面的一个例子:
|
|
变量作用域
与 c++ 中类似,成员变量也有访问控制机制,除了 @public
、@protected
、@public
三种访问控制之外,还有第四种 @package
:
- 被声明为
@package
的变量,在实现这个类的可执行镜像中表现为@public
(可以直接访问),对外则表现为@private
(不可以访问); - 它类似于对 c++ 中的 private 关键字的扩展,如果外部尝试访问被声明为
@package
的变量,则会导致编译器的连接错误;
成员方法
定义方法
我们下面列举一个方法的定义来介绍一个方法定义的完整结构:
|
|
- 第一个部分是
+/-
:- 加号(+)代表类方法(class method),不需要实例就可以调用,与C++ 的静态函数相似;
- 减号(-)即是一般的实例方法(instance method);
- 第二个部分是
(instancetype)
指明方法的返回值类型; Objective-C
的函数名可以分开写,也就是说上面定义的函数名实际为initWithNameandAge
;(这个语法简直太鬼畜了)- 函数的参数则是冒号后定义的参数类型和参数名,上面定义的是
name
与age
;
实现方法
下面是一个上面方法的实现:
|
|
super
关键词是一个编译器符号,用于调用父类方法;self
代指对象本身;
下面是第二个方法的实现:
|
|
调用方法
其实在第二个方法实现的过程中已经用到了相关方法 的调用。
因为第二个方法是静态方法,我们也可以直接通过下面的方式对方法进行调用:
|
|
其他补充
如何在方法外访问类的成员?
- 同
c++/java
一样,只能通过定义Setter
与Getter
方法。 - 为了避免大量的冗余代码,我们引入了属性的概念。
属性 Property
定义属性
什么是属性?
- 如果你定义个名为
member
的属性,编译器会自动生成名为_member
的成员变量; - 编译器会自动为声明为属性的成员变量添加
Setter
与Getter
方法。比如member
这个属性的如果被定义,则会生成setterMember
与getterMember
这两个方法。
定义一个属性,主要通过下面的语法实现:
|
|
比如我们可以将之前 Person 的两个成员变量定义为属性:
|
|
属性修饰符
把上面的语法中,括号内的部分称作属性的修饰符(我不知道应该怎么翻译了),那么它有以下这些选项可以选择:
- 可写性控制:设置这个属性主要用于控制编译器是否自动生成
Setter
方法。它有两个可选值readwrite
与readonly
,前者是默认值; Setter
语法控制:与 iOS 的内存管理机制有关,详见 ARC 内存管理机制;- 原子性控制:
atomic
与nonatomic
两种取值,前者是默认值。如果设置为前者,在多线程操作的时候都会取到一个原子性操作之后的值。
使用属性
比如我们需要访问或设置一个对象的属性值,可以使用传统的 c++ 点语法:
|
|
也可以使用 Setter
函数与 Getter
函数:
|
|
类别 Category
什么是类别?
Objective-C
可以为现有的类添加新的方法,并不需要通过继承的方式实现,也不需要访问原生代码,这种动态特征称为类别。- 通过类别可以动态的为现有类添加新的方法,并且可以将类定义模块化地分布到多个相关文件中。
在 Xcode 添加 Objective-C File,并将类型设置为 Category
、其指定的类设置为 Person
、将其名称命名为 Read
。xcode 会自动生成以下 Person+Read.h
与 Person+Read.m
两个文件:
|
|
|
|
PostScript:
- 类别方法会影响所有子类;
- 可以为一个类定义多个类别;
- 类别方法会覆盖原有的类方法;
- 多个类别重名方法结果不确定(避免重名)。
扩展 Extension
扩展与类别同样是为了解决代码的可扩展性问题,它与类别的主要区别是:
扩展仅能在原始类的
.m
文件中实现;扩展可以添加新的属性,类别 “不行”;
类别是运行时特性,扩展属于编译器特性。
实际上就是在 .m
文件中进行额外的声明,它与 .h
文件中进行声明的区别是,外部只能引用头文件中定义的属性、成员、方法等。
协议 Protocol
定义协议
什么是协议?
- 定义公共接口的地方,只要类申明遵守了一个协议,就等于在头文件中定义了协议里的方法。
- 协议可以将不同类的共同行为抽象出来,类似于
Java
里的接口,但更加强大。
在 Xcode 中,与创建类别一样的步骤,将文件类型设置为 Protocol 即可。与之前的几种都不同,xcode 只会自动生成一个 .h
头文件,如下:
|
|
下面是一个协议实现的例子,我们通过这个例子解释一下协议定义语法的各个部分:
|
|
- 声明一个协议的关键词是
@protocol
与@end
;跟在前者后面的PrintProtocol
是协议名称; - 尖括号语法
<NSObject>
是父协议,支持多继承,逗号分隔; @optional
与@requireed
是成员的修饰符,分别指明实现这个协议的模块,可选或者必须地实现相应的成员或属性;- 协议中的
property
相当于只能名Getter
,Setter
方法。
实现协议
在定义类的时候需要通过尖括号,比如下面的方式指定协议:
|
|
此即定义 Person
继承自 NSObject
,并实现协议 PrintProtocol
。
同样定义扩展或类别时,也可以通过尖括号指定协议,比如:
|
|
另外需要在 .m
文件中实现对应的方法:
|
|
也可以用泛型表示一个满足协议的类:id<TargetProtocol>
选择器 Selector
参考官方文档
在 Objetive-C 中,选择器 selector
有两个含义:
- 在源代码中,它可以代指一个对象的成员方法;
- 另外,在源代码已经编译完成的二进制文件中,它是一个唯一标识符。考虑到执行效率,编译器会抛弃源代码中用人类可读的 ASCII 表示的标识符,生成一个唯一标识写入表格中,它们类型为
SEL.
;
也就是说,什么是 @selector
:
- 选择器就是在 Objective-C 语言中,对方法进行唯一标示的标识符;
基本常用类
- NSNumber
- NSString、NSMutableString
- NSArray、NSMutableArray
- NSDictionary、NSMutableDictionary
- NSData、NSMutableData