likes
comments
collection
share

Swift/ObjC 语言基础

作者站长头像
站长
· 阅读数 4

请比较 Swift 中的结构体和类的主要区别?

在Swift中,结构体和类是两种常用的数据类型,它们有一些相似之处,但也有一些主要区别。下面是它们的主要区别:

综上所述,结构体和类在Swift中有一些重要的区别,包括值类型 vs 引用类型、内存管理、继承和默认初始化器等。根据具体的需求和场景,选择合适的数据类型可以提高代码的可读性和性能。


Learn more:

Swift 的 Copy-on-Write 是怎么实现的

Swift的Copy-on-Write是一种资源管理技术,用于在可修改的资源上实现高效的“复制”操作。在Swift中,有引用类型(类)和值类型(结构体、元组、枚举)。值类型具有复制语义,这意味着当将值类型赋值给变量或作为参数传递给函数时(除非是inout参数),其底层数据将被复制。你将拥有两个具有相同内容的值,但分配在两个不同的内存地址上。Swift标准库为某些值类型(如Array)实现了一组机制,其中只有在发生变异时才会复制值,而且仅当它有多个引用时才会复制,因为如果该值是唯一引用的,就不需要复制,可以直接在引用上进行变异。因此,仅仅将Array赋值给变量或将其传递给函数并不一定意味着它会被复制,这真正提高了性能[2]

Copy-on-Write并不是值类型的默认行为,它是在Swift标准库中为某些类型(如Array和Collections)实现的。这意味着并非标准库中的每个值类型都具有这种行为。除非你自己实现,否则你创建的值类型也不具备Copy-on-Write行为[2]

要实现自定义值类型的Copy-on-Write行为,你可以自己实现。下面是一个示例代码,展示了如何使用引用类型来实现泛型值类型T的Copy-on-Write。基本上,它是一个管理引用类型的包装器,如果该值不是唯一引用的,则返回一个新实例。否则,它只是变异引用类型的值[2]


Learn more:

  1. How to implement copy-on-write behavior for my own collections? - Using Swift - Swift Forums
  2. Understanding Swift Copy-on-Write mechanisms | by Luciano Almeida | Medium
  3. Copy on Write in Swift explained with examples

WHY Swift not ObjC?

There are several reasons why Swift is often preferred over Objective-C for iOS and macOS development. Here are some key advantages of Swift:

  1. Safety and Performance: Swift was designed with a focus on safety and performance. It includes modern features like optionals, type inference, and automatic memory management, which help reduce common programming errors and improve code stability [1]. Additionally, Swift's compiler is optimized for performance, resulting in faster execution compared to Objective-C [2].

  2. Simplicity and Readability: Swift has a more concise and expressive syntax compared to Objective-C. It eliminates many of the cumbersome and error-prone aspects of Objective-C, such as manual memory management and the need for header files. Swift's syntax is designed to be more readable and easier to understand, making it more approachable for beginners and more productive for experienced developers [1].

  3. Interoperability: Swift is fully interoperable with Objective-C, allowing developers to use existing Objective-C code and frameworks in their Swift projects. This makes it easier to adopt Swift gradually and leverage the extensive ecosystem of Objective-C libraries and tools [1].

  4. Active Development and Community Support: Swift is an open-source language with an active and growing community. It is continuously evolving, with regular updates and improvements from Apple and the community. This ensures that Swift remains modern, efficient, and well-supported for future development [2].

  5. Playgrounds and Interactive Development: Swift's Playgrounds feature allows developers to experiment, prototype, and test code in an interactive and visual environment. This makes learning and exploring Swift concepts more engaging and efficient [2].

  6. Platform Compatibility: While Objective-C is primarily used for Apple platforms, Swift is designed to be cross-platform. It can be used not only for iOS and macOS development but also for server-side programming and Linux applications [1].

In summary, Swift offers a safer, more modern, and more efficient alternative to Objective-C. Its simplicity, readability, and interoperability make it an attractive choice for iOS and macOS development.


Learn more:

  1. Objective-C vs Swift: iOS Comparison [2024 Update] - Netguru
  2. Swift vs. Objective-C: 10 reasons the future favors Swift | InfoWorld
  3. Objective-C or Swift | Apple Developer Forums

if a struct is an immutable value type, why we can mutate the size property?

In Swift, structs are generally considered to be immutable types because they are copied on mutation. However, it is possible to mutate properties of a struct instance, including the size property, even if the struct is declared as a constant (let).

