swift

一、值类型和引用类型

1:值类型(Value types)

每个实例都保留一份独有的数据拷贝,一般以结构体 (struct) 、 枚举(enum) 或者元组(tuple)的形式出现。如下OBAnimal的结构体

typedef struct {
    NSString *name;
}OBAnimal;

int main(int argc, char * argv[]) {
    OBAnimal ani1;
    ani1.name = @"ob";
    NSLog(@"%p:%@",&ani1,ani1.name);
    
    OBAnimal ani2 = ani1;
    ani1.name = @"newOB";
    NSLog(@"%p:%@",&ani1,ani1.name);
    NSLog(@"%p:%@",&ani2,ani2.name);
    return 0;
}

//打印如下
2020-08-13 14:55:12.980223+0800 ani1=0x7ffee7d2af08:ob
2020-08-13 14:55:12.980709+0800 ani1=0x7ffee7d2af08:newOB
2020-08-13 14:55:12.980785+0800 ani2=0x7ffee7d2af00:ob

OBAnimal ani2 = ani1;时,系统将会创建一个新的实例并将ani1中的值拷贝一份,赋值给ani2,所以修改ani1中的nameani2不会改变;ani2ani1是两个内存的值

2:引用类型(Reference types)

每个实例共享一份数据来源,一般以类(class)的形式出现。

如果上面的OBAnimal是一个类,那么修改其中任意一个实例的name属性。那么另一个也会改变。
因为OBAnimal *ani2 = ani1;操作时,堆上只有一个实例对象,ani2ani1是两个指向这个实例对象的指针。

引用类型和值类型区别
  1. 类是引用类型, 结构体为值类型
  2. 结构体不可以继承
  3. 值类型被赋予给一个变量、常量或者被传递给一个函数的时候,其值会被拷贝
  4. 引用类型在被赋予到一个变量、常量或者被传递到一个函数时,其值不会被拷贝。因此,引用的是已存在的实例本身而不是其拷贝

二、 mutating关键字

类是引用类型,而结构和枚举是值类型。

默认情况下,不能在其实例方法中修改值类型的属性。为了修改值类型的属性,必须在实例方法中使用mutating关键字。

使用此关键字,方法将能够更改属性的值,并在方法实现结束时将其写回到原始结构

png1
所以要改成mutating

struct Person {
    var name = "ob";
    mutating func test(_ newName: String) {
        self.name = newName;
        print(self.name);
    }
};
var tom = Person.init(); //这里就不能用let了,因为值改变了
tom.test("lsy");

//打印如下
lsy

三、strong、weak、 unowned

Swift 的内存管理机制与 Objective-C一样。

只有引用类型变量所引用的对象才需要使用引用计数器进行管理,对于枚举、结构体等,他们都是值类型的。因此不需要使用引用计数进行管理。

1:strong

strong 代表着强引用,是默认属性。不用写关键字strong

let cat = Animal.init();
2:weak

weak代表着弱引用。当对象被声明为 weak 时,该对象的引用计数不会增加1。它在对象释放后弱引用也随即消失。继续访问该对象,程序会得到 nil,不会崩溃

weak var cat = Animal.init();

因为对象释放时:需要对cat指针进行cat = nil操作,所以这里只能用var可变类型

3:unowned

unowned 与弱引用本质上一样。唯一不同的是,对象在释放后,依然有一个无效的引用指向对象,它不是 Optional 也不指向 nil。如果继续访问该对象,程序就会崩溃。和oc的__unsafe_unreturned一样

    unowned var cat = Animal.init();
    print(cat as Any); //发生崩溃

weak 和 unowned 的引入是为了解决由 strong 带来的循环引用问题。weak安全性高

四、闭包

1:闭包
    func testBlock() {
        let a = { () -> () in  //无参数无返回值的闭包
            print("aaa");
        };
        a();
        
        
        let b = { () in		//无参数无返回值的闭包的简写-1
            print("bbb");
        };
        b();
        let c = {			//简写-2
            print("ccc");
        };
        c();
    }
2:值捕获

在OC中,Block的值捕获

    int a = 10;
    void(^ob_blick)(void) = ^{
        printf("%d", a); // 10   如果在int前加 __block,那么打印20
    };
    a = 20;
    ob_blick();

