Swift-28-值和引用类型、内存管理以及objectivc与swift混合调用

本小节的内容稍有点多,这三块内容基本没太大关系,只是笔者想在这一节中把swift的oop收下尾,所以全写在一起了。

值类型和引用类型

和其它语言一样,函数调用时对于参数到底是指针引用还是类似String这样的值传递(不可变),在swift中也需要在结构体、类中进行引用和区分。

引用传递

即多个引用指向同一个内存地址,一变全变。

class GreekGod {var name: Stringinit(name: String) {self.name = name}
}let hecate = GreekGod(name: "Hecate")
let anotherHecate = hecate
anotherHecate.name = "AnotherHecate"

结构体

struct Pantheon {var chiefGod: GreekGod
}let hecate = GreekGod(name: "Hecate") 
let pantheon = Pantheon(chiefGod: hecate)// GreekGod的name值为Hecate
let zeus = GreekGod(name: "Zeus")  // GreekGod的name值为Zeus
pantheon.chiefGod = zeus //这行代码会报错,因为结构体中的属性相当于let类型的,一旦初始化后不能改变
hecate.name = "Trivia" //这样的话值可以改变
let greekPantheon = pantheon
greekPantheon.chiefGod.name // Trivia

复制

swift未提供任何深复制的方法,如果需要的话需要自己写代码。

浅复制

其原理大概如下所示:
在这里插入图片描述

let athena = GreekGod(name: "Athena")//定义了一个数组:[{name "Athena"}, {name "Trivia"}, {name "Zeus Jr."}]
let gods = [athena, hecate, zeus]
let godsCopy = gods
gods.last?.name = "Jupiter"//以下输出全是:[{name "Athena"}, {name "Trivia"}, {name "Jupiter"}]
gods
godsCopy
  1. 值类型相等的比较用 ==
  2. 引用类型相等的比较用 ===,内存地址比较

深复制

在这里插入图片描述

//: Playground - noun: a place where people can playimport Cocoafileprivate class IntArrayBuffer {var storage: [Int]init() {storage = []}init(buffer: IntArrayBuffer) {storage = buffer.storage}
}struct IntArray {private var buffer: IntArrayBufferinit() {buffer = IntArrayBuffer()}private mutating func copyIfNeeded() {if !isKnownUniquelyReferenced(&buffer) {print("Making a copy of \(buffer.storage)")buffer = IntArrayBuffer(buffer: buffer)}}mutating func insert(_ value: Int, at index: Int) {copyIfNeeded()buffer.storage.insert(value, at: index)}mutating func append(_ value: Int) {copyIfNeeded()buffer.storage.append(value)}mutating func remove(at index: Int) {copyIfNeeded()buffer.storage.remove(at: index)}func describe() {print(buffer.storage)}
}var integers = IntArray()
integers.append(1)
integers.append(2)
integers.append(4)
integers.describe()print("Copying `integers` to `ints`")
var ints = integers
print("Inserting into `ints`")
ints.insert(3, at: 2)integers.describe()
ints.describe()/*
[1, 2, 4]
Copying `integers` to `ints`
Inserting into `ints`
Making a copy of [1, 2, 4]
[1, 2, 4]
[1, 2, 3, 4]
*/

内存ARC管理

Swift会自动处理好大部分内存问题,但并没有使用垃圾回收器(gc)。它使用的是引用计数系统(现在是自动计数,在新版本的系统中不能使用原来的retain等方法了,称为ARC技术-automatic reference counting),当计数为0时会释放内存,然后自动调用deinit()方法,在deinit这个方法中一般也会做一些资源释放的操作。

概述

Swift是在ARC的基础上构建的,另外Swift也没有开放获取引用计数的接口。

