iOS block 总结

Block objects are a C-based language feature that you can use in your C, Objective-C, and C++ code. Blocks make it easy to define a self-contained unit of work.

One of the key advantages of blocks is their ability to use variables from outside their own lexical scope. When you define a block inside a function or method, the block acts as a traditional code block would in some ways. For example, a block can read the values of variables defined in the parent scope. Variables accessed by the block are copied to the block data structure on the heap so that the block acan access them later. When blocks are added to a dispatch queue, these values must typically be left in a read-only format. However, blocks that are executed synchronously can also use variables that have the __block keyword prepended to return data back to the parent’s calling scope.

You declare blocks inline with your code using a syntax that is similar to the syntax used for function pointers. The main difference between a block and a fuction pointer is that the block name is preceded with a caret (^) instead of an asterisk (*). Like a function pointer, you can pass arguments to a block and receive a return value from it.

int x = 123;
int y = 456;

// Block declaration and assignment
void (^aBlock)(int) = ^(int z)
{
  printf("%d %d %d\n", x, y, z);
};

// Execute the block
aBlock(789); //pritns: 123 456 789

The following is a summary of some of the key guidelines you should consider when designing your blocks:

  • For blocks that you plan to perform asynchronously using a dispatch queue, it is safe to capture scalar variables from the parent function or method and use them in the block. However, you should not try to capture large structures or other pointer-based variables that are allocated and deleted by the calling context. By the time your block is executed, the memory referenced by that pointer may be gone. Of course, it is safe to allocate memory yourself and explicitly hand off ownership of that memory to the block.
  • Dispatch queues copy blocks that are added to them, and they release blocks when they finish executing. In other words, you do not need to explicitly copy blocks before adding them to a queue.
  • Although queues are more efficient than raw threads at executing small tasks, there is still overhead to creating blocks and executing them on a queue. If a block does too little work, it may be cheaper to execute it inline than dispatch it to a queue. The way to tell if a block is doing too little work is to gather metrics for each path using the performance tools and compare them.
  • Do not cache data relative to the underlying thread and expect that data to be accessible from a different block. If tasks in the same queue need to share data, use the context pointer of the dispatch queue to store the data instead.
  • If your block creates more than a few Objective-C objects. you might want to enclose parts of your block’s code in an @autorelease block to handle the memory management for those objects. Although GCD dispatch queues have their own autorelease pools, they make no guarantees as to when those pools are drained. If your application is memory constrained, creating your own autorelease pool allows you to free up the memory for autoreleased objects at more regular intervals.

Floating Point in C --- CS:APP

All versions of C provide two different floating-point data types: float and double. On machines that support IEEE floating point, these data types correspond to single-and double-precision floating point. In addition, the machines use the round-to-even rounding mode. Unfortunately, since the C standards do not require the machine to use IEEE floating point, there are no standard methods to change the rounding mode or to get special values such as -0, +00,-00, or NaN.

Most systems provide a combination of include (‘.h’) files and procedure libraries to provide access to these features, but the details vary from one system to another. For example, the GNU compiler GCC defines program constants INFINITY and NAN when the following sequence occurs in the program file:

#define _GNU_SOURCE 1

#define <math.h>

More recent versions of C, including ISO C99, include a third floating-point data type, long double. For many machines and compilers, this data type is equivalent to the double data type. For Intel-compatible machines, however, GCC implements this data type using an 80-bit “extended precision” format, providing a much larger range and precision than does the standard 64-bit format.

When casting values between int, float, and double formats, the program changes the numeric values and the bit representations as follows (assuming a 32-bit int):

  • From int to float, the number cannot overflow, but it may be rounded.
  • From int or float to double, the exact numeric value can be preserved because double has both greater range(i.e., the range of representable values), as well as greater precision(i.e., the number of significant bits).
  • From double to float, the value can overflow to +00 or -00, since the range is smaller. Otherwise, it may be rounded, because the precision is smaller.
  • From float or double to int the value will be rounded toward zero. For examople, 1.999 will be converted to 1, while -1.999 will be converted to -1. Furthermore, the value may overflow. The C standards do not specify a fixed result for this case. Intel-compatible microprocessors designate thebit pattern[10…00] as an integer indefinite value. Any conversion from floating point to integer that cannot assign a reasonable integer approximation yields this value. Thus, the expression (int) +1e10 yields -21483648, generating a negative value from a positive one.

