洋仔的博客 洋仔的博客
首页
  • 个人心法总结

    • 价值心法
  • 技术文档
  • GitHub技巧
  • Nodejs
  • 博客搭建
  • iOS基础知识
  • 前端
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 投资体系
  • 毛选
  • 友情链接
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

洋仔

奋斗的小青年
首页
  • 个人心法总结

    • 价值心法
  • 技术文档
  • GitHub技巧
  • Nodejs
  • 博客搭建
  • iOS基础知识
  • 前端
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 投资体系
  • 毛选
  • 友情链接
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 技术文档

  • GitHub技巧

  • Nodejs

  • 博客搭建

  • iOS基础知识

    • iOS底层相关

    • Runloop系列

    • Runtime系列

    • 内存管理系列

    • Block系列

    • 线程系列

    • KVC跟KVO系列以及通知中心

    • UI系列

    • 离屏渲染系列

    • 组件化系列跟架构

    • OC跟webview交互系列

    • 持久化系列

    • APP编译系列

    • APP性能优化系列

    • cocoapods系列

    • swift系列

      • swift跟OC对比
        • Class 和 Struct 的区别
        • 理解Swift值类型的写时复制
        • Swift 与 Objective-C 的联系与区别?
        • Swift中的常量和OC中的常量有啥区别?
        • Any和AnyObject的区别?
        • Swift 中的属性观察器有哪些,它们各自有什么用途?
        • 为什么数组越界会崩溃,而字典用下标取值时key没有对应值的话返回的是nil而不会崩溃?
        • SwiftUI用一种叫做属性包装器的东西来标记可以作为状态的变量。这些包括:
        • 介绍下combine
        • swift和OC的区别?
        • String 与 NSString 的关系与区别?
        • Swift中的访问控制权限?
        • 什么时候使用 final?
        • Swift的Copy On Write机制了解过吗?
        • In-Out(inout关键字)参数了解过吗?
        • swift的派发机制 ((函数的派发机制:静态派发(直接派发)、函数表派发、消息派发))
        • swift显示指定派发方式?
        • map、filter、reduce 的作用
        • swift 闭包函数
      • swift面试
      • swiftUI面试
    • Git系列

    • 网络相关

    • 三方库系列

    • 系统原理

    • 总结系列

    • 算法系列

    • 数据结构系列

  • 前端

  • 技术
  • iOS基础知识
  • swift系列
洋仔
2023-09-10
目录

swift跟OC对比

# 面向对象开发核心&& 向协议开发核心

面向对象开发核心是: 封装-继承-(多态),弊端:继承链很长,代码高度耦合 面向协议开发核心是: 模块化(组件化)

OC不能面向协议开发的原因是,OC中的协议只能有方法的声明,不能有方法的实现,而Swift可以通过协议拓展,来声明方法和实现方法,所以swift可以进行面向协议编程。

protocol Emitterable {
     
}
//协议拓展
extension Emitterable{
    
    /// 方法的声明和实现
    func start() {
    }
  }
1
2
3
4
5
6
7
8
9
10

面向协议开发应用:

很多UIView会通过xib进行描述, 而我们经常需要从一个xib中加载UIView, 抽取单独的协议, 需要从xib中加载类, 只需要遵守协议即可,而不需要继承自父类,再转为子类。

使用loadFromNib方法,从xib中加载UIView,

protocol NibLoadable {
	
}

extension NibLoadable where Self : UIView {
    static func loadFromNib(_ nibname : String? = nil) -> Self {
        let loadName = nibname == nil ? "\(self)" : nibname!
        return Bundle.main.loadNibNamed(loadName, owner: nil, options: nil)?.first as! Self
    }
}


1
2
3
4
5
6
7
8
9
10
11
12

一般会使用where Self对可遵守协议的类型进行限制。此处UIView要求的子类才可遵守该协议,并且可以直接使用UIView相关的属性和方法。

# Class 和 Struct 的区别

swift中struct与的class的区别

共同点:

都可以将多个数据封装为一个整体

不同点:

1.类可以添加方法,结构体不可以

2.结构体在栈区,类在堆区

3.结构体是值类型,类是引用类型

4.类可以继承

使用场景: 1.如果封装的不仅有数据还有方法,只能使用类,

2.不涉及到方法的前提下:如果属性较少,就定义为结构体,存放栈中,方便调用,如果属性较多,就定义为类。

# 理解Swift值类型的写时复制

只有当一个结构体发生了写入行为时才会有复制行为。

在结构体内部用一个引用类型来存储实际的数据,在不进行写入操作的普通传递过程中,都是将内部的reference的应用计数+1,在进行写入操作时,对内部的reference做一次copy操作用来存储新的数据,防止和之前的reference产生意外的数据共享。

swift中提供该[isKnownUniquelyReferenced]函数,他能检查一个类的实例是不是唯一的引用,如果是,我们就不需要对结构体实例进行复制,如果不是,说明对象被不同的结构体共享,这时对它进行更改就需要进行复制。

# Swift 与 Objective-C 的联系与区别?

Swift和Objective-C 共用一套运行时环境,Swift 的类型可以桥接到Objective-C(下面我简称OC),反之亦然。两者可以互相引用混合编程。 其次就是,OC 之前积累的很多类库,在 Swift 中大部分依然可以直接使用,当然,Swift3之后,一些语法改变了很多,不过还是有迹可循的。OC出现过的绝大多数概念,比如引用计数、ARC、属性、协议、接口、初始化、扩展类、命名参数、匿名函数等,在Swift中继续有效(可能最多换个术语)。Swift大多数概念与OC一样。当然Swift也多出了一些新兴概念,这些在OC中是没有的,比如范型、元组等。

# Swift中的常量和OC中的常量有啥区别?

OC中用 const 是用来表示常量的,而 Swift 中用 let 是用来判断是不是常量。

OC中的常量(const)是编译期决定的,Swift中的常量(let)是运行时确定的。

上面的区别更进一步说,OC中 const 表明的常量类型和数值是在 compilation time 时确定的;而 Swift 中 let 只是表明常量(只能赋值一次),其类型和值既可以是静态的,也可以是一个动态的计算方法,它们在 runtime 时确定的。

# Any和AnyObject的区别?

AnyObject只能表示引用类型的任何实例,相当于Objective-C中的id类型。

Any可以表示类,结构体,枚举的任何实例。

AnyObject是Any的子集。 (延伸 oc中 id的本质是struct objc_object结构体指针,可以指向任何OC对象,id指向基础数据类型会报编译错误。)

# Swift 中的属性观察器有哪些,它们各自有什么用途?

Swift中的属性观察器包括willSet和didSet,它们用于监控属性值的变化,从而可以在属性的值即将更改和已经更改时执行自定义操作。这两个观察器的用途如下:

1、 willSet观察器在属性的值即将更改之前被调用。它使你可以读取即将被设定的新值,并可以执行一些自定义代码。willSet观察器可以带有一个默认参数名newValue,如果你不指定参数名,可以直接使用newValue来访问新的属性值。

2、 didSet观察器在属性的值已经更改后立即被调用。它使你可以读取已经被更改的旧值,并可以基于新值执行一些自定义代码。didSet观察器可以带有一个默认参数名oldValue,如果你不指定参数名,可以直接使用oldValue来访问旧的属性值。

属性观察器提供了一种强大的方式来响应属性值的变化,使得开发者可以在适当的时机插入自己的逻辑或执行一些清理工作。

# 为什么数组越界会崩溃,而字典用下标取值时key没有对应值的话返回的是nil而不会崩溃?

数组的对象的储蓄地址是连续的,如果越界了,那取到的地址不一定可用,所以报错。

