Programming with objective-c

About Objective-C


Objective-c 是在为ios和mac的设备写app的主要语言,它是c的超集并且提供面向对象的能力和dynamic run  time;

objective-c继承了c的语法和原始的类型以及c的流程控制,并在此基础上添加了定义类和方法的语法结构。他也为对象图形管理和对象排版添加了一些语言学的支持,同时也提供了动态类型和binding,把这些责任推迟到运行时

At a Glance

这篇文档介绍了objective-c语言并且他的使用的一些扩展的例子,从这篇文档将学到怎样创建一个描述你自己类的对象,怎样与cocoa与cocoa touch提供的框架类协同工作。尽管框架类并不属于objective-c语言,但许多语言级的特征取决于这些类所提供的行为

An App Is Built from a Network of Objects

当你在构建app时,你的大部分时间都在和object打交道,这些对象都是objective-c类的实例,一些对象是由cocoa touch或cocoa提供,一些对象是由你自己写的

如果你自己写一个类,你就要提供这个类详细的公共接口,接口包括封装相关数据的属性。通常还列出一些方法。方法的声明表明了一个对象所能接受的信息,包含了方法在调用时所需要的参数。在类的实现中,包含了在接口中所声明的方法的实现代码

Categories Extend Existing Classes

当需要为一个存在的类提供一些附加的功能时,通常不需要重新定义这个类,而是用category为存在的类添加一些行为。可以用category为任何类添加方法,包括不是你自己实现的源代码,例如NSString

如果你有一个类的源代码,可以用类的扩展去添加一些新的属性,或者更改存在的属性


Protocols Define Messaging Contracts

在objective-c app中,对象通常要相互之间发送信息,这些信息通常是在interface中都有明确的声明。但是有时候需要定义一些相关的方法,但是这些方法并没有特别指定是属于哪一个类的。在objective-c中,用protocol支持定义这样一个方法的集合,这些方法我们通常把他叫做delegate,任何类都能采纳一个协议,但采纳这个协议的类必须实现这个协议中所有required方法。

Values and Collections Are Often Represented as Objective-C Objects


通常在objective-c中用cocoa touch类去代表某个值,NSString类代替c中的string,NSNumber类是不同类型number的集合,NSValue类代替C结构体,当然也能够用原始的数据类型如int,float等

Blocks Simplify Common Tasks


Blocks是一个被c,objective-c和c++所引进的代表一个工作单元的语言特征,用blocks通常简化常见的任务如:collection enumeration,sorting和test,

Error Objects Are Used for Runtime Problems


尽管objective-c包含了异常处理的语法

Objective-C Code Follows Established Conventions

在写objective-c代码的时候,你应该遵守一些约定俗成的规则。比如方法的名字首字母要小写

一 Defining Classes

当你为os x或者ios写app时,大部分的时间将花费在object上,objects用相关的行为封装了数据。

一个app是一个由对象组成的大的生态系统,在这个对象系统中各个对象相互联系相互作用共同去解决一个特定的问题,例如显示一个可视化的接口,对用户的输入做出响应或者存储信息,cocoa或cocoa touch库中包含了许多已经存在的对象。

在cocoa或cocoa touch库中一些对象是立即可用的,例如基本的数据类型strings和numbers等,例如基本的用户界面元素buttons或table views。

当然也可以自己定义一个对象,自己定义它的行为。app开发的过程就是用自己定义的对象和cocoa 所提供的对象协同合作去完成某个功能

在面向对象的实例中,一个对象就是一个类的实例。自己定义一个类,首先要声明一个interface,interface中声明了属性和方法,还需要一个implementation,在那里实现在interface中所声明的方法

Classes Are Blueprints for Objects

类描述了特定类型对象的行为和属性,每一个类的实例都share相同的行为和属性

Mutability Determines Whether a Represented Value Can Be Changed

一些类所定义的对象是immutable,这意味着当对象被创建的时候,它内部的contents必须被设定,随后不能被别的对象所改变