signed vs. unsigned in C ---CS:APP

Some of the peculiar behavior arises due to C’s handling of expressions containing combinations of signed and unsigned quantities. When an operation is performed where one operand is signed and the other is unsigned, C implicitly casts the signed argument to unsigned and performs the operations assuming the numbers are nonnegative. As we will see, this convention makes little difference for standard arithmetic operations, but it leads to nonintuitive results for relational operators such as < and >.

Consider the comparison -1 < 0U. Since the second operand is unsigned, the first one is implicitly cast to unsigned, and hence the expression is unsigned, the first one is implicitly cast to unsigned, and hence the expression is equivalent to the comparison 4294967295U < 0U, which of course is false.

Pointers and Arrays --- the C programming language

In ANSI C, generic pointer is void *;

The unary operator & gives the address of an object, so the statement p = &c; assigns the address of c to the variable p, and p is said to “point to” c. The & operator only applies to objects in memory: variables and arrays element. It cannot be applied to expressions, constants, or register variables.

The unary operator * is the indirection or dereferencing operator; when applied to a pointer, it accesses the object the pointer points to.

int x = 1, y = 2, z[10];

int *ip; /* ip is a pointer to int */

ip = &x; /*ip now points to x */

y = *ip; /*y is now 1 */

*ip = 0; /* x is now 0*/

ip = &z[0]; /* ip now points to z[0] */

You should also not the implication that a pointer is constrained to point to a particular kind of object: every pointer points to a specific data type. (There is one exception: a “pointer to void” is used to hold any type of pointer but cannot be dereferenced iteself.)

If ip points to the integer x, then *ip can occur in any context where x could, so

*ip = *ip + 10;

increments *ip by 10.

The unary operators * and & bind more tightly than arithmetic operators, so the assignment

y = *ip + 1

takes whatever ip points at, adds 1, and assigns the result to y , while

*ip += 1

incrments what ip points to, as do

++*ip

and (*ip)++

The parentheses are necessary in this last example; without them, the expression would increment ip instead of what it points to , beacuse unary operators like * and ++ associate right to left.

Finally, since pointers are variables, they can be used without dereferencing. For example, if iq is another pointer to int, iq = ip

copies the contents of ip into iq, thus making iq point to whatever ip point to whatever ip pointed to.

Pointers and Arrays

Any operation that can be achieved by array subscripting can also be done with pointers. The pointer version will in general be faster but, at least to the uninitiated, somewhat harder to understand.

If pa points to a particular element of an arrya, then by definition pa+1 points to the next element, pa+i points i elements after pa, and pa-i points i elements before. Thus, if pa points to a[0],

*(pa + 1)

refers to the contents of a[1], pa+i is the address of a[i], and *(pa+i) is the contents of a[i].

The correspondence between indexing and pointer arithmetic is very close. By definition, the value of a variable or expression of type array is the address of element zero of the array. Thus after the assignment pa = &a[0]; pa and a have identical values. Since the name of an array is a synonym for the location of the initial element, the assignment pa = &a[0] can also be written as pa = a;

In short, an array-and-index expression is equivalent to one written as a pointer and offset.

There is one difference between an array name and a pointer that must be kept in mind. A pointer is a variable, so pa=a and pa++ are legal. But array name is not a variable;constructions like a=pa and a++ are illegal.

When an array name is passed to a function, what is passed is the location of the initial element. Within the called function, this argument is a local variable, and so an array name parameter is a pointer, that is, a variable containing an address.

Address Arithmetic

Pointer arithmetic is consistent: if we had been dealing with floats, which occupy more storage than chars, and if p were a pointer to float, p++ would advance to the next float. Thus we could write another version of alloc that maintains floats instead of chars, merely by changing char to float throughout alloc and afree. All the pointer manipulations automatically take into account the size of the object pointed to.

The valid pointer operations are assignment of pointers of the same type, adding or subtracting a pointer and an integer, subtracting or comparing two pointers to members of the same array, and assigning or comparing to zero. All other pointer arithmetic is illegal. It is not legal to add two pointers, or to multiply or divide or shift or mask them, or to add float or double to them, or even, except for void *, to assign a pointer of one type to a pointer of another type without a cast.

