【Swift 和 OC】

  • Post author:
  • Post category:其他




swift和oc的区别

1,最明显的区别:OC一个类由.h和.m两个文件组成,而swift只有.swift一个文件,所以整体的文件数量比OC有一定减少。

2,不像C语言和OC语言一样都必须有一个主函数main()作为程序的入口,swift程序从第一句开始向下顺序执行,一直到最后。(swift将全局范围内的首句可执行代码作为程序入口,swift也有main函数,只是不用我们自己编写。)

3,swift数据类型都会自动判断,只区分变量var和常量let。

4,关于BOOL类型更加严格,swift不再是OC的非0就是真,而是true才是真false才是假。

5,swift的switch语句后面可以跟各种数据类型了,如Int,字符串,元组,区间都行,并且里面不用写break。

6,swift 中可以定义不继承于其它类的类,称之为基类(base calss)。

7,final关键字

使用final关键字修饰的类,不能被其他类继承。

继承final修饰的类会报错:Inheritance from a final class ‘……’



Swift比Objective-C优势

1,Swift容易阅读,语法和文件结构简易化。

2,Swift更易于维护,文件分离后结构更清晰。

3,Swift更加安全,它是类型安全的语言:

  Swift 是一个类型安全(type safe)的语言。类型安全的语言可以让你清楚地知道代码要处理的值的类型。如果你的代码需要一个 String ,你绝对不可能不小心传进去一个 Int 。

由于 Swift 是类型安全的,所以它会在编译你的代码时进行类型检查(type checks),并把不匹配的类型标记 为错误。这可以让你在开发的时候尽早发现并修复错误。

当你要处理不同类型的值时,类型检查可以帮你避免错误。然而,这并不是说你每次声明常量和变量的时候都需 要显式指定类型。如果你没有显式指定类型,Swift 会使用类型推断(type inference)来选择合适的类型。有了类型推断,编译器可以在编译代码的时候自动推断出表达式的类型。原理很简单,只要检查你赋的值即可。

因为有类型推断,和 C 或者 Objective-C 比起来 Swift 很少需要声明类型。常量和变量虽然需要明确类型,但 是大部分工作并不需要你自己来完成。

4,Swift代码更少,简洁的语法,可以省去大量冗余代码。

5,Swift速度更快,运算性能更高。



swift独有

1、范围运算符

a…b 表示 [a,b] 包括a和b 。 (如3…5 就是范围取3,4,5)

a…<b 表示 [a,b) 包括a,不包括b 。 (如3…5 就是范围取3,4)

常见的如for循环:for i in 0…9{}

2、独有的元组类型

元组(tuples)把多个值组合成一个复合值。元组内的值可以使任意类型,并不要求是相同类型。eg:

var value = (Int,String) = (x:15,y:“abc”) 复制代码

3、swift中使用let定义常量,var定义变量

使用常量,更加安全,不能够被修改,在需要对对象进行修改的时候 只能用var修饰.

4、if let 、 guard let 的用法

缩减代码量,安全处理数据逻辑。



Swift调用OC

由于很多常用的第三方框架还是oc版本,没有swift版本,但是项目里可能需要调用那些框架的功能,所以swift语言的工程还需要用到oc版本的三方,或者公司项目需要用到swift和oc混合开发时,就涉及到了Swift调用OC或者OC调用Swift

1、手动新建一个桥接头文件,文件名格式默认为:{targetName}-Bridging-Header.h,然后在Build Settings设置路径,如下

在这里插入图片描述

2、或者在swift工程里新建一个oc的文件时会自动弹出弹框询问是否自动创建桥接文件,如下

在这里插入图片描述

在这里插入图片描述

想用oc的哪些头文件,在桥接头文件中导入头文件即可,例#import “Person.h”,则在swift工程中,Person类就暴漏给swift使用了

Person.h

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

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

NS_ASSUME_NONNULL_END

Person.m

#import "Person.h"

@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文件中Swift代码

let 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



OC调用Swift

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

这里需要注意的是:工程名如果带有中划线-时,buildsettings的路径变成了下划线_,import引用时,写下划线的文件名即可;工程名如果带有下划线_时,buildsettings的路径也是下划线,引用时会报错找不到文件。文件内部放的是swift要暴露给oc的代码,那么也不是所有的swift代码都要暴露给oc,要暴漏给oc的代码要继承NSObject

2、Swift暴露给OC的类最终继承自NSObject(原因:因为oc有runtime,runtime是要求类有isa指针,isa指针肯定是继承NSObject才有的,所以最终要继承NSObject)

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

4、使用@objcMembers修饰类

  • 代表默认所有成员都会暴露给oc(包括扩展中定义的成员)
  • 最终是否成功暴露,还需要考虑成员自身的访问级别

    例子如下:

    swift文件:
class Car: NSObject {
    var price:Double
    var band:String
    @objc 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{
    @objc func test(){
        print(price,band,"test")
    }
}

OC文件:

Car *car = [[Car alloc] initWithPrice:1.55 band:@"bannana"];
[car run]; // 报错:No visible @interface for 'Car' declares the selector 'run'
[Car run]; //报错:No known class method for selector 'run'
[car test];
被@objc修饰的属性或者方法才能被调用

也可写成如下:

swift文件:

@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文件:

Car *car = [[Car alloc] initWithPrice:1.55 band:@"bannana"];
[car run];
[Car run];
[car test];

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

6、可以通过@objc重命名Swift暴露给OC的符号名(类名、属性名、函数名等)

swift文件:

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

OC文件:

XXCar *car = [[XXCar alloc] initWithPrice:1.55 band:@"bannana"];
car.name = @"banban";
[car drive];
[car exec];
[XXCar run];



Swift 选择器(selector)

1、Swift中依然可以使用选择器,使用#selector(name)定义一个选择器

2、必须是被@objcMembers或@objc修饰的方法才可以定义选择器。

selector(选择器)是依赖于runtime的,oc里才有runtime,纯swift里是不存在runtime的

@objcMembers class XXPerson: 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))
    }
}

p.run()底层是怎么调用的?

oc文件:

Person.h

#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface Person : NSObject

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

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

- (void)run;
@end

Person.m

#import "Person.h"
#import "ludan-Swift.h"

@implementation Person

- (instancetype)initWithAge:(NSInteger)age name:(NSString *)name{
    if (self = [super init]) {
        self.age = age;
        self.name = name;
    }
    return self;
}
- (void)run{NSLog(@"%zd %@-run",_age,_name);}
@end

swift文件:

var p:Person = Person(age: 10, name: "jack")
p.run()

从汇编来看,走runtime的机制,objc_msgSend

反过来,OC调用Swift底层又是如何调用?

swift写的类继承自NSObject类,暴漏给OC调用,同样走runtime那套机制,objc_msgSend

car.run()底层是怎么调用的?

第一种情况:

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

var car:Car = Car(price: 10.5, band: "Audi")
car.run()

在swift文件里边调用,没有走oc的runtime机制,如果想要走objc_msgSend,则在run方法前边加上dynamic关键字修饰

第二种情况(不继承自NSObject,并且没有@objcMembers修饰):

class Car{
    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")
    }
}

var car:Car = Car(price: 10.5, band: "Audi")
car.run()

纯swift类方法调用是走虚表v-Table那一套机制



版权声明:本文为AHekey原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。