在objective-c中,NSString和NSNumber都是immutable的

但都提供了mutable的版本,


Classes Inherit from Other Classes

当一个类从父类继承的时候,这个类继承了父类所定义的所有的方法和属性,这个类也能定义他自己的行为和属性,或者覆盖父类的行为

The Root Class Provides Base Functionality

在objective-c中,一些方法或属性是所有的对象所共有的,在objective-c中,定义了NSObject类是所有类的父类,

The Interface for a Class Defines Expected Interactions

Use Pointers to Keep Track of Objects


-(void)myMethod{
int someInteger = 42;
}
someInteger是在方法myMethod里的一个当地变量,他仅在方法内部有效,当一个local variable 远去时,value也会消失

而对于objective-c语言,被分配轻微的不同,objective-c在方法中比标量值的生命周期更长,一个对象的内存被分配和再分配都是自动进行的

用指针来追踪内存中的对象

在方法中的指针与local variable一样,生命周期在方法内部,当脱离方法之后,指针变量消失,但指针所指向的对象还在内存中

Objects Can Send Messages to Themselves


当在写一个方法的实现时,已经获取了一个重要的隐藏值self,self是一种参考到接受到这条信息的对象的一种方式,self就是一个指针,能够调用当前对象的一个方法。

Objects Can Call Methods Implemented by Their Superclasses

在objective-c中还有另外一个关键字super.,发送信息给super,调用父类所实现的方法。通常在覆盖一个方法中调用super.

Objects Are Created Dynamically

内存为对象动态的分配空间,创建对象的第一步就是要确保有足够的内存去分配,不仅要为对象所属类定义的property分配内存,也要为类的父类中定义的property分配内存


root class提供了class method:alloc来handle内存的分配

alloc的返回值是ID,ID是一个指针,这个指针指向一种未知类型的对象

alloc方法有一个重要的任务,就是清除为对象的属性所分配的内存并设定他们为0.这样做避免了内存中包含垃圾数据,不能够完全初始化对象

通常alloc与init同时使用,init确保对象的属性值在创建的时候由一个合适的值,

init的返回值也是id

Initializer Methods Can Take Arguments

Class Factory Methods Are an Alternative to Allocation and Initialization

类也定义了自己的构造方法,在类的构造方法和alloc,init之间可以选择任何一个。

Use new to Create an Object If No Arguments Are Needed for Initialization

可以用class的new方法去创建一个class的实例,new方法是有NSObject所提供的,等效于调用alloc和init没有参数

Literals Offer a Concise Object-Creation Syntax

还可以用简洁平实的方法来创建实例

如NSString *someString = @"I an a string";  创建了一个NSString类的实例

上面的创建等价于:

NSString *someString =[ [NSString alloc] initWithCString:"hello world" encoding:NSUTF8StringEncoding];

Objective-C Is a Dynamic Language


用指针去追踪内存中的对象

Determining Equality of Objects

==

isEqual

compare

Working with nil


在声明标量值是总是初始化它如:

BOOL success = NO;

但是对指向对象的指针,没有必要让一个指针指向nil,在默认情况下,指针总是指向nil,在objective-c中发送一个信息给nil是安全的。如果发送一个信息给nil将不会做任何事情

检查一个对象是否是nil

if(someObject !=nil){


}

if(someObject){

}

如果someObject是nil,他就没有地址,someObject就为假

二 Encapsulating Data

对象通常通过属性来封装数据

Properties Encapsulate an Object’s Values

大部分数据为了去执行他们的任务而需要去追踪信息

Declare Public Properties for Exposed Data

一个属性有两个获取方法:setter和getter方法

getter方法的名字与property的名字一样

setter方法的名字在property的名字前加set

也可在属性声明时指定获取方法

@property(strong,getter = isFinished) BOOL finished

Dot Syntax Is a Concise Alternative to Accessor Method Calls

Most Properties Are Backed by Instance Variables

在默认情况下,实例变量的默认属性是readwrite property,自动被编译器人工合成

