Swift(学习):从OC到Swift

MARK、TODO、FIXME

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

  左边在代码中写的标记,当你点击上方的结构栏会相应展示成右边那样

 左边在代码中写的标记,当你点击上方的结构栏会相应展示成右边那样

注意点:MARK - 和MARK的区别在于有MARK - 标记的在上方会有条横线

#warning: 在代码中的具体显示:


条件编译

  • 在 Build Settings中可以设置整个工程的flag:

添加flag方式有两种:

1.  在Active Compilation Conditions 直接点击添加

2.  在Other Swift Flags 中 -D 加上你要添加的flag

  • 使用Flag的方式


打印

log("123") //输出:main.swift_2690_TestSwift: 123

func test(){
    log("456", file: #file, line: #line, fn: #function)
}

test() //输出:main.swift_2692_test(): 456

系统版本检测


API可用性说明

//iOS系统10以前的不能用Person类,10以后可以
//macOS系统10.15以前的不能用Person类,10.15以后可以
@available(iOS 10, macOS 10.15, *)
class Person {}

struct Student {
    //study_()方法已经不能用,被重新命名为study()
    @available(*, unavailable, renamed: "study")
    func study_() { }
    func study() { }
    //iOS系统11以前的不能用run方法,11以后可以
    //macOS系统10.12以前的不能用run方法,10.12以后可以
    @available(iOS, deprecated: 11)
    @available(macOS, deprecated: 10.12)
    func run() { }
}
  • 更多用法参考:https://docs.swift.org/swift-book/ReferenceManual/Attributes.html

iOS程序的入口

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


Swift调用OC

  • 新建1个桥接头文件,文件名格式默认为:{targetName}-Bridging-Header.h

  • 在{targetName}-Bridging-Header.h 文件中#import OC需要暴露给Swift的内容

  • 当我们在Swift工程里建立OC文件时,会自动提示你是否建立相应的桥接文件,点击Create Bridging Header就会自动建立相应的桥接文件

  • Swift调用OC-MYPerson.h
@interface MYPerson : 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

int sum(int a, int b);
  • Swift调用OC-MYPerson.m
#import "MYPerson.h"

@implementation MYPerson

- (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调用OC-Swift代码
var p = MYPerson(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
MYPerson.run() // Person +run
MYPerson.eat("Pizza", other: "Banana") //person +eat Pizza,Banana
print(sum(10, 20)) //30

OC调用Swift

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

  • OC调用Swift - Car.swift
  1. Swift暴露给OC的类最终继承自NSObject 
  2. 使用@objc修饰需要暴露给OC的成员
  3. 使用@objcMembers修饰类:1)代表默认所有成员都会暴露给OC(包括扩展中定义的成员);2)最终是否成功暴露,还需要考虑成员自身的访问级别
import UIKit

@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调用Swift - {targetName}-Swift.h
  • Xcode会根据Swift代码生成对应的OC声明,写入{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_DEPRECATED_MSG("-init is unavailable");
@end


@interface Car (SWIFT_EXTENSION(TestSwiftCode))
- (void)test;
@end
  • OC调用Swift - OC代码
Car *c = [[Car alloc] initWithPrice:10.5 band:@"BMW"];
NSLog(@"%f, %@", c.price, c.band); //10.500000, BMW
c.band = @"Bently";
c.price = 108.5;
[c run]; //108.5 Bently run
[c test]; //108.5 Bently test
[Car run]; //Car run
  • OC调用Swift - @objc
  • 可以通过@objc 重命名Swift暴露给OC的符号名(类名、属性名、函数名等)
import UIKit

@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)
    func test() {
        print(price, band, "test")
    }
}

 

MYCar *c = [[MYCar alloc] initWithPrice:10.5 band:@"BMW"];
NSLog(@"%f, %@", c.price, c.name); //10.500000, BMW
c.name = @"Bently";
c.price = 108.5;
[c drive]; //108.5 Bently run
[c exec]; //108.5 Bently test
[MYCar run]; //Car run

选择器(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(_:_:)))
    }
}

1.  为什么swift暴露给OC的类最终要继承自NSObject?

因为OC调用Swift的类也要alloc,alloc来自于NSObject,另外最终要走objc_msgSend消息机制,要调用isa指针,isa指针来自于NSObject。反汇编如下:

2.  Swift调用OC底层是怎么调用的?反过来OC调用swift底层又是如何调用的?

纯swift中通过虚表进行调用,通过callq等来调用方法,Swift中调用OC还是走runtime机制的,而OC调用Swift之前说过了,也是走objc_msgSend,走runtime机制

3.  在Swift中调用@objcMembers修饰的Car.run()底层是怎么调用的

在Swift中调用@objcMembers class Car会发现还是走swift虚表callq方法那一套,如果实在要走objc_msgSend机制,可以在方法前使用dynamic关键字。反汇编如下:


猜你喜欢

转载自blog.csdn.net/weixin_42433480/article/details/98387586