Pointer Arrays

initialization of pointer arrays:

The syntax is similar to previous initializations:

/* month_name: return name of n-th month */

char *month_name(int n)

{

static char *name[] = {

    "Illegal month",

    "January", "February", "March",

    "April", "May", "June",

    "July", "August", "September",

    "October", "November", "December"

};

return (n < 1 || n > 12) ? name[0] : name[n];

}

Pointers vs. Multi-dimensional Arrays

Newcomers to C are sometimes confused about the difference between a two-dimensional array and an array of pointers, such as name in the example above. Given the definitions

int a[10][20];

int *b[10];

then a[3][4] and b[3][4] are both syntactically legal references to a single int. But a is a true two-dimensional array: 200 int-sized locations have been set aside, and the conventional rectangular subscript calculation 20xrow+col is used to find the element a[row][col]. For b, however, the definition only allocates 10 pointers and does not initialize them; initialization must be done explicitly, either statically or with code. Assuming that each element of b does point to a twenty-element array, then there will be 200 ints set aside, plus ten cells for the pointers. The important advantage of the pointer array is that the rows of the array may be of different lengths. That is, each element of b need not point ot a twenty-element vector; some may point to two elements, some to fifty, and some to none at all.

C initialization --- the C programming language

In the absence of explicit initialization, external and static variables are guaranteed to be initialized to zero; automatic and register variables have undefined (i.e., garbage) initial values.

Scalar variables may be initialized when they are defined, by following the name with an equals sign and an expression:

int x = 1;

char squote = '\'';

long day = 1000L * 60L * 24L; /* milliseconds/day */

For external and static variables, the initializer must be a constant expression; the initialization is done once, conceptually before the grogram begins execution.

For automatic and register variables, the initializer is not restricted to being a constant: it may be any expression involving previoously defined values, even function calls.

In effect, initializations of automatic variables are just shorthand for assignment statements. Which form to prefer is largely a matter of taste. We have generally used explicit assignments, because initializers in declarations are harder to see and further away from the point of use.

An array may be initialized by following its declaration with a list of initializers enclosed in braces and separated by commas. For example, to initialize an array days with the number of days in each month:

int days[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31,30, 31};

when the size of the array is omitted, the compiler will compute the length by counting the initializers, of which there are 12 in this case.

If there are fewer initializers for an array than the number specified, the missing elements will be zero for external, static, and automatic variables. It is an error to have too many initializers. There is no way to specify repetition of an initializer, nor to initialize an element in the middle of an array without supplying all the preceding values as well.

character arrays are a special case of initialization; a string may be used instead of the braces and commas notation:

char pattern[] = "ould";

is a shorthand for the longer but equivalent

char pattern[] = { 'o','u','l','d','\0'};

In this case, the array size is five (four characters plus the terminating ‘\0’).

Debugging--the practice of programming

With the right attitude debugging can be fun, like solving a puzzle, but whether we enjoy it or not, debugging is an art that we will practice regularly. Still, it would be nice if bugs didn’t happen, so we try to avoid them by writing code well in the first place. Well-written code has fewer bugs to begin with and those that remain are easier to find.

Once a bug has been seen, the first thing to do is to think hard about the clues it presents. How could it have come about? Is it something familiar? Was something just changed in the program? Is there something special about the input data that provoked it? A few well-chosen test cases and a few print statements in the code may be enough.

If there aren’t good clues, hard thinking is still the best first step, to be followed by systematic attempts to narrow down the location of the problem. One step is cutting down the input data to make a small input that fails; another is cutting out code to eliminate regions that can’t be related. It’s possible to insert checking code that gets turned on only after the program has executed some number of steps, again to try to localize the problem. All of these are instances of a general strategy, divide and conquer, which is as effective in debugging as it is in politics and war.

Use other aids as well. Explaining your code to someone else (even a teddy bear) is wonderfully effective. Use a debugger to get a stack trace. Use some of the commercial tools that check for memory leaks, array bounds violations, suspect code, and the like. Step through your program when it has become clear that you have the wrong mental picture of how the code works.

Know yourself, and the kinds of errors you make. Once you have found and fixed a bug, make sure that you eliminate other bugs that might be similar. Think about what happened so you can avoid making that kind of mistake again.

iOS 中文英文字符集

