概述

Objective-C 主要有以下三种文件类型:

扩展名内容类型
.h头文件。头文件包含类,类型,函数和常数的声明。
.m源代码文件。这是典型的源代码文件扩展名,可以包含 Objective-C 和 C 代码。
.mm源代码文件。带有这种扩展名的源代码文件,还可以包含 C++ 代码。
仅在你的 Objective-C 代码中确实需要使用 C++ 类或者特性的时候才用这种扩展名。

新建类文件,在 xcode 中使用快捷键 command + N。可见它创建了一下的两个文件:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// Person.h
#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface Person : NSObject

@end

NS_ASSUME_NONNULL_END
1
2
3
4
5
6
// Person.m
#import "Person.h"

@implementation Person

@end

下面由这两个文件,展开介绍一下 OC 的内容:

  1. Objective-C 的类规格说明包含了两个部分:定义(interface)与实现(implementation)。

    1. 定义(interface)部分包含了类声明和实例变量的定义,以及类相关的方法。
    2. 实现(implementation)部分包含了类方法的实际代码。
  2. NSObjectNSObject 是大部分 OC 类继承体系的根类。

    这个类提供了一些通用的方法,比如对象的创建与初始化方法。对象通过继承 NSObject,可以从其中继承访问运行时的接口,并让对象具备 OC 对象的基本能力:

    1
    2
    3
    4
    5
    6
    7
    8
    
    @interface NSObject <NSObject>
    
    //instancetype:返回结果以方法所在的类为类型
    - (instancetype)init;		//初始化对象
    + (instancetype)alloc;	//类方法,创建与分配内存空间
    + (instancetype)new;		//等价于 alloc + init
    
    @end
    
  3. 如何创建并初始化一个对象:

    1
    2
    3
    
    NSObject *obj = [[NSObject alloc] init];
    
    Person *person = [[Person alloc] init];	// 这类似于 Person.alloc().init()
    

成员变量

成员定义

成员定义的位置是在类定义最开始的一个花括号中:

1
2
3
4
5
6
@interface ClassName : ItsSuperclass
{
    // Instance variable declarations.
}
// Method and property declarations.
@end

比如下面的一个例子:

1
2
3
4
5
@interface Person : NSObject {
    NSString *_name;		// NSString 是 OC 中的字符串
    NSInteger _age;			// NSInteger 是 OC 中的整型
}
@end

变量作用域

与 c++ 中类似,成员变量也有访问控制机制,除了 @public@protected@public 三种访问控制之外,还有第四种 @package

  • 被声明为 @package 的变量,在实现这个类的可执行镜像中表现为 @public(可以直接访问),对外则表现为 @private(不可以访问);
  • 它类似于对 c++ 中的 private 关键字的扩展,如果外部尝试访问被声明为 @package 的变量,则会导致编译器的连接错误;

成员方法

定义方法

我们下面列举一个方法的定义来介绍一个方法定义的完整结构:

1
2
3
4
5
6
7
@interface Person : NSObject {NSString *_name; NSInteger _age;}

- (instancetype)initWithName:(NSString *)name andAge:(NSInteger)age;

+ (instancetype)personWithName:(NSString *)name andAge:(NSInteger)age;

@end
  1. 第一个部分是 +/-
    1. 加号(+)代表类方法(class method),不需要实例就可以调用,与C++ 的静态函数相似;
    2. 减号(-)即是一般的实例方法(instance method);
  2. 第二个部分是 (instancetype) 指明方法的返回值类型;
  3. Objective-C 的函数名可以分开写,也就是说上面定义的函数名实际为 initWithNameandAge;(这个语法简直太鬼畜了)
  4. 函数的参数则是冒号后定义的参数类型和参数名,上面定义的是 nameage

实现方法

下面是一个上面方法的实现:

1
2
3
4
5
6
7
8
- (instancetype)initWithName:(NSString *)name andAge:(NSInteger)age {
    self = [super init];
    if (self) {
        _name = name;
        _age = age;
    }
    return self;
}
  1. super 关键词是一个编译器符号,用于调用父类方法;
  2. self 代指对象本身;

下面是第二个方法的实现:

1
2
3
4
+ (instancetype)personWithName:(NSString *)name andAge:(NSInteger)age {
    Person *person = [[Person alloc] initWithName:name andAge:age];
    return person;
}

调用方法

其实在第二个方法实现的过程中已经用到了相关方法 的调用。

因为第二个方法是静态方法,我们也可以直接通过下面的方式对方法进行调用:

1
Person *person1 = [[Person alloc] initWithName:@"小明" andAge:18];

其他补充

如何在方法外访问类的成员?

  • c++/java 一样,只能通过定义 SetterGetter 方法。
  • 为了避免大量的冗余代码,我们引入了属性的概念。

属性 Property

定义属性

什么是属性?

  • 如果你定义个名为 member 的属性,编译器会自动生成名为 _member 的成员变量;
  • 编译器会自动为声明为属性的成员变量添加 SetterGetter 方法。比如 member 这个属性的如果被定义,则会生成 setterMembergetterMember 这两个方法。

定义一个属性,主要通过下面的语法实现:

1
@property (attribute1 [, attribute2, ...]) type name;

比如我们可以将之前 Person 的两个成员变量定义为属性:

