1. C, C ++ baCkground
Many people ask, "I don't want to learn C, but I don't want to learn Objective-C"
Here is a few simple sentences: Objective-C 90% code is C, and many open source code are C and C ++. You cannot learn C well. You can only be a second-stream developer in the unix world! It may be too harsh, but you may consider it yourself.
2. Runtime (Runtime)
Objective-C is a dynamic language, and many new users or developers are often confused by Runtime. This is a very important concept. Why is it important !? I can ask, "how do you start to implement a computer language ?" Few Programmers think that way. However, such a question will force you to think from a higher level (1) the previous question. Note that the key to the design is implementation.
I divide the implementation into three different layers:
1. Traditional process-oriented language development, such as C language. Implementing the C language compiler is simple. You only need to implement an LALR syntax analyzer according to the syntax rules. Compiler optimization is a very difficult topiC. It is not discussed here and ignored. Here we have implemented one of the most basic and original goals of the compiler is to convert the function name in the code into a relative memory address, convert the statement that calls this function into a jmp jump command. When the program starts running, the call statement can jump to the corresponding function address correctly. This is very good and straightforward,... It's too rigid. Everything is per-determined
2. We want to be flexible, so we need to develop object-oriented languages, such as C ++. C ++ adds the class part based on C. But what exactly does this mean? How should we consider the compiler for writing it again? In fact, it is to let the compiler turn around and add a Class processing mechanism to the strict C compiler to limit a function to the Class environment in which it is located, each time you request a function call, find its object, its type, return value, parameters, and so on. After determining these, jmp jumps to the required function. In this way, many programs increase flexibility. Similarly, a function call will return completely different results based on the Request Parameters and the class environment. After adding the class mechanism, it simulates the abstract pattern of the real world. Different objects have different attributes and methods. In the same way, different classes have different behaviors! Here we can see what further thinking has been made as a compiler developer. However. We still call C ++ static language.
3. Hope to be more flexible! So we completely abstract the implementation part of the above class into a complete runtime detection environment. This time, write the compiler and even retain the sytax name, name error detection, and runtime environment registration of global classes, functions, variables, and so on, we can add the necessary functions for this layer infinitely. When calling a function, the system detects the possible parameters in the runtime environment and then performs a jmp jump. This is the runtime. The compiler is more curved than above. However, this layer greatly increases program flexibility. For example, when calling a function in the first two languages, it is very likely that a jmp has an Invalid Address that causes the program Crash. However, in this layer, runtime filters out these possibilities. That's why dynamic langauge is stronger. Because the compiler and runtime environment developers have already helped you solve these problems.
Now let's look at Objective-C. Can you understand this statement?
- id obj=self;
- if ([obj respondsToSelector:@selector(function1:)) {
- }
- if ([obj isKindOfClass:[NSArray class]] ) {
- }
- if ([obj conformsToProtocol:@protocol(myProtocol)]) {
- }
- if ([[obj class] isSubclassOfClass:[NSArray class]]) {
- }
- [obj someNonExistFunction];
A seemingly simple statement, but in order to enable the language to implement this capability, the language developer has to make a lot of efforts to implement the runtime environment. The runtime environment handles checks for weak types and functions. Runtime checks whether the corresponding functions and types exist in the registration list, determines the correct function address, and then saves the register status, presses the stack, and CALLS functions.
- id knife=[Knife grateKnife];
- NSArray *monsterList=[NSArray array];
- [monsterList makeObjectsPerformSelector:@selector(killMonster:) withObject:knife];
In the c and c ++ years, it was very troublesome to complete this function, but the dynamic language was very simple.
About execution efficiency. "Static language execution efficiency is higher than Dynamic Language execution efficiency. Some cpu computing losses are in the runtime process. The machine commands generated by static languages are more concise. Because of this, developers have made a lot of effort to keep the runtime small. Therefore, objecitve-c is a super set of c + a small runtime environment. However, in other words, from an algorithm perspective, this complexity is not different, and the Big O notation results will not be different. (It's not log (n) vs n ^ 2)
Easy to understand: "Runtime is everything between your each function call ."
Runtime is like the soul of objective-c. Many things appear on this basis. So it refers to what you need to understand.
3. thread
"Thread synchronization another notorious trouble! "
I can't remember the Operating System Course I learned at school. There will be a special chapter on task scheduling and producer and consumer issues. This is to lay the foundation for future use of processes and thread development. The concept is very simple, but few people are familiar with it. The difficulty lies in synchronization, because 1. there is no 100% deadlock detection algorithm. if there is, no deadlock at all. 2. this type of errors is often obscure, and static analysis is hard to find. 3. A high degree of abstraction requires experience to grasp.
In general, the problems I have encountered in this regard can be divided into the following points:
1. I don't know the basis points of multi-thread development. the more confused I am when I look at other people's code. NSThread, *****, block, and so on... Apple encapsulates multi-threaded APIs, And the down to core multithreading structure is basically
You can see that in multi-threaded development, you can choose these different methods. Mach is the most important part of the operating system. You can use it, but it is not necessary, too tired.
Pthread is flexible and lightweight, but it requires a complicated theoretical basis for development. The most important POSIX open thread cannot use cocoa. According to the apple documentation, to use cocoa only under pthread, you must first detach at least one NSThread object. in this way, [NSThread isMultiThreaded] can be used.
NSThread is a high-level multi-threaded API released after Mac OS 10.0, but it lacks flexibility.
The open-source multi-threaded library introduced by Grand Central Dispatch 10.6 is between pthread and NSThread. It is more flexible and small than NSThread, but does not need to consider many lock issues like pthread. Blocks, one of the new syntax features released by objective-c 2.0, is based on this multithreading demand.
When you write multi-threaded code or read multi-threaded code, you must be clear about this.
2. Problems Caused by thread and runloop
In fact, when thread and runloop were put in the past, developers were not considered a problem at all. Because there is no runtime capability, runloop is a fixed thread that executes the loop. Currently, cocoa developers cannot understand much about it. What is the relationship between nsunloop and NSThread? As there are many problems, I will explain them separately in the 4th points.
3. Problems Caused by thread and Reference Counting memory management.
Reference
Do all the methods in the thread need to be put in the NSAID utoreleasepool?
This type of problem is common, mainly because the NSAID pool does not understand what it is. The relationship between the NSAID utoreleasepool and thread is not significant. It provides a temporary memory management space, such as a sandbox, to ensure that no improper memory allocation is leaked, the newly allocated objects in this space need to register with this pool and tell: "pool, I have allocated a new space ". When pool drain drops or release, the allocated memory is also released. It is not very relevant to thread. However, when we read the code, we often see that the function of the new thread always ends with the nutoreleasepool. Why !? Because the thread is exactly the most suitable place to need it! The thread function should calculate a large amount of data and take a long time (supposed to be heavy ). A large number of objects may be generated in the thread, and the use of autoreleasepool for management is simpler. Therefore, the answer here is that you do not have to put the NSAID utoreleasepool in the online environment. You can use the NSAID utoreleasepool in any place in the cocoa environment. If you do not use the NSAID utoreleasepool in the thread, remember to ensure that there is no memory leakage when alloc and relase are paired internally.
It is worth mentioning that autorelease. NSObject has the autorelease method. Why? What is auto based on?
4. Questions about mainthread and secondary thread
Reference
What are the differences between the detachNewThreadSelector of NSThread and self's javasmselectoronmainthread methods?
5. Asynchronous (Asynchronous) vs. Synchronous (Synchronous)
Reference
I want to display multiple web Images in one view. I would like to ask if I should adopt asynchronous download method one by one, or use multiple threads for simultaneous download or both, which method is better?
Let's take a look at this question. This sentence is very strange to me, because I think the person who asks the question does not understand what synchronous Asynchronization means. "One-by-one download" is a synchronous action, while "multi-thread simultaneous download" is an asynchronous action. All mixed up!
4. runloop
Now let's talk about why runloop is confusing in cocoa development. Because many new users do not look at it dynamically. First, let's look back at the concept of runtime introduced. Then I will give a question.
Now I have a program snippet as follows:
- - (void)myThread:(id)sender
- {
- NSAutoreleasePool *pool=[[NSAutoreleasePool alloc] init];
- while (TRUE) {
-
- //do some jobs
- //break in some condition
-
- usleep(10000);
-
- [pool drain];
- }
-
- [pool release];
- }
Now, we need to make some designs so that when this thread runs, we can also add or remove different computing tasks from other threads. This is the original development intention of the nsunloop. Makes computing tasks of one thread more flexible. This function can be implemented in c and c ++, but it is very difficult. The most important reason is the limitation of language capabilities, which were rarely considered by programmers in the past.
Well, now we have made a very simple evolution to the above Code:
- - (void)myThread:(id)sender
- {
- NSAutoreleasePool *pool=[[NSAutoreleasePool alloc] init];
- while (TRUE) {
-
- //do some jobs
- //break in some condition
-
- usleep(10000);
-
- [pool drain];
- }
-
- [pool release];
- }
Now, we need to make some designs so that when this thread runs, we can also add or remove different computing tasks from other threads. This is the original development intention of the nsunloop. Makes computing tasks of one thread more flexible. This function can be implemented in c and c ++, but it is very difficult. The most important reason is the limitation of language capabilities, which were rarely considered by programmers in the past.
Well, now we have made a very simple evolution to the above Code:
- NSMutableArray *targetQueue;
- NSMutableArray *actionQueue;
-
- - (void)myThread:(id)sender
- {
- NSAutoreleasePool *pool=[[NSAutoreleasePool alloc] init];
- while (TRUE) {
-
- //do some jobs
- //break in some condition
- int n=[targetQueue count];
- assert(n==[actionQueue count]);
- for(int i=0;i<n;i++){
- id target=[targetQueue objectAtIndex:i];
- SEL action=NSSelectorFromString([actionQueue objectAtIndex:i]);
- if ([target respondsToSelector:action]) {
- [target performSelector:action withObject:nil];
- }
- }
-
- usleep(10000);
-
- [pool drain];
- }
-
- [pool release];
- }
Note that thread security is not handled here. Remember Mutable container is not thread safe.
This simple extension shows how to use the runtime capability to make the thread flexible. When we add objects and methods to targetQueue and actionQueue from another thread at the same time, this thread function has the ability to execute an additional code.
However, someone may ask, where is runloop? Which one is nsunloop? It cannot be seen.
- while (TRUE) {
- //break in some condition
- }
This structure in a thread is called the runloop of a thread. It is similar to the name of the nsunloop class, but it is not a thing at all. In the past, when I started using a static language, programmers were not confused, because there was no such thing as the nsunloop. Next I will discuss how this nsunloop came about.
In the second extension code, there is indeed no such thing as the nsunloop. We will continue to make 3rd improvements. This time, we abstract the dynamic part.
5. delegate, protocol
This will be shown as I feel that the number of queries is only in the memory management section, they are used frequently, and they are an important part of the multi-clock design pattern.
6. responder chain
7. Memory Reference Counting (RC) & Automatic Reference Counting (ARC)
This may be the most asked question. These problems often come from three sources: 1. Do not understand the underlying mechanism; 2. Do not have thorough rules; 3. Do not understand the Reference Counting feature of commonly used iner, or, you have no time to read the corresponding document.
1. Underlying Mechanism
Do you know what the RC and ARC mechanisms in the old age mean? Why is ARC superior to rc in terms of development speed, execution speed, and stability?
The development speed is self-evident. You write a lot less release code, and even seldom worry about this part.
What is the execution speed? This should start with runtime. Do you still remember to say "Runtime is everything between your each function call ."
RC is an old memory management philosophy, who allocates and who releases it. Count the number of users of the resource through counting. The principle is simple, but often simple things make mistakes. No programmer can say with confidence that I have never written any code with Memory leakage. In this way, we need to let the program handle this management mechanism on its own, so we need to put this mechanism into runtime.
So RC-> ARC is to move the memory management part from the common developer's function to the runtime outside the function. Because the development prototype of runtime is simple and the logic level is higher, the probability of errors in development and management is lower. In fact, compiler developers have gone through countless tests on this part, so it can be said that there will be almost no error in using arc. In addition, due to the additional compilation optimization, this part is much faster than the code written by the programmer. In addition, for some common development modes such as autorelease objects, arc has better algorithms to ensure fewer objects in autoreleasepool.
2. RC rules
First, let's talk about what rc is. For r-Reference, Reference c-counting count, and rc is Reference count. As the saying goes, it is to record the number of users. For example, if I have an empty room, you can go in and use it as needed. But before you enter the room, you need to add the Count sign + 1 at the door and choose-1 at the door. At this time, the sign at the door is the number of people in the room. Once this sign is set to 0, I can close the room.
This rule allows NSObject to decide whether to release the memory. When alloc is an object, the system allocates a piece of memory and the object automatically counts retainCount = 1. At this time, every time [object retain] retainCount + 1 (although abbreviated here is rc, it is just a coincidence or the developer deliberately chooses retain at that time. right) every time the [object release] retainCount-1 when retainCount = 0, the object actually returns this fast memory to the system.
3. Common Reference Counting features of container
This rule is simple. But this is indeed the biggest headache for new users. The problem is that new users always want to verify rc rules and find that they do not meet their expectations.
The following sentence has been written countless times.
- NSLog(@"%d",[object retainCount]);
- while([object retainCount]>0){
- [object release];
- }
Of course, I have also done similar actions, the kind of mentality that I hope everything is under control. But you will see other people telling you that this is totally meaningless. Rc does not work this way. Maybe such a violent release will work, but retainCount is not used for this. Each number means that other objects reference this resource, and such brute force release can easily cause program crash. This number may not be in your mind. It is difficult to track which objects reference the resource. The resources you create using code are not only used by your code. The various frameworks you call and the frameworks called by the Framework may change the retainCount of this resource. therefore, it is not wise to verify rc rules.
What you can do is to understand the rules, use the rules, and read the document to understand the reference features of the container. Or simply move to the arc to let the runtime environment handle these problems.
Finally, let's talk about the situation where arc is not used. Currently, many third-party libraries do not support arc, so if your old project uses these libraries, check whether the author has released a new version, or you need to correct and support it yourself.
8. class heritage
9. English
10. Just trying to be smart
In fact, there are several points to talk about, but let's take a look at it. Similar ideas
For example, you just saw this problem:
Reference
Now there is A * a; A * B
[NSMutableArray addObject: a];
[NSMutableArray replaceObjectAtIndex: 0 withObject: B]
After these two steps are executed, the 0 position in the variable array is the B element. Where does a go at this time ?? Does it still occupy the memory? How can I release it if it is used ??
It's kind of silly. In fact, if you really understand the above knowledge points, you will not ask such questions again. Why don't you think about it? Before you ask this question, why? "How would you write a replaceObjectAtIndex function for NSMutableArray? "Isn't a single [obj release] considered? Then, according to ARC, whether it is released is self-evident.
In fact, there are many such questions in the Forum. When you are confused, ask yourself why you are confused.
(1) This is actually very interesting. Why do I use "higher-level thinking" instead of "lower-level thinking ". As a compiler and language developer, the problems they face are indeed more fundamental, but they think about a higher dimension and more abstract. For example, a person in a 3D world is dealing with a line in a two-dimensional world.