Swift5.0 - day10- 从 OC 到 Swift(上)

简介: Swift5.0 - day10- 从 OC 到 Swift(上)

一、OC 到 Swift 基础差异



  • 1.1、提示符:MARK、TODO、FIXME、 #warning("")
  • <1>、// MARK: 类似于OC中的 #pragma mark


// MARK: 测试
func test() -> () {
}



image.png


<2>、// MARK: - 类似于OC中的 #pragma mark -

// MARK: - 上面的方法
// MARK: 方法一
func test() -> () {
}
// MARK: - 下面的方法
// MARK: 方法一
func test3() -> () {
}


image.png


<3>、// TODO: 用于标记未完成的任务

func test2() -> () {
  // TODO: 未完成的任务
}


image.png

<4>、// FIXME: 用于标记待修复的问题

func test3() -> () {
    // FIXME: 待修复
}


image.png


<5>、#warning("") 警告信息


image.png



1.2、条件编译

// 操作系统: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


  • 自定义条件编译


image.png


  • 系统默认的是 DEBUG,但是我们可以改名字,根据自己的需要了


// debug模式 
#if DEBUG
// release模式 #else
#endif
#if TEST
print("test")
#endif
#if OTHER
print("other")
#endif
  • 1.3、DUBUG 和 Release 模式下的打印
  • OC 中


#ifdef DEBUG
#define JKLog(...) NSLog(@"%s 第%d行: %@\n\n",__func__,__LINE__,[NSString stringWithFormat:__VA_ARGS__])
#else
#define JKLog(...)
#endif
  • Swift 中


/// 自定义打印
/// - Parameter msg: 打印的内容
/// - Parameter file: 文件路径
/// - Parameter line: 打印内容所在的函数
/// - Parameter fn: 打印内容的函数名
func JKLog<T>(_ msg: T,
               file: NSString = #file,
               line:Int = #line,
                 fn: String = #function) {
    #if DEBUG
    let prefix = "------\n当前文件是:\(file.lastPathComponent)\n第 \(line) 行\n函数名:\(fn)\n打印内容:msg\n------"
    print(prefix)
    #endif
}
  • 1.4、系统版本检测
  • 对于iOS平台,只在iOS10及以上版本执行
  • 对于macOS平台,只在macOS 10.12及以上版本执行
  • 最后的*表示在其他所有平台都执行


if #available(iOS 10, macOS 10.12, *) {
}
  • 1.5、API 可用性说明


@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() {}
}
  • 方法更名: @available(*, unavailable, renamed: "study")


image.png


  • 1.6、拓展小技巧
  • 当我们在写一个函数的时候,可能里面不知道些什么,又不想让它报错,我们可以在作用域内写上: fatalError()


func testDo() -> Int {
    fatalError()
}


二、Swift 项目说明



  • 2.1、iOS 程序的入口
  • 在AppDelegate上面默认有个 @UIApplicationMain 标记,这表示:编译器自动生成入口代码(main函数代码),自动设置AppDelegate为APP的代理
  • 也可以删掉 @UIApplicationMain,自定义入口代码:新建一个main.swift文件,如下:JKUIApplication 使我们自定义的入口


import UIKit
class JKUIApplication: UIApplication {}
UIApplicationMain(CommandLine.argc, CommandLine.unsafeArgv, NSStringFromClass(JKUIApplication.self), NSStringFromClass(UIApplication.self))


  • 2.2、Swift 调用 OC
  • 先建一个桥接文件:直接见一个OC类,Xcode会直接提示我们创建一个桥接文件,文件名格式默认为: {targetName}-Bridging-Header.h



image.png


image.png

image.png


  • {targetName}-Bridging-Header.h 文件中#import OC需要暴露给Swift的内容,如下
  • 自定义类 Animal, 其中 .h 和 .m文件内容如下