# SwiftUI用一种叫做属性包装器的东西来标记可以作为状态的变量。这些包括:

  • @State:用来管理普通的UI状态。

  • @Binding:实现视图与子视图之间双向数据绑定。

  • @ObservedObject:用于在视图之间共享数据。 在SwiftUI中,@ObservedObject属性包装器充当了多视图间共享对象引用的角色。这个功能在我们操作复杂的数据模型,且该模型被多个视图同时使用时显得尤为重要。一旦某个视图基于新数据更新了这个对象,所有通过@ObservedObject引用了这个对象的视图,都能同步获得更新。 那么,被@ObservedObject观察的对象能否是单例呢?答案是可以的。实际上,推荐的做法就是向不同的视图注入同一个实例,这样可以确保数据的一致性。

  • @EnvironmentObject:用于在整的应用程序中跨视图共享数据。

# 介绍下combine

Combine框架由三个核心部分组成:

  1. 发布者(Publishers):发布者是一种逐步发布值的对象,可以视为数据的起始点,数据可能来源于用户输入、网络请求或定时器等多种途径。发布者能够发布各种类型的数据,如整数、字符串或自定义类型,并且可以根据需要发布任意数量的值,无论是有限还是无限。

  2. 操作符(Operators):操作符是用于处理、筛选或组合发布者值流的功能性工具。它们可以接收一个或多个发布者作为输入,并输出一个新的发布者,携带经过转换的值。常见的操作符包括map、filter、flatMap和zip等。

  3. 订阅者(Subscribers):订阅者是接收并处理发布者值的消费者。它们可以以多种方式处理接收到的值,比如打印到控制台、更新用户界面或存储到数据库。订阅者能够接收不同类型的值,可以请求一定数量的值,或者无限接收值。

# swift和OC的区别?

  • swift是一门支持多编程范式的语言,既支持面向对象编程,也支持面向协议编程,同时还支持函数式编程,OC面向对象编程。

  • swift注重值类型,OC注重引用类型。

  • swift的协议不仅可以被类实现,也可以被struct和enum实现

# String 与 NSString 的关系与区别?

1)本质区别:String是结构体,NSString是类,结构体是值类型,值类型被赋予给一个变量、常量或者被传递给一个函数的时候,其值会被拷贝。

# Swift中的访问控制权限?

Open:实体可被同一模块内所有实体访问,模块外可导入该模块即可访问,模块外可被继承和重写。

Public:实体可被同一模块内所有实体访问,模块外可导入该模块即可访问,模块外不能被继承和重写。

Internal:实体可被同一模块内所有实体访问,模块外无法访问,大部分实体默认是Internal级别。

fileprivate:限制实体只能在当前文件内访问到,不管是否在本类的作用域。

private: 限制实体只能在本类的作用域且在当前文件内能访问。

# 什么时候使用 final?

  • final关键字可以在class、func和var前修饰,表示不能被继承或重写,否则编译器会报错, 可以将类或者类中的部分实现保护起来,从而避免子类破坏。

  • 它可以显示的指派函数的派发机制。

# Swift的Copy On Write机制了解过吗?

  • Swift中参数传递是值类型传递,它会对值类型进行copy操作,当传递一个值类型变量时(变量赋值,函数传参),它传递的是一份新的copy值,两个变量指向不同的内存区域。如果频繁操作的变量占内存较大,会产生性能问题。

  • Copy On Write是一种优化值类型copy的机制,对String、Int、Float等非集合数据类型,赋值直接拷贝,对于Array等集合类型数据,只有传递的内容值改变后才进行拷贝操作。

  • Copy On Write的实现:set函数中判断是否存在多个引用,只有存在多个引用的情况下才会进行拷贝操作。另外,自定义结构体是不支持Copy On Write的。

# In-Out(inout关键字)参数了解过吗?

默认情况下,函数参数默认是常量,试图从函数体中去改变一个函数的参数值会报编译错误。如果希望函数修改参数值,并在函数调用结束后仍然保留。这个时候就需要用到inout关键字。

inout关键字修饰的变量传递过程:

  1. 函数被调用,参数值会被拷贝

  2. 在函数体中,修改的是拷贝的值

  3. 函数返回时,拷贝的值会赋值给原参数