1
2
3
4
5
6
@interface Person : NSObject

@property (nonatomic, strong) NSString *name;
@property (nonatomic, assign) NSInteger age;

@end

属性修饰符

把上面的语法中,括号内的部分称作属性的修饰符(我不知道应该怎么翻译了),那么它有以下这些选项可以选择:

  1. 可写性控制:设置这个属性主要用于控制编译器是否自动生成 Setter 方法。它有两个可选值 readwritereadonly,前者是默认值;
  2. Setter 语法控制:与 iOS 的内存管理机制有关,详见 ARC 内存管理机制;
  3. 原子性控制:atomicnonatomic 两种取值,前者是默认值。如果设置为前者,在多线程操作的时候都会取到一个原子性操作之后的值。

使用属性

比如我们需要访问或设置一个对象的属性值,可以使用传统的 c++ 点语法:

1
2
3
Person *person = [[Person alloc] init];
person.name = @"小明";						//点语法设置
NSString *name = person.name;			//点语法访问

也可以使用 Setter 函数与 Getter  函数:

1
2
[person setAge:18];						//Setter 方法设置
NSInteger age = [person age];	//Getter 方法访问

类别 Category

什么是类别?

  • Objective-C 可以为现有的类添加新的方法,并不需要通过继承的方式实现,也不需要访问原生代码,这种动态特征称为类别。
  • 通过类别可以动态的为现有类添加新的方法,并且可以将类定义模块化地分布到多个相关文件中。

在 Xcode 添加 Objective-C File,并将类型设置为 Category、其指定的类设置为 Person、将其名称命名为 Read。xcode 会自动生成以下 Person+Read.hPerson+Read.m 两个文件:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// Person+Read.h
#import "Person.h"

NS_ASSUME_NONNULL_BEGIN

@interface Person (Read)

@end

NS_ASSUME_NONNULL_END
1
2
3
4
5
6
// Person+Read.m
#import "Person+Read.h"

@implementation Person (Read)

@end

PostScript

  • 类别方法会影响所有子类;
  • 可以为一个类定义多个类别;
  • 类别方法会覆盖原有的类方法;
  • 多个类别重名方法结果不确定(避免重名)。

扩展 Extension

扩展与类别同样是为了解决代码的可扩展性问题,它与类别的主要区别是:

  1. 扩展仅能在原始类的 .m 文件中实现;

  2. 扩展可以添加新的属性,类别 “不行”;

  3. 类别是运行时特性,扩展属于编译器特性。

实际上就是在 .m 文件中进行额外的声明,它与 .h 文件中进行声明的区别是,外部只能引用头文件中定义的属性、成员、方法等。

协议 Protocol

定义协议

什么是协议?

  • 定义公共接口的地方,只要类申明遵守了一个协议,就等于在头文件中定义了协议里的方法。
  • 协议可以将不同类的共同行为抽象出来,类似于 Java 里的接口,但更加强大。

在 Xcode 中,与创建类别一样的步骤,将文件类型设置为 Protocol 即可。与之前的几种都不同,xcode 只会自动生成一个 .h 头文件,如下:

1
2
3
4
5
6
7
8
9
#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@protocol PrintProtocol <NSObject>

@end

NS_ASSUME_NONNULL_END

下面是一个协议实现的例子,我们通过这个例子解释一下协议定义语法的各个部分:

1
2
3
4
5
6
7
8
9
@protocol PrintProtocol <NSObject>

@optional
@property (nonatomic, strong) NSString *name;

@required
- (void)printName;

@end
  1. 声明一个协议的关键词是 @protocol@end;跟在前者后面的 PrintProtocol 是协议名称;
  2. 尖括号语法 <NSObject> 是父协议,支持多继承,逗号分隔;
  3. @optional@requireed 是成员的修饰符,分别指明实现这个协议的模块,可选或者必须地实现相应的成员或属性;
  4. 协议中的 property 相当于只能名 Getter, Setter 方法。

实现协议

在定义类的时候需要通过尖括号,比如下面的方式指定协议:

1
@interface Person : NSObject <PrintProtocol>

此即定义 Person 继承自 NSObject,并实现协议 PrintProtocol

同样定义扩展或类别时,也可以通过尖括号指定协议,比如:

1
@interface Person (Read) <PrintProtocol>

另外需要在 .m 文件中实现对应的方法:

1
2
3
- (void)printName {
    NSLog(@"%@", self.name);
}

也可以用泛型表示一个满足协议的类:id<TargetProtocol>

选择器 Selector

参考官方文档

在 Objetive-C 中,选择器 selector 有两个含义:

  1. 在源代码中,它可以代指一个对象的成员方法;
  2. 另外,在源代码已经编译完成的二进制文件中,它是一个唯一标识符。考虑到执行效率,编译器会抛弃源代码中用人类可读的 ASCII 表示的标识符,生成一个唯一标识写入表格中,它们类型为 SEL.

也就是说,什么是 @selector

  • 选择器就是在 Objective-C 语言中,对方法进行唯一标示的标识符;

基本常用类

  • NSNumber
  • NSString、NSMutableString
  • NSArray、NSMutableArray
  • NSDictionary、NSMutableDictionary
  • NSData、NSMutableData