// .h文件
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
int sum(int a, int b);
@interface Animal : 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
NS_ASSUME_NONNULL_END
// .m 文件
#import "Animal.h"
@implementation Animal
- (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(@"Animal +run");
}
- (void)run {
     NSLog(@"%zd %@ -run", _age, _name);
}
+ (void)eat:(NSString *)food other:(NSString *)other {
     NSLog(@"Animal +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;
}
  • 在 {targetName}-Bridging-Header.h  桥接文件里面 导入 OC的文件,如下


#import "Animal.h"
  • 调用 Swift 调用 OC 代码


let p = Animal(age: 10, name: "Jack")
p.age = 18
p.name = "Rose"
// 18 Rose -run
p.run()
// 18 Rose -eat Apple Water
p.eat("Apple", other: "Water")
// Animal +run
Animal.run()
// Animal +eat Pizza Banana
Animal.eat("Pizza", other: "Banana")
// 30
print(sum(10, 20))
  • Swift 调用 @_silgen_name
    如果我们在C 语言有一个方法和 swift 里面的方法重名,那么在调用的时候,在Swift项目里面会 优先调用 swift 里面的方法,如果我们也想调在Swift里面调用C的方法,我们可以使用 @_silgen_name修改 C 函数名


// 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


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


image.png


  • (1)、在 OC 的文件里面导入 {targetName}-Swift.h 文件,
  • (2)、要求 Swift要在OC里面使用的类继承于 NSObject,如果 Swift 类里面的某个成员或者方法我们想要暴露给外面,就要在  某个成员或者方法 前面加 @objc
  • (3)、使用 @objcMembers 修饰类代表默认所有成员都会暴露给OC(包括扩展中定义的成员)最终是否成功暴露,还需要考虑成员自身的访问级别


@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") }
}

提示:Xcode会根据Swift代码生成对应的OC声明,写入{targetName}-Swift.h 文件

  • 上述代码在编译后就会在  {targetName}-Swift.h 文件 中生成如下代码


@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(JKSwiftDemo))
- (void)test;
@end
  • 在 OC 文件中使用Swift的类,先导入 #import "targetName-Swift.h"


#import "targetName-Swift.h"
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
  • 通过 @objc 重命名Swift暴露给OC的符号名(类名、属性名、函数名等),如下


@objc(JKCar)
@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(newTest)
    func test() { print(price, band, "test") }
}
  • 在OC里面的调用


JKCar *c = [[JKCar alloc] initWithPrice:10.5 band:@"BMW"]; 
c.name = @"Bently";
c.price = 108.5;
[c drive]; // 108.5 Bently run
[JKCar run]; // Car run


  • 2.4、根据2.2和2.3 列举的几个问题
  • 1.为什么 Swift 暴露给 OC 的类最终要继承自 NSObject?
    答:因为 在OC 里面用类 ,必然继承于 NSObject,在OC里面调用方法必然还要用到 runtime 那套流程,里面牵扯到 isa指针,isa指针来自NSObject
  • 2.p.run()底层是怎么调用的?反过来,OC调用 Swift 底层又是如何调用?
    答:前面的:OC的东西在Swift里面调用,我们可以看到调用了runtime那套机制;后面的:Swift的东西在OC里面调用,打断点看汇编可以发现调用的也是runtime那套机制
  • 3.Swift  里面的 car.run() 底层是怎么调用的?
    答:走的是Swift那套流程,如果我们强行让它走OC那套runtime机制,可以在 run()  函数前加 dynamic


  • 2.5、选择器(Selector)
    Swift中依然可以使用选择器,使用 #selector(name) 定义一个选择器,但是 必须是被 @objcMembers@objc 修饰的方法才可以定义选择器


@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))
      }
}


目录
相关文章
|
6月前
|
API Swift iOS开发
45 Swift和OC的混编
Swift和OC的混编
82 0
|
7月前
|
Swift iOS开发
iOS OC混编Swift 后者无法走断点
iOS OC混编Swift 后者无法走断点
49 0
|
Swift
Swift和OC控制器互相跳转
Swift和OC控制器互相跳转
286 0
|
Swift iOS开发
Swift - 与OC混编时如何创建桥接头文件
Swift - 与OC混编时如何创建桥接头文件
374 0
Swift - 与OC混编时如何创建桥接头文件
|
Swift
OC 和 swift 创建单例方法
OC 和 swift 创建单例方法
289 0
|
Swift iOS开发
oc与swift混编
1.swift中调用oc;2.oc中调用swift
217 0
oc与swift混编
|
Swift iOS开发 容器
iOS 仿支付宝银行卡界面(支持Swift/OC)
在有支付相关的APP中,都有对应的钱包,虽然现在的支付宝,微信支付很流行,但是都是需要绑定自己的银行卡,那么这个银行卡的卡包页面该怎么实现呢?在网上找了许久也没有找到合适的,那就索性自己造轮子。
323 0
|
Swift iOS开发
Swift与OC的混编
Swift调用OC文件 OC调用Swift文件
331 0
Swift与OC的混编
|
Swift
OC与swift的数据传输
该项目主要介绍了oc与swift之间、swift内部几种常见的传值方式(属性传值、代码块传值、代理传值、通知) 如果oc与swift之间的桥接有疑问请参考:OC与swift桥接互调
182 0
OC与swift的数据传输
|
存储 API Swift
Swift5.0 - day10- 从 OC 到 Swift(下)
Swift5.0 - day10- 从 OC 到 Swift(下)
155 0
Swift5.0 - day10- 从 OC 到 Swift(下)

相关课程

更多