1
+ (NSCharacterSet *) chineseAndEngSet
{
    if (chineseNameSet == nil)
   {
      NSMutableCharacterSet *aCharacterSet = [[NSMutableCharacterSet alloc] init];

      NSRange lcEnglishRange;
      lcEnglishRange.location = (unsigned int)0x4e00;
      lcEnglishRange.length = (unsigned int)0x9fa5 - (unsigned int)0x4e00;
      [aCharacterSet addCharactersInRange:lcEnglishRange];
      [aCharacterSet addCharactersInString:@"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"];
      chineseAndEngSet = aCharacterSet;
    }
  return chineseAndEngSet;
}

中文字符的正则表达式(包含繁体中文) ^[\u4E00-\u9FA5]+$

验证中文

1
- (BOOL)validateInputWithString:(NSString *)aString
{
  NSString * const regularExpression = @"^[\u4E00-\u9FA5]+$";
  NSError *error = NULL;
  NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:regularExpression
                                                                         options:NSRegularExpressionCaseInsensitive
                                                                           error:&error];
  if (error) {
      NSLog(@"error %@", error);
  }

  NSUInteger numberOfMatches = [regex numberOfMatchesInString:aString
                                                      options:0
                                                        range:NSMakeRange(0, [aString length])];
  return numberOfMatches > 0;
}

判断双空格方法

1
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string{

//Check for double space
 return !(range.location > 0 &&
        [string length] > 0 &&
        [[NSCharacterSet whitespaceCharacterSet] characterIsMember:[string characterAtIndex:0]] &&
        [[NSCharacterSet whitespaceCharacterSet] characterIsMember:[[textField text] characterAtIndex:range.location - 1]]);

}

iOS View Controller Lifecycle

ViewDidLoad:

After instantiation and outlet-setting, viewDidLoad is called, this is an exceptionally good place to put a lot of setup code.

-(void)viewDidLoad
{

[super viewDidLoad];//always let super have a chance in lifecycle methods

//do some setup of my MVC
}

But be careful because the geometry of your view(its bounds) is not set yet!

viewWillAppear:(BOOL)animated:

Just before the view appears on screen, you get notified

(argument is just whether you are appearing instantly or over time via animation)

Your view will only get “loaded” once, but it might appear and disappear a lot. So don’t put something in this method that really wants to be in viewDidLoad. Otherwise, you might be doing something over and over unnecessarily.

Do something here if things you display are changing while your MVC is off-screen.

Summary: This method is for geometry-related initialization, lazy execution and late updating.

-(void)viewWillDisappear:(BOOL)animated

You get notified when you will disappear off screen, this is where you put “remember what’s going on” and cleanup code.

-(void)viewWillDisappear:(BOOL)animated

{

[super viewWillDisappear:animated];//call super in all the viewWill/Did... methods

//let's be nice to the user and remember the scroll position they were at...

[self saveDataToPermanentStore];//maybe do in did instead?

//but be careful not to do anything time-consuming here, or app will be sluggish

//maybe even kick off a thread to do what needs doing here

}

In ios with AutoLayout, you have to do such stuff in viewDidLayoutSubviews (also previously mentioned). viewDidLayoutSubviews is the method sent to you after constraints have been processed.Note that while viewWillAppear: will get called only as you go (back) on screen…

viewDidLayoutSubviews is called every time self.view’s bounds changes (i.e. more ofter). This makes perfect sense when you think about it.

Doing geometry-dependent stuff in viewWillAppear: is an artifact from the days when

a) there was no Autolayout and b) there was no viewDidLayoutSubviews!

NSInvocation简单用法

1
NSMutableSet *set = [NSMutableSet set];
NSString *stuff = @"Stuff";
SEL selector = @selector(addObject:);
NSMethodSignature *sig = [set methodSignatureForSelector:selector];

NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:sig];
[invocation setTarget:set];
[invocation setSelector:selector];
//将第一个参数放在index2
[invocation setArgument:&stuff atIndex:2];
[invocation invoke];

index 0 是target(self) index 1 是selector(_cmd). 需要注意的是传递的是指针(&stuff),而不是变量本身。

Invocation 很灵活,但是却很慢。

创建一个invocation比直接调用方法会慢很多。但是执行Invocation很cheap,并且Invocation是可以复用的。