一个对象的实例变量在对象的生命周期内能够存在和保持它的值。当对象第一次被创建时,内存就会为实例变量分配内存,在重新分配对象时就会释放这些实例变量

合成实例的名字与属性名字一样,但在属性名字前多了_;

You Can Customize Synthesized Instance Variable Names
如果你不想用默认的instance variable,可以指定一个instance variable:

@synthesize propertyName = instanceVariableName;

You Can Define Instance Variables without Properties

Access Instance Variables Directly from Initializer Methods

setter方法有附加的边际效应,可能触发KVC notification,或者执行进一步的任务

在初始化方法中总是直接获取实例变量,因为在属性被设置的时候,对象还没有完全的初始化

通常一个初始化方法应该像这样:

-(id)init{

self = [super init];

if(self){

_firstName = aFirstName;

_lastName = aLastName;

}

return self;

}

The Designated Initializer is the Primary Initialization Method
在一个对象中如果有一个或多个初始化方法,需要指定designated 初始化方法

You Can Implement Custom Accessor Methods

Properties Are Atomic by Default

Atomic意味着合成存储器即使在多个线程同时调用他,他所获取的值或设定的值总是成对出现的。

Manage the Object Graph through Ownership and Responsibility


objective-c中的内存被动态的分配,需要用指针去追踪这个对象

考虑两个对象之间的关系

当一个对象以某种方式依赖other对象时,这个对象就获取了other 对象的所有权,也就是说别的对象有一个strong reference指向这个 对象,在objective-c中,当有一个strong reference指向一个object时,那这个对象就会keep alive,



当XYZPerson Object重新分配的时候,如果没有别的strong refernce指向这两个NSString对象的话,他没也会重新分配,

Avoid Strong Reference Cycles


strong reference从对象与对象的关系上去管理对象,但如果在一个对象群中,如果有对象的联系形成一个循环,那这些对象将永远keep alive;

一个潜在的reference cycle存在的例子是tableView对象和他的delegate之间的关系

table view有一个strong reference 指向他的delegate对象,这个delegate对象有一个strong reference指向table view

,

如果别的对象放弃了指向table view和delegate的指针



这样就会产生memory cycle,

解决memory cycle的方法就是用一个weak reference代替strong reference


当别的对象不再指向他时


此时delegate object就会被内存释放

Use Strong and Weak Declarations to Manage Ownership

当地变量总是保持strong reference

Use Unsafe Unretained References for Some Classes

有些类不支持weak reference,包括NSTextView,NSFont,

所以需要__unsafe_unretained unsafeReference

__unsafe_unretained与weak一样,但当他所指向的对象被重新分配时,指向这个对象的指针将不会被分配为nil.这是如果有信息发送给该对象时,将会是程序崩溃

Copy Properties Maintain Their Own Copies

三 Customizing Existing Classes

对象应该有清晰并且定义好的task, 比如modeling special information,display visual content,controlling the flow of information,正如你所看到的,一个类的interface定义了这个对象与别的对象之间的交互的方式,,这些对象一起协作完成task

但有时候在某种情况下可能需要扩展存在的类,给这些类添加一些行为,例如在应用中你需要显示一个字符串,不需要每次需要显示字符串的时候都去创建一个字符串画的对象,最好的就是给让NSString 对象本身有画字符串的能力

在这种情况下,你不能添加行为给NSString对象,因为并非所有的NSString对象都需要画的能力

Objective-c允许你通过category或class extension去添加自己的方法到存在的类

Categories Add Methods to Existing Classes

你想要添加方法到存在的类,或者在你自己的应用程序中添加一些功能使得做某些事情十分的容易,最简单的方式就是用categories

声明一个category的语法:比较像标准objective-c的描述,但没有父类的继承,取而代之的是categoryName.

@interface ClassName(categoryName)


@end

一个category可以为任何类声明,即使你没有任何的关于这个类的实现代码,你在category中声明的所有方法对所有的original 类的实例都是可用的,其中也包括original class的子类,在runtime,在category中的方法与在original class中的实现的方法是没有任何区别的

