MARK, TODO, FIXME

  • //MARK: 类似于OC中的 #pragma mark
  • //MARK: - 类似于OC中的 #pragma mark -
  • //TODO:用于标记未完成的任务
  • //FIXME:用于标记待修复的问题

条件编译

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 操作系统:macOS\iOS\tvOS\watchOS\Linux\Android\Windows\FreeBSD
#if os(macOS) || os(iOS)
// CPU架构:i386\x86_64\arm\arm64
#elseif arch(x86_64) || arch(arm64)
// swift版本
#elseif swift(<5) && swift(>=3)
// 模拟器
#elseif targetEnvironment(simulator)
// 可以导入某模块
#elseif canImport(Foundation)
#else
#endif

// debug模式
#if DEBUG

// release模式
#else

#endif

// 自定义
#if TEST
print("test")
#endif
#if OTHER
print("other")
#endif

打印

1
2
3
4
5
6
7
8
9
10
//自定义Log输出格式
func log<T>(_ msg: T,
file: NSString = #file,
line: Int = #line,
fn: String = #function) {
#if DEBUG
let prefix = "\(file.lastPathComponent)_\(line)_\(fn):"
print(prefix, msg)
#endif
}

系统版本检测

1
2
3
4
5
6
// 对于iOS平台,只在iOS10及以上版本执行
// 对于macOS平台,只在macOS 10.12及以上版本执行
// 最后的*表示在其他所有平台都执行
if #available(iOS 10, macOS 10.12, *) {

}

API可用性说明

1
2
3
4
5
6
7
8
9
10
11
12
@available(iOS 10, macOS 10.15, *)
class Person {}

struct Student {
@available(*, unavailable, renamed: "study")
func study_() {}
func study() {}

@available(iOS, deprecated: 11)
@available(macOS, deprecated: 10.12)
func run() {}
}

iOS程序的入口

  • 在AppDelegate上面默认有个@UIApplicationMain标记,这表示
    • 编译器自动生成入口代码(main函数代码),自动设置AppDelegate为APP的代理
  • 也可以删掉@UIApplicationMain,自定义入口代码:新建一个main.swift文件
1
2
3
4
5
6
7
8
import UIKit

class MyApplication: UIApplication { }

UIApplicationMain(CommandLine.argc,
CommandLine.unsafeArgv,
NSStringFromClass(MyApplication.self),
NSStringFromClass(AppDelegate.self))

Swift调用OC

  • 新建1个桥接头文件,文件名格式默认为:**{targetName}-Bridging-Header.h**
  • {targetName}-Bridging-Header.h 文件中 #import OC需要暴露给Swift的内容
    • TARGETS -> Build Settings -> briding
1
2
3
4
#import "***.h"
#import "***.h"
#import "***.h"
...

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
//Person.h文件
int sum(int a, int b);

@interface Person : NSObject
@property (nonatomic, assign) NSInteger age;
@property (nonatomic, copy) NSString *name;

- (instancetype)initWithAge:(NSInteger)age name:(NSString *)name;
+ (instancetype)personWithAge:(NSInteger)age name:(NSString *)name;

- (void)run;
+ (void)run;

- (void)eat:(NSString *)food other:(NSString *)other;
+ (void)eat:(NSString *)food other:(NSString *)other;
@end

//Person.m文件
#import Person.h
@end
@implementation Person
- (instancetype)initWithAge:(NSInteger)age name:(NSString *)name {
if (self = [super init]) {
self.age = age;
self.name = name;
}
return self;
}

+ (instancetype)personWithAge:(NSInteger)age name:(NSString *)name {
return [[self alloc] initWithAge:age name:name];
}

+ (void)run { NSLog(@"Person +run"); }
- (void)run { NSLog(@"%zd %@ -run", _age, _name); }

+ (void)eat:(NSString *)food other:(NSString *)other { NSLog(@"Person +eat %@ %@", food, other); }
- (void)eat:(NSString *)food other:(NSString *)other { NSLog(@"%zd %@ -eat %@ %@", _age, _name, food, other); }
@end

int sum(int a, int b) { return a + b; }

//Swift文件中使用Person类
var p = Person(age: 10, name: "Jack") p.age = 18
p.name = "Rose"
p.run() // 18 Rose -run
p.eat("Apple", other: "Water") // 18 Rose -eat Apple Water

Person.run() // Person +run
Person.eat("Pizza", other: "Banana") // Person +eat Pizza Banana

print(sum(10, 20)) // 30

Swift调用OC – @_silgen_name

  • 如果C语言暴露给Swift的函数名跟Swift中的其他函数名冲突了
    • 可以在Swift中使用 @_silgen_name 修改C函数名