无论是ObjectiveC还是Swift都会存在两种数据类型,值和引用。所谓的内存管理就是对上述两种不同的数据类型进行管理操作。

  • 值类型:枚举和结构属于值类型,他们的的内存分配和管理比较简单,新建值类型的实例时,系统会自动为实例分配合理大小的内存。任何传递实例的动作都会创建实例的副本。当实例不再使用时,Swift会自动回收内存。
  • 类对象:也称引用类型,新建类的实例时系统也会自动分配内存给类实例,但是在传递过程中不会复制副本,而是引用指向同一个地址指针,无论在任何地方修改类实例,所有的引用都会看到变化。

比如下面的示例,Person为一个普通的自定义类对象


class Person : CustomStringConvertible {let name: Stringinit(name: String){self.name = name;}deinit{print("\(self) is being destroy")}
} 

用下面的代码调用

//~~ created Optional (Person(Bob))
var bob: Person? = Person(name:"Bob")//~~ (Person(Bob) is is being destroy
bob = nil //这行代码执行时会调用 deinit 方法

内存泄露

内存泄露一般是由循环引用引起的,比如下面的代码:

class Asset : CustomStringConvertible {let name: Stringlet value: Doublevar owner: Person? //强引用 Personvar description: String {if let actualOwner = owner {return "Asset(\(name), worth \(value), owned by \(actualOwner))"} else {return "Asset(\(name), worth \(value), not owned by anyone)"}}init(name: String, value: Double) {self.name = nameself.value = value}deinit {print("\(self) is being deallocated")}
}
class Person : CustomStringConvertible {let name: Stringvar assets = [Asset]() //强引用 Assetvar description: String {return "Person(\(name))"}init(name: String) {self.name = name}   deinit {print("\(self) is being deallocated")}//人拥有的资产func takeOwnership(of asset: Asset) {asset.owner = self //创建资产的归属,反向引用assets.append(asset)}
}    

测试代码

//创建人
var bob: Person? = Person(name: "Bob")//创建三个资产
var laptop: Asset? = Asset(name: "Shiny Laptop", value: 1_500.0)
var hat: Asset? = Asset(name: "Cowboy Hat", value: 175.0)
var backpack: Asset? = Asset(name: "Blue Backpack", value: 45.0)//创建资产的归属,正向引用
bob?.takeOwnership(of: laptop!)
bob?.takeOwnership(of: hat!)backpack = nil

当执行backpack = nil时,只有backpack被回收了,但它创建的asset.owner = Person的关系中的的bob对象并没有被释放。因为bob是一个引用类型,但此时这些资产实例无法访问了,但内存不会被回收,因为每个实例的引用计数都大于0。
在这里插入图片描述
在这里插入图片描述

引用类型

在Swift中一共有两种引用类型,有些语言会更多,比如java有4种之多。

  1. strong:强引用,引用一次会增加一次计数;默认情况下所有的引用都是强引用类型,
  2. weak:弱引用不会增加所指向实例的计数;

强引用

可通过nil赋值手动释放内存,但会比较麻烦,这种就怕被循环引用,因为循环引用外外层对象释放了,但内层对象有可能不被释放,进而造成内存泄漏(memory leak)。比如下面的代码,一旦实例化后就很难处理。

弱引用

解决上述循环依赖不释放内存的情况的方案一种是手动设置为nil,但很容易出错,所以Swift提供了弱引用类型来解决这个问题。如下面的代码:

class Person : CustomStringConvertible {let name: Stringlet accountant = Accountant()weak var assets = [Asset]() //用weak关键字来声明弱引用。
}   

这样在上例中,只有在main函数中实例化var bob: Person? = Person(name: “Bob”)时bob的引用计数才会+1,在Assert中的引用都不会增加计数,这样当把bob=nil时,计数就会降为0,自然也就全部释放了。

弱引用有以下几点需要注意:

  • 弱引用必须用var声明,不能用let;
  • 弱引用必须声明为可空类型,因为能变成nil的类型只有可空类型;

ObjectiveC和Swift混合调用

因为Swift是一门很新的语言,所以在开发中有一种很常见的情况是,需要在Objective-C实现的现有工程基础上工作。

objective-c调用swift代码

ObjectiveC调用swift代码可能会存在一些问题,另外ObjectiveC只能调用swift的类不能调用struct结构体。