一个category通常声明在一个单独的header file中,在一个单独的source code file中实现

#import "XYZPerson.h"
 
@interface XYZPerson (XYZPersonNameDisplayAdditions)
- (NSString *)lastNameFirstNameString;
@end
在上面例子中,你的category所在的header file的名字应该叫做:XYZPerson+XYZPersonNameDisplayAdditions.h

在一个category中的方法对这个类及其子类都是可用的,但是需要预先声明

category的实现可能像这样

#import "XYZPerson+XYZPersonNameDisplayAdditions.h"


@Implementation XYZPerson (XYZPersonNameDisplayAdditions)

- (NSString *)lastNameFirstNameString{

return [NSString stringWithFormat:@"%@ %@",self.lastName,self.firstName];

}

@end

一旦你为某个类添加了category方法,并且也实现了这个方法,那么在这个类及其子类中都可用这个方法,在category中的所有的方法就等价于original class中声明并实现的方法

象用category添加方法一样,也可以用category把一个存在的比较复杂的类分成多个源代码文件

category适合于去声明class method或者实例方法,但不适合于添加property,

在一个category中添加一个属性语法上是合理的,但不能在category中添加一个实例变量,也就是说编译器不能合成一个instance variable,也不能合成任何的获取方法

当然你可以在category的实现文件中自己写property的获取方法,但你不能追踪这个属性的值,除非这个属性在original class中存储

因此需要为存在的类添加property的唯一方式就是添加class extension

Avoid Category Method Name Clashes

因为category的声明是添加到一个存在的class中的,因此你要避免category中method的名字与original class的method的名字发生冲突

如果category中声明的方法的名字与original class的名字相同或者与在original class的别的category中声明的方法的名字相同,那么在run time,运行那一个实现的方法将是不可预知的

例如用到远程网络服务的一个应用,可能需要用base64 coding对一个字符串进行编码

为了避免无法确定的行为,最好的方法就是给category的方法名添加一个前缀,

Class Extensions Extend the Internal Implementation


class extension和category有点相像,具体语法:

@interface ClassName()


@end

一般放在一个custom类的实现文件中,可以隐藏这个类的一些属性

没有名字的category

class extension能够添加自己的属性和instance variable到一个类中,在类的扩展中声明了一个属性:

@interface XYZPerson()

@property NSObject *extraProperty;

@end

编译器会自动综合属性的获取方法,这个属性同在original class中声明的property


Use Class Extensions to Hide Private Information

一个类的公共接口

Consider Other Alternatives for Class Customization

面向对象的最主要的目标是写一些可以重复使用的代码,那就意味着类可以重复使用,例如当你创建了一个view class时,你可能需要在多种情况下使用

一种是用继承

另外一种是用delegate object

Interact Directly with the Objective-C Runtime


app的运行需要一个运行时系统,可以直接和运行时系统进行交互


四 Working with Protocols

在面向对象编程的世界中,把在某种情况下某个对象所拥有的behavior放到一个set中,例如一个table view需要和数据源进行通信,去发现什么需要显示,也就是说data source必须对table view所发送的特定的信息进行回应
data source可能是任何类的实例,比如view controller,也可以是专用数据源类
对table view来讲,决定是否一个对象可以作为data source,看这个对象是否实现了必要的方法
objective-c允许你去定义protocols,protocols声明了在某些特定情况下想要用的方法这张描述了定义了一个正常协议的方法
解释了怎样让一个类去遵守一个协议,遵守这个协议的类必须实现protocol的方法

Protocols Define Messaging Contracts


类的interface声明了与类相关的method和property,类似的protocol声明了独立于任何类的method和property
定义一个protocol的基本的语法:
@protocol ProtocolName

@end
protocol的声明包括class method,instance method,property
为了使view尽可能的重用,关于信息的所有的决定都应该交给另外一个对象,a data source,这就意味着相同view的多个实例能够显示不同的信息,配备不同的data source


