欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 汽车 > 时评 > iOS内存管理中的强引用问题

iOS内存管理中的强引用问题

2025/4/21 23:38:29 来源:https://blog.csdn.net/Unlimited_ci/article/details/147279970  浏览:    关键词:iOS内存管理中的强引用问题

iOS内存管理

关于强引用循环

强引用循环是 ARC 无法自动处理的常见问题。如果两个对象互相强引用对方,就会造成引用计数不为零,导致对象无法释放。典型的情况是在闭包中引用 self 时,self 和闭包之间可能会互相持有,形成强引用循环。

解决方法: 使用 [weak self][unowned self] 来避免强引用循环,特别是在闭包内。

强引用(Strong Reference)是指一个对象在内存中持有对另一个对象的引用时,这个引用会增加被引用对象的引用计数。当一个对象的引用计数增加时,它的生命周期就被延长,直到引用计数变为零,表示没有任何对象引用它时,ARC 会自动释放这个对象的内存。

在 iOS 中,默认情况下,所有对象的引用都是强引用。强引用会导致引用的对象在内存中不会被释放,直到所有强引用都解除。

强引用的基本理解

  • 强引用:指对象通过 strong 持有另一个对象。该对象的生命周期会被引用方延长,直到引用方解除对它的持有。
  • 当两个对象之间通过强引用相互持有时,就会形成 强引用循环(Retain Cycle),这种情况下,即使它们不再需要对方,它们的引用计数也不会降到零,从而导致内存泄漏。

强引用的例子

1. 基本的强引用示例

假设你有两个对象:PersonCarPerson 强引用 Car,意味着只要 Person 存在,Car 就不会被释放。

class Person {var car: Car?
}class Car {var owner: Person?
}var john: Person? = Person()
var toyota: Car? = Car()john?.car = toyota
toyota?.owner = john

在这个例子中,john 对象强引用了 toyota 对象,同时 toyota 也强引用了 john 对象。由于两个对象互相持有对方,即使这两个对象的生命周期已经结束,它们的引用计数也不会变为零,因为它们彼此依赖,这就形成了 强引用循环。这会导致内存泄漏。

2. 解决强引用循环

可以通过使用 弱引用weak)来打破这种循环,确保当 PersonCar 不再需要时,它们能正确释放。

class Person {var car: Car?
}class Car {weak var owner: Person?  // 使用 weak 避免强引用循环
}var john: Person? = Person()
var toyota: Car? = Car()john?.car = toyota
toyota?.owner = john

在这个例子中,owner 属性使用 weak 修饰符,这样当 john 被释放时,toyota 中的 owner 引用就会变为 nil,从而打破强引用循环。

3. 强引用与内存管理的关系

当我们使用强引用时,ARC 会增加被引用对象的引用计数。下面是一个具体例子,说明如何影响内存管理:

class Dog {var name: Stringinit(name: String) {self.name = name}deinit {print("\(name) is being deinitialized")}
}var dog1: Dog? = Dog(name: "Buddy")
var dog2 = dog1  // dog2 强引用 dog1dog1 = nil  // dog1 被置为 nil,但是 dog2 仍然持有 Buddy 的引用

在上面的代码中:

  • dog1 被赋值为 nil 时,它不再持有 Buddy 对象。
  • 但是由于 dog2 仍然强引用着 BuddyBuddy 对象并不会立即被释放。
  • Buddy 只有当 dog2 也被置为 nil 后,才会释放内存。
4. 常见场景:闭包中的强引用

闭包是导致强引用循环的一个常见场景。例如,下面的代码就会导致强引用循环:

class ViewController: UIViewController {var someProperty: String = "Hello"func setupClosure() {let closure = {print(self.someProperty)  // 闭包捕获了 self}closure()}
}let vc = ViewController()
vc.setupClosure()

在上面的代码中,closure 捕获了 self(即 ViewController 的实例),这意味着 ViewController 对象将始终被闭包引用,self 的引用计数不会变为零,导致 ViewController 永远不会被释放。为了避免这种情况,我们通常使用 弱引用无主引用

func setupClosure() {let closure: () -> Void = { [weak self] inprint(self?.someProperty ?? "")}closure()
}

使用 [weak self] 来捕获 self,可以避免闭包对 ViewController 的强引用,从而避免强引用循环。

但需要注意的是,并不是所有的闭包都会产生强引用

不会产生强引用的情况

在闭包中使用 weakunowned 主要是为了避免 强引用循环(retain cycle),尤其是当闭包捕获 self 时。是否需要使用 weakunowned 取决于闭包与被捕获对象之间的生命周期关系。以下是几种情况,你可以根据这些情况来判断何时不需要使用 weakunowned

1. 闭包的生命周期比捕获的对象短

如果闭包的生命周期比被捕获的对象短(即闭包不会持有对象,且对象的生命周期不会依赖于闭包),则无需使用 weakunowned

示例

  • 闭包作为局部变量在方法内使用,并且被调用后立即销毁。
  • 闭包没有持有对对象的引用,且被调用时对象已被销毁。
class MyClass {var value = 10func performAction() {// 闭包在执行后立即销毁,不会产生强引用循环let closure = {print(self.value)}closure()}
}let obj = MyClass()
obj.performAction()

在这个例子中,闭包在方法内部执行时,仅仅是一个局部变量。它不会在方法外部持有 self,且方法执行完毕后,闭包就会被销毁,所以不需要使用 weakunowned 来捕获 self

2. 闭包被存储在对象的属性中,并且对象的生命周期与闭包相同

如果闭包是对象的一个属性,并且该对象的生命周期与闭包相同(即闭包和对象一起销毁),则也不需要使用 weakunowned

例如,闭包可能会作为一个委托回调、通知、或者是一些任务的完成闭包。如果该对象本身不会被提前销毁,闭包引用 self 是安全的,因为对象的生命周期会保持直到闭包不再需要。

示例

class Task {var completion: (() -> Void)?func start() {completion = {print("Task is done")}}
}let task = Task()
task.start()

在这个例子中,Task 对象持有 completion 闭包,闭包本身不会引发强引用循环,因为 Task 对象会一直存在直到 completion 被销毁。这里没有强引用循环问题。

3. 捕获的对象不会导致内存泄漏

如果闭包捕获的对象是一个“非持有”对象(例如全局对象、常量、静态对象),或者闭包执行时被捕获的对象本身在执行完成后就会被销毁(比如临时创建的对象),则不需要使用 weakunowned

示例

class MyClass {func fetchData() {let completion: () -> Void = {print("Data fetched!")}completion()}
}let instance = MyClass()
instance.fetchData()

在这个例子中,completion 闭包没有捕获 self,因此不会形成强引用循环。并且 completion 的生命周期非常短,执行完成后就会被销毁。

4. 闭包没有捕获 self

如果闭包本身不捕获 self(即没有使用 [weak self][unowned self]),且闭包本身不会导致引用循环,也可以不使用 weakunowned。这种情况下,闭包本身不会持有对象。

示例

class MyClass {var name = "Hello"func fetchData() {let completion: () -> Void = {print("Data fetched!")}completion()}
}let instance = MyClass()
instance.fetchData()

这里 completion 不捕获 self,所以 MyClass 对象不会因为闭包而延长生命周期。

总结:

我们需要注意闭包和实例是否会相互引用,还需要注意闭包和实例的生命周期

你不需要使用 weakunowned 来捕获 self 的情况包括:

  1. 闭包的生命周期比捕获的对象短,例如临时的局部闭包。
  2. 闭包被存储在对象属性中,并且对象的生命周期与闭包相同。
  3. 闭包没有捕获 self,且不涉及持有对象的引用。
  4. 捕获的对象不会导致内存泄漏,例如全局变量、常量等对象。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词