Swift和OC的区别
语言层面
-
swift是静态语言,有类型推断,OC是动态语言。静态语言和动态语言
-
swift是一门支持多编程范式的语言,既支持面向对象编程,也支持面向协议编程,同时还支持函数式编程,OC面向对象编程。
-
swift支持静态派发(效率高)、动态派发(函数表派发、消息派发)方式,OC支持动态派发(消息派发)方式。Swift派发机制
-
swift单文件、oc双文件。
语法层面
- swift注重值类型,OC注重引用类型。
- swift支持泛型,OC只支持轻量泛型。
- swift的协议不仅可以被类实现,也可以被struct和enum实现。
- swift有元组类型、支持运算符重载。
- swift支持命名空间,NSClassFromString需要加上命名空间(命名空间.类名)。
- swift支持默认参数。
- swift比oc代码更加简洁。
- oc的类都是集成于NSObject,swift可以定义不继承于任何的类。
- swift的switch可以判断类型比较多,oc只能判断基本数据类型。
- swift支持运算符重载
- swift有可选类型
- 枚举
Swift比Objective-C优势
- swift容易阅读,语法和文件结构简易化。
- swift更易于维护,文件分离后结构更清晰。
- swift更加安全,它是类型安全的语言:
- swift代码更少,简洁的语法,可以省去大量冗余代码。
- swift速度更快,运算性能更高。
便利构造函数 convenience
便利构造函数通常用在对系统的类进行构造函数的扩充时使用
便利构造函数的特点:
//1.能够提供条件检测
//2.能够允许返回 nil,默认(指定)的构造函数,必须要创建对象
//3.便利构造函数,必须在条件检测完成之后,以 self. 的方式调用其他的构造函数,创建对象
//4.能够简化对象的创建方法
convenience init?(name: String, age: Int){
if age < 0 || age > 100 {
//不能创建对象
return nil
}
//调用其他的构造函数初始化属性 在一个构造函数中调用了另外一个构造函数
self.init(dict: ["name":name, "age": age])
}
Class和Struct的区别
struct SRectangle {
var width = 200
}
class CRectangle {
var width = 200
}
虽然两者写法看起来很像,其实区别还是很大的:
- struct不可以继承,class可以继承。
- Struct无法修改自身属性值,函数需要添加mutating关键字
- 类中的每一个成员变量都必须被初始化,否则编译器会报错,而结构体不需要,编译器会自动帮我们生成init函数,给变量赋一个默认值
- 类属于引用类型,结构体属于值类型
- 值类型变量直接包含数据,赋值时也是值拷贝,或者叫深拷贝,所以多个变量的操作不会相互影响。
- 引用类型变量存储的是对数据的引用地址,后者称为对象,赋值时,是将对象的引用地址复制过去,也叫浅拷贝,因此若多个变量指向同一个对象时,操作会相互影响。
- 值类型数据没有引用计数,也就不会因为循环引用导致内存泄漏,而引用类型存在引用计数,需要小心循环引用导致的内存泄漏
- 拷贝时,struct是深拷贝,拷贝的是内容,class则需要选用正确的深浅拷贝类型。
- 因为值类型数据是深拷贝,所以是线程安全的,而引用类型数据则不是
inout
swift中需要对参数只进行修改,需要用到inout 关键字,调用函数时加&
通俗的讲,就是使用inout关键字修饰的值,在接下来的方法中可以修改
final
Swift中,final关键字可以在class、func和var前修饰,表示不允许对其修饰的内容进行继承或者重新操作
final:防止方法、属性、下标被重写。
final class Person{}
class Programmer: Person{} //编译错误
is:类型检查运算符,用于确定实例是否为某个子类类型。
dynamic
dynamic的作用就是让swift代码也能有oc中的动态机制,常用的就是KVO。
使用dynamic关键字标记属性,使属性启用Objc的动态转发功能。
dynamic只用于类,不能用于结构体和枚举,因为它们没有继承机制,而Objc的dynamic:指明编译器不会对类成员或者函数的方法进行内联或虚拟化。这意味着对这个成员的访问是使用 Objective-C 运行时进行动态派发的(代替静态调用)。
class Person {
//比如 KVO、KVC、Swizzling
dynamic var name: String?
}
defer
defer的字面意思是延时执行,在swift中修饰一个block,该block内的代码会在当前函数的所有代码执行完毕之后才会被执行。
- 必须有执行到defer才会触发defer,如果提前return或抛出异常则不会执行。
- 多个defer,执行顺序是倒序的。最后定义的defer先执行。
@objc与@objcMembers的区别
在Swift中,继承自NSObject的类如果有比较多的属性或方法都需要加上@objc的话,会多比较多的代码。那么可以利用@objcMembers减少代码。被@objcMembers修饰的类,会默认为类、子类、类扩展和子类扩展的所有属性和方法都加上@objc。当然如果想让某一个扩展关闭@objc,则可以用@nonobjc进行修饰。
- selector调用的方法前需要加@objc,目的是允许函数在“运行时”通过oc消息机制调用
override func viewDidLoad() {
super.viewDidLoad()
let btn = UIButton(type:.contactAdd)
btn.addTarget(self,action:#selector(click),for:.touchUpInside)
}
@objc func click(){
print("Button clicked")
}
- 协议的方法可选时,协议和可选方法前要用@objc声明
@objc protocol OptionalProtocol{
@objc optional func optionalMethold1()
@objc optional func optionalMethold2()
}
- 用weak修饰协议时,协议前面要用@objc声明
@objc protocol MyDelegate{
funcmethold1()
}
class MyClass{
weak var delegate:MyDelegate?
}
- 类前加上 @objcMembers,那么它及其子类、扩展里的方法都会隐式的加上 @objc
@objcMembers class Person{}
- 扩展前加上 @objc,那么里面的方法都会隐式加上 @objc
class Person{
funcwork(){}
}
@objc extension Person{
funceat(){}//包含隐式的
@objc func sleep(){}//包含隐式的 @objc
}
copy-on-write
当你有一个占用内存很大的一个值类型,并且不得不将它赋值给另一个变量或者当做函数的参数传递的时候,拷贝它的值是一个非常消耗内存的操作,因为你不得不拷贝它所有的东西放置在另一块内存中。
为了优化这个问题,Swift 对于一些特定的值类型(集合类型:Array、Dictionary、Set)做了一些优化,在对于 Array 进行拷贝的时候,当传递的值进行改变的时候才会发生真正的拷贝。而对于String、Int 等值类型,在赋值的时候就会发生拷贝。
- Copy-on-Write 是一种用来优化占用内存大的值类型的拷贝操作的机制。
- 对于 Int、String 等基本类型的值类型,它们在赋值的时候就会发生拷贝,- - 它们没有 Copy-on-Write 这一特性(因为Copy-on-Write带来的开销往往比直接复制的开销要大)。
- 对于 Array、Dictionary、Set类型,当它们赋值的时候不会发生拷贝,只有在修改的之后才会发生拷贝,即 Copy-on-Write。
- 对于自定义的结构体,不支持 Copy-on-Write。
open, public, internal, fileprivate, private
- open: 具备最高访问权限,其修饰的类可以和方法,可以在任意 模块中被访问和重写.
- public: 权限仅次于 open,和 open 唯一的区别是: 不允许其他模块进行继承、重写
- internal: 默认权限, 只允许在当前的模块中访问,可以继承和重写,不允许在其他模块中访问
- fileprivate: 修饰的对象只允许在当前的文件中访问;
- private: 最低级别访问权限,只允许在定义的作用域内访问
Swift 和OC 如何相互调用
- Swift 调用 OC代码
需要创建一个 Target-Bridging-Header.h 的桥接文件,在桥接文件导入需要调用的OC代码头文件即可
- OC 调用 Swift代码
直接导入 Target-Swift.h文件即可, Swift如果需要被OC调用,需要使用@objc对方法或者属性进行修饰;如果想要所有成员都暴露给OC,就使用@objcMembers来修饰类;暴露给OC的类需要最终继承自NSObject。
typealias和associatedtype的区别
associatedtype:关联类型的关键字,处理 协议 中的范型使用场景。
associatedtype 只能在 protocol 中使用,class 中应该使用typealias进行代替。
protocol Stack {
associatedtype Element
func push(e: Element) -> Void
func pop() -> Element
}
class Stack<T>: Stackble {
typealias Element = T
func push(e: T) {
}
func pop() -> T? {
return nil
}
}
/// 指定类型 Stack
let strStack = Stack<String>()
strStack.push(e: "")
let result = strStack.pop()