pie chart包含的基本信息包括:分割的个数,每个部分相对的大小,每个部分的标题
因此pie chart data source协议应该如下:
@protocol XYZPieChartDataSourceProtocol 
- (NSUInteger) numberOfSegments;
-(CGFloat)sizeOfSegmentAtIndex:(NSUInteger )segmentIndex;
-(NSString*)titleForSegmentAtIndex:(NSUInteger)segmentIndex;
@end
the pie chart view class interface需要一个property去追踪data source object,由于data source object可以是任何类,所以基本的property类型应该是id.对data source 对象我们仅仅知道他应该遵守data source协议
定义一个class作为data source的语法:

@interface XYZPieChartView:UIView
@property (weak) id <XYZPieChartDataSourceProtocol> dataSource;
@end

objective-c用< >来服从某个协议

指定某个属性应该服从某个协议,如果你set这个属性到没有服从协议的对象上编译器会出现一个警告
不会关系这个对象是哪一个类的实例,最关心的是对象服从协议,pie chart view能够知道他能够请求他所需要的

Protocols Can Have Optional Methods


默认情况下,在protocol里面声明的所有方法都是required的,意味着服从协议的任何类都必须实现protocol的所有方法
在protocol里面也可以指定optional的方法,这些方法服从protocol的class可以实现也可以不实现
在这个case中,可以指定title方法是optional的,如果数据源没有实现这个方法,那么pie chart view将不会有标题
@protocol XYZPieChartDataSourceProtocol 
- (NSUInteger) numberOfSegments;
-(CGFloat)sizeOfSegmentAtIndex:(NSUInteger )segmentIndex;
@optional
-(NSString*)titleForSegmentAtIndex:(NSUInteger)segmentIndex;
@end
Check that Optional Methods Are Implemented at Runtime
如果一个方法在protocol中标记为optional,在调用他之前你必须检查对象是否实现了该optional method
例如:
NSString *thisSegmentTitle;
if([self.dataSource respondsToSelector:@selector(titleForSegmentAtIndex:)]){
thisSegmentTitle =  [self.dataSource titleForSegmentAtIndex:index];
}
Local  object  variables are automatically initialized to  nil .

Protocols Inherit from Other Protocols

可以设定协议遵从另外一个协议
最好是你的协议遵从NSObject协议
语法:
@protocol customProtocol <NSObject>

@end
customProtocol 采纳 <NSObject>协议

Conforming to Protocols


一个class采纳一个协议的语法:
@interface MyClass : NSObject <MyProtocol>

@end

这表明MyClass不仅仅能够对在interface中的方法进行响应,也能够对MyProtocol中的方法进行响应,MyClass必须对协议中required的方法进行实现

没有必要在class 的interface中重新声明协议中声明的方法。

如果一个类需要采纳多个协议:

@interface MyClass : NSObject <MyProtocol, MyProtocol1,MyProtoocol2>


@end

Cocoa and Cocoa Touch Define a Large Number of Protocols

Protocols Are Used for Anonymity

id <XYZFrameworkUtility> utility = [frameworkObject anonymousUtility];

五 Values and Collections

在cocoa或cocoa touch中也有一些附加的标量类型可用,NSInteger,NSUInteger,CGFloat;

Basic C Primitive Types Are Available in Objective-C

Dot语法是对一个属性的获取方法的封装

Objective-C Defines Additional Primitive Types

Bool声明的变量可以去保存一个bool 值,YES or NO;


C Structures Can Hold Primitive Values

NSRange是一个结构体,由location和length构成

NSString *mainString =  @"this is a long string";

NSRange *subStringRange = [mainString rangeOfsubString:@"long"];

subStringRange = {10,4};index 10从0开始,index 4从1开始,

Objects Can Represent Primitive Values

Strings Are Represented by Instances of the NSString Class

Format Strings Are Used to Build Strings from Other Objects or Values

Numbers Are Represented by Instances of the NSNumber Class

Represent Other Values Using Instances of the NSValue Class

NSNumber类是NSValue类的子类