1
2
3
4
5
6
7
8
9
10
// C语言
int sum(int a, int b) {
return a + b;
}

// Swift
@_silgen_name("sum") func swift_sum(_ v1: Int32, _ v2: Int32) -> Int32
print(swift_sum(10, 20)) // 30

print(sum(10, 20)) // 30

OC调用Swift

  • Xcode已经默认生成一个用于OC调用Swift的头文件,文件名格式是: {targetName}-Swift.h

    • TARGETS -> Build Settings -> generated interface
  • Swift暴露给OC的类最终继承自NSObject

  • 使用 @objc 修饰需要暴露给OC的成员

  • 使用 @objcMembers 修饰类

    • 代表默认所有成员都会暴露给OC(包括扩展中定义的成员)
    • 最终是否成功暴露,还需要考虑成员自身的访问级别
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import Foundation

@objcMembers class Car: NSObject {
var price: Double
var band: String
init(price: Double, band: String) {
self.price = price
self.band = band
}
func run() { print(price, band, "run") }
static func run() { print("Car run") }
}

extension Car {
func test() { print(price, band, "test") }
}

//对应转化生成的OC代码如下:
@interface Car : NSObject
@property (nonatomic) double price;
@property (nonatomic, copy) NSString * _Nonnull band;
- (nonnull instancetype)initWithPrice:(double)price band:(NSString * _Nonnull)band OBJC_DESIGNATED_INITIALIZER; - (void)run;
+ (void)run;
- (nonnull instancetype)init SWIFT_UNAVAILABLE;
+ (nonnull instancetype)new SWIFT_UNAVAILABLE_MSG("-init is unavailable");
@end
//扩展
@interface Car (SWIFT_EXTENSION(_Swift))
- (void)test;
@end

OC调用Swift,OC文件中的代码示例:

1
2
3
4
5
6
7
8
9
10
#import "_Swift-Swift.h"
int sum(int a, int b) {
Car *c = [[Car alloc] initWithPrice:10.5 band:@"BMW"];
c.band = @"Bently";
c.price = 108.5;
[c run]; // 108.5 Bently run
[c test]; // 108.5 Bently test
[Car run]; // Car run
return a + b;
}

OC调用Swift – @objc

  • 可以通过 @objc 重命名Swift暴露给OC的符号名(类名、属性名、函数名等)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    @objc(MyCar)
    @objcMembers class Car: NSObject {
    var price: Double
    @objc(name)
    var band: String
    init(price: Double, band: String) {
    self.price = price
    self.band = band
    }
    @objc(drive)
    func run() { print(price, band, "run") }
    static func run() { print("Car run") }
    }

    extension Car {
    @objc(exec:v2:)
    func test() { print(price, band, "test") }
    }

    MyCar *c = [[MyCar alloc] initWithPrice:10.5 band:@"BMW"];
    c.name = @"Bently";
    c.price = 108.5;
    [c drive]; // 108.5 Bently run
    [c exec:10 v2:20]; // 108.5 Bently test
    [MyCar run]; // Car run

选择器(Selector)

  • Swift中依然可以使用选择器,使用 #selector(name) 定义一个选择器
  • 必须是被 @objcMembers @objc 修饰的方法才可以定义选择器
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    @objcMembers class Person: NSObject {
    func test1(v1: Int) { print("test1") }
    func test2(v1: Int, v2: Int) { print("test2(v1:v2:)") }
    func test2(_ v1: Double, _ v2: Double) { print("test2(_:_:)") }
    func run() {
    perform(#selector(test1))
    perform(#selector(test1(v1:)))
    perform(#selector(test2(v1:v2:)))
    perform(#selector(test2(_:_:)))
    perform(#selector(test2 as (Double, Double) -> Void))
    }
    }

只能被Class继承的协议

  • @objc 修饰的协议,还可以暴露给OC去遵守实现
    1
    2
    3
    4
    5
    protocol Runnable1: AnyObject {}

    protocol Runnable2: class {}

    @objc protocol Runnable3 {}

可选协议

  • 可以通过 @objc 定义可选协议,这种协议只能被 class 遵守
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    @objc protocol Runnable {
    func run1()
    @objc optional func run2()
    func run3()
    }

    class Dog: Runnable {
    func run3() { print("Dog run3") }
    func run1() { print("Dog run1") }
    }

    var d = Dog()
    d.run1() // Dog run1
    d.run3() // Dog run3

dynamic

  • 被 @objc dynamic 修饰的内容会具有动态性,比如调用方法会走 runtime 那一套流程
    1
    2
    3
    4
    5
    6
    7
    class Dog: NSObject {
    @objc dynamic func test1() {}
    func test2() {}
    }
    var d = Dog()
    d.test1()
    d.test2()

参考

  • 李明杰老师课件