Here's why we can mutate the size property in an immutable struct:

  1. Copy-on-Write (COW) Optimization: When a struct is assigned to a new variable or passed as a function argument, a copy of the struct is made. However, the actual copying of the struct's data is deferred until a mutation is attempted. This optimization is known as Copy-on-Write (COW) [1].

  2. In-Place Mutation: In many cases, the Swift compiler optimizes the mutation of a struct's property by performing the mutation in-place, without creating a new copy of the entire struct. This means that the original struct instance is modified directly, even if it is declared as a constant (let). However, logically, you should still consider the struct as immutable because the mutation is not observable from outside the struct [1].

  3. let vs var: The ability to mutate properties of a struct instance is not related to whether the struct is declared as a constant (let) or a variable (var). Both let and var allow you to mutate the properties of a struct instance. The difference between let and var is that let declares the struct instance itself as a constant, meaning you cannot reassign a new value to the struct instance, while var allows reassignment of the struct instance [1].

In summary, while structs in Swift are generally considered to be immutable types, the ability to mutate properties of a struct instance, including the size property, is possible due to the Copy-on-Write optimization and in-place mutation. However, it is important to remember that the mutation is not observable from outside the struct, and the struct should still be treated as immutable.


Learn more:

  1. Why are structs in Swift said to be immutable? - Using Swift - Swift Forums
  2. c# - Why are mutable structs “evil”? - Stack Overflow
  3. Structs and mutation in Swift - Chris Eidhof

What are the basic categories of types in Swift?

In Swift, there are two basic categories of types: named types and compound types.

  1. Named Types:

    • Named types are types that can be given a specific name when they are defined.
    • Examples of named types in Swift include:
      • Classes: Classes are reference types that can have properties, methods, and inheritance.
      • Structures: Structures are value types that can have properties and methods. They are typically used for simpler data structures.
      • Enumerations: Enumerations define a group of related values and can have associated values and methods.
      • Protocols: Protocols define a set of methods, properties, and other requirements that a type must conform to.
  2. Compound Types:

    • Compound types are types that are composed of multiple values.
    • Examples of compound types in Swift include:
      • Tuples: Tuples allow you to group multiple values of different types into a single compound value.
      • Functions: Functions are a type that can take parameters and return a value.
      • Closures: Closures are self-contained blocks of functionality that can be passed around and used in your code.

Learn more:

  1. Types - Documentation - Swift.org
  2. Swift - Data Types - GeeksforGeeks
  3. Swift - Data Types

What is a Tuple in Swift?

A tuple in Swift is a lightweight data structure that allows you to group multiple values together into a single compound value. It is a convenient way to store and pass around related values without defining a custom struct or class.

Here are some key features of tuples in Swift:

  1. Multiple Values: Tuples can contain two or more values of any type, including different types. For example, a tuple can hold a combination of integers, strings, booleans, or even other tuples.

  2. Type Safety: Each value within a tuple can have its own type, and the types of the values are enforced by the Swift compiler. This ensures type safety and helps catch any type mismatches at compile-time.

  3. Accessing Values: You can access the individual values within a tuple using dot syntax followed by the index or name of the value. For example, if you have a tuple (name: String, age: Int), you can access the name value using tupleName.name and the age value using tupleName.age.

  4. Function Return Values: Tuples are commonly used as return types for functions when you need to return multiple values. This allows you to conveniently return multiple values without defining a custom struct or class.

  5. Pattern Matching: Tuples can be used in pattern matching to extract values and perform conditional logic based on the values. This is particularly useful when working with functions that return tuples or when using switch statements.

Here's an example of creating and using a tuple in Swift:

let person = (name: "John", age: 30, isEmployed: true)

print(person.name)        // Output: John
print(person.age)         // Output: 30
print(person.isEmployed)  // Output: true

In the example above, we create a tuple person with three values: name, age, and isEmployed. We can access each value using dot syntax.

Tuples are a flexible and lightweight way to group related values together in Swift, providing a convenient alternative to defining custom data structures.

String 与 NSString 的关系与区别:

String 和 NSString 是在不同的编程语言中表示字符串的类型。String 是 Swift 语言中的字符串类型,而 NSString 是 Objective-C 语言中的字符串类型。它们之间有一些区别和关系。