Most Collections Are Objects

Collections如NSArray,NSDictionary等,这些类是用作管理对象的,所以放在这些类中的item必须是class的实例

如果需要向collection中添加一个标量值,需要把这个标量值先转化为NSValue或NSNumber对象

在collection中的每一个对象都有一个强的指针去追踪他也就是说添加到collection中的任何对象的生命周期与collection的生命周期一样长

Arrays Are Ordered Collections

NSArray实例中的所有对象都是有序的

Creating Arrays
+ arrayWithObject:(id)anObject;

+ arrayWithObjects:(id)firstObject, ... ;

+ initWithObjects:(id)firstObject,... ;

arrayWithObjects和initWithObjects都带了一个nil-terminated,也就是说必须包括nil作为最后一个value,nil是一个终结符,告诉array对象到此为止

NSArray *someArray =
  [NSArray arrayWithObjects:someObject, someString, someNumber, someValue, nil];
someObject的index是0;最后一个对象是someValue,index是3,


Literal Syntax
NSArray *someArray = @[firstObject, secondObject, thirdObject];
如果secondObject=nil,在运行时会出现异常,如果确实需要使得array中的某一个元素值为nil,应用NSNull代替

Querying Array Objects
创建了一个数组之后可以查询数组。例如查询数组中是否有某个item查询数组中元素的个数

NSUInteger numberOfItems = [someArray count];

if([someArray containsObject:something]){


}

也能查询数组中某个元素

if([someArray count] >0){

NSLog(@"First Item is %@",[someArray objectAtIndex:0]);

}

Subscripting

if([someArray count] >0){

NSLog(@"First Item is %@",someArray[0]);

}

Sorting Array Objects
NSArray *unsortedStrings = @[@"gammaString", @"alphaString", @"betaString"];
    NSArray *sortedStrings =
                 [unsortedStrings sortedArrayUsingSelector:@selector(compare:)];

Mutability

Sets Are Unordered Collections

set与array类似,但是必须是完全不同的对象组成的

NSSet也是不可更改的,所以必须在内容必须在创建的时候被指明

NSSet *simpleSet = [NSSet setWithObjects:@"Hello world",@42,aValue,anObject,nil];

initWithObjects和setWithObjects都以nil结束

set中存储的元素不能重复

Dictionaries Collect Key-Value Pairs


Creating Dictionaries
NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys:
                   someObject, @"anObject",
             @"Hello, World!", @"helloString",
                          @42, @"magicNumber",
                    someValue, @"aValue",
                             nil];

 dictionaryWithObjectsAndKeys: and initWithObjectsAndKeys:先指明对象,在指明key,最终以nil标志结束

Literal Syntax
 NSDictionary *dictionary = @{
                  @"anObject" : someObject,
               @"helloString" : @"Hello, World!",
               @"magicNumber" : @42,
                    @"aValue" : someValue
    };

用这种方法创建时key在value前

Querying Dictionaries
创建了一个dictionary之后,可以用被给的key来得到对象

NSNumber *storedNumber = [dictionary objectForKey:@"magicNumber"];
如果key没有发现,objectForKey将返回nil

也可以这样访问

 NSNumber *storedNumber = dictionary[@"magicNumber"];
 
Mutability
[dictionary setObject:@"another string" forKey:@"secondString"];
[dictionary removeObjectForKey:@"anObject"];

Represent nil with NSNull


Use Collections to Persist Your Object Graph

NSArray和NSDictionary对象使得到硬盘上写内容变得容易

NSURL *fileURL = 

NSArray *array = @[@"first",@"second",@"three"];


Bool success = [array writeToURL:fileURL atomically:YES];

if(!success){

NSLog(@"file write failure");

}

Use the Most Efficient Collection Enumeration Techniques

许多collection采纳了NSFastEnumeration protocol,包括NSArray,NSDictionary,NSSet
  for (id eachObject in array) {
        NSLog(@"Object: %@", eachObject);
    }
   for (NSString *eachKey in dictionary) {
        id object = dictionary[eachKey];
        NSLog(@"Object: %@ for key: %@", object, eachKey);
    }