注意事项:

inout关键字只能修饰变量,无法修饰常量,因为常量和字面量不能被修改。

inout参数不能有默认值,可变参数不能标记为inout。

调用函数的时候,应该在变量名前放置&符号表示该变量可以由函数修改。


var variable: Int = 1

func changeNumber(num:inout Int) {

    num = 2

    print(num)

}

changeNumber(num: &variable) // 2

1
2
3
4
5
6
7
8
9
10
11
12
13

# swift的派发机制 ((函数的派发机制:静态派发(直接派发)、函数表派发、消息派发))

  1. swift中所有值类型:struct、enum使用直接派发。

  2. swift中协议的extensions(类似于OC的分类)使用直接派发,初始声明函数使用函数表派发

  3. swift中class中extensions使用直接派发,初始化声明函数使用函数表派发,dynamic修饰的函数使用消息派发。

  4. swift中NSObject的子类用@nonobjc或final修饰的函数使用直接派发,初始声明函数使用函数表派发,dynamic修饰的extensions使用消息派发

# swift显示指定派发方式?

  1. 添加final关键字的函数使用直接派发

  2. 添加static关键字函数使用直接派发

  3. 添加dynamic关键字函数使用消息派发

  4. 添加@objc关键字的函数使用消息派发

  5. 添加@inline关键字的函数告诉编译器可以使用直接派发

# map、filter、reduce 的作用

map 用于映射, 可以将一个列表转换为另一个列表


[1, 2, 3].map{"\($0)"}// 数字数组转换为字符串数组
["1", "2", "3"]

1
2
3
4

filter 用于过滤, 可以筛选出想要的元素


[1, 2, 3].filter{$0 % 2 == 0} // 筛选偶数
// [2]

1
2
3
4

reduce 合并


[1, 2, 3].reduce(""){$0 + "\($1)"}// 转换为字符串并拼接
// "123"

1
2
3
4

# swift 闭包函数

  1. 闭包表达式种类
  • 逃逸闭包

当一个闭包作为参数传到一个函数中,但是这个闭包在函数返回之后才被执行,我们称该闭包从函数中逃逸。当你定义接受闭包作为参数的函数时,你可以在参数名之前标注 @escaping,用来指明这个闭包是允许“逃逸”出这个函数的。

一种能使闭包“逃逸”出函数的方法是,将这个闭包保存在一个函数外部定义的变量中。举个例子,很多启动异步操作的函数接受一个闭包参数作为 completion handler。这类函数会在异步操作开始之后立刻返回,但是闭包直到异步操作结束后才会被调用。在这种情况下,闭包需要“逃逸”出函数,因为闭包需要在函数返回之后被调用。


var completionHandlers: [() -> Void] = []
func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
    completionHandlers.append(completionHandler)
}

1
2
3
4
5
6
  • 自动闭包

自动闭包是一种自动创建的闭包,用于包装传递给函数作为参数的表达式。这种闭包不接受任何参数,当它被调用的时候,会返回被包装在其中的表达式的值。这种便利语法让你能够省略闭包的花括号,用一个普通的表达式来代替显式的闭包。

自动闭包让你能够延迟求值,因为直到你调用这个闭包,代码段才会被执行。延迟求值对于那些有副作用(Side Effect)和高计算成本的代码来说是很有益处的,因为它使得你能控制代码的执行时机

如果你想让一个自动闭包可以“逃逸”,则应该同时使用 @autoclosure 和 @escaping 属性。@escaping 属性的讲解见上面的 逃逸闭包。

编辑 (opens new window)
上次更新: 2024/10/23, 23:26:17
cocoapods原理
swift面试

← cocoapods原理 swift面试→

最近更新
01
数组
10-25
02
数组双指针系列之对撞指针
10-25
03
数组双指针系列之快慢指针
10-25
更多文章>
Theme by Vdoing | Copyright © 2019-2024 Evan Xu | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式