The code for this article is in Apple Swift Version 5.0.1, learn Swift notes

The enumeration

Simple enumeration, usually written like this

enum TestEnum{
    case test1 = 1,test2 = 2,test3 = 3}Copy the code

In C, TestEnum takes up 1 bit of space, so what do we do in Swift?

Test the code to see how much space enumerations take up

enum Password {
	case number(Int,Int,Int,Int)
	case other
}
enum TestEnum {
	case test1,test2,test3,test4
}
var testum = TestEnum.test1
var p1 = MemoryLayout.stride(ofValue: testum)
var p2 = MemoryLayout.size(ofValue: testum)
var p3 = MemoryLayout.alignment(ofValue: testum)
print(\(p1) occupy: \(p2) align: \(p3)") // Allocate: 1 occupy: 1 align: 1 varpwd= password. number(3, 5, 7, 8)// bytes 8*4 =32 //pwdStride (ofValue:pwd)// 40
 p2 = MemoryLayout.size(ofValue: pwd)//33
 p3 = MemoryLayout.alignment(ofValue: pwd) / / 8print(\(p1) occupy: \(p2) align: \(p3)")

Copy the code

It can be concluded from the code:

Simple enumeration, case when the default is a number, take up 1 byte space, associated enumeration takes up more, we mainly from the memory analysis why take up so much

Start by looking at the memory layout at key code break points

enum Password {
	case number(Int,Int,Int,Int)
	caseOther} // varpwd= password. number(3, 5, 7, 8)// bytes 8*4 =32Copy the code

Four ints take up 32 bytes. Why does the system take up 33 bytes? What does the other byte store?

Mj’s big brother’s gadget
DEBUG->DEBUG Workflow ->Always Show Disassembly

You can see that their memory address is 0x1000088A0, or use the LLDB command to view the data

(lldb) x/9x 0x1000088a0
0x1000088a0: 0x00000003 0x00000000 0x00000005 0x00000000
0x1000088b0: 0x00000007 0x00000000 0x00000008 0x00000000
0x1000088c0: 0x00000000
Copy the code

Seeing that we assigned 3/5/7/8 followed by 0x0, we test the code below to see memory

let pwd2 = password. other// Same variable is 33 bytesCopy the code

Check the assembly

Movq $0x1,0x4b3b(%rip) movq $0x1,0x4b3b(%rip) movQ $0x1,0x4b3b(%rip)

The memory size stays the same, but the last bit is changed from 0x0 to 0x1.

Modify the enumeration slightly

enum Password {
	case number1(Int,Int,Int,Int)
	case number2(Int,Int,Int)
	case number3(Int,Int)
	case number4(Int)
	case other
}
Copy the code

Let’s test the code again

let pwd2 = password. other// Same variable is 33 bytesCopy the code

let pwd4 = password.number2 (5, 6, 7)// Same variable is 33 bytesCopy the code

let pwd5 = password.number4 (8)// The same variable is 33 bytesCopy the code

switch .case

You can also test

enum Password {
	case number1(Int,Int,Int,Int)
	case number2(Int,Int,Int)
	case number3(Int,Int)
	case number4(Int)
	case other
}
Copy the code

It also takes up 33 bits. The first 32 bits are stored values, and the 33rd bit is stored in which category.

Error parsing

What memory do let and VAR modify?

letS2 = Point(x: 10, y: 12) s2 = Point(x: 10, y: 10letS2. x = 11// s2.y = 12// / errorletRet = PointClass() ret. X = 0 ret. Y = 0 ret = PointClass(letModified class pointer (stack) so Pointers cannot be changed, but properties to which Pointers point can be changedCopy the code

Let is similar to const, except that the pointer cannot be changed.

closure

A function is combined with its captured constant and variable environment to become a closure

  • Usually a function defined inside a function
  • Generally it captures local variables/constants of the outer function
Typealias Clo = (Int) -> Int getFunc () -> Clo {var num = 5 Each call accesses the heap space. func plus(_ v:Int) ->Int{ num += vreturnNum // breakpoint B}returnVar f = getFunc()print(f(1)) //5+1 = 6
print(f(2)) //6 + 2 =8 var f2 = getFunc()print(f2(3))// 5 + 3 = 8
Copy the code

Each time getFunc() is executed, the stack space is reallocated to store separate stacks of num, f and F2. At breakpoint A, print the address of num

x/3xg 0x00000001005735f0
0x1005735f0: 0x0000000100002080 0x0000000000000002
0x100573600: 0x00007fff76760ab9
Copy the code

Then run to breakpoint B and print the value of num again

(lldb) x/3xg 0x00000001005735f0
0x1005735f0: 0x0000000100002080 0x0000000000000002
0x100573600: 0x0000000000000005
Copy the code

It then runs once, breaking the second time to position B

(lldb) x/3xg 0x00000001005735f0
0x1005735f0: 0x0000000100002080 0x0000000200000002
0x100573600: 0x0000000000000006
Copy the code

A closure is like a class, local variables are like properties (member variables of the class), and class-defined functions are like closure code.

class CloClass{
    var num = 6
    func plus(_ v:Int) -> Int {
        num += v
        return num
    }
}
Copy the code

Class instantiations are in the heap, closures are in the heap, classes have member variables, closures have properties, classes can define functions, closures can define functions…

Now that the closure principle is clear, let’s do two dishes to calm things down.

And then two questions, what is the output?

Subject to a

typealias FnClo = (Int) -> (Int,Int)
func getFns() -> (FnClo,FnClo) {
    var num1 = 0
    var num2 = 0
    func plus(_ i:Int) -> (Int,Int){
        num1 += i
        num2 += i << 1
        return (num1,num2)
    }
    func mnus(_ i:Int) -> (Int,Int){
        num1 -= i
        num2 -= i << 1
        return (num1,num2)
    }
    return (plus,mnus)
}

let (p,m) = getFns()
print(p(5))
print(m(4)) 
print(p(3))
print(m(2))
Copy the code

Topic 2

What is the output? Why is that?

class Person {
	var age = 9
}
typealias Clo = (Int) -> Int
func getFunc () -> Clo {
	var p = Person()
	func plus(_ v:Int) ->Int{
		p.age += v;
		return p.age
	}
	return plus
}
var f = getFunc() 
print(f(3))
Copy the code

Answers are here, and more questions can be asked here.

inout

When you pass a variable to inout and you just pass the address in, what about passing it in to compute the property?

struct Photo {
	
	var age:Int {
		get{
			return height/2
		}
		set{
			height =  newValue * 2
		}
	}
	var height = 12
}
var p = Photo(height: 10)

func test(_ v: inout Int) {
	v = 9
}
test(&p.age)
Copy the code

Look at the assembly and see that it’s still passing the address of p.age, but it’s passing the address through the getter and then passing the address inside the function.

When the attribute observer is set, the copy-in-copy-cout strategy is used to assign values and call will and set functions.

struct Photo {
	
	var age:Int {
		willSet{
			print("will\(newValue)")
		}
		didSet{
			print("didset old:\(oldValue)")
		}
	}
}
var p = Photo(age: 5)
func test(_ v: inout Int) {
	v = 9
}
test(&p.age)// Here is the break pointCopy the code

And then look at the assembly

Inout Passes the memory address directly to the function if it has a physical memory address and no attribute viewer. If an attribute is calculated or an attribute observer is configured, call the function copy-in-copy-out. Copy the value of the argument, generate a Copy, pass the memory address of the Copy to the function (the Copy is passed by reference), modify the value of the Copy inside the function, and overwrite the value of the argument after the function returns.

attribute

Attribute is divided into instance attribute and type attribute. Instance attribute is the attribute that can be accessed by instance, and type attribute is the attribute that can be accessed by class.

We define an instance attribute age and a class attribute level

struct Propty {
    var age = 10
    static var level = 11
}
var pro = Propty()
pro.age = 11;
var level = Propty.level;
Copy the code

Static modifier memory is allocated only once and can be accessed by the class, while this can also be implemented as a singleton, essentially executing dispatch_once_f.

class fileManger {
    public static let  manger:fileManger = fileManger()
    private init(){// Set private, not externally accessibleprint("init")
    }
    open func close()  {
        print("close")
    }
    public  func open() {//mode accessibleprint("open"}} var file = filemanger.manger file.open() file.close() file.init()//error: init' is inaccessible due to 'private' protection level
Copy the code

In fact, the essence of INout is reference passing (address passing), which can be divided into direct address passing and duplicate address passing.

other

The Subscript Subscript

struct Point {
	var x = 0,y = 0
	
}
class Pointcls {
	var p = Point()
	
	subscript (index index:Int) ->Point{
		set{
			print("set")
			p = newValue
		}
		get{print("get"); return p }
	}
	
}
var p = Pointcls()
p[index: 0] = Point(x: 1, y: 2)
// set
// get
Copy the code

In fact, from the code can also be seen, the subscript is the execution of the set and GET methods, this point without assembly analysis.

The initializer

Each class must specify an init, if not, the system generates init by default. The other is the convenience build initializer, which must be specified to invoke.

class Size { var width = 0,height = 0 convenience init(_ w:Int,_ h:Int) { self.init(width:w,height:h) //code } convenience init(w:Int) { self.init(width:w,height:0) //code } convenience init(h:Int) { self.init(width:0,height:h) Private init(width:Int,height:Int) {self.width = width self.height = height}}Copy the code

Size designs one init and three convenient initializers, init can do the necessary configuration, and the other convenient initializers can be handled separately, so that critical code doesn’t get lost.

X.self type(of:X)

In OC there is obj.class, and in Swift there is the counterpart of obj.Self

var t = 1.self
var t2 = type(of: [1])
print(t2,t)//[Int].type Int.type
Copy the code

You can use the IS keyword to determine if it is a class

var age = 1
if age is Int{
    print("age is int")}else if age is Double{
    print("age is int")
}
//age is int
Copy the code

Self represents the current class, not the specific class.

protocol Run {
    func test() -> Self
}
class RunSub: Run {
    func test() -> Self {
        returnRunsub.init ()// Cannot convertreturn expression of type 'RunSub' to return type 'Self'}}Copy the code

RunSub may have subclasses and cannot return runsub.init () directly. This means that the type is written dead. Normally, we write this way

class RunSub: Run {
    required init(){
        //code
    }
    func test() -> Self {
        let ty = type(of: self)
        return ty.init()
    }
}
Copy the code

Required ensures that subclasses must implement init functions of type type(of: self).

String

In Swift, String is also optimized for small data but not for big data. In OC, it is called tag Pointer, which actually means that pointer is not used for small data but pointer is used for big data. In Swift, pointer is used for storage when the length is greater than 15 and data is directly stored when the length is less than 15. When the value is less than 15String, the value is stored in the data segment. When append() is performed, the data segment is copied to the stack, and the data is stored using Pointers. Length small spare 15 in memory layout

//var str2 = "123456789012345"// 0x3837363534333231 0xef35343332313039
//var str2 = "12345678901234"   // 0x3837363534333231 0xee00343332313039
//var str2 = "1234567890123"      // 0x3837363534333231 0xed00003332313039
Copy the code

Length greater than 15 in memory layout

var str2 = "123456789012345678901234"0xd000000000000019 0x80000001000056C0 0x19 Is a string of charactersCopy the code

0x80000001000056C0 needs to add 0x20 to be the real address of the string, similar to OC mask.

//str2 Real Address: 0x80000001000056C0-0x7ffffffffFFFfFe0 = 0x1000056E0 // 0x1000056C0 + 0x20 = 0x1000056E0 /* 0x1000056E0: 31 32 33 34 35 36 37 38 39 30 31 32 33 34 35 36 1234567890123456 0x1000056f0: 00 0a 00 20 00 00 00 00 00 00 00 00 00 00 00 00 */Copy the code

When the append ()

Str2 address 0xf000000000000019 0x000000010340A490 Takes +0x20 0x10340A490 +0x20= 0x10340A4B0 0x10340A4B0 data X /4xg 0x10340A4B0 0x10340a4b0: 0x3837363534333231 0x3635343332313039 0x10340a4c0: 0x3433323130393837 0x0004000000000035Copy the code

Pointer to the address

array

The array is 8 bytes long and points to the real address where the data is stored. The default array size is 4, and the capacity is expanded when the load exceeds 0.5. The capacity expansion coefficient is 2.

var arr = [Int]()
for i in1... 3{ arr.append(i) } 0x00007fff90381cc0 0x0000000200000002 0x0000000000000003 0x0000000000000008 0x0000000000000001 0x0000000000000002 0x0000000000000003 0x0004003c00000000for i in1... 9{ arr.append(i) } 0x00007fff90381cc0 0x0000000200000002 0x0000000000000009 0x0000000000000020 0x0000000000000001 0x0000000000000002 0x0000000000000003 0x0000000000000004 0x0000000000000005 0x0000000000000006 0x0000000000000007 0x0000000000000008 0x0000000000000009 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x00007fff59610004 0x00007fff90381d58 0x0000000000000000 0x0000000000000000Copy the code

Optional type

Optional types are essentially Optional types

var age:Int? = 10
Copy the code

The equivalent of

var age1:Optional<Int> = .some(10)
Copy the code

? It’s just grammar sugar. It’s easier.

The overflow operator &+ &- &* &/

The advanced operator overflow operator is the lowest value greater than the maximum value +1, and the minimum value -1 is the maximum.

var v:Int8 = Int8.max &+ 1
print(v) // -128
v = Int8.min &- 1
print(v)//127
Copy the code

Operator overloading

The operators ==, -, +, +=, -=, /, * can implement overloading themselves and are not supported in OC.

Struct Point:Equatable{var x = 0,y = 0 static func +(p1:Point,p2:Point) -> Point{returnPoint(x: p1.x+p2.x,y: p1.y+p2.y)} static func -(p1:Point,p2:Point) -> Point{returnPoint(x: p1.x-p2.x,y: p1.y - p2.y)} static func *(p1:Point,p2:Int) -> Point{returnPoint(x: p1. X *p2,y: p1. Y *p2)} static func /(p1:Point,p2:Int) -> Point{if p2 == 0{
			return p1
		}
		returnPoint(x: p1.x/p2,y: p1.y/p2)} static prefix func -(p1:Point) -> Point{return Point(x: -p1.x, y: -p1.y)
	}
	static postfix func --(p1: inout Point) {
		p1.x -= 1
		p1.y -= 1
	}
	static postfix func ++(p1: inout Point) {
		p1.x += 1
		p1.y += 1
	}
	
	
	static func == (p1:Point,p2:Point)->Bool{
		if p1.x == p2.x  && p1.y == p2.y{
			return true
		}
		return false
	}
	static func === (p1:Point,p2:Point)->Bool{
		if p1.x == p2.x  && p1.y == p2.y {
			return true
		}
		return false
	}
}

var p1 = Point(x: 10, y: 10)
var p3 = p1 + p1
print(p1 == p1)
print(p1 === p3)
Copy the code

Effective area

There are five levels of permission control

  • openThe highest permission, which can be accessed and inherited by any module
  • publicIt can be accessed by any module and cannot be overridden by other modules
  • internalThe default permission allows access in the current module, not in other modules
  • fileprivateAllows access in the current file
  • privateAllows currently defined valid range access

Private is not necessarily smaller than Fileprivate, which defines attributes in a class. Fileprivate accesses a larger valid area, which is the entire file. Private is the entire file, which is in the current class.

class test {
//	private class testSub{}
//	fileprivate class testSub2:testSub{}// error becausetestThe Sub valid area istestThe function,testSub2 is a real file. } private classtestSub{} // private and Fileprivate are equivalent fileprivate classestestSub2:testSub{}
Copy the code

When an attribute has a high read permission and a low write permission

class Person {
	private(set) var age = 0
	fileprivate(set) var weight = 100
}
Copy the code

The age of a person is not set separately, it is changed according to the number of years, weight is changed according to what you eat, it is not set separately, so it can be read and not written.

Closure escape and non-escape

  • Non-escape is execution in the body of a function
  • Escape closure is not clear if execution inside a function requires a keyword@escapingFor general useselfAlso need toweakTo deal with
// Non-escape closure functest(_ fn() ->()){fn()} // Escape closure functest2 (_ fn:@escaping ()->()) -> ()->(){
	return fn
}
func test3 (fn:@escaping ()->()) -> Void{dispatchqueue.global ().async {fn()}} public class Run {var age = 0 functest4(_ fn:@escaping ()->()) -> Void {
		DispatchQueue.global().async {
			fn()
			print(self.age)
		}
	}
}
Copy the code

Memory access error (Simultaneous accesses)

Simultaneous accesses 

var step = 1
func plus(_ n:inout Int)  {
	 n += step
}
plus(&step)
Copy the code

It just needs to be changed a little bit. Numeric, copy is storing the value of step in another memory.

var copy = step
plus(&copy)
Copy the code

Only multiple single-write operations can be performed on the same memory at the same time

Match the pattern

Operator, and mixed with switch, custom several operators, and then overloading Int contrast function.

prefix operator ~=;
prefix operator ~>=;
prefix operator ~<=;
prefix operator ~<;

prefix func ~= (_ v:Int) ->  ((Int)->Bool){return{ $0>v}}
prefix func ~>= (_ v:Int) -> ((Int)->Bool){return{ $0 >= v}}
prefix func ~<= (_ v:Int) -> ((Int)->Bool){return{$0 <= v}}
prefix func ~< (_ v:Int) ->  ((Int)->Bool){return{$0 < v}}
extension Int{
    static func ~=(pattern:(Int)->Bool,value:Int) -> Bool{
        return pattern(value)
    }
}
var age = 10
switch age {
case~ > = 0:print("~ > = 0")
case~ < = 100:print("~ < = 100")
default:
    break
}
Copy the code

$$$$$$$$$$$$$$$$$$$$$$$$$$$$$

Data reference

  • Swift document
  • swift.org