AboutObjective-C
Objective-c是在为ios和mac的设备写app的主要语言,它是c的超集并且提供面向对象的能力和dynamicrun time;objective-c继承了c的语法和原始的类型以及c的流程控制,并在此基础上添加了定义类和方法的语法结构。他也为对象图形管理和对象排版添加了一些语言学的支持,同时也提供了动态类型和binding,把这些责任推迟到运行时
AtaGlance
这篇文档介绍了objective-c语言并且他的使用的一些扩展的例子,从这篇文档将学到怎样创建一个描述你自己类的对象,怎样与cocoa与cocoatouch提供的框架类协同工作。尽管框架类并不属于objective-c语言,但许多语言级的特征取决于这些类所提供的行为
AnAppIsBuiltfromaNetworkofObjects
当你在构建app时,你的大部分时间都在和object打交道,这些对象都是objective-c类的实例,一些对象是由cocoatouch或cocoa提供,一些对象是由你自己写的如果你自己写一个类,你就要提供这个类详细的公共接口,接口包括封装相关数据的属性。通常还列出一些方法。方法的声明表明了一个对象所能接受的信息,包含了方法在调用时所需要的参数。在类的实现中,包含了在接口中所声明的方法的实现代码
CategoriesExtendExistingClasses
当需要为一个存在的类提供一些附加的功能时,通常不需要重新定义这个类,而是用category为存在的类添加一些行为。可以用category为任何类添加方法,包括不是你自己实现的源代码,例如NSString如果你有一个类的源代码,可以用类的扩展去添加一些新的属性,或者更改存在的属性
ProtocolsDefineMessagingContracts
在objective-capp中,对象通常要相互之间发送信息,这些信息通常是在interface中都有明确的声明。但是有时候需要定义一些相关的方法,但是这些方法并没有特别指定是属于哪一个类的。在objective-c中,用protocol支持定义这样一个方法的集合,这些方法我们通常把他叫做delegate,任何类都能采纳一个协议,但采纳这个协议的类必须实现这个协议中所有required方法。
ValuesandCollectionsAreOftenRepresentedasObjective-CObjects
通常在objective-c中用cocoatouch类去代表某个值,NSString类代替c中的string,NSNumber类是不同类型number的集合,NSValue类代替C结构体,当然也能够用原始的数据类型如int,float等
BlocksSimplifyCommonTasks
Blocks是一个被c,objective-c和c++所引进的代表一个工作单元的语言特征,用blocks通常简化常见的任务如:collectionenumeration,sorting和test,
ErrorObjectsAreUsedforRuntimeProblems
尽管objective-c包含了异常处理的语法
Objective-CCodeFollowsEstablishedConventions
在写objective-c代码的时候,你应该遵守一些约定俗成的规则。比如方法的名字首字母要小写
一DefiningClasses
当你为osx或者ios写app时,大部分的时间将花费在object上,objects用相关的行为封装了数据。一个app是一个由对象组成的大的生态系统,在这个对象系统中各个对象相互联系相互作用共同去解决一个特定的问题,例如显示一个可视化的接口,对用户的输入做出响应或者存储信息,cocoa或cocoatouch库中包含了许多已经存在的对象。在cocoa或cocoatouch库中一些对象是立即可用的,例如基本的数据类型strings和numbers等,例如基本的用户界面元素buttons或tableviews。当然也可以自己定义一个对象,自己定义它的行为。app开发的过程就是用自己定义的对象和cocoa所提供的对象协同合作去完成某个功能在面向对象的实例中,一个对象就是一个类的实例。自己定义一个类,首先要声明一个interface,interface中声明了属性和方法,还需要一个implementation,在那里实现在interface中所声明的方法
ClassesAreBlueprintsforObjects
类描述了特定类型对象的行为和属性,每一个类的实例都share相同的行为和属性
MutabilityDeterminesWhetheraRepresentedValueCanBeChanged
一些类所定义的对象是immutable,这意味着当对象被创建的时候,它内部的contents必须被设定,随后不能被别的对象所改变在objective-c中,NSString和NSNumber都是immutable的但都提供了mutable的版本,
ClassesInheritfromOtherClasses
当一个类从父类继承的时候,这个类继承了父类所定义的所有的方法和属性,这个类也能定义他自己的4000行为和属性,或者覆盖父类的行为
TheRootClassProvidesBaseFunctionality
在objective-c中,一些方法或属性是所有的对象所共有的,在objective-c中,定义了NSObject类是所有类的父类,TheInterfaceforaClassDefinesExpectedInteractions
UsePointerstoKeepTrackofObjects
-(void)myMethod{intsomeInteger=42;}someInteger是在方法myMethod里的一个当地变量,他仅在方法内部有效,当一个localvariable远去时,value也会消失而对于objective-c语言,被分配轻微的不同,objective-c在方法中比标量值的生命周期更长,一个对象的内存被分配和再分配都是自动进行的用指针来追踪内存中的对象在方法中的指针与localvariable一样,生命周期在方法内部,当脱离方法之后,指针变量消失,但指针所指向的对象还在内存中
ObjectsCanSendMessagestoThemselves
当在写一个方法的实现时,已经获取了一个重要的隐藏值self,self是一种参考到接受到这条信息的对象的一种方式,self就是一个指针,能够调用当前对象的一个方法。
ObjectsCanCallMethodsImplementedbyTheirSuperclasses
在objective-c中还有另外一个关键字super.,发送信息给super,调用父类所实现的方法。通常在覆盖一个方法中调用super.
ObjectsAreCreatedDynamically
内存为对象动态的分配空间,创建对象的第一步就是要确保有足够的内存去分配,不仅要为对象所属类定义的property分配内存,也要为类的父类中定义的property分配内存rootclass提供了classmethod:alloc来handle内存的分配alloc的返回值是ID,ID是一个指针,这个指针指向一种未知类型的对象alloc方法有一个重要的任务,就是清除为对象的属性所分配的内存并设定他们为0.这样做避免了内存中包含垃圾数据,不能够完全初始化对象通常alloc与init同时使用,init确保对象的属性值在创建的时候由一个合适的值,init的返回值也是id
InitializerMethodsCanTakeArguments
ClassFactoryMethodsAreanAlternativetoAllocationandInitialization
类也定义了自己的构造方法,在类的构造方法和alloc,init之间可以选择任何一个。
UsenewtoCreateanObjectIfNoArgumentsAreNeededforInitialization
可以用class的new方法去创建一个class的实例,new方法是有NSObject所提供的,等效于调用alloc和init没有参数
LiteralsOfferaConciseObject-CreationSyntax
还可以用简洁平实的方法来创建实例如NSString*someString=@"Ianastring"; 创建了一个NSString类的实例上面的创建等价于:NSString*someString=[[NSStringalloc]initWithCString:"helloworld"encoding:NSUTF8StringEncoding];
Objective-CIsaDynamicLanguage
用指针去追踪内存中的对象
DeterminingEqualityofObjects
==isEqualcompare
Workingwithnil
在声明标量值是总是初始化它如:BOOLsuccess=NO;但是对指向对象的指针,没有必要让一个指针指向nil,在默认情况下,指针总是指向nil,在objective-c中发送一个信息给nil是安全的。如果发送一个信息给nil将不会做任何事情检查一个对象是否是nilif(someObject!=nil){}或if(someObject){}如果someObject是nil,他就没有地址,someObject就为假
二EncapsulatingData
对象通常通过属性来封装数据
PropertiesEncapsulateanObject’sValues
大部分数据为了去执行他们的任务而需要去追踪信息
DeclarePublicPropertiesforExposedData
一个属性有两个获取方法:setter和getter方法getter方法的名字与property的名字一样setter方法的名字在property的名字前加set也可在属性声明时指定获取方法@property(strong,getter=isFinished)BOOLfinished
DotSyntaxIsaConciseAlternativetoAccessorMethodCalls
MostPropertiesAreBackedbyInstanceVariables
在默认情况下,实例变量的默认属性是readwriteproperty,自动被编译器人工合成一个对象的实例变量在对象的生命周期内能够存在和保持它的值。当对象第一次被创建时,内存就会为实例变量分配内存,在重新分配对象时就会释放这些实例变量合成实例的名字与属性名字一样,但在属性名字前多了_;
YouCanCustomizeSynthesizedInstanceVariableNames
如果你不想用默认的instancevariable,可以指定一个instancevariable:@synthesizepropertyName=instanceVariableName;
YouCanDefineInstanceVariableswithoutProperties
AccessInstanceVariablesDirectlyfromInitializerMethods
setter方法有附加的边际效应,可能触发KVCnotification,或者执行进一步的任务在初始化方法中总是直接获取实例变量,因为在属性被设置的时候,对象还没有完全的初始化通常一个初始化方法应该像这样:-(id)init{self= [superinit];if(self){_firstName=aFirstName;_lastName=aLastName;}returnself;}
TheDesignatedInitializeristhePrimaryInitializationMethod
在一个对象中如果有一个或多个初始化方法,需要指定designated初始化方法
YouCanImplementCustomAccessorMethods
PropertiesAreAtomicbyDefault
Atomic意味着合成存储器即使在多个线程同时调用他,他所获取的值或设定的值总是成对出现的。
ManagetheObjectGraphthroughOwnershipandResponsibility
objective-c中的内存被动态的分配,需要用指针去追踪这个对象考虑两个对象之间的关系当一个对象以某种方式依赖other对象时,这个对象就获取了other对象的所有权,也就是说别的对象有一个strongreference指向这个 对象,在objective-c中,当有一个strongreference指向一个object时,那这个对象就会keepalive,
当XYZPersonObject重新分配的时候,如果没有别的strongrefernce指向这两个NSString对象的话,他没也会重新分配,AvoidStrongReferenceCycles
strongreference从对象与对象的关系上去管理对象,但如果在一个对象群中,如果有对象的联系形成一个循环,那这些对象将永远keepalive;一个潜在的referencecycle存在的例子是tableView对象和他的delegate之间的关系tableview有一个strongreference指向他的delegate对象,这个delegate对象有一个strongreference指向tableview,如果别的对象放弃了指向tableview和delegate的指针这样就会产生memorycycle,解决memorycycle的方法就是用一个weakreference代替strongreference当别的对象不再指向他时此时delegateobject就会被内存释放UseStrongandWeakDeclarationstoManageOwnership
当地变量总是保持strongreferenceUseUnsafeUnretainedReferencesforSomeClasses
有些类不支持weakreference,包括NSTextView,NSFont,所以需要__unsafe_unretainedunsafeReference__unsafe_unretained与weak一样,但当他所指向的对象被重新分配时,指向这个对象的指针将不会被分配为nil.这是如果有信息发送给该对象时,将会是程序崩溃CopyPropertiesMaintainTheirOwnCopies
三CustomizingExistingClasses
对象应该有清晰并且定义好的task,比如modelingspecialinformation,displayvisualcontent,controllingtheflowofinformation,正如你所看到的,一个类的interface定义了这个对象与别的对象之间的交互的方式,,这些对象一起协作完成task但有时候在某种情况下可能需要扩展存在的类,给这些类添加一些行为,例如在应用中你需要显示一个字符串,不需要每次需要显示字符串的时候都去创建一个字符串画的对象,最好的就是给让NSString对象本身有画字符串的能力在这种情况下,你不能添加行为给NSString对象,因为并非所有的NSString对象都需要画的能力Objective-c允许你通过category或classextension去添加自己的方法到存在的类CategoriesAddMethodstoExistingClasses
你想要添加方法到存在的类,或者在你自己的应用程序中添加一些功能使得做某些事情十分的容易,最简单的方式就是用categories声明一个category的语法:比较像标准objective-c的描述,但没有父类的继承,取而代之的是categoryName.@interfaceClassName(categoryName)@end一个category可以为任何类声明,即使你没有任何的关于这个类的实现代码,你在category中声明的所有方法对所有的original类的实例都是可用的,其中也包括originalclass的子类,在runtime,在category中的方法与在originalclass中的实现的方法是没有任何区别的一个category通常声明在一个单独的headerfile中,在一个单独的sourcecodefile中实现#import"XYZPerson.h" |
|
@interfaceXYZPerson(XYZPersonNameDisplayAdditions) |
-(NSString*)lastNameFirstNameString; |
@end |
在上面例子中,你的category所在的headerfile的名字应该叫做:XYZPerson+XYZPersonNameDisplayAdditions.h在一个category中的方法对这个类及其子类都是可用的,但是需要预先声明category的实现可能像这样#import"XYZPerson+XYZPersonNameDisplayAdditions.h"@ImplementationXYZPerson(XYZPersonNameDisplayAdditions)-(NSString*)lastNameFirstNameString{return[NSStringstringWithFormat:@"%@%@",self.lastName,self.firstName];}@end一旦你为某个类添加了category方法,并且也实现了这个方法,那么在这个类及其子类中都可用这个方法,在category中的所有的方法就等价于originalclass中声明并实现的方法象用category添加方法一样,也可以用category把一个存在的比较复杂的类分成多个源代码文件category适合于去声明classmethod或者实例方法,但不适合于添加property,在一个category中添加一个属性语法上是合理的,但不能在category中添加一个实例变量,也就是说编译器不能合成一个instancevariable,也不能合成任何的获取方法当然你可以在category的实现文件中自己写property的获取方法,但你不能追踪这个属性的值,除非这个属性在originalclass中存储因此需要为存在的类添加property的唯一方式就是添加classextensionAvoidCategoryMethodNameClashes
因为category的声明是添加到一个存在的class中的,因此你要避免category中method的名字与originalclass的method的名字发生冲突如果category中声明的方法的名字与originalclass的名字相同或者与在originalclass的别的category中声明的方法的名字相同,那么在runtime,运行那一个实现的方法将是不可预知的例如用到远程网络服务的一个应用,可能需要用base64coding对一个字符串进行编码为了避免无法确定的行为,最好的方法就是给category的方法名添加一个前缀,ClassExtensionsExtendtheInternalImplementation
classextension和category有点相像,具体语法:@interfaceClassName()@end一般放在一个custom类的实现文件中,可以隐藏这个类的一些属性没有名字的categoryclassextension能够添加自己的属性和instancevariable到一个类中,在类的扩展中声明了一个属性:@interfaceXYZPerson()@propertyNSObject*extraProperty;@end编译器会自动综合属性的获取方法,这个属性同在originalclass中声明的propertyUseClassExtensionstoHidePrivateInformation
一个类的公共接口ConsiderOtherAlternativesforClassCustomization
面向对象的最主要的目标是写一些可以重复使用的代码,那就意味着类可以重复使用,例如当你创建了一个viewclass时,你可能需要在多种情况下使用一种是用继承另外一种是用delegateobjectInteractDirectlywiththeObjective-CRuntime
app的运行需要一个运行时系统,可以直接和运行时系统进行交互四WorkingwithProtocols
在面向对象编程的世界中,把在某种情况下某个对象所拥有的behavior放到一个set中,例如一个tableview需要和数据源进行通信,去发现什么需要显示,也就是说datasource必须对tableview所发送的特定的信息进行回应datasource可能是任何类的实例,比如viewcontroller,也可以是专用数据源类对tableview来讲,决定是否一个对象可以作为datasource,看这个对象是否实现了必要的方法objective-c允许你去定义protocols,protocols声明了在某些特定情况下想要用的方法这张描述了定义了一个正常协议的方法解释了怎样让一个类去遵守一个协议,遵守这个协议的类必须实现protocol的方法ProtocolsDefineMessagingContracts
类的interface声明了与类相关的method和property,类似的protocol声明了独立于任何类的method和property定义一个protocol的基本的语法:@protocolProtocolName@endprotocol的声明包括classmethod,instancemethod,property为了使view尽可能的重用,关于信息的所有的决定都应该交给另外一个对象,adatasource,这就意味着相同view的多个实例能够显示不同的信息,配备不同的datasourcepiechart包含的基本信息包括:分割的个数,每个部分相对的大小,每个部分的标题因此piechartdatasource协议应该如下:@protocolXYZPieChartDataSourceProtocol -(NSUInteger)numberOfSegments;-(CGFloat)sizeOfSegmentAtIndex:(NSUInteger)segmentIndex;-(NSString*)titleForSegmentAtIndex:(NSUInteger)segmentIndex;@endthepiechartviewclassinterface需要一个property去追踪datasourceobject,由于datasourceobject可以是任何类,所以基本的property类型应该是id.对datasource对象我们仅仅知道他应该遵守datasource协议定义一个class作为datasource的语法:@interfaceXYZPieChartView:UIView@property(weak)id<XYZPieChartDataSourceProtocol>dataSource;@endobjective-c用<>来服从某个协议指定某个属性应该服从某个协议,如果你set这个属性到没有服从协议的对象上编译器会出现一个警告不会关系这个对象是哪一个类的实例,最关心的是对象服从协议,piechartview能够知道他能够请求他所需要的ProtocolsCanHaveOptionalMethods
默认情况下,在protocol里面声明的所有方法都是required的,意味着服从协议的任何类都必须实现protocol的所有方法在protocol里面也可以指定optional的方法,这些方法服从protocol的class可以实现也可以不实现在这个case中,可以指定title方法是optional的,如果数据源没有实现这个方法,那么piechartview将不会有标题@protocolXYZPieChartDataSourceProtocol -(NSUInteger)numberOfSegments;-(CGFloat)sizeOfSegmentAtIndex:(NSUInteger)segmentIndex;@optional-(NSString*)titleForSegmentAtIndex:(NSUInteger)segmentIndex;@endCheckthatOptionalMethodsAreImplementedatRuntime
如果一个方法在protocol中标记为optional,在调用他之前你必须检查对象是否实现了该optionalmethod例如:NSString*thisSegmentTitle;if([self.dataSourcerespondsToSelector:@selector(titleForSegmentAtIndex:)]){thisSegmentTitle= [self.dataSourcetitleForSegmentAtIndex:index];}Local object variablesareautomaticallyinitializedto nil
.ProtocolsInheritfromOtherProtocols
可以设定协议遵从另外一个协议最好是你的协议遵从NSObject协议语法:@protocolcustomProtocol<NSObject>@endcustomProtocol采纳<NSObject>协议ConformingtoProtocols
一个class采纳一个协议的语法:@interfaceMyClass:NSObject<MyProtocol>@end这表明MyClass不仅仅能够对在interface中的方法进行响应,也能够对MyProtocol中的方法进行响应,MyClass必须对协议中required的方法进行实现没有必要在class的interface中重新声明协议中声明的方法。如果一个类需要采纳多个协议:@interfaceMyClass:NSObject<MyProtocol,MyProtocol1,MyProtoocol2>@endCocoaandCocoaTouchDefineaLargeNumberofProtocols
ProtocolsAreUsedforAnonymity
id<XYZFrameworkUtility>utility=[frameworkObjectanonymousUtility];
五ValuesandCollections
在cocoa或cocoatouch中也有一些附加的标量类型可用,NSInteger,NSUInteger,CGFloat;BasicCPrimitiveTypesAreAvailableinObjective-C
Dot语法是对一个属性的获取方法的封装Objective-CDefinesAdditionalPrimitiveTypes
Bool声明的变量可以去保存一个bool值,YESorNO;CStructuresCanHoldPrimitiveValues
NSRange是一个结构体,由location和length构成NSString*mainString= @"thisisalongstring";NSRange*subStringRange=[mainStringrangeOfsubString:@"long"];subStringRange={10,4};index10从0开始,index4从1开始,ObjectsCanRepresentPrimitiveValues
StringsAreRepresentedbyInstancesoftheNSStringClass
FormatStringsAreUsedtoBuildStringsfromOtherObjectsorValues
NumbersAreRepresentedbyInstancesoftheNSNumberClass
RepresentOtherValuesUsingInstancesoftheNSValueClass
NSNumber类是NSValue类的子类MostCollectionsAreObjects
Collections如NSArray,NSDictionary等,这些类是用作管理对象的,所以放在这些类中的item必须是class的实例如果需要向collection中添加一个标量值,需要把这个标量值先转化为NSValue或NSNumber对象在collection中的每一个对象都有一个强的指针去追踪他也就是说添加到collection中的任何对象的生命周期与collection的生命周期一样长ArraysAreOrderedCollections
NSArray实例中的所有对象都是有序的CreatingArrays
+arrayWithObject:(id)anObject;+arrayWithObjects:(id)firstObject,...;+initWithObjects:(id)firstObject,...;arrayWithObjects和initWithObjects都带了一个nil-terminated,也就是说必须包括nil作为最后一个value,nil是一个终结符,告诉array对象到此为止NSArray*someArray= |
[NSArrayarrayWithObjects:someObject,someString,someNumber,someValue,nil]; |
someObject的index是0;最后一个对象是someValue,index是3,LiteralSyntaxNSArray*someArray=@[firstObject,secondObject,thirdObject];
如果secondObject=nil,在运行时会出现异常,如果确实需要使得array中的某一个元素值为nil,应用NSNull代替QueryingArrayObjects
创建了一个数组之后可以查询数组。例如查询数组中是否有某个item查询数组中元素的个数NSUIntegernumberOfItems=[someArraycount];if([someArraycontainsObject:something]){}也能查询数组中某个元素if([someArraycount]>0){NSLog(@"FirstItemis%@",[someArrayobjectAtIndex:0]);}Subscriptingif([someArraycount]>0){NSLog(@"FirstItemis%@",someArray[0]);}SortingArrayObjects
NSArray*unsortedStrings=@[@"gammaString",@"alphaString",@"betaString"]; |
NSArray*sortedStrings= |
[unsortedStringssortedArrayUsingSelector:@selector(compare:)]; |
Mutability
SetsAreUnorderedCollections
set与array类似,但是必须是完全不同的对象组成的NSSet也是不可更改的,所以必须在内容必须在创建的时候被指明NSSet*simpleSet=[NSSetsetWithObjects:@"Helloworld",@42,aValue,anObject,nil];initWithObjects和setWithObjects都以nil结束set中存储的元素不能重复DictionariesCollectKey-ValuePairs
CreatingDictionaries
NSDictionary*dictionary=[NSDictionarydictionaryWithObjectsAndKeys: |
someObject,@"anObject", |
@"Hello,World!",@"helloString", |
@42,@"magicNumber", |
someValue,@"aValue", |
nil]; |
dictionaryWithObjectsAndKeys:
and initWithObjectsAndKeys:先指明对象,在指明key,最终以nil标志结束
LiteralSyntaxNSDictionary*dictionary=@{ |
@"anObject":someObject, |
@"helloString":@"Hello,World!", |
@"magicNumber":@42, |
@"aValue":someValue |
}; |
用这种方法创建时key在value前QueryingDictionaries
创建了一个dictionary之后,可以用被给的key来得到对象NSNumber*storedNumber=[dictionaryobjectForKey:@"magicNumber"];
如果key没有发现,objectForKey将返回nil也可以这样访问NSNumber*storedNumber=dictionary[@"magicNumber"];
Mutability
[dictionarysetObject:@"anotherstring"forKey:@"secondString"]; |
[dictionaryremoveObjectForKey:@"anObject"]; |
RepresentnilwithNSNull
UseCollectionstoPersistYourObjectGraph
NSArray和NSDictionary对象使得到硬盘上写内容变得容易NSURL*fileURL= NSArray*array=@[@"first",@"second",@"three"];Boolsuccess=[arraywriteToURL:fileURLatomically:YES];if(!success){NSLog(@"filewritefailure");}UsetheMostEfficientCollectionEnumerationTechniques
许多collection采纳了NSFastEnumerationprotocol,包括NSArray,NSDictionary,NSSetfor(ideachObjectinarray){ |
NSLog(@"Object:%@",eachObject); |
} |
for(NSString*eachKeyindictionary){ |
idobject=dictionary[eachKey]; |
NSLog(@"Object:%@forkey:%@",object,eachKey); |
} |
快速枚举不允许在collection中添加或删除对象,否则将产生一个运行时异常MostCollectionsAlsoSupportEnumeratorObjects
It’salsopossibletoenumeratemanyCocoaandCocoaTouchcollectionsbyusingan NSEnumerator
object.for(ideachObjectin[arrayreverseObjectEnumerator]){ |
... |
} |
ideachObject;while(eachObject=[enumeratornextObject]){} 用enumerator来输出对象六WorkingwithBlocks
Objective-c定义了一个可以把数据和行为结合起来的对象,有时候仅仅代表了一个单独的任务,而不是方法的collectionblocks是objective-c的对象,因此就能够添加到collection如NSArray和NSDictionary中这一小节介绍了block的语法以及怎样用block去处理一个简单的任务BlockSyntax
声明一个变量去追踪block,void(^simpleBlock)(void);assignmentsimpleBlock=^{}declareandassignmentvoid(^simpleBlock)(void)=^{}用已经赋值和声明的block变量来激活一个blocksimpleBlock();用没有赋值的block变量激活block将会使程序崩溃BlocksTakeArgumentsandReturnValues
block也可以带参数和返回值block变量的声明double(^multipleTwoValues)(double,double);block的定义:^(doublefirstValue,doublesecondValue){}根据喜好也能自己指定返回类型^double(doublefirstValue,doublesecondValue){}一旦声明和定义了一个block就可以像函数一样用它double(^MultipleTwoValue)(double,double)=^(doublefirstValue,doublesecondValue){returnfirstValue*secondValue;}doubleresult=MultipleTwoValue(2.0,3.0);在block内可以readblock外面定义的变量block不能改变originalvariable的值Use__blockVariablestoShareStorage
如果你需要在block内部改变在block外部声明的变量的值,可以用__blockkeywords__blockintanInteger=42; |
|
void(^testBlock)(void)=^{ |
NSLog(@"Integeris:%i",anInteger); |
}; |
|
anInteger=84; |
|
testBlock(); |
resultis:Integeris84;YouCanPassBlocksasArgumentstoMethodsorFunctions
通常需要把block当成参数传给函数或方法,例如可能需要用GrandCentroldisPatch去在后台激活一个block,或者让block通过不断的激活去完成一个任务,例如collection的枚举当某个任务完成时,让block去执行内部的代码,在一个方法中把block当作一个参数-(IBAction)fetchRemoteInformation:(id)sender{ |
[selfshowProgressIndicator]; |
|
XYZWebTask*task=... |
|
[taskbeginTaskWithCallbackBlock:^{ |
[selfhideProgressIndicator]; |
}]; |
} |
在方法内部激活block -(void)beginTaskWithCallbackBlock:(void(^)(void))callbackBlock{ |
... |
callbackBlock(); |
} |
ABlockShouldAlwaysBetheLastArgumenttoaMethod
UseTypeDefinitionstoSimplifyBlockSyntax
为simpleBlock定义一个没有返回值没有参数的类型typedefvoidvoid(^XYZSimpleBlock)(void)ObjectsUsePropertiestoKeepTrackofBlocks
@interfaceXYZObject:NSObject |
@property(copy)void(^blockProperty)(void); |
@end |
self.blockProperty=^{ |
... |
}; |
self.blockProperty(); |
typedefvoid(^XYZSimpleBlock)(void); |
|
@interfaceXYZObject:NSObject |
@property(copy)XYZSimpleBlockblockProperty; |
@end |
AvoidStrongReferenceCycleswhenCapturingself
当你在block中用到self是,要考虑避免memorycycle在block内部被用到的所有的对象都有一个strongreference指向他-(void)configureBlock{ |
XYZBlockKeeper*__weakweakSelf=self; |
self.block=^{ |
[weakSelfdoSomething];//capturetheweakreference |
//toavoidthereferencecycle |
} |
} |
在block内部用到self时,用weakSelf代替self,__weak关键字紧邻变量名XYZBlockKeeper*__weakweakSelf=self;
BlocksCanSimplifyEnumeration
[arrayenumerateObjectsUsingBlock:^(idobj,NSUIntegeridx,BOOL*stop){ |
if(...){ |
*stop=YES; |
} |
}]; |
前两个对象一个是对象,另外一个是对象的index,NSDictionary*dictionary=... |
[dictionaryenumerateKeysAndObjectsUsingBlock:^(idkey,idobj,BOOL*stop){ |
NSLog(@"key:%@,value:%@",key,obj); |
}]; |
Thismakesitmoreconvenienttoenumerateeachkey-valuepairthanwhenusingatraditionalloop,forexample.BlocksCanSimplifyConcurrentTasks
ios提供了多种技术去并发执行,如OperationqueuesandGrandCentralDispatch,UseBlockOperationswithOperationQueues
AnoperationqueueistheCocoaandCocoaTouchapproachtotaskscheduling通过创建一个NSOperation实例,然后封装task和task所需的数据,然后再把NSOperation实例添加到NSOperationqueue中也可以创建NSOperation的子类去实现更加复杂的任务,也可以用NSBlockOperation去用block创建一个NSOperation实例NSBlockOperation*operation=[NSBlockOperationblockOperationWithBlock:^{ |
... |
}]; |
可以手动的执行一个NSBlockOperation,也可以把operation放在operationqueue中去自动执行//scheduletaskonmainqueue: |
NSOperationQueue*mainQueue=[NSOperationQueuemainQueue]; |
[mainQueueaddOperation:operation]; |
|
//scheduletaskonbackgroundqueue: |
NSOperationQueue*queue=[[NSOperationQueuealloc]init]; |
[queueaddOperation:operation]; |
如果用operationqueue,可以配置他的优先权,operations之间的从属关系,例如可以指定一个operation在其他的operation都执行完之后再执行ScheduleBlocksonDispatchQueueswithGrandCentralDispatch
如果你需要预先安排block的执行顺序,可以直接用dispatchqueue,dispatchqueue是由GrandCentraldispatch来控制的YoucaneithercreateyourowndispatchqueueoruseoneofthequeuesprovidedautomaticallybyGCD七DealingwithErrors
每一个app都会遇见错误,而这些错误不是你能掌控的,比如没有网络所有的error都通过NSErrorclass的实例表现出来UseNSErrorforMostErrors
error是不可避免的,你唯一要做的就是当发生错误的时候提供给用户最好的体验比如说从远程服务器上downloading信息,你会发现你至少要实现一个发生错误的方法例如:NSURLConnectionDelegateprotocol包含了connection:didFailWithError:method-(void)connection:(NSURLConnection*)connectiondidFailWithError:(NSError*)error;如果error发生,thedelegatemethod将提供给你一个NSError对象去描述这个问题NSError对象包含一个数字代码,domain,descriptionSomeMethodsPassErrorsbyReference
-(BOOL)writeToURL:(NSURL*)aURL |
options:(NSDataWritingOptions)mask |
error:(NSError**)errorPtr; |
NSError*anyError; |
BOOLsuccess=[receivedDatawriteToURL:someLocalFileURL |
options:0 |
error:&anyError]; |
if(!success){ |
NSLog(@"Writefailedwitherror:%@",anyError); |
//presenterrortouser |
} |
RecoverifPossibleorDisplaytheErrortotheUser
最好的用户体验就是从错误中恢复出来,例如你在获取远程网页响应的时候,你可能会试着从不同服务器上去获取网页的响应如果不可能从错误中恢复出来,你应该alertusers,如果你用cocoatouch,你需要用UIAlertView去显示errorGeneratingYourOwnErrors
在创建你自己的NSError对象之前,需要去创建你自己的errordomaincom.companyName.appOrFrameworkName.ErrorDomain
同时也需要为你的domain中的每一个error指定一个唯一的代码NSString*domain=@"com.MyCompany.MyApplication.ErrorDomain"; |
NSString*desc=NSLocalizedString(@"Unableto…",@""); |
NSDictionary*userInfo=@{NSLocalizedDescriptionKey:desc}; |
|
NSError*error=[NSErrorerrorWithDomain:domain |
code:-101 |
userInfo:userInfo]; |
ExceptionsAreUsedforProgrammerErrors
@try{ |
//dosomethingthatmightthrowanexception |
} |
@catch(NSException*exception){ |
//dealwiththeexception |
} |
@finally{ |
//optionalblockofclean-upcode |
//executedwhetherornotanexceptionoccurred |
} |