在swift中

        var age = 10;
        let c = {
            print("ccc--:\(age)"); //打印 20 
        };
        age = 20;
        c();

发现打印的是20,和oc中的表现不一样啊,怎么回事?不急再来换个写法

        var age = 10;
        let c = { [age] in  	//---->这里有变化
            print("ccc--:\(age)");
        };
        age = 20;
        c();

原来在swift中,闭包也是会捕获变量的,但是分情况

  1. 默认情况下,只有在执行block()时,才会捕获变量
  2. 加上[]中括号后,就是立即捕获,和OC一样了

同样的struct和enum也符合这个情况,除了class

    struct OBDog {
        var name = "jack";
    }
    func testBlock() {
        var dog = OBDog.init();
        let block = {
            [dog] in   //注销就会打印tom 不注销立即捕获就会打印jack 
            print("ccc--:\(dog.name)");
        };
        dog.name = "tom";
        block();
    }

如果吧struct OBDog换成class OBDog,那么就会一直打印 tom因为class是引用类型,捕获的是指针,防止循环引用可以使用[weak dog] in[unowned dog],具体使用哪个?根据闭包和捕获对象的生命周期而定,

unowned:性能高,可读性强,不需要解包
weak :性能相对低(释放时还需要去weak表里操作),安全性高,需要解包,

3:逃逸闭包 escaping

当闭包作为参数传递给函数时,闭包被称为转义函数,在函数返回后调用。

就是将参数闭包赋值给其他全局的或者静态的变量,就要使用逃逸闭包

typealias  Block  =  (Int) ->();
class Model {
    var myblock: Block;
    func test3(call: @escaping (Int)->()) {
        self.myblock = call;
    }
    init() {
        
    }
}

  1. @escaping和@non-escaping修饰符是用来修饰闭包的。闭包默认为@non-escaping类型
  2. @escaping:转义闭包,闭包的生命周期不在传入的函数范围内管理。由函数外部变量持有。当函数结束之后,闭包才去执行
  3. @non-escaping:闭包在函数内执行完后,函数才去执行,闭包销毁。

当使用@escaping时,要考虑self被循环引用。使用weak或者unowned解决

五、inout

Swift中inout只不过是按值传递,然后再写回原变量,类似于c语言中的指针传递

func test(_ num: inout Int) {
    num = num+1;
}

var num = 3;
test(&num);
print(num);
// 打印
4

六、auto变量

在计算机编程领域,自动变量(Automatic Variable)指的是局部作用域变量,具体来说即是在控制流进入变量作用域时系统自动为其分配存储空间,并在离开作用域时释放空间的一类变量。在许多程序语言中,自动变量与术语“局部变量”(Local Variable)所指的变量实际上是同一种变量,所以通常情况下“自动变量”与“局部变量”是同义的。

七、Swift值类型的写时复制

当执行如下代码时:

func testArr() {
        var arr = [1,2];
        let brr = arr;
        
        printValuePoint(arr);
        printValuePoint(brr);
        arr.append(4);
        
        print("----");
        
        printValuePoint(arr);
        printValuePoint(brr);
    }
    
    func printValuePoint(_ obj: UnsafeRawPointer) {
        print(obj);
    }

//打印如下-----------
0x0000600003f37140
0x0000600003f37140
----
0x0000600002402560
0x0000600003f37140

经过多方查证以及自身的实验,得出如下结论:

基本数据类型Int, String, Double以及Struct都是在赋值的时候进行赋值的,
而集合类型ArraySetDictionary确实是写时复制的

八、defer

注意defer的作用域, if - else - while - for 大括号结束都是可以执行defer的。

使用defer代码块来表示在函数返回前,函数中最后执行的代码。具有延时执行的特性。

    func testDefer(t: Bool)   {
        print("start --1");
        defer {
            print("defer --2");
        }
        print("end --3");
    }

//打印
start --1
end --3
defer --2

当有多个defer时,后加入的先执行,可以猜测Swift使用了stack来管理defer。

defer不会像OC的block一样捕捉变量,

func testDefer(t: Bool)   {
        print("start --");
        var age = 10;
        defer {
            print("defer --\(age)");
        }
        age = 20;
        print("end --");
}
start --
end --
defer --20

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值