郴州制作网站设计较好的公司,帮人代做静态网站多少钱,如何做微信网站做广告,网站建设go文章目录 一、通过委托与数据源协议进行对象间通信1.委托模式2.要点 二、将类的实现代码分散到便于管理的数个分类之中1.如何实现2.要点 三、总是为第三方类的分类名称加前缀1.为什么总是为第三方类的分类名称加前缀2.要点 三、勿在分类中声明属性1.勿在分类中声明属性的原因2.… 文章目录 一、通过委托与数据源协议进行对象间通信1.委托模式2.要点 二、将类的实现代码分散到便于管理的数个分类之中1.如何实现2.要点 三、总是为第三方类的分类名称加前缀1.为什么总是为第三方类的分类名称加前缀2.要点 三、勿在分类中声明属性1.勿在分类中声明属性的原因2.要点 四、使用“class-continuation分类”隐藏实现细节1.什么是class-continuation分类2.“class-continuation分类”的合理用法3.要点五、通过协议提供匿名对象1.什么是匿名对象2.如何使用3.要点 一、通过委托与数据源协议进行对象间通信
1.委托模式
对象之间经常需要相互 通信而通信方式有很多种。OC开发者广泛使用一种“委托模式”的编程设计模式来实现对象间的通信该模式的主旨是定义一套接口某对象若想接收另一个对象的委托则需遵从此接口以便于成为其“委托对象”而这“另一个对象”则可以给委托对象回传一些信息也可以在发生相关事件时通知委托对象。 此模式可将数据与业务逻辑解藕。比如说用户界面里有个现实一系列数据所用的视图那么此视图只应包含显示数据所需的逻辑代码而不应决定要显示和中数据以及数据之间如何交互等问题。视图对象的属性中可以包含负责数据与事件处理的对象。这两种对象分别称为“数据源”与“委托”。
protocol EOCNetworkFetcherDelegate
- (void) networkFetcher:(EOCNetworkFetcher *)fetcher didReceiveData:(NSData *)data;
- (void) networkFetcher:(EOCNetworkFetcher *)fetcher didFailWithError:(NSError *)error;
end
有了这个协议之后类就可以用一个属性来存放其委托对象了。在本例总这个类就是EOCNetworkFetcher类。此类的接口可以写成这样
interface EOCNetworkFetcher : NSObject
property (nonatomic, weak) id EOCNetworkFetcherDelegate delegate;
end
这个id类型的协议属性一定要定义成weak而非strong因为两者之间必须为“非拥有关系”。一般情况下扮演delegate的那个对象也要持有本对象直到用完本对象之后才会释放。 假如声明属性的时候用strong将本对象与委托对象之间定为“拥有关系”那么就会引入“保留环”。因此 本类中存放委托对象的这个属性要么定义为weak要么定义为unsafe_unretained。如果需要在相关对象销毁时自动清空则定义为前者若不需要自动清空则定义为后者。 某类若要遵从委托协议可以在其接口中声明也可以在“分类”中声明。如果要象外界公布此类实现了某协议那么就在接口中声明而如果这个协议是个委托协议的话那么通常只会在类的内部使用。所以说这种情况一般都是在“分类”里声明的
implementation EOCDataModel () EOCNetworkFetcherDelegate
end
implementation EOCDataModel
- (void) networkFetcher: (EOCNetworkFetcher *)fetcher didReceiveData: (NSData *) data {/* Handle data */
}
- (void) networkFetcher: (EOCNetworkFetcher *)fetcehr didFailWithError: (NSError *)error {/* Handle error */;
}
end
委托协议中的方法一般都是“可选的”因为扮演“受委托者”角色的这个对象未必关心其中的所有方法。这时我们就可以使用optional关键字来标注其大部分或全部的方法
protocol EOCNetworkFetcherDelegate
optional
- (void)networkFetcher:(EOCNetworkFetcher *)fetcher didReceiveData:(NSData *)data;
- (void)networkFetcher:(EOCNetworkFetcher *)fetcher didFailWithError: (NSError *)error;
end
然而在委托对象上调用可选方法时就必须提前使用类型信息查询方法来判断这个委托对象能否响应相关选择子。以EOCNetworkFetcher为例应该这样写
NSData *data /* data obtained from network */;
if ([_delegate respondsToSelector: selector(networkFetcher:didReceiveData:)]) {[_delegate networkFetcher:self didReceiveData:data];
}
判断委托对象是否实现了相关方法如果实现了就调用如果没有实现就不执行任何操作因为给nil发送消息将使if语句的值成为false。 也可以像下方传入发起委托的实例然后在delegete对象在实现相关方法时根据传入的实例分别执行不同的代码子
- (void)networkFetcher: (EOCNetworkFetcher *)fetcher didReceiveData: (NSData *)data {if (fetcher _myFetcherA) {/* Handle data */} else if (fetcher _myFetcherB) {/* Handle data */}
}
上面这段代码表明委托对象有两个不同的“网络数据获取器”所以他们必须根据所传的参数判断到底是哪个EOCNetworkFetcher获取到了数据。若没有此信息则委托对像在同一时间只能使用一个网络请求获取器这么做不太好。 delegate里的方法也可以用于从获取委托对象中获取信息。比方说EOCNetworkFetcher类也许想提供一种机制在获取数据的时候如果遇到了“重定向”那么将询问其委托对象是否应该发生重定向。delegate对象中的相关方法也可以写成这样
- (BOOL)networkFetcher: (EOCNetworkFetcher *)fetcher shouldFollowRedirectToURL: (NSURL *) url;
用结构体缓存委托对象是否能响应特定的选择子。实现缓存功能所用的代码可以写在delegate属性所对应的设置方法里
- (void) setDelegate:(idEOCNetworkFetcher) delegate {_delegate delegate;_delegateFlags.didReceiveData [delegate respondsToSelector: selector(networkFetcher:didReceiveData:)];_delegateFlags.didFailWithError [delegate respondsToSelector: selector(networkFetcher:didFailWithError:)];_delegateFlags.didUpdateProgressTo [delegate respondsToSelector:selector(networkFetcher:didUpdateProgressTo:)];
}
2.要点
委托模式为对象提供了一套接口使其可由此将相关事件告知其他对象。将委托对象应该支持的接口定义成协议在协议中把可能需要处理的事件定义成方法。当某对象需要从另外一个对象中获取数据时可以使用委托模式。这种情况下该模式亦称“数据源协议”若有必要可实现含有位段的结构体将委托对象是否能响应相关协议方法这一信息缓存至其中。
二、将类的实现代码分散到便于管理的数个分类之中
1.如何实现
OC中有一个分类机制但是通常是使用它来补充一个需要的类但其实还有更好的用途那就是用来规划我们的代码我们可以通过OC的“分类”机制把类代码按逻辑划入几个分区中。就像这样
#import Foundation/Foundation.hinterface EOCPerson : NSObject
property (nonatomic, copy, readonly) NSString *firstName;
property (nonatomic, copy, readonly) NSString *lastName;
property (nonatomic, strong, readonly) NSArray *friends;- (id)initWithFirstName: (NSString *)firstName andLastName:(NSString *)lastName;/ * Friendship methods * /
- (void) addFriend:(EOCPerson *)person;
- (void) removeFriend:(EOCPerson *)person;
- (BOOL) isFriendsWith:(EOCPerson *)person;/ * Work methods * /
- (void) performDaysWork;
- (void) takeVacationFromWork;/ * Play methods * /
- (void) goToTheCinema;
- (void) goToSportsGame;end
现在类的实现代码按照方法分成了好几个部分。所以说这项语言特性就叫做“分类”。本例中类的基本要素诸如属性与初始化方法等都声明在“主实现”里。可是随着分类数量增加当前这份实现文件很快就膨胀得无法管理了此时就可以把每个分类提取到各自的文件中去以EOCPerson为例可以按照其分类拆分成下列几个文件
EOCPerson Friendship(.h/.m)
EOCPerson Work(.h/.m)
EOCPerson Play(.h/.m)
//EOCPerson Friendship.h
#import EOCPerson.hinterface EOCPerson (Friendship)
- (void) addFriend:(EOCPerson *)person;
- (void) removeFriend:(EOCPerson *)person;
- (void) isFriendsWith:(EOCPerson *)person;
end//EOCPerson Friendship.m
#import EOCPerson Friendship.himplementation EOCPerson (Friendship)
- (void) addFriend: (EOCPerson *)person {/* ... */
}
- (void) removeFriend:(EOCPerson *)person {/* ... */
}
- (BOOL) isFriendsWith:(EOCPerson *)person {/* ... */
}end
并且我们之前有说过私有方法的命名通过特殊的前缀将私有方法指示出来那么我们学了分类规划之后我们还可以通过创建一个分类这个分类其中全是私有方法通过这种方法将这些私有方法都规划到一个类中当然其还是的遵循之前的命名规则。
2.要点
使用分类机制把类的实现代码划分成易于管理的小块。将应该视为“私有”的方法归入名叫Private的分类中以隐藏实现细节。 三、总是为第三方类的分类名称加前缀
1.为什么总是为第三方类的分类名称加前缀
分类机制通常用 于向无源码的既有类中新增功能。这个特性极为强大但在使用时也很 容易忽视其中可能产生的问题。这个问题在于:分类中的方法是直接添加在类里面的它们 就好比这个类中的固有方法。将分类方法加人类中这一操作是在运行期系统加载分类时完成 的。运行期系统会把分类中所实现的每个方法都加人类的方法列表中。如果类中本来就有此 方法而分类又实现了一次那么分类中的方法会覆盖原来那一份实现代码。实际上可能会 发生很多次覆盖比如某个分类中的方法覆盖了“ 主实现” 中的相关方法而另外一个分类 中的方法又覆盖了这个分类中的方法。多次覆盖的结果以最后 一个分类为准。 比方说要给NSString 添加分类并在其中提供一些辅助方法用于处理与HTTP URL 有关的字符串。你可能会把分类写成这样:
interface NSString (HTTP)//Encode a string with URL encoding
- (NSString *)urlEncodeString;//Decode a URL encoded string
- (NSString *)urlDecodedString;end
现在看起来没什么问题可是如果还有 一个分类也往NSString里添加方法那会如何呢?那个分类里可能也有个名叫urIEncodedstring的方法其代码与你所添加的大同小 异但却不能正确实现你所需的功能。那个分类的加载时机如果晚于你所写的这个分类那 么其代码就会把你的那 一份覆盖掉这样的话你在代码中调用urlEncodedstring 方法时实际执行的是那个分类里的实现代码。由于其执行结果和你预期的值不同所以自己所写 的那些代码也许就无法正常运行了。这种bug很难追查因为你可能意识不到实际执行的 urlEncodedString代码并不是自己实现的那一份。 要解决此问题**一般的做法是:以命名空间来区别各个分类的名称与其中所定义的方法。 想在Objective-C中实现命名空问功能只有 一个办法就是给相关名称都加上某个共用的 前缀。与给类名加前级时所应考虑的因素相似给分类所加的前缀也要选 得怡当才行。**一般来说这个前级应该与应用程序或程序库中其他地方所用的前缀相同。 于 是我们可以给刚才那个NSString分类加上ABC前缀:
interface NSString (ABC_HTTP)//Encode a string with URL encoding
- (NSString *)abc_urlEncodedString;//Decode a URL encoded string
- (NSString *)abc_urlDecodedString;end
2.要点
向第三方类中添加分类时总应给其名称加上你专用的前缀向第三方类中添加分类时总应给其中的方法名加上你专用的前缀
三、勿在分类中声明属性
1.勿在分类中声明属性的原因
属性是封装数据的方式。尽管从技术上说分类里也可以声明属性但这种做法还是要尽量避免。原因在于除了“class-continuation分类 ” 之 外 其 他分类都无法向类中新增实例变量因此它们无法把实现属性所需的实例变量合成出来。
所以你要是想在分类中实现属性就得自己进行设置该属性的存取方法。你就可以用dynamic或者消息转发机制来实现其存取方法但是还是不太提倡那样做的。
当然除了上述的方法可以实现设置分类中属性的存取方法之外还有关联对象也可以解决在分类中不能合成实例变量的问题。就像这样
这样做可行但是不太理想要把相似的代码写很多遍而且内存管理问题上容易出错因为我们在为属性实现存取方法时经常会忘记遵从其内存管理语义。
正确的做法应该是把所有属性定义在主接口里。类所封装的全部数据都应该定义在主接口中这是唯一能够实现实例变量的地方。并且分类机制它的目的就是用于扩展类的功能而非封装数据。
有时属性可读也可以定义在分类中我们可以用其get方法获取相关的信息但是这样的话还不如直接定义一个方法多好用处还多所以说分类还是好好使用其扩展功能吧。
2.要点
把封装数据所用的全部属性都定义在主接又里。在“class-continuation分类”之外的其他分类中可以定义存取方法但尽量不要定义属性。
四、使用“class-continuation分类”隐藏实现细节
1.什么是class-continuation分类
OC动态消息系统的工作方式决定了其不可能实现真正的私有方法或者私有实例变量。那么怎么实现私有变量和私有方法呢这就要用到特殊的“class-continuation分类”了。
“class-continuation分类”和普通的分类不同他必须定义在其所接续的那个类的实现文件里并且这个类没有名字。
interface EOCPerson ()
// Methods here
end
这样你就可以在其中定义你的私有方法和私有变量了这样有什么好处呢公共接口里本来就能定义实例变量。不过把它们定义在“class-continuation分类”或“实现块”中可以将其隐藏起来只供本类使用。这些实例变量也并非真的私有因为在运行期总可以调用某些方法绕过此限制不过从一般意义上来说他们还是私有的。此外由于没有声明在公共头文件里所以将代码作为程序库的一部分来发行时其隐藏程度更好。
2.“class-continuation分类”的合理用法
“class-continuation分类”还有一种合理用法就是将public接口中声明为“只读”的属性扩展为“可读写”以便在类的内部设置其值。
就是说你在外部.h文件中定义一个“只读”的属性然后你又在“class-continuation分类”将其的“只读”属性改为“可读写”的那么这样下来在外部看来他就是一个“只读”的属性但是你可以在其内部自定义的设置其值了他在内部来说就是“可读写”的了。
这样做很有用既能令外界无法修改对象又能在其内部按照需要管理其数据。这样封装在类中的数据就由实例本身来控制而外部代码则无法修改其值。
还有一种用法若对象所遵从的协议只应视为私有则可在“class-continuation分类”中声明名这样就不会泄漏我们所遵从的协议
#import EOCPerson.h
#import EOCSecretDelegate.hinterface EOCPerson () EOCSecretDelegate
endimplementation EOCPerson
/* ... */
end
3.要点
通过“class-continuation分类”向类中新增实例变量。如果某属性在主接口中声明为“只读”而类的内部又要用设置方法修改此属性那么就在“class-continuation分类”中将其扩展为“可读写”。把私有方法的原型声明在“class-continuation分类”里面。若想使类所遵循的协议不为人所知则可于“class-continuation分类”中声明。
五、通过协议提供匿名对象
1.什么是匿名对象
若是接口背后有多个不同的实现类而你又不想指明具体使用哪个类那么可以考虑用这个方法——因为有时候这些类可能会变有时候他们又无法容纳于标准的类继承体系中因而不能以某个公共基类来统一表示。此概念通常称为“匿名对象”。
property (nonatomic, weak) idEOCDelegete delegate;
这个delegate就是“匿名的”因为当你调用这个delegate的时候你并不知道它指的是那个类而你却又能使用它所指代类的方法这就把那个类给隐藏起来了匿名对象也是同样的原理。
因为你可能定义很多的类但是我们不能将它们都继承于同一个类并且在OC中只有id类型可以将这些类的随便一个类都返回所以我们在使用匿名对象的时候一定是返回的id类型。比如我们将所有数据库都具备的那些方法放到协议中令返回的对象遵从此协议。
2.如何使用
先定义一个协议其中包括数据库都有的方法
protocol EOCDatabaseConnection
- (void)connect;
- (void)disconnect;
- (BOOL)isConneceted;
- (NSArray *)performQuery:(NSString *)query;
end
提供一个单例接口
#import Foundation/Foundation.hprotocol EOCDatabaseConnection;interface EOCDatabaseConnection;(id)sharedInstance;
- (idEOCDatabaseConnection)connectionWithIdentifier: (NSString *)identifier;end
这样的话处理数据库连接的类名称就不会暴露了来自不同框架的那些类限制就都可以使用同一个方法来返回了而不用对每个类都写一个这种协议。
3.要点
协议可在某种程度上提供匿名类型。具体的对象类型可以淡化成遵从某协议的id类型协议里规定了对象所实现的方法。使用匿名对象来隐藏类型名称或类名 。如果具体类型不重要重要的是对象能够相应定义在协议里的特定方法那么可使用匿名对象来表示。