详细步骤

  1. 创建一个Command Line Tool类型的项目,名称为 swift2objective ,工程代码选择Objective-C语言;
  2. 创建名为"Contacts.swift"的文件,这个类必须要继承一个ObjC的类,一般选择继承NSObject对象
  3. 创建.swift文件时,XCode默认提示要生成一个桥接头文件"swift2objective-Bridging-Header.h",默认为空内容,此文件作用是暴露接口,全局一个;
  4. 在.m文件中引入头文件,此例中为"#import “Contacts-Swift.h”(一种规范命名方式),此文件作用是暴露.swift代码,注意这个不是上面生成的头文件,是编译时生成的一个动态文件;
创建Swift文件
  • 创建名为 Contacts.swift 的文件时,生成一个桥接文件,这个文件的命名由:工程名称 + -Bridging-Header.h,如下:
    在这里插入图片描述

在Contacts.swift文件中写入如下代码:

//~~~Contacts.swift
import Foundation//要从 Objective-C调用Swift,继承Objective-C类这一步是必要的,swift必须用类,因为结构体对于OjeC是不可见的
class Contacts:NSObject{let name: String = "aaa"@objc public func sayHello(){ //注意前面的@ojbc一定要写,否则调不到print(name);}
}
调用Swift类

在main.m文件中添加如下代码

//~~~ main.m
#import <Foundation/Foundation.h>//注意这行引用
#import "swift2objective-Swift.h"int main(int argc, const char * argv[]) {@autoreleasepool {Contacts *contact = [[Contacts alloc] init];[contact sayHello];}return 0;
}

swift2objective-Swift.h 是一个编译期生成的文件,在源码中看不到,可从项目属性中设置,如下图:
在这里插入图片描述
上面的程序会输出如下代码
在这里插入图片描述

swift调用objective-c代码

swift可以调用全部的ObjectiveC类库

详细步骤

接着上面的工程,

  1. 创建名为"Address"的Cocoa class类,它会生成Address.m和Address.h两个文件;
  2. 在上面生成的"swift2objective-Bridging-Header.h"桥接文件中添加"#import “Address.h”;
  3. 然后就可以在Contacts.swift文件中调用了
创建Cocoa class文件
#import <Foundation/Foundation.h>NS_ASSUME_NONNULL_BEGIN@interface Address : NSObject- (void) printContact;@endNS_ASSUME_NONNULL_END
在桥接头文件中引入.h声明文件

这个文件中只一行代码,桥接文件的作用是把ObjectiveC代码暴露给swift代码。

//~~~swift2objective-Bridging-Header
//
//  Use this file to import your target's public headers that you would like to expose to Swift.
//
#import "Address.h"
swift调用Cocoa class类

修改Contacts.swift文件,源码如下:

//~~~ Contacts.swift
import Foundation//要从 Objective-C调用Swift,继承Objective-C类这一步是必要的,swift必须用类,因为结构体对于OjeC是不可见的
class Contacts:NSObject{let name: String = "aaa"@objc public func sayHello(){ //注意前面的@ojbc一定要写,否则调不到print(name);Address.init().printContact(); //调用Objective-C代码}
}

测试结果如下:
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/643547.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【MySQL】A01、性能优化-语句分析

1、数据库优化方向 A、SQL及索引优化 根据需求写出良好的SQL&#xff0c;并创建有效的索引&#xff0c;实现某一种需求可以多种写法&#xff0c;这时候我们就要选择一种效率最高的写法。这个时候就要了解sql优化 B、数据库表结构优化 根据数据库的范式&#xff0c;设计表结构&…

【计算机网络】(三)物理层 - 通信基础

文章目录 【计算机网络】&#xff08;三&#xff09;物理层 - 通信基础前言3.1 物理层的基本概念3.2 数据通信的基础知识3.2.1 数据、信号、码元3.2.2 信源、信宿、信道3.2.3 编码、调制3.2.3.1 基带调制&#xff08;编码&#xff09;3.2.3.2 带通调制&#xff08;调制&#xf…

系统安全与应用(1)

目录 1、账号安全管理 &#xff08;1&#xff09;禁止程序用户登录 &#xff08;2&#xff09;锁定禁用长期不使用的用户 &#xff08;3&#xff09;删除无用的账号 &#xff08;4&#xff09;禁止账号和密码的修改 2、密码安全管理 设置密码有效期 1&#xff09;针对已…

2024 年选择安全运营中心 (SOC) 工具指南

安全运营中心 (SOC) 是对抗网络威胁的前线。他们使用各种安全控制措施来监控、检测和快速响应任何网络威胁。这些控制措施对于确保信息系统全天候安全至关重要。 大型组织中的现代 SOC 与各种安全供应商合作&#xff0c;处理 75 到 100 种不同的工具。让我们探讨一下您可能遇到…

数据结构基础:链表操作入门

数据结构基础&#xff1a;链表操作入门 数据结构基础&#xff1a;链表操作入门链表的基本概念链表的基本操作输出链表插入节点删除节点查找值 完整的链表操作示例结语 数据结构基础&#xff1a;链表操作入门 在计算机科学中&#xff0c;数据结构是组织和存储数据的方式&#x…

图书租赁系统-借阅图书

图中展示了所有可以借阅的图书&#xff0c;点击“借阅”按钮便可以借阅图书。 借阅成功后&#xff0c;可以到bookorder菜单中阅读该书。 阅读功能待开发。 add.html借阅图书页面 <!DOCTYPE html> <html lang"zh" xmlns:th"http://www.thymeleaf.org…

笔记本电脑耗电和发热比较厉害怎么处理

工作中会遇到有同事反馈笔记本电脑耗电和发热比较厉害&#xff0c;主要检查以下几个地方 1、CPU频率 很多人觉得是cpu使用率高就代表电脑跑得快&#xff0c;发热量就大&#xff0c;其实不是的&#xff0c;主要是看的cpu频率&#xff0c;频率越高&#xff0c;电脑发热量越大。如…

[论文笔记] EcomGPT:COT扩充数据的电商大模型

社区供稿 | EcomGPT:基于任务链数据的电商大模型(附魔搭推理实践) - 知乎 https://arxiv.org/pdf/2312.15696.pdf EcomInstruct指令数据集构建 数据集组成 COT方式构造垂域训练数据:把原本的垂域任务分解成了原子任务,构造了基于解决原子任务的数据。这样能用类似…

【学习】服务器解决:重新分配同样端口号后,连不上VScode

原来服务器分配的环境有问题&#xff0c;重新分配了一下。还是同样的端口号&#xff0c;Xshell和xftp能够连接上&#xff0c;但是VScode连接不上。 问题解决: 清除本地 SSH 缓存中与远程主机相关的条目可以通过编辑 known_hosts 文件来实现。这个文件包含了您曾经连接过的远程主…

java实现解析html获取图片或视频url

一、前言 有时在实际项目中&#xff0c;比如发布某篇文章&#xff0c;需要取文章中的某张图片作为封面&#xff0c;那么此时需要文章内容&#xff0c;获取html内容中的图片地址作为封面&#xff0c;下面讲下如何获取html中的图片或视频地址。 二、实现 1.先定义一个工具类&…

uniapp APP检测更新

需求&#xff1a; 1.首次进入APP给出弹窗提示是否存在最新版本APP&#xff0c;可选择更新或者取消 2.选择取消后&#xff0c;在使用期间不再弹出该弹窗 3.在设置中增加按钮&#xff0c;点击进行版本检测&#xff0c;再弹窗 效果图&#xff1a; 使用到的插件&#xff1a;APP升…

CentOS-7安装grafana

一、通用设置&#xff08;分别在4台虚拟机设置&#xff09; 1、配置主机名 hostnamectl set-hostname --static 主机名2、修改hosts文件 vim /etc/hosts 输入&#xff1a; 192.168.15.129 master 192.168.15.133 node1 192.168.15.134 node2 192.168.15.136 node33、 保持服…