Programming Language Features
C
volatile
Objects declared as volatile are omitted from optimization because their values can be changed by code outside the scope of current code at any time. The system always reads the current value of a volatile object from the memory location rather than keeping its value in temporary register at the point it is requested, even if a previous instruction asked for a value from the same object. So the simple question is, how can value of a variable change in such a way that compiler cannot predict. Consider the following cases for answer to this question.
https://www.geeksforgeeks.org/understanding-volatile-qualifier-in-c/
Array is Treated as Pointer
int max(int number[50], int size);
int max(int*, int);
Block
Blocks are like functions. They are also called closures, lambdas. Code Block can be used as method argument as well as method return type. It is mainly used for avoiding observer pattern, when simple asynchronous method need to do something as its operation is done. If Observer patttern is used for every async task in a project, sometimes it make overheads of project.
C Block
A captured variable is automatically settable from within the closure. So __block is not necessary here.
A closure is said to escape a function when the closure is passed as an argument to the function, but is called after the function returns. escape means asynchronous where nonescape(default) means synchronous.
An autoclosure is a closure that is automatically created to wrap an expression that’s being passed as an argument to a function. It doesn’t take any arguments, and when it’s called, it returns the value of the expression that’s wrapped inside of it.
https://swiftunboxed.com/lang/closures-escaping-noescape-swift3/
C++ Lambda
Capture List
Function Pointer
https://stackoverflow.com/questions/1485983/calling-c-class-methods-via-a-function-pointer
http://www.newty.de/fpt/fpt.html#defi
Constant Pointer vs. Constant Pointed-to Data
pre-increment/ post-increment in a loop
post - increments causes more instruction than pre-increment as post-increment keeps the incremented value in a different register and continue with the register which has current value. After completing the execution the loop, incremented value from the different register is copied to the current register. But for pre-increment, only one register is used which is incremented at starting of the loop and uses that incremented value though out the execution.
Objective C
Messaging
Class Object
The compiler creates just one object, a class object, to represent the class. Although a class object keeps the prototype of a class instance, it’s not an instance itself. It has no instance variables of its own and it can’t perform methods intended for instances of the class. However, a class definition can include methods intended specifically for the class object—class methods as opposed to instance methods. A class object inherits class methods from the classes above it in the hierarchy, just as instances inherit instance methods.
Extension
Category
Property
C++
Loop Fusion/Fission
Loop Unrolling
Loop Peeling/Splitting
https://www.wikiwand.com/en/Loop_splitting
https://medium.com/@savas/template-metaprogramming-compile-time-loops-over-class-methods-a243dc346122
https://www.wikiwand.com/en/Loop_unrolling
https://www.wikiwand.com/en/Loop_fission_and_fusion
Swift
Swift static vs class type
lazy variable must have to use with closure. lazy loads the variable in memory when using the app for the first time.
Swift structs are laid out similar to C. Classes are very similar to structs, the main difference being they have a header directly before the properties. The header contains the type’s
Custom Operator
Acquiring Type Metadata
Swift stores a metadata record for every type. This can include things like its kind (struct, class, enum, etc), field names, field offsets and so on.
Value and Reference Types
Access Control
Increasing Performance by Reducing Dynamic Dispatch
Swift allows a class to override methods and properties declared in its superclasses. This means that the program has to determine at runtime which method or property is being referred to and then perform an indirect call or indirect access. This technique, called dynamic dispatch, increases language expressivity at the cost of a constant amount of runtime overhead for each indirect usage.
In Swift, dynamic dispatch calls are implemented by looking up a function from a method table and then performing an indirect call. This is slower than performing a direct call. Additionally, indirect calls also prevent many compiler optimizations, making the indirect call even more expensive.
Basic Event Loop
https://arobenko.gitbooks.io/bare_metal_cpp/content/basic_concepts/event_loop.html
https://baudehlo.com/2013/02/14/how-an-event-loop-works/
https://github.com/codebrainz/grinder
http://bou.io/RunRunLoopRun.html
volatile
Objects declared as volatile are omitted from optimization because their values can be changed by code outside the scope of current code at any time. The system always reads the current value of a volatile object from the memory location rather than keeping its value in temporary register at the point it is requested, even if a previous instruction asked for a value from the same object. So the simple question is, how can value of a variable change in such a way that compiler cannot predict. Consider the following cases for answer to this question.
- Global variables modified by an interrupt service routine outside the scope
- Global variables within a multi-threaded application
https://www.geeksforgeeks.org/understanding-volatile-qualifier-in-c/
C storage classes
there are four storage classes: automatic, register, external, and static.
auto
The scope of automatic variables is local to the block in which they are declared, including any blocks nested within that block. For these reasons, they are also called local variables. By default, storage class within a block is auto.
register
register suggest to the compiler that particular automatic variables should be allocated to CPU registers, if possible and it is not an obligation for the CPU to do this. Thus, register variables provide a certain control over the efficiency of program execution.
extern
For defining a global variable, this keyword is used. There is no need to include the header file where that variable is defined. The variable must have to initialize with a default value. Which files use that global variable, there should be declared the variable as extern to access that variable. If you include the header where that variable declared, then it cannot be declared as extern as it is automatically including the variable within the header file.
declare in h1.h as
declare in h1.h as
extern int ex_a;
h1.c as
ex_c = 34;
then use in h2.c as
extern int ex_a;
printf("ex b=%d\n",ex_a);
if you include h1.h in h2.c then you cannot do
int ex_a = 98; //compilte time error
extern int ex_a = 78; //compilte time error
if you dont initialize the extern variable in c file then it will also show error on compile time.
static
static means a variable will be globally known only in this file. A local variable defined in a function can also be declared as static. static variable cannot be used without initialization. If you want to use the static variable in other source file where it is not declared, then the header file where it is defined need to be included.
Pointer Variable
- A pointer variable (or pointer in short) is basically the same as the other variables, which can store a piece of data. Unlike normal variable which stores a value (such as an int, a double, a char), a pointer stores a memory address.
- A pointer is associated with a type (of the value it points to), which is specified during declaration. A pointer can only hold an address of the declared type.
- You can initialize a pointer to 0 or NULL, i.e., it points to nothing. It is called a null pointer.
Reference Variable
A reference is an alias, or an alternate name to an existing variable. For example, suppose you make
'x' a reference (alias) to 'y', you can refer to the variable as either 'x' or 'y'. The main use of references is acting as function formal parameters to support pass-by-reference. If an reference variable is passed into a function, the function works on the original copy (instead of a clone copy in pass-by-value). Changes inside the function are reflected outside the function.
Pass-by-Value
Arguments are passed into functions by value (except arrays which is treated as pointers). That is, a clone copy of the argument is made and passed into the function. Changes to the clone copy inside the function has no effect to the original argument in the caller.
Pass-by-Reference with Pointer Arguments
void square(int *);
To modify the original copy directly (especially in passing huge object or array) to avoid the overhead of cloning. This can be done by passing a pointer of the object into the function, known as pass-by-reference.
Pass-by-Reference with Reference Arguments
void square(int &);
Instead of passing pointers into function, you could also pass references into function, to avoid the clumsy syntax of referencing and dereferencing. Otherwise pointer passing and reference passing is same.
You can also pass the return-value as reference or pointer.
int & squareRef(int &);
int * squarePtr(int *);
Array is Treated as Pointer
In C/C++, an array's name is a pointer, pointing to the first element (index 0) of the array. For example, suppose that numbers is an int array, numbers is a also an int pointer, pointing at the first element of the array. That is, numbers is the same as &numbers[0]. Consequently, *numbers is number[0]; *(numbers+i) is numbers[i].
An array is passed into a function as a pointer to the first element of the array. For example, the following declarations are equivalent
int max(int numbers[], int size);
int max(int *numbers, int size);
They will be treated as int* by the compiler, as follow. The size of the array given in [] is ignored.
Block
Blocks are like functions. They are also called closures, lambdas. Code Block can be used as method argument as well as method return type. It is mainly used for avoiding observer pattern, when simple asynchronous method need to do something as its operation is done. If Observer patttern is used for every async task in a project, sometimes it make overheads of project.
C Block
A block pointer to a block taking an int and returning a float:
typedef void (^Block)(void);
The code of the block is compiled and loaded into the binary like any other function. The memory a block requires is that of the variables it has closed around; that is, any variables the block references need to be copied into the block's private memory.
When you define a block literal, you create the storage for this literal on the stack. The function Block_copy() takes a block pointer, and if it's a stack block, copies it to the heap, or if it's already a heap block, increases its retain count. When we're done with the block, just release it with Block_release().
typedef void(^Block)(void);
Block blockMaker() { int a = 3;
Block block = ^ { return a;
} return Block_copy(block); // (1)
} int main() {
Block block2 = blockMaker(); int b = block2();
Block_release(block2); // (2) return 0;
Using variables in the closure scope is very straight-forward if you're just reading them. Just use them, and they will be magically managed by your block. However, if you want to be able to modify the variable, it need to be prefixed by the __block storage qualifier.Objective-C Block
Blocks are actually Objective-C objects. Even if you create them from a C++ library, the specification says that every block must have the memory layout of an Objective-C object. The runtime then adds Objective-C methods to them.
Blocks in Objective-C have one more very important difference from blocks in C in handling variables that reference objects. All local objects are automatically retained as they are referenced! If you reference an instance variable from a block declared in a method, this retains self, as you're implicitly doing self->theIvar.
When A method that simply returns a block. The block is copied, as you can't return a block literal since it's on the stack. With even self being retained, this is a very easy way to accidentally create reference cycles and thus memory leaks. To avoid this behavior, Just give the variable __weak storage.
__weak __typeof(self) selff = self
And, if the variable is __block, it can be changed from within a block. Otherwise that variable will be affected inside the block only.
Swift Closure
Closures are self-contained blocks of functionality that can be passed around and used in your code. Closures in Swift are similar to blocks in C and Objective-C and to lambdas in other programming languages.
Closure expression syntax has the following general form:
- Global functions are closures that have a name and do not capture any values.
- Nested functions are closures that have a name and can capture values from their enclosing function.
{ [weak/unowned self] (parameters) -> return type in
statements
}
The parameters in closure expression syntax can be in-out parameters, but they can’t have a default value. Variadic parameters can be used if you name the variadic parameter. Tuples can also be used as parameter types and return types.
Whenever you assign a function or a closure to a constant or a variable, you are actually setting that constant or variable to be a reference to the function or closure. A closure is said to escape a function when the closure is passed as an argument to the function, but is called after the function returns. escape means asynchronous where nonescape(default) means synchronous.
An autoclosure is a closure that is automatically created to wrap an expression that’s being passed as an argument to a function. It doesn’t take any arguments, and when it’s called, it returns the value of the expression that’s wrapped inside of it.
https://swiftunboxed.com/lang/closures-escaping-noescape-swift3/
nil
In swift nil is an enum type, which is Optional.none
whereas in objective-c nil means just 0.
C++ Lambda
Capture List
[] //no variables defined. Attempting to use any external variables in the lambda is an error. [x, &y] //x is captured by value, y is captured by reference [&] //any external variable is implicitly captured by reference if used [=] //any external variable is implicitly captured by value if used [&, x] //x is explicitly captured by value. Other variables will be captured by reference [=, &z] //z is explicitly captured by reference. Other variables will be captured by value
Writing
()
immediately after the lambda definition []{}();
will call the lambda, so the result is of type of the return type of the lambda.
If you omit the
()
suffix, a lambda type (unspecified) will be returned, which is basically a callable functor.
In C/C++, functions, like all data items, have an address. The name of a function is the starting address where the function resides in the memory, and therefore, can be treated as a pointer. We can pass a function pointer into function as well. The syntax for declaring a function pointer is:
// Function-pointer declaration
return-type (* function-ptr-name) (parameter-list)http://www.newty.de/fpt/fpt.html#defi
Constant Pointer vs. Constant Pointed-to Data
- Non-constant pointer to constant data: Data pointed to CANNOT be changed; but pointer CAN be changed to point to another data.
int i1 = 8, i2 = 9; const int * iptr = &i1; // non-constant pointer pointing to constant data // *iptr = 9; // error: assignment of read-only location iptr = &i2; // okay
- Constant pointer to non-constant data: Data pointed to CAN be changed; but pointer CANNOT be changed to point to another data.
int i1 = 8, i2 = 9;
int * const iptr = &i1; // constant pointer pointing to non-constant data
// constant pointer must be initialized during declaration
*iptr = 9; // okay // iptr = &i2; // error: assignment of read-only variable
- Constant pointer to constant data: Data pointed to CANNOT be changed; and pointer CANNOT be changed to point to another data.
int i1 = 8, i2 = 9; const int * const iptr = &i1; // constant pointer pointing to constant data // *iptr = 9; // error: assignment of read-only variable // iptr = &i2; // error: assignment of read-only variable
- Non-constant pointer to non-constant data: Data pointed to CAN be changed; and pointer CAN be changed to point to another data.
int i1 = 8, i2 = 9; int * iptr = &i1; // non-constant pointer pointing to non-constant data *iptr = 9; // okay iptr = &i2; // okay
pre-increment/ post-increment in a loop
post - increments causes more instruction than pre-increment as post-increment keeps the incremented value in a different register and continue with the register which has current value. After completing the execution the loop, incremented value from the different register is copied to the current register. But for pre-increment, only one register is used which is incremented at starting of the loop and uses that incremented value though out the execution.
Objective C
Does Objective-C support pass-by-reference ?
C does not support pass-by-reference and Objective-C, being a strict superset of C doesn't either.
In C (and Objective-C) you can simulate pass-by-reference by passing a pointer, but it's important to remember that you're still technically passing a value, which happens to be the value of a pointer.
Messaging
In Objective-C, messages aren’t bound to method implementations until runtime. The compiler converts a message expression, into a call on a messaging function, objc_msgSend. This function takes the receiver and the name of the method mentioned in the message—that is, the method selector—as its two principal parameters: Any arguments passed in the message are also handed to objc_msgSend:
[receiver message]
objc_msgSend(receiver, selector)
objc_msgSend(receiver, selector, arg1, arg2, ...)
The messaging function does everything necessary for dynamic binding:
- It first finds the procedure (method implementation) that the selector refers to. Since the same method can be implemented differently by separate classes, the precise procedure that it finds depends on the class of the receiver.
- It then calls the procedure, passing it the receiving object (a pointer to its data), along with any arguments that were specified for the method.
- Finally, it passes on the return value of the procedure as its own return value.
Every class structure includes these two essential elements:
If a pointer to a class name is used in place of id in an object declaration, the compiler restricts the value of the declared variable to be either an instance of the class named in the declaration or an instance of a class that inherits from the named class.
Statically typed objects have the same internal data structures as objects declared to be ids. The type doesn’t affect the object; it affects only the amount of information given to the compiler about the object.
Static typing also doesn’t affect how the object is treated at runtime. Statically typed objects are dynamically allocated by the same class methods that create instances of type id.
Messages sent to statically typed objects are dynamically bound, just as objects typed id are. The exact type of a statically typed receiver is still determined at runtime as part of the messaging process.
- A pointer to the superclass.
- A class dispatch table. This table has entries that associate method selectors with the class-specific addresses of the methods they identify.
When a new object is created, memory for it is allocated, and its instance variables are initialized. the isa pointer is required for an object to work with the Objective-C runtime system. An object needs to be “equivalent” to a struct objc_object (defined in objc/objc.h) in whatever fields the structure defines.
When a message is sent to an object, the messaging function follows the object’s isa pointer to the class structure where it looks up the method selector in the dispatch table. If it can’t find the selector there, objc_msgSend follows the pointer to the superclass and tries to find the selector in its dispatch table.
In this way methods are dynamically bound to messages. To speed the messaging process, the runtime system caches the selectors and addresses of methods as they are used. There’s a separate cache for each class, and it can contain selectors for inherited methods as well as for methods defined in the class.
In this way methods are dynamically bound to messages. To speed the messaging process, the runtime system caches the selectors and addresses of methods as they are used. There’s a separate cache for each class, and it can contain selectors for inherited methods as well as for methods defined in the class.
Getting a Method Address
The only way to circumvent dynamic binding is to get the address of a method and call it directly as if it were a function. With a method defined in the NSObject class, methodForSelector:, you can ask for a pointer to the procedure that implements a method, then use the pointer to call the procedure.
Dynamic Method Resolution
You can implement the methods resolveInstanceMethod: and resolveClassMethod: to dynamically provide an implementation for a given selector for an instance and class method respectively.
Message Forwarding
If you send a message to an object that does not handle that message, before announcing an error the runtime sends the object a forwardInvocation: message with an NSInvocation object as its sole argument—the NSInvocation object encapsulates the original message and the arguments that were passed with it.
To forward a message, all a forwardInvocation: method needs to do is:
Determine where the message should go, and Send it there with its original arguments.
Compiled selectors are assigned to a special type, SEL, to distinguish them from other data. Valid selectors are never 0. You must let the system assign SEL identifiers to methods; it’s futile to assign them arbitrarily. The @selector() directive lets you refer to the compiled selector, rather than to the full method name.
Pointer Arguments
A pointer can be used to pass information to the receiver by reference. When invoked, the method looks at what’s stored in the address it’s passed. The Cocoa distributed objects system takes inout to be the default modifier for all pointer arguments except those declared const, for which in is the default.
Determine where the message should go, and Send it there with its original arguments.
The return value of the message that’s forwarded is returned to the original sender.
Selector
For efficiency, full ASCII names are not used as method selectors in compiled code. Instead, the compiler writes each method name into a table, then pairs the name with a unique identifier that represents the method at runtime. The runtime system makes sure each identifier is unique: No two selectors are the same, and all methods with the same name have the same selector.
Selector
For efficiency, full ASCII names are not used as method selectors in compiled code. Instead, the compiler writes each method name into a table, then pairs the name with a unique identifier that represents the method at runtime. The runtime system makes sure each identifier is unique: No two selectors are the same, and all methods with the same name have the same selector.
Compiled selectors are assigned to a special type, SEL, to distinguish them from other data. Valid selectors are never 0. You must let the system assign SEL identifiers to methods; it’s futile to assign them arbitrarily. The @selector() directive lets you refer to the compiled selector, rather than to the full method name.
Pointer Arguments
A pointer can be used to pass information to the receiver by reference. When invoked, the method looks at what’s stored in the address it’s passed. The Cocoa distributed objects system takes inout to be the default modifier for all pointer arguments except those declared const, for which in is the default.
Default Dynamic Behavior
- The memory for objects is dynamically allocated at runtime by class methods that create new instances.
- Objects are dynamically typed. In source code (at compile time), any object variable can be of type id no matter what the object’s class is. The exact class of an id variable (and therefore its particular methods and data structure) isn’t determined until the program runs.
- Messages and methods are dynamically bound, as described in “Dynamic Binding”. A runtime procedure matches the method selector in the message to a method implementation that “belongs to” the receiver.
-
These features give object-oriented programs a great deal of flexibility and power, but there’s a price to pay. In particular, the compiler can’t check the exact types (classes) of id variables. To permit better compile-time type checking, and to make code more self-documenting, Objective-C allows objects to be statically typed with a class name rather than generically typed as id. It also lets you turn some of its object-oriented features off in order to shift operations from runtime back to compile time.
Messages are somewhat slower than function calls
Static Typing
Static Typing
benefits of static typing is in code editing mainly
- In certain situations, it allows for compile-time type checking.
- It can free objects from the restriction that identically named methods must have identical return and argument types.
- It permits you to use the structure pointer operator to directly access an object’s instance variables.
The class definition is a prototype for a kind of object. The compiler creates just one accessible object for each class, a class object that knows how to build new objects belonging to the class. The class object is the compiled version of the class; the objects it builds are instances of the class.
A class definition is a specification for a kind of object. The class, in effect, defines a data type. The type is based not just on the data structure the class defines (instance variables), but also on the behavior included in the definition (methods).
All instances of a class have the same set of methods, and they all have a set of instance variables cut from the same mold. Each object gets its own instance variables, but the methods are shared.
Class Object
The compiler creates just one object, a class object, to represent the class. Although a class object keeps the prototype of a class instance, it’s not an instance itself. It has no instance variables of its own and it can’t perform methods intended for instances of the class. However, a class definition can include methods intended specifically for the class object—class methods as opposed to instance methods. A class object inherits class methods from the classes above it in the hierarchy, just as instances inherit instance methods.
Extension
Category
Property
C++
Pointer Variable vs Reference Variable
- A pointer can be re-assigned,A reference cannot, and must be assigned at initialization
- A pointer has its own memory address and size on the stack (4 bytes on x86), whereas a reference shares the same memory address (with the original variable) but also takes up some space on the stack
- You can have pointers to pointers to pointers offering extra levels of indirection. Whereas references only offer one level of indirection
- Pointer can be assigned
nullptr
directly, whereas reference cannot
- A pointer needs to be dereferenced with
*
to access the memory location it points to, whereas a reference can be used directly
- References cannot be stuffed into an array, whereas pointers can be
- A pointer is a variable that holds a memory address. Regardless of how a reference is implemented, a reference has the same memory address as the item it references
Templates
Template Metaprogramming
The use of templates as a metaprogramming technique requires two distinct operations: a template must be defined, and a defined template must be instantiated. calculation(method execution) is done at compile time.
unsigned int factorial(unsigned int n) {
return n == 0 ? 1 : n * factorial(n - 1);
}
// Usage examples:
// factorial(0) would yield 1;
// factorial(4) would yield 24.
The code above will execute at run time to determine the factorial value of the literals 4 and 0.
template <unsigned int n>
struct factorial {
enum { value = n * factorial<n - 1>::value };
};
template <>
struct factorial<0> {
enum { value = 1 };
};
// Usage examples:
// factorial<0>::value would yield 1;
// factorial<4>::value would yield 24.
The code above calculates the factorial value of the literals 4 and 0 at compile time and uses the results as if they were precalculated constants.
template<int N>
int factorial(){
return N*factorial<N-1>();
}
template<>
int factorial<1>(){
return 1;
}
The above code will not execute at compile time. Which will work as normal template. I am not clear yet why this is only working on enum and static const int variable.
operator overloading
operator overloading
e& operator--(e &x) ; -> means --var; //prefix
e& operator--(e &x,int); -> var--; //postfix
Loop Fusion/Fission
Loop Unrolling
Loop Peeling/Splitting
https://www.wikiwand.com/en/Loop_splitting
https://medium.com/@savas/template-metaprogramming-compile-time-loops-over-class-methods-a243dc346122
https://www.wikiwand.com/en/Loop_unrolling
https://www.wikiwand.com/en/Loop_fission_and_fusion
Swift
Swift static vs class type
static
and class
both associate a method with a class, rather than an instance of a class. The difference is that subclasses can override class
methods; they cannot override static
methods.
unowned
and weak
The difference between
unowned
and weak
is that weak
is declared as an Optional while unowned
is not. By declaring it weak
you get to handle the case that it might be nil inside the closure at some point. If you try to access an unowned
variable that happens to be nil, it will crash the whole program. So only use unowned
when you are positive that variable will always be around while the closure is around. It's sort of like a weak optional reference that's implicitly unwrapped with x!
every time it's accessed.lazy variable must have to use with closure. lazy loads the variable in memory when using the app for the first time.
Swift structs are laid out similar to C. Classes are very similar to structs, the main difference being they have a header directly before the properties. The header contains the type’s
isa
pointer, and the strong and weak reference counts.Custom Operator
prefix operator *
postfix operator **
infix operator ***
func ***(lhs: Int, rhs: Int) -> Int {
return lhs + rhs
}
prefix func *(rhs: Int) -> Int {
return rhs + 1
}
postfix func **(rhs:inout Int) -> Int{
rhs = rhs + 1
return rhs
}
operator overloading
operator overloading
extension CGSize {
static func +(lhs: CGSize, rhs: CGSize) -> CGPoint {
return CGPoint(x: lhs.width + rhs.width,y: lhs.height + rhs.height)
}
}
Acquiring Type Metadata
Swift stores a metadata record for every type. This can include things like its kind (struct, class, enum, etc), field names, field offsets and so on.
Value and Reference Types
The most basic distinguishing feature of a value type is that copying — the effect of assignment, initialization, and argument passing — creates an independent instance with its own unique copy of its data.
In Swift, Array, String, and Dictionary are all value types. They behave much like a simple int value in C, acting as a unique instance of that data.
Use a reference type (e.g. use a class) when:
- Comparing instance identity with === makes sense
Access Control
- private entities are available only from within the source file where they are defined.
- internal entities are available to the entire module that includes the definition (e.g. an app or framework target).
- public entities are intended for use as API, and can be accessed by any file that imports the module, e.g. as a framework used in several of your projects.
Increasing Performance by Reducing Dynamic Dispatch
Swift allows a class to override methods and properties declared in its superclasses. This means that the program has to determine at runtime which method or property is being referred to and then perform an indirect call or indirect access. This technique, called dynamic dispatch, increases language expressivity at the cost of a constant amount of runtime overhead for each indirect usage.
In Swift, dynamic dispatch calls are implemented by looking up a function from a method table and then performing an indirect call. This is slower than performing a direct call. Additionally, indirect calls also prevent many compiler optimizations, making the indirect call even more expensive.
The final keyword is a restriction on a class, method, or property that indicates that the declaration cannot be overridden. This allows the compiler to safely elide dynamic dispatch indirection.
It is possible to mark an entire class as final by attaching the attribute to the class itself. This forbids subclassing the class, implying that all functions and properties of the class are final as well.
Applying the private keyword to a declaration restricts the visibility of the declaration to the current file. This allows the compiler to find all potentially overriding declarations. The absence of any such overriding declarations enables the compiler to infer the final keyword automatically and remove indirect calls for methods and property accesses.
Declarations with internal access (the default if nothing is declared) are only visible within the module where they are declared. Because Swift normally compiles the files that make up a module separately, the compiler cannot ascertain whether or not an internal declaration is overridden in a different file. However, if Whole Module Optimization is enabled, all of the module is compiled together at the same time. This allows the compiler to make inferences about the entire module together and infer final on declarations with internal if there are no visible overrides.
SwiftUI
@Binding
This is not the same as
This lets us share model data anywhere it’s needed, while also ensuring that our views automatically stay updated when that data changes. Rather than creating some data in view A, then passing it to view B, then view C, then view D before finally using it, you can create it in view and put it into the environment so that views B, C, and D will automatically have access to it.
Note: Environment objects must be supplied by an ancestor view – if SwiftUI can’t find an environment object of the correct type you’ll get a crash.
@State
https://developer.apple.com/swift/blog/?id=27
SwiftUI
@Binding
This is not the same as
@ObservedObject
or @EnvironmentObject
, both of which are designed for reference types to be shared across potentially many views. it lets us declare that one value actually comes from elsewhere, and should be shared in both places. @EnvironmentObject
. This lets us share model data anywhere it’s needed, while also ensuring that our views automatically stay updated when that data changes. Rather than creating some data in view A, then passing it to view B, then view C, then view D before finally using it, you can create it in view and put it into the environment so that views B, C, and D will automatically have access to it.
Note: Environment objects must be supplied by an ancestor view – if SwiftUI can’t find an environment object of the correct type you’ll get a crash.
@State
SwiftUI
manages the storage of any property you declare as a state. When the state value changes, the view invalidates its appearance and recomputes the body. Use the state as the single source of truth for a given view.
A State instance isn’t the value itself; it’s a means of reading and mutating the value. To access a state’s underlying value, use its value property.
@Published
is one of the most useful property wrappers in SwiftUI, allowing us to create observable objects that automatically announce when changes occur. SwiftUI will automatically monitor for such changes, and re-invoke the body
property of any views that rely on the data. In practical terms, that means whenever an object with a property marked @Published
is changed, all views using that object will be reloaded to reflect those changes.https://developer.apple.com/swift/blog/?id=27
https://arobenko.gitbooks.io/bare_metal_cpp/content/basic_concepts/event_loop.html
https://baudehlo.com/2013/02/14/how-an-event-loop-works/
https://github.com/codebrainz/grinder
http://bou.io/RunRunLoopRun.html
Comments
Post a Comment