快速枚举不允许在collection中添加或删除对象,否则将产生一个运行时异常

Most Collections Also Support Enumerator Objects

It’s also possible to enumerate many Cocoa and Cocoa Touch collections by using an  NSEnumerator  object.
 for (id eachObject in [array reverseObjectEnumerator]) {
        ...
    }
id eachObject;
while(eachObject = [enumerator nextObject]){

}

 用enumerator来输出对象

六 Working with Blocks

Objective-c 定义了一个可以把数据和行为结合起来的对象,有时候仅仅代表了一个单独的任务,而不是方法的collection

blocks是objective-c的对象,因此就能够添加到collection如NSArray和NSDictionary中

这一小节介绍了block的语法以及怎样用block去处理一个简单的任务

Block Syntax

声明一个变量去追踪block,

void (^simpleBlock)(void);

assignment

simpleBlock = ^ {


}

declare and assignment

void (^simpleBlock)(void) = ^{


}

用已经赋值和声明的block变量来激活一个block

simpleBlock ();

用没有赋值的block变量激活block将会使程序崩溃

Blocks Take Arguments and Return Values

block也可以带参数和返回值

block变量的声明

double (^multipleTwoValues)(double,double);

block的定义:

^ (double firstValue,double secondValue) {


}

根据喜好也能自己指定返回类型

^ double (double firstValue,double secondValue){


}

一旦声明和定义了一个block就可以像函数一样用它

double (^MultipleTwoValue)(double ,double) = ^(double firstValue,double secondValue) {

return firstValue * secondValue;

}

double result = MultipleTwoValue(2.0,3.0);

在block内可以read block外面定义的变量

block不能改变original variable的值

Use __block Variables to Share Storage
如果你需要在block内部改变在block外部声明的变量的值,可以用__block key words

    __block int anInteger = 42;
 
    void (^testBlock)(void) = ^{
        NSLog(@"Integer is: %i", anInteger);
    };
 
    anInteger = 84;
 
    testBlock();
result is : Integer is 84;

You Can Pass Blocks as Arguments to Methods or Functions

通常需要把block当成参数传给函数或方法,例如可能需要用Grand Centrol disPatch去在后台激活一个block,或者让block通过不断的激活去完成一个任务,例如collection的枚举

当某个任务完成时,让block去执行内部的代码,

在一个方法中把block当作一个参数

- (IBAction)fetchRemoteInformation:(id)sender {
    [self showProgressIndicator];
 
    XYZWebTask *task = ...
 
    [task beginTaskWithCallbackBlock:^{
        [self hideProgressIndicator];
    }];
}
在方法内部激活block
- (void)beginTaskWithCallbackBlock:(void (^)(void))callbackBlock {
    ...
    callbackBlock();
}

A Block Should Always Be the Last Argument to a Method

Use Type Definitions to Simplify Block Syntax

为simpleBlock定义一个没有返回值没有参数的类型

typedef void void(^XYZSimpleBlock)(void)

Objects Use Properties to Keep Track of Blocks

@interface XYZObject : NSObject
@property (copy) void (^blockProperty)(void);
@end
   self.blockProperty = ^{
        ...
    };
    self.blockProperty();
typedef void (^XYZSimpleBlock)(void);
 
@interface XYZObject : NSObject
@property (copy) XYZSimpleBlock blockProperty;
@end

Avoid Strong Reference Cycles when Capturing self

当你在block中用到self是,要考虑避免memory cycle

在block内部被用到的所有的对象都有一个strong reference指向他

- (void)configureBlock {
    XYZBlockKeeper * __weak weakSelf = self;
    self.block = ^{
        [weakSelf doSomething];   // capture the weak reference
                                  // to avoid the reference cycle
    }
}

在block内部用到self时,用weakSelf代替self,__weak关键字紧邻变量名

XYZBlockKeeper * __weak weakSelf = self;
 

