Code block development 2

Source: Internet
Author: User

This blog shows that the code is easy to write, which is a bit more. You need to thoroughly read the previous code block before starting with this

After learning the code block, the Programming Skill of IOS goes to a higher level.

Reposted blog: http://blog.sina.com.cn/s/blog_71715bf8010167tl.html


Ios4 introduces a new feature that supports the use of code blocks, which will fundamentally change your programming method. The code block is an extension of the C language, so it is fully supported in objective-C. If you have learned Ruby, Python, or lisp programming languages, you must know the power of code blocks. Simply put, you can encapsulate a group of code statements through code blocks and treat them as an object. The use of code blocks is a new encoding style that allows you to freely use new APIs in ios4. Let's take a look at two examples of using code blocks in ios4 (which you may have seen): View animati ***** and enumeration. The first example is the code block, suppose we create a card game, and we need to show the animated Effect of cards distributed to players. Fortunately, an animation effect can be easily achieved through the uikit framework. But what kind of animation is ultimately determined by your program. You can specify the animation content in the code block and then pass the code block to the animatewithduration: animati *****: method, as shown below: [uiview animatewithduration: 2.0 animati *****: ^ {self. cardview. alpha = 1.0; self. cardview. frame = cgrectmake (176.0, 258.0, 72.0, 96.0); self. cardview. transform = cgaffinetransformmakerotation (m_pi) ;}]; when this animation code block is executed, our card shows three types of animations: changing its alpha value to fade into the display, change its position to the lower right corner (player position) and rotate 180 degrees (to make it better ). The example of the second code block is to iterate a set of cards and print its name and the index value in the set. You can use the for loop to achieve the goal, but in ios4, The nsarray class has a convenient method that uses the code block: enumerateobjectsusingblock :. The following describes how to use it: nsarray * cards = [nsarray arraywithobjects: @ "Jack", @ "Queen", @ "King", @ "Ace", nil]; [cards enumerateobjectsusingblock: ^ (ID object, nsuinteger index, bool * Stop) {nslog (@ "% @ card at index % d", object, index);}]; this code block uses three parameters: an object in the array, the index of the object, and a flag that identifies whether the iteration ends. We will discuss it further later. Enumerateobjectsusingblock: This method passes each element in the set into a corresponding parameter and calls the method in the code block. Therefore, the advantage of using a code block in your Mac and iOS programs is that it allows you to append arbitrary code to the Methods officially provided by Apple. Although similar in concept to proxy, it is more convenient and elegant to use short Inline code blocks in methods. This is a good start, but it is important to understand its internal processing. When I learn new things, I like to divide them into simple parts, learn how they work, and then assemble them together, in this way, I am confident in my own code and quick solutions to problems. Therefore, let's go back and learn how to declare and call simple code blocks. The basic concept of a code block can be simply considered as a set of executable code. For example, the following code block prints the current date and time: ^ {nsdate * Date = [nsdate date]; nslog (@ "the date and time is % @", date) ;}; Insert the symbol (^) to declare the beginning of a code block. A pair of braces {} constitute the body of the code block. You can think that the code block is similar to an anonymous function. So how can we call this code block for an anonymous function? The most common way to use a code block is to pass it into the method for method callback, just as we have seen view animati ***** and enumeration before. Another way to use a code block is to assign it to a code block variable, which can then be used to directly call the code block. Here is how to declare our code block and assign it to the code block variable now: void (^ now) (void) = ^ {nsdate * Date = [nsdate date]; nslog (@ "the date and time is % @", date) ;}; it takes some time to adapt to the syntax for declaring a Block Variable. This is interesting. If you have used function pointers, the code block variables are similar. On the Right of the above Code equal sign is the code block we have already introduced. The left side of the equal sign declares a code block variable now. Code block variables have a ^ symbol and are enclosed in parentheses. The code block variables have a type definition. Therefore, the now variable in can apply any code block without parameters and return values. The code block we declared previously meets this requirement, so we can safely allocate it to the now variable. As long as there is a code block variable within its scope, we can call it like calling a function. The following is how to call our code block: Now (); you can declare the code block variable in the C function or the objective-C method, and then call it within the same scope, as we described earlier. When the code block is executed, it prints the current date and time. So far, progress has been smooth. A code block is a closure. If this is all the code blocks, it is exactly the same as a function. But the fact is that a code block is not just a set of executable code. A code block can capture declared variables in the same scope. Because the code block is a closure, the variables used are included in the code block when the code block is declared. To illustrate this, let's change the previous example to move the date initialization out of the code block. Nsdate * Date = [nsdate date]; void (^ now) (void) = ^ {nslog (@ "the date and time is % @", date );}; now (); when you call this code block for the first time, it is exactly the same as our previous version: print the current date and time. But when we call the code block after changing the date, there will be a significant difference, sleep (5); Date = [nsdate date]; now (); although we changed the date before calling the code block, the previous date and time are still printed when calling the code block. It's like the date is paused when the code block is declared. Why? When the program executes the declaration of a code block, the code block performs a read-only backup on the variables used in the same scope and block. You can think that the variable is frozen in the code block. Therefore, whenever a code block is called, it is immediately called or five seconds later, as long as it prints the initial date and time before the program exits. In fact, the example of the code block shown above as a closure is not perfect. After all, you can pass the date into the code block as a parameter (as explained below ). But when you pass a code block between different methods, the closure feature becomes very useful because the variables in it remain unchanged. Code block parameters are like functions. A code block can pass in parameters and return results. For example, if we want a code block that can return three times the specified number, the following is the implemented code block: ^ (INT number) {return number * 3 ;}; declares a variable triple for the code block, as shown in the following figure: int (^ triple) (INT) = ^ (INT number) {return number * 3 ;}; as mentioned above, we need to be familiar with the syntax for declaring code block variables on the left of the equal sign. Now let's split it from left to right: the leftmost Int Is the return value type, the center is the name of the inserted symbol ^ and the code block variable surrounded by parentheses, And the last parentheses, the parameter type is enclosed (in the preceding example, there is only one int parameter ). The code block declaration on the right of the equal sign must comply with the definition on the left. One thing to note is that for convenience, the compiler will make a judgment from the return statement without declaring the return type of the code block. To call this code block, you need to input a parameter that requires 3 and accept the return value, like this: int result = triple (2 ); next you will know how to declare and create a code block that requires two int-type parameters, multiply them, and then return the result: int (^ multiply) (INT, INT) = ^ (int x, int y) {return x * Y;}; how to call this code block: int result = multiply (2, 3 ); declaring code block variables gives us the opportunity to explore the code block types and how to call them. Code block variables are similar to function pointers. Calling code blocks is similar to calling functions. Unlike function pointers, code blocks are actually objective-C objects, which means they can be passed like objects. In practice, a code block is often used as a parameter to pass in a method for callback. When a code block is used as a parameter, it is usually used as an Inline code block compared to allocating a code block variable. For example, we can see the following examples: View animati ***** and enumeration. Apple has added some code block methods to their frameworks. You can also write some APIs that use code blocks. For example, we want to create a worker class using the code block class method. This method repeatedly calls the code block for a specified number of times and processes the results returned each time. The following is how we use an Inline code block to call this method. The code block is responsible for returning three times the number of rows from 1 to 10. [Worker repeat: 10 withblock: ^ (INT number) {return number * 3;}]; this method can take any code block that accepts an int-type parameter and returns an int-type result as a parameter. If you want to double the number, you only need to change the code block of the passed-in method. In this section, we will focus on writing our own code block method. By understanding how to use code blocks in your own code, you will master a new design technology. And you may realize that code blocks make your code easy to read and maintain. Writing a code block method leaves a task in the first part: Write a class method that calls the code block of the work class, repeat the number of times specified by the code block, and process the return value of each code block. If we want to get three times the value from 1 to 5, the following is how we call this method with an Inline code block: [Worker repeat: 5 withblock: ^ (INT number) {return number * 3;}]; I often design a class like this. First, write the code to call a fictitious method, which is also a simple way to form an API before submission, once I think that this method is called correctly, I will implement this method. In this way, the method name is repeat: withblock:, which I think is inappropriate (I know this name is in the first part, but I have changed my mind ). This name is easy to confuse, because the method does not actually repeat the same thing. This method iterates from 1 to the specified number of times and processes the return of the code block. So let's start renaming it correctly: [Worker iteratefromoneto: 5 withblock: ^ (INT number) {return number * 3;}]; i'm satisfied with the name of the method using two parameters iteratefromoneto: withblock: an int parameter indicates the number of times a code block is called and a code block parameter to be called. Now let's implement this method. For beginners, how should I declare this iteratefromoneto: withblock: method? First, we need to know the types of all parameters. The first parameter is easy to use, and it is an int type. The second parameter is a code block, and the code block has a return type. In this example, this method can accept any code block with an int parameter and return an int result as a parameter. The following is the actual code block type: int (^) (INT) has the method name and Its Parameter type, and we can declare this method. This is a worker class method. declare it in H: @ interface worker: nsobject {} + (void) iteratefromoneto :( INT) limit withblock :( int (^) (INT) block; @ end first glance, code block parameters are not easy to understand. Remember that all method parameters in objective-C are composed of two parts. The parameter type and name. In this example, the parameter must be of the int type and a code block of the INT (^) type (INT) type (you can name the parameter as any name, not necessarily block ). The implementation of this method is in worker. M file, relatively simple: # import "worker. H "@ implementation worker + (void) iteratefromoneto :( INT) limit withblock :( int (^) (INT) block {for (INT I = 1; I <= limit; I ++) {int result = block (I); nslog (@ "Iteration % d => % d", I, result );}} @ end method calls the code block every time through a loop and prints the returned results of the code block. Remember that once we have a code block variable in the scope, we can use it like a function. Here, the code block parameter is a code block variable. Therefore, when block (I) is executed, the incoming code block is called. When the code block returns the result, it continues to be executed. Now we can use the Inline code block to call iteratefromoneto: withblock: method, like this: [Worker iteratefromoneto: 5 withblock: ^ (INT number) {return number * 3;}]; we can also pass in a code block variable as the parameter without using an Inline code block: int (^ tripler) (INT) = ^ (INT number) {return number * 3 ;}; [Worker iteratefromoneto: 5 withblock: tripler]; regardless of the method, the output is as follows: iteration 1 => 3 iteration 2 => 6 iteration 3 => 9 iteration 4 => 12 iteration 5 => 15 of course we can pass in a code block for any operation. Do you want to get the square of the number? No problem. Just input a different code block: [Worker iteratefromoneto: 5 withblock: ^ (INT number) {return number * number;}]; now our code can run, below is a bit of code. Good at using typedef to quickly declare the type of the code block is easy to confuse, even in this simple example, the function correction syntax still has many shortcomings: + (void) iteratefromoneto :( INT) limit withblock :( int (^) (INT) block; imagine that the code block uses multiple parameters, and some parameters are pointer types. In this case, you almost need to completely rewrite your code. To improve readability and avoid. H and. m. You can use typedef to modify worker. hfile: typedef int (^ computationblock) (INT); @ interface worker: nsobject {}+ (void) iteratefromoneto :( INT) limit withblock :( computationblock) block; @ end typedef is a keyword of C language. It can be understood as a nickname for a complicated name. In this case, we define a code block variable computationblock, which has an int type parameter and an int type return value. Then, when we define the iteratefromoneto: withblock: method, computationblock can be directly used as the code block parameter. Similarly, in worker. M file, we can simplify the code by using computationblock: # import "worker. H "@ implementation worker + (void) iteratefromoneto :( INT) limit withblock :( computationblock) block {for (INT I = 1; I <= limit; I ++) {int result = block (I); nslog (@ "Iteration % d => % d", I, result) ;}@ end well, this is much better, the code is easy to read and does not repeatedly define the code block type in multiple files. In fact, you can use computationblock anywhere in your program. As long as you import "worker. H", you will encounter similar typedef in the new ios4 API. For example, the alassetslibrary class defines the following method:-(void) assetforurl :( nsurl *) asseturl resultblock :( partial) resultblock failureblock :( alassetslibraryaccessfailureblock) failureblock this method calls two code blocks, A code block is called when the required resources are found, and the other is called when the code block is not found. Their typedef is as follows: typedef void (^ alassetslibraryassetforurlresultblock) (alasset * asset); typedef void (^ alassetslibraryaccessfailureblock) (nserror * error ); then, you can use alassetslibraryassetforurlresultblock and alassetslibraryaccessfailureblock in your program to represent the corresponding code block variables. We recommend that you use typedef when writing a public method that uses code blocks. This helps your code clean and tidy and makes it easy for other developers to use. Let's take a look at the closure. You should remember that the code block is a closure. Let's briefly describe the closure mentioned in the first part. The example of the first part of the closure is not practical, and I say the closure will become particularly useful when passed between methods. Now that we know how to write a practical code block method, let's analyze another closure example: int multiplier = 3; [Worker iteratefromoneto: 5 withblock: ^ (INT number) {return number * multiplier;}]; we use the previously written iteratefromoneto: withblock: method. The difference is that the code block does not have a hard-coded multiple to be obtained, this multiple is declared outside the code block as a local variable. The execution result of this method is the same as that before. multiply the number between 1 and 5 by 3: iteration 1 => 3 iteration 2 => 6 iteration 3 => 9 iteration 4 => 12 iteration 5 => 15 this code is an example of a strong closure. The Code breaks the general scope rules. In fact, you can call the multiplier variable in the iteratefromoneto: withblock: Method to regard it as a local variable. Remember, the code block captures the surrounding state. When a code block is declared, it automatically takes a read-only snapshot of the variables used internally. Because our code block uses the multiplier variable, the value of this variable is saved as a part of the code block for later use. That is to say, the multiplier variable has become a part of the code block status. When the code block is passed in to iteratefromoneto: withblock: method, the quick status is also passed in. Well, what if we want to change the multiplier variable inside the code block? For example, each time a code block is called, the multiplier must be converted to the result of the previous calculation. You may try to directly change the multiplier variable in the code block, such as: int multiplier = 3; [Worker iteratefromoneto: 5 withblock: ^ (INT number) {multiplier = Number * multiplier; return multiplier; // compile Error!}]; In this case, the compiler reports the error "assignment of read-only variable 'mutilplier '". This is because the code block uses a copy of the variable, which is a constant in the stack. These variables cannot be changed in the code block. If you want to modify a variable defined outside the block and used in the block, you need to add a new prefix _ block when declaring the variable, such as :__ block int multiplier = 3; [Worker iteratefromoneto: 5 withblock: ^ (INT number) {multiplier = Number * multiplier; return multiplier;}]; nslog (@ "multiplier => % d", multiplier ); in this way, the code can be compiled and the running result is as follows: iteration 1 => 3 iteration 2 => 6 iteration 3 => 18 iteration 4 => 72 iteration 5 => 360 multiplier => 360 note that after the code block is run, the value of the multiplier variable has changed to 360. In other words, the code block is not a copy of the variable. Declaring a variable modified by _ block is to pass its reference into the code block. In fact, the variable modified by _ block is shared by all the code blocks that use it. It should be emphasized that _ block should not be used at will. There will be marginal costs when moving something into the memory heap, unless you are sure you want to modify the variable, do not use the _ block modifier. Sometimes we need to write a method to return code blocks. Let me first look at an incorrect example: + (computationblock) raisedtopower :( INT) y {computationblock = ^ (int x) {return (INT) Pow (x, y );}; return block; // don't do this !} This method creates a code block that calculates the X power of Y and returns it. It uses the computationblock we used previously through typedef. The following figure shows the expected effect of the returned code block: computationblock block = [Worker raisedtopower: 2]; block (3); // 9 block (4 ); // 16 block (5); // 25 in the above example, we get the code block and pass in the corresponding parameter. It should return the square of the passed value. But when we run it, we will get the runtime error "exc_bad_access ". What should I do? The key to solving this problem is to understand how the code block allocates memory. The life cycle of a code block starts in the stack, because the memory allocated in the stack is relatively block. If it is a stack variable, it will be destroyed after being popped up from the stack. This happens when the method returns the result. Review our raisedtopower: method. You can see that a code block is created in the method and it is returned. In this way, the life cycle of the code block is defined. When we return the code block variable, the code block is actually destroyed in the memory. The solution is to move the code block from the stack to the heap before returning the code. This sounds complicated, but it is actually very simple. You only need to simply copy the code block and the code block will be moved to the heap. The modified method meets our expectation: + (computationblock) raisedtopower :( INT) y {computationblock = ^ (int x) {return (INT) pow (x, y) ;}; return [[block copy] autorelease];} after using copy, you must balance the reference counter with autorelease, avoid Memory leakage. Of course, we can also manually release a code block after it is used, but this does not conform to the principle of who creates and releases it. You do not need to copy code blocks frequently, but you need to copy the code blocks if they are described above. Please pay attention to this. So we can integrate what we have learned into a more practical example. Suppose we want to design a simple class for playing a movie. The user of this class wants to accept a callback to display the specific logic of the application after the movie is played. The code block has been proved to be a convenient method for processing callback. Let's start writing code, from the perspective of a developer using this class: movieplayer * player = [[movieplayer alloc] initwithcallback: ^ (nsstring * title) {nslog (@ "hope you enjoyed % @", title) ;}]; [Player playmovie: @ "Inception"]; we can see that we need the movieplayer class. There are two methods: initwithcallback: And playmovie: receives a code block during initialization, saves it, and calls the code block after executing playmovie: method. This code block requires a parameter (movie name) and returns the void type. We use typedef for the type of the callback code block and property to save the code block variables. Remember, a code block is an object. You can use it like an instance variable or attribute. Here we use it as an attribute. Below is movieplayer. h: typedef void (^ movieplayercallbackblock) (nsstring *); @ interface movieplayer: nsobject {}@ property (nonatomic, copy) movieplayercallbackblock callbackblock;-(ID) initwithcallback :( callback) block; -(void) playmovie :( nsstring *) title; @ end: movieplayer. m: # import "movieplayer. H "@ implementation movieplayer @ synthesize callbackblock;-(ID) initwithcallback :( moviepla Yercallbackblock) block {If (Self = [Super init]) {self. callbackblock = block;} return self;}-(void) playmovie :( nsstring *) Title {// play the movie self. callbackblock (title) ;}- (void) dealloc {[callbackblock release]; [Super dealloc] ;}@ end in initwithcallback: Declares the code block to be used as the callbackblock attribute in the method. Because the attribute is declared as the copy method, the code block automatically performs the copy operation to move it to the heap. When playmovie: method call, we pass in the movie name as a parameter to call the code block. Now let's assume that a developer wants to use our movieplayer class in a program to manage a group of movies you want to watch. After watching a movie, it will be removed from the group. The following is a simple implementation with the closure: nsmutablearray * moviequeue = [nsmutablearray arraywithobjects: @ "Inception", @ "The Book of Eli", @ "Iron Man 2 ", nil]; movieplayer * player = [[movieplayer alloc] initwithcallback: ^ (nsstring * Title) {[moviequeue removeobject: title];}]; for (nsstring * Title in [nsarray arraywitharray: moviequeue]) {[Player playmovie: title] ;}; note that the code block uses the local variable moviequeue, which is part of the code block status. When a code block is called, a movie is removed from the array moviequeue, although the array is out of the scope of the code block. After all the movies are played, moviequeue will be an empty array. The following are some important things that need to be mentioned: 1. The moviequeue variable is an array pointer, and we cannot modify its direction. We modify the content it points to, so we do not need to use _ block. 2. In order to iterate the moviequeue array, we need to create a copy of The moviequeue array. Otherwise, if we use the moviequeue array directly, colleagues in the iteration array will still remove its elements, this causes an exception. 3. If no code block is used, we can declare a protocol, write a proxy class, and register this proxy as the callback. Obviously, this example is more convenient to use Inline code blocks. 4. You can add new functions to the movieplayer class without changing the movieplayer class. For example, another developer can share a movie on Twitter or comment on it after watching it.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.