区别:

  1. 可变性:String 是不可变的,即一旦创建就不能修改其内容。而 NSString 是可变的,可以通过 NSMutableString 类来修改其内容。
  2. 语法:String 使用双引号(")来表示字符串,而 NSString 使用@"来表示字符串。
  3. 类型推断:String 可以使用类型推断,不需要显式声明变量的类型。而 NSString 需要显式声明变量的类型为 NSString。
  4. 方法和属性:String 和 NSString 有一些相同的方法和属性,但也有一些不同的方法和属性。例如,String 有一个 count 属性来获取字符串的字符数,而 NSString 则有一个 length 方法来获取字符串的字符数。

关系:

  1. 桥接:String 和 NSString 之间可以进行桥接,即可以相互转换。可以使用 as 关键字将 String 转换为 NSString,也可以使用 as? 或 as! 进行可选类型转换。同样,可以使用 as 关键字将 NSString 转换为 String。
  2. 共享基础实现:String 和 NSString 在底层共享相同的字符串表示方式,即 Unicode 编码。因此,它们可以相互转换而不会导致数据丢失。

综上所述,String 和 NSString 是不同编程语言中表示字符串的类型,它们有一些区别和不同的语法,但也可以相互转换并共享相同的字符串表示方式。


Learn more:

  1. NSString | Apple Developer Documentation
  2. ios - Difference between String and NSString in Struct in Swift - Stack Overflow
  3. Swift String vs. NSString - Swift Talk - objc.io

defer 使⽤场景:

iOS中的defer语句可以在即将离开当前代码块时执行一系列语句,用于执行一些必要的清理工作。下面是一些常见的iOS中使用defer的场景:

  1. 文件操作:在打开文件后,可以使用defer语句来确保文件在离开当前代码块时被关闭,以防止忘记关闭文件或发生错误而导致文件未能正常关闭[1]
func readFile() {
    let file = open("file.txt")
    defer {
        close(file)
    }
    // 读取文件内容
}
  1. 内存管理:在手动分配内存的情况下,可以使用defer语句来确保在离开当前代码块时释放内存,以防止忘记释放内存或发生错误而导致内存泄漏[1]
func allocateMemory() {
    let pointer = UnsafeMutablePointer<Int>.allocate(capacity: 1)
    defer {
        pointer.deallocate()
    }
    // 使用指针进行操作
}
  1. 加锁/解锁:在使用锁进行临界区操作时,可以使用defer语句来确保在离开当前代码块时解锁,以防止忘记解锁或发生错误而导致死锁[1]
func performCriticalTask() {
    objc_sync_enter(lock)
    defer {
        objc_sync_exit(lock)
    }
    // 执行临界区操作
}
  1. 调用completion block:在某些情况下,一个函数可能有多个分支,可能会忘记调用completion block,使用defer语句可以确保在离开当前代码块时调用completion block,以避免潜在的bug[1]
func performTask(completion: () -> Void) {
    defer {
        completion()
    }
    // 执行任务
}
  1. 调用super方法:在重写父类方法时,可以使用defer语句来确保在离开当前代码块之前调用super方法,以便在super方法之前进行一些准备工作[1]
override func viewDidLoad() {
    defer {
        super.viewDidLoad()
    }
    // 准备工作
}

这些是一些常见的iOS中使用defer的场景,它们可以帮助我们在离开当前代码块时执行必要的清理工作,提高代码的可读性和可维护性。


Learn more:

iOS struct 在堆区还是栈区?

在iOS中,struct(结构体)通常是分配在栈区(stack)而不是堆区(heap)[1]。栈区是一种后进先出(LIFO)的数据结构,用于存储局部变量和函数调用的上下文。下面是关于iOS中struct在栈区的一些特点:

  1. 栈区分配:

    • 当一个函数被调用时,函数内的局部变量和参数会被分配在栈区。
    • 结构体实例作为局部变量时,会被分配在栈区。
    • 栈区的分配和释放是自动进行的,不需要手动管理内存。
  2. 栈区的特点:

    • 栈区的访问速度非常快,因为它是线性的数据结构,可以直接访问最新分配的内存。
    • 栈区的大小是有限的,通常比堆区小。
    • 栈区的内存分配和释放是自动的,不需要手动管理内存。

需要注意的是,如果结构体包含引用类型的属性(如类实例),那么这些引用类型的属性实际上是存储在堆区的,而结构体本身仍然是分配在栈区的[1]


Learn more:

  1. ios - Swift stack and heap understanding - Stack Overflow
  2. Memory management in Swift(Heap, Stack, ARC) | by Manasa M P | Medium
  3. Is String allocated on the Stack o… | Apple Developer Forums

属性中,值属性,和计算属性有什么区别?

值属性(Stored Properties)和计算属性(Computed Properties)是iOS开发中属性的两种不同类型,它们有以下区别:

  1. 存储方式:值属性是直接存储值的属性,它们在内存中有自己的存储空间。而计算属性不直接存储值,它们通过计算或其他逻辑来获取值。

  2. 存储位置:值属性通常存储在实例的内存中或者类的静态存储区中。而计算属性没有自己的存储位置,它们的值是通过计算得到的。

  3. 访问方式:值属性的值可以直接读取和设置,类似于普通的变量。而计算属性的值是通过计算得到的,不能直接设置,只能通过计算逻辑来获取。

  4. 依赖关系:计算属性可以依赖于其他属性的值进行计算。当依赖的属性发生变化时,计算属性会重新计算并返回新的值。而值属性的值是固定的,不会因其他属性的变化而改变。

下面是一个示例,展示了值属性和计算属性的区别:

struct Circle {
    var radius: Double // 值属性
    
    var area: Double { // 计算属性
        return Double.pi * radius * radius
    }
}

var circle = Circle(radius: 5.0)
print(circle.area) // 输出: 78.53981633974483

circle.radius = 7.0
print(circle.area) // 输出: 153.93804002589985

在上面的示例中,radius 是值属性,它直接存储了圆的半径。area 是计算属性,它通过计算 radius 的平方乘以π来获取圆的面积。当修改 radius 的值时,area 会重新计算并返回新的面积值。

总结来说,值属性是直接存储值的属性,而计算属性是通过计算或其他逻辑来获取值的属性。选择使用哪种属性类型取决于属性的特性和需求。

声明⼀个只有⼀个参数没有返回值闭包的别名

在iOS开发中,声明一个只有一个参数没有返回值的闭包的别名可以使用typealias关键字。以下是一个示例:

typealias SimpleClosure = (Int) -> Void

func performClosure(closure: SimpleClosure) {
    closure(10)
}

let myClosure: SimpleClosure = { number in
    print("Number: \(number)")
}

performClosure(closure: myClosure) // 输出: Number: 10

在上面的示例中,我们使用typealias关键字创建了一个名为SimpleClosure的闭包类型别名,它接受一个Int类型的参数并且没有返回值。然后,我们声明了一个名为myClosure的闭包变量,它符合SimpleClosure的类型定义。最后,我们通过调用performClosure函数并传递myClosure闭包来执行闭包。

通过使用闭包类型别名,我们可以更方便地声明和使用具有相同类型的闭包变量或参数。

什么是泛型,swift哪些地方使用了泛型?

泛型(Generics)是一种在编程中使用的技术,它允许我们编写通用且可重用的代码,避免重复。通过使用泛型,我们可以创建可以适用于不同数据类型的函数、类和结构体。

在Swift中,泛型被广泛应用于以下几个方面:

  1. 泛型函数(Generic Functions):我们可以创建可以适用于任何数据类型的函数。通过在函数定义中使用类型参数(type parameter),我们可以在函数内部使用这些参数来处理不同类型的数据。例如,我们可以创建一个泛型函数来打印任意类型的数据 [2]
func displayData<T>(data: T) {
  print("Data Passed:", data)
}

// 使用泛型函数打印不同类型的数据
displayData(data: "Swift") // 输出: Data Passed: Swift
displayData(data: 5) // 输出: Data Passed: 5
  1. 泛型类(Generic Classes):类也可以是泛型的,这意味着我们可以创建一个可以适用于不同类型数据的类。通过在类定义中使用类型参数,我们可以在类内部使用这些参数来定义属性、方法等。例如,我们可以创建一个泛型类来存储任意类型的数据 [2]
class Information<T> {
  var data: T
  
  init(data: T) {
    self.data = data
  }
  
  func getData() -> T {
    return self.data
  }
}

// 使用泛型类存储不同类型的数据
var intObj = Information<Int>(data: 6)
print(intObj.getData()) // 输出: 6

var strObj = Information<String>(data: "Swift")
print(strObj.getData()) // 输出: Swift
  1. 泛型约束(Generic Constraints):有时候,我们希望泛型只能接受特定类型的数据,而不是任意类型。在这种情况下,我们可以使用泛型约束来限制泛型的类型范围。例如,我们可以创建一个泛型函数,只接受符合特定协议的数据类型 [2]
func addition<T: Numeric>(num1: T, num2: T) {
  print("Sum:", num1 + num2)
}

// 使用泛型函数进行加法运算
addition(num1: 5, num2: 10) // 输出: Sum: 15
addition(num1: 5.5, num2: 10.8) // 输出: Sum: 16.3

在上述示例中,我们使用了Numeric协议作为泛型约束,这意味着泛型函数只能接受符合Numeric协议的数据类型(如IntDouble等)。

泛型在Swift中的应用使得我们可以编写更加通用和灵活的代码,提高了代码的重用性和可维护性。


Learn more:

  1. Generics in Swift explained with code examples - SwiftLee
  2. Swift Generics (With Examples)
  3. Learn Generics in Swift. Know what it is, how to use them, and… | by Taha Bebek | Better Programming

请解释 Swift 中的泛型是什么?并描述一下它的本质?

泛型(Generics)是 Swift 中的一种特性,它允许我们编写通用且可重用的代码,避免重复。泛型类型或函数在当前作用域中创建了约束,要求输入值符合这些要求。

泛型的本质是参数化类型,它允许我们在编写代码时不指定具体的类型,而是使用占位符来表示类型。这样一来,我们可以编写一次代码,适用于多种类型,提高了代码的灵活性和可重用性。

使用泛型可以使现有代码更具可重用性。一个经典的例子是使用一个元素的堆栈,也就是 Swift 中的数组。在没有使用泛型的情况下,我们可能会创建一个只支持整数的堆栈(IntStack),当我们需要一个字符串的堆栈时,就需要创建一个新的堆栈(StringStack),这样会导致代码重复和维护多个类型的问题。而使用泛型,我们可以定义一个通用的堆栈(Stack),其中的元素类型由泛型参数 Element 决定,从而避免了代码重复和多个类型的维护。

除了在类型级别上使用泛型,我们还可以在方法中使用泛型。例如,我们可以编写一个打印方法,该方法接受一个符合 CustomStringConvertible 协议的元素,并将其转换为字符串后打印出来。通过使用泛型,我们可以在方法定义中使用类型参数 T,并对 T 添加约束,要求它符合 CustomStringConvertible 协议。

泛型还可以作为返回类型使用。例如,我们可以编写一个将任意元素转换为数组的方法,该方法的返回类型是一个泛型数组。通过使用泛型返回类型,我们可以在调用该方法时根据实际情况确定返回的数组类型。

在 Swift 中,我们还可以使用关联类型和协议扩展来创建带有约束的泛型代码。通过在协议扩展中使用关联类型,我们可以对特定类型的数组添加特定的方法或属性。

总之,泛型允许我们编写可重用的代码,避免重复,并提高代码的灵活性和可维护性。但是,使用泛型需要一定的思维方式来将函数泛化,使其具有通用性。在编写代码时,应该始终从强类型的角度出发,只有在确实需要泛型代码提供的灵活性时才使用泛型。


Learn more:

  1. Generics in Swift explained with code examples - SwiftLee

  2. What is Generics in Swift ? | by Şevval Alev | Medium

  3. Swift Generics (With Examples)

What's the difference between opaque type and generic?

In Swift, both opaque types and generics are used to provide type abstraction and flexibility. However, there are some key differences between the two.

Generics:

  • Generics allow the code that calls a function to pick the type for that function's parameters and return value in a way that's abstracted away from the function implementation [1].
  • With generics, the type information is preserved and can be accessed and manipulated by the caller of the function.
  • Generics are useful when you want to write code that can work with different types without sacrificing type safety.
  • Generic functions and types are defined using type parameters, which are placeholders for actual types that will be provided when the code is used.
  • The type parameters can be used throughout the function or type to define the behavior and constraints.

Opaque Types:

  • Opaque types, on the other hand, are used when you want to hide the underlying type and provide a consistent interface without exposing the implementation details [1].
  • Opaque types are the reverse of generics. Instead of allowing the caller to choose the type, the implementation of the function or type chooses the type and returns an opaque type to the caller.
  • The opaque type hides the actual type and only exposes the interface defined by a protocol or a concrete type.
  • Opaque types are useful when you want to provide an abstract interface without revealing the implementation details or when you want to optimize performance by using concrete types instead of protocols [2].
  • Opaque types are defined using the some keyword followed by a protocol or a concrete type. The actual type is determined by the implementation and is not visible to the caller.

Summary:

  • Generics allow the caller to choose the type, while opaque types hide the underlying type and provide a consistent interface.
  • Generics preserve type information and allow type manipulation, while opaque types hide the type information and only expose the interface.
  • Generics are useful for writing code that works with different types, while opaque types are useful for providing abstract interfaces and optimizing performance.

Learn more:

  1. Opaque and Boxed Types - Documentation - Swift.org
  2. Opaque, Existential & Generic Types in Swift - Explained In 5 minutes | by Daniel James | Medium
  3. Why use generics and opaque type when the protocol seems adequate? - Using Swift - Swift Forums
转载自:https://juejin.cn/post/7303138588858204194
评论
请登录