了解那些关键字的使用之前必须先了解系统内存的分配规则
栈区(stack):由编译器自动分配释放,存放的是运行时候运行函数分配的局部变量,函数参数,返回数据,返回地址等。其操作类似数据结构中的栈。栈的效率高,但是程序员无法自动控制。
堆区(heap):由程序员分配,程序员如果不释放,可能由OS回收,分配方式类似于链表。堆是 new alloc分配,速度慢,容易产生内存碎片。
全局区(静态区)(static):存放全局变量,静态数据,初始化的数据放在一块,为初始化的数据放在一块,程序结束后由系统统一释放。
- assign: 只是简单的赋值操作,指向同一个内存区,一个地方的变了,其他的也跟着变。引用计数不变。
- retain: 浅拷贝,指针拷贝,引用计数+1;
- weak: ARC引入,跟assign修饰符功能一样,简单的赋值操作,只是当对象被释放时,会将weak引用置为nil,防止野指针。assign不能修饰对象,weak可以修饰对象。
- strong: ARC引入,用来替代retain。修饰对象的时候两者一样,但是在mrc修饰block的时候strong相当于copy,而retain相当于assign会报野指针。
- copy: 深拷贝,地址拷贝,旧的引用计数不变,新的引用计数为1;这也是为什么block为什么要用copy是最好的,因为block默认是声明在栈当中,否则当函数返回时候block就销毁了,这样就容易引起崩溃,所以为了让block可以访问外部变量,就必须拷贝一份到堆中,而strong是浅拷贝,不会真正意义上的内存拷贝。
- readwrite:可读写,默认生成set/get方法;
- readonly:只读,默认生成get方法
- nonatomic:非原子性;
- atomic:原子性,非线程安全
- 基本类型默认关键字:atomic,readwrite,assign
- OC对象默认关键字:atomic,readwrite,strong;
- atomic本质上是对对象的set/get方法加锁,当引用A的set/get方法和引用B的set/get方法同时操作时,虽然加了锁,但是引用Bget到的可能是引用Aset之后的,所以线程不安全,且消耗性能,所以建议不用atomic关键字。
- weak的实现原理 :
1、初始化时:runtime会调用objc_initWeak函数,初始化一个新的weak指针指向对象的地址。
2、添加引用时:objc_initWeak函数会调用 objc_storeWeak() 函数, objc_storeWeak() 的作用是更新指针指向,创建对应的弱引用表。
3、释放时,调用clearDeallocating函数。clearDeallocating函数首先根据对象地址获取所有weak指针地址的数组,然后遍历这个数组把其中的数据设为nil,最后把这个entry从weak表中删除,最后清理对象的记录。可变类型(NSMutableArray,NSmutableString)等只能用浅拷贝 strong,如果用copy(深拷贝)生成的是不可变类型,会报错。而不可变类型,深拷贝 浅拷贝都可以。下面通过一个例子解释copy修饰,
根绝打印结果,所有不管是可变还是不可变对象,用copy修饰的就是重新拷贝一份内存,所以arr5的值改变都不会影响他们,而strong只是对象地址指针拷贝。
代理用weak还是assign修饰:最好的是使用weak修饰,因为assign只是指针赋值,如果使用之后没有置为nil,有可能出现野指针,但是用weak就不使用之后系统会置为nil,永远不会出现野指针。
NSInteger跟int的区别
NSInteger是苹果对int的封装 能自动识别系统的位数,自动返回最大的类型。
Swift所有常用关键字解释
let:声明常量 类似于const,用let声明的变量不可以再赋值,不然会报错;
var:声明变量 声明变量,是可以改变值;
class:声明一个类
enum:声明一个枚举
func:声明一个函数
init:相对于类的构造方法的修饰
deinit: 属于析构函数。析构函数(destructor) 与构造函数相反,当对象结束其生命周期时(例如对象所在的函数已调用完毕),系统自动执行析构函数,和OC中的dealloc 一样的,通常在deinit和dealloc中需要执行的操作有:
- 对象销毁
- KVO移除
- 移除通知
- NSTimer销毁
对于类的构造和释构在swift 中需要使用关键词来修饰,而很多高级语言并不需要特别的指定,如C++ 只需要类名与构造函数名相同就可以,不需要额外的关键词;
extension:扩展,类似于OC的categories,Swift 中的可以扩展以下几个:
- 添加计算型属性和计算静态属性
- 定义实例方法和类型方法
- 提供新的构造器
- 定义下标
- 定义和使用新的嵌套类型
- 使一个已有类型符合某个接口
protocol:协议,也可以叫接口,这个往往在很多高级语言中不能多重继承的情况下使用协议是一个比较好的多态方式。
static:全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 当类首次被加载时static定义的变量被分配空间,程序结束后由系统释放.
如果在一个程序里过多的使用static定义的成员,确实很占内存,因为他的生命周期是整个程序,程序运行时无法被gc所回收,直到程序结束,释放内存,在函数内部声明, 当函数结束后, 不会从内存中释放, 再次调用函数时, 直接使用上次的结果。
从打印的结果看 static修饰的变量只会初始化一次,并且只会伴随程序停止运行而回收内存。
一般什么情况下使用static修饰的变量:就是这个对象初始化一次,力求这个类的所有对象都能使用,不破坏封装性。并且她的值是可以更新的。他的作用域只在当前类,但是static也可以修饰局部变量。
extern:全局变量 也称为外部变量,是在方法外部定义的变量,他不属于哪个方法,而是属于整个程序.
如果全局便利和局部变量重名,则在局部变量作用域内,全局变量被屏蔽,不起作用。编程时候尽量不使用全
局变量。举个例子。
struct:定义一个结构体
subscript:下标索引修饰.可以让class、struct、以及enum使用下标访问内部的值,例子如下
typealias:为此类型声明一个别名.和 typedef类似.(前提是这个类是已经存在的)
override:如果我们要重写某个方法, 或者某个属性的话, 我们需要在重写的变量前增加一个override关键字,swift是支持重写父类的属性的
final:Swift中,final关键字可以在class、func和var前修饰。表示 不可重写,可以将类或者类中的部分实现保护起来,从而避免子类破坏
break:跳出循环跟OC一样
case:switch的选择分支 跟OC一样
continue: 跳过本次循环,继续执行后面的循环.
in:范围或集合操作,多用于遍历.
fallthrough: swift语言特性switch语句的break可以忽略不写,满足条件时直接跳出循环.fallthrough的作用就是执行完当前case,继续执行下面的case.类似于其它语言中省去break里,会继续往后一个case跑,直到碰到break或default才完成的效果.
where:用于条件判断,和数据库查询时的where 'id > 10'这样功能. swift语言的特性.OC中并没有.
switch 0
case let (x,y) wherex==y
pritf("where 判断正确才执行")
is : 等同于OC里面的isKindClass. as 与强制转换含义雷同.字面理解就是有强项转换,即向下转型,子类(派生类)向父类转换,官方解释说这是一个不被保证的转换,可能会因为强转的失败而会导致崩溃。同时 !是一个陷阱的标志,就像⚠️一样,用起来存在一定危险性。类似OC的 UIButton *btn=(UIButton *)currentView
associativity:运算符的结合性
print("COLUMN = (#column) \n FILE = (#file) \n FUNCTION = (#function) \n LINE = (#line)")
associativity:运算符的结合性
get set:当我们得到属性值的时候,会调用该属性的get方法,当我们去设置属性值的时候,会调用该属性的set方法,当我们给属性设置值的时候,Swift会自动给我们创建一个名为newValue的常量并且可以用于set{}中。Swift中有储值属性和计算属性,一般我们应该是给计算属性添加get和set方法,先通过一个Example展示:
willSet、didset:在Swift语言中用了willSet和didSet这两个特性来监视属性的除初始化之外的属性值变化,例子如下
infix:表示要定义的是一个中位操作符,即前后都是输入
inout:inout作为函数声明时,引用传值的关键字。但是在调用的时候引用的是地址,所以在引用的时候要加上&
func testInout(a : inout Int, b: inout Int) -> Int {
return a+b
}
var num1 = 3
var num2 = 6
let number = me.testInout(a: &num1, b: &num2)
left:可用于拓展的约束属性
mutating:写在func前面,以便于让func可以修改struct和protocol的extension中的成员的值。 如果func前面不加mutating,struct和protocol的extension中的成员的值便被保护起来,不能修改
operator、postfix:在Swift中定义属于自己的运算符
unowned、unowned(sale)、unowned(unsafe):无宿主引用
与弱引用一样,当把一个实例声明为无主引用时,此实例不会持有这个对象,即不会使对象的引用计数加1。但与弱引用不同的是,当对象被废弃,其无主引用并不会被置为 nil。
unowned(safe):当访问 unowned(safe) 类型的无主引用时,运行时会进行安全检查,如果对象已经废弃,将抛出异常并终止程序。
unowned(unsafe):unowned(unsafe) 的作用效果实际上相当于 Objective-C 属性标示符中的 assign/unsafeunretained。访问 unowned(unsafe) 类型的无主引用时,运行时的安全检查被禁用,这时会有三种情况:
-
废弃对象内存还未被覆盖:程序正常运行
-
废弃对象内存被部分覆盖:奇怪的 crash,不确定的结果
-
废弃对象内存正好填入新的对象:由于向新的对象发送了调用旧对象方法的消息,会出现 unrecognized selector exceptions
weak:weak 即弱引用,当把一个实例声明为弱引用时,此实例不会持有这个对象,即不会使对象的引用计数加1。当对象被废弃,其所有的弱引用会被置为 nil。这样保证了当你调用一个弱引用对象时,你能得到一个对象或者nil.
对于class来讲,默认或指定的初始化方法作为所谓的Designated初始化。
若重载的初始化需要调用Designated初始化则将它作为convenience初始化,在方法前要加上convenience关键字。
defer:用来包裹一段代码,这个代码块将会在当前作用域结束的时候被调用。这通常被用来对当前的代码进行一些清理工作,比如关闭打开的文件等。
可以在同一个作用域中指定多个 defer代码块,在当前作用域结束时,它们会以相反的顺序被调用,即先定义的后执行,后定义的先执行。
guard : 当某些条件不满足的情况下,跳出作用域.
??:空合运算符. 空合运算符做的事情很简单。x ?? y
表示判断x
是否为nil
,若不为nil
,则将x
解包后返回,否则,取y
的值。比如下面的代码:假设有一个“系统”,用户可以实名登录,也可作为游客匿名登录,此时,用户的登录名loginName
则应该表示为可选型。而系统最终显示的用户名username
则需要根据loginName
的内容做一个判断,如果loginName
为空,则使用默认名称"Guest"
。
let username = login() ?? "Guest"
class var: 在swift中对于enum和struct来说支持用static关键字来标示静态变量,但是对于class成员来说,只能以class var的方式返回一个只读值。例如:
这样其实很好的区分了struct和class的功能