Blocks Can Simplify Enumeration

  [array enumerateObjectsUsingBlock:^ (id obj, NSUInteger idx, BOOL *stop) {
        if (...) {
            *stop = YES;
        }
    }];
前两个对象一个是对象,另外一个是对象的index,
 NSDictionary *dictionary = ...
    [dictionary enumerateKeysAndObjectsUsingBlock:^ (id key, id obj, BOOL *stop) {
        NSLog(@"key: %@, value: %@", key, obj);
    }];

This makes it more convenient to enumerate each key-value pair than when using a traditional loop, for example.

Blocks Can Simplify Concurrent Tasks

ios提供了多种技术去并发执行,如 Operation queues and Grand Central Dispatch,

Use Block Operations with Operation Queues

An operation queue is the Cocoa and Cocoa Touch approach to task scheduling
通过创建一个NSOperation实例,然后封装task和task所需的数据,然后再把NSOperation实例添加到NSOperation queue中

也可以创建NSOperation的子类去实现更加复杂的任务,也可以用NSBlockOperation去用block创建一个NSOperation 实例

NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
    ...
}];
可以手动的执行一个NSBlockOperation,也可以把operation放在operation queue中去自动执行

// schedule task on main queue:
NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
[mainQueue addOperation:operation];
 
// schedule task on background queue:
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperation:operation];
如果用operation queue,可以配置他的优先权,operations之间的从属关系,例如可以指定一个operation在其他的operation都执行完之后再执行

Schedule Blocks on Dispatch Queues with Grand Central Dispatch

如果你需要预先安排block的执行顺序,可以直接用dispatch queue,dispatch queue是由Grand Central dispatch来控制的

You can either create your own dispatch queue or use one of the queues provided automatically by GCD

七 Dealing with Errors

每一个app都会遇见错误,而这些错误不是你能掌控的,比如没有网络

所有的error都通过NSError class的实例表现出来

Use NSError for Most Errors

error是不可避免的,你唯一要做的就是当发生错误的时候提供给用户最好的体验

比如说从远程服务器上downloading信息,你会发现你至少要实现一个发生错误的方法

例如:NSURLConnectionDelegate protocol 包含了connection:didFailWithError: method

-(void) connection:(NSURLConnection*)connection didFailWithError:(NSError*)error;

如果error发生,the delegate method将提供给你一个NSError对象去描述这个问题

NSError对象包含一个数字代码,domain,description

Some Methods Pass Errors by Reference

- (BOOL)writeToURL:(NSURL *)aURL
           options:(NSDataWritingOptions)mask
             error:(NSError **)errorPtr;

NSError *anyError;
    BOOL success = [receivedData writeToURL:someLocalFileURL
                                    options:0
                                      error:&anyError];
    if (!success) {
        NSLog(@"Write failed with error: %@", anyError);
        // present error to user
    }

Recover if Possible or Display the Error to the User

最好的用户体验就是从错误中恢复出来,例如你在获取远程网页响应的时候,你可能会试着从不同服务器上去获取网页的响应

如果不可能从错误中恢复出来,你应该alert users,如果你用cocoa touch,你需要用UIAlertView去显示error

Generating Your Own Errors

在创建你自己的NSError对象之前,需要去创建你自己的error domain

com.companyName.appOrFrameworkName.ErrorDomain
同时也需要为你的domain中的每一个error指定一个唯一的代码

 NSString *domain = @"com.MyCompany.MyApplication.ErrorDomain";
    NSString *desc = NSLocalizedString(@"Unable to…", @"");
    NSDictionary *userInfo = @{ NSLocalizedDescriptionKey : desc };
 
    NSError *error = [NSError errorWithDomain:domain
                                         code:-101
                                     userInfo:userInfo];

Exceptions Are Used for Programmer Errors

  @try {
        // do something that might throw an exception
    }
    @catch (NSException *exception) {
        // deal with the exception
    }
    @finally {
        // optional block of clean-up code
        // executed whether or not an exception occurred
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值