起名Silverlight+WCF 实战-网络象棋最终篇之迎战视频-下篇[客户端发送与服务端中转](六)

1. 写这么些只是为了协调回想,有相关pdf文件,如需要留下邮箱。。

本篇继上一篇:Silverlight+WCF
实战-网络象棋最后篇之对战视频-上篇[客户端开启视频/注册编号/接收视频](五)

2. 在类的头文件中尽量少引入其他头文件

  • 只有确有必要,否则不要引入头文件。一般的话,应在某个类的头文件中行使向前阐明来提及其余类(使用@class),并在落实文件中引入这多少个类的头文件,这样做可以尽量降低类之间的耦合。
  • 假设要阐明某个类遵守某个协议,应该把这些协议放到分类中,或者把共商单独放在一个头文件中,然后将其引入。

 

3. 多用字面量语法,少用与之等价的法门

  上面是二种办法的对照:

// 使用字面量语法的例子
NSArray *array1 = @[@"1",,@"2"];

NSNumber *number1 = @1;

NSDictionary *dictionary1 = @{@"key":@"value"};

// 使用与之对应的方法
NSArray *array2 = [NSArray arrayWithObjects:@"1",@"2",nil];

NSNumber *number2 = [NSNumber numberWithInt:2];

NSDictionary *dictionary2 = [NSDictionary dictionaryWithWithObjectsAndKeys:@"value":@"key"];
  •  使用字面量语法来创建字符串、数值、数组、字典。与正常办法相相比,更加从简
  • 相应经过取下标操作来访问数组下标或字典中的键所对应的元素
  • 应用字面量语法创立数组或字典时,若值中有nil,则会抛出分外,因而,需确保值里面不含nil

 一:对阵录像 简单原理

4. 多用类型常量,少用#define预处理指令

概念一个常量的方法:

// 第一种:预处理指令
#define ANIMATION_DURATION 0.3

// 第二种:定义静态常量
static const NSTimeInterval kAnimationDuration = 0.3

 我们一般推荐应用第两种,这一个主意定义的常量包含类型音信,有助于代码阅读。

只顾:常量命名法是:若常量局限于“编译单元”(也就是促成公文,.m文件)之内,则在眼前加字母k;若常量在类之外可见,则一般以类名为前缀。

如我辈需要对外披露某个常量,大家得以写成上面的代码:

// Test.h
#import <Foundation/Foundation.h>

extern NSString *const TestDidChangeNotification;

@interface Test : NSObject

@end

// Test.m

#import "Test.h"

NSString *const TestDidChangeNotification = @"TestDidChangeNotification";

@implementation Test
  •  不要用预处理指令定义常量。这样定义出来的常量不含类型音讯,编译器只是会在编译前依照此执行查找和替换。尽管有人重新定义了常量值,编译器也不会有警示,这将造成应用程序中的常量值不相同
  • 在.m文件中运用 static const
    来定义“编译单元内可见常量”,无需加类名前缀,加k
  • 在头文件中采取 extern
    来声称全局常量,并在相关落实公文中定义其值,这种常量要加类名前缀。

略,内容见上篇。

5. 用枚举来代表意况、选项、状态码

  • 采取枚举来表示状态机的境况、传递给艺术的选项以及状态码等值,给这个值起个最先的名字
  • 用NS_ENUM 与 NS_OPTIONS 宏来定义枚举类型,并指明其底层数据类型。
  • 在拍卖枚举类型的switch语句中不用事先default分支,这样的话,插足新枚举之后,编译器就会指示开发者:switch语句并未处理所有枚举

 

6. 明白“属性”这一定义

  • 运用@property语法来定义对象中所封装的数额
  • 通过“特质”属性关键字来指定存储数据所需的不错语义
  • 在装置属性所对应的实例变量时,一定要遵循该属性所评释的语义。

二:迎阵视频 步骤解析:

7. 在目标内部尽量直接访问实例变量

例如,Person类有个name属性,我们在那些类的中间想获取这么些name属性的数量的时候,一种是通过
self.name,一种是 _name.

那几种的界别:

  • 向来访问实例变量的进度比较快,编译器所生成的代码会一贯访问保存对象实例变量的这块内存
  • 直白访问实例变量,不会调用其“设置方法”,那就绕过了为相关属性所定义的“内存管理语义”,比如,在ARC下直接访问一个注解为copy的性质,那么并不会拷贝该属性,只会保留新值,释放旧值
  • 假使一直访问实例变量,那么不会接触“KVO”,这样做是否会时有发生问题,取决于具体的对象行为。
  • 通过属性来访问有助于排查与之相关的失实,因为可以给“获取情势”或“设置方法”中新增“断点”,监控该属性的调用者及其访问时机。

注意点:

  • 在对象内部读取数据时,应该一向通过实例变量来读,而写入数据时,则应透过性能来写
  • 在最先化方法及dealloc方法中,总是应该直接通过实例变量来读写多少
  • 偶尔会接纳惰性开始化技术配置某份数据,这种意况下,需要经过性能来读取数据

略,内容见上篇。

8. 接头“对象等同性”这一概念

  • 若想检测对象的等同性,请提供“isEqual:”与hash方法
  • 同等的靶子必须具备同等的哈希码,可是五个哈希码相同的对象却不一定相同
  • 决不盲目标依次检测每条属性,而是遵照实际需求来指定方案

 

9. “以类族情势”隐藏实现细节

“类族”是一种很有种的情势,可以隐蔽“抽象基类”背后的贯彻细节。OC的系统框架中常见采纳此形式,比如有一个拍卖雇员的类,每个雇员都有“名字”和“薪水”这四个属性,管理者可以命令其履行平常工作,但是各样雇员的办事内容却不同,主任在引导雇员做项目时,无需关系每个人咋样成功其现实做事,仅需提示其动工就行。我们重构三个子类,把各种人成功具体做事的措施,在子类实现。

率先定义一个抽象基类:

typedef NS_ENUM(NSUInteger, EOCEmployeeType){
    EOCEmployeeTypeDeveloper,
    EOCEmployeeTypeDesigner,
    EOCEmployeeTypeFinance     
}

@interface EOCEmployee : NSObject
@property (copy, nonatomic) NSString *name;
@property (assign, nonatomic) NSInteger salary;

// 创建一个雇员对象
+(EOCEmployee*)employeeWithType:(EOCEmployeeType)type;

// 让雇员工作
- (void)doADaysWork;

@implementation EOCEmployee

+ (EOCEmployee *)employeeWithType:(EOCEmployeeType)type{
    switch (type){
          case EOCEmployeeTypeDeveloper:
                  return [EOCEmployeeTypeDeveloper new];
                  break;
          case EOCEmployeeTypeDeveloper:
                  return [EOCEmployeeTypeDesigner new];
                  break;
           case EOCEmployeeTypeDeveloper:
                  return [EOCEmployeeTypeFinance new];
                  break;
    }  
}

- (void)doADayWork{
  // 子类去实现
}

@end

 然后,每个“实体子类”都从基类继承而来,例如:

@interface EOCEmployeeDeveloper : EOCEmployee

@end

@implementation EOCEmployeeDeveloper

- (void)doADaysWork{
   [self wirteCode];
}

@end

 在本例中,基类实现了一个“类措施”,该方法依照待创立的雇员连串分配好相应的雇员类实例,这种“工厂情势”是开创类族的措施之一。

即便目标所属的类位居某个类族中,你可能以为自己创办了某个类的实例,可是事实上创造的却是其子类的实例。

OC中的NSNumber、NSArray等都是类族。

  • 类族情势可以把贯彻细节隐藏在一套简单的国有接口前边。
  • 系统框架中时时利用类族
  • 从类族的公家抽象基类中集成子类时要当心,若有开发文档,应先阅读。

三:对阵录像 具体实施

10. 在既有类中应用关联对象存放自定义数据

突发性需要在目的中存放相关音信,这时候我们日常都会从目标所属类中连续一个子类,然后改用这一个子类对象,可是有时候类的实例可能是由某种机制所创立的,而开发者不可能令这种体制创制出团结所写的子类实例。OC中有一项强大的特征可以缓解,就是“关联对象”。

基于runtime来落实,此处就不多说。

  • 可以通过“关联对象”机制来把几个目的连起来。
  • 概念关联对象时可指定内存管理语义,用以模仿定义属性时所使用的“拥有关系”与“非用有提到”
  • 除非在此外做法不可行时才应该接纳关联对象,这种做法司空见惯会引入难于查找的bug

1:怎样打开录像

 11. 理解objc_msgSend的作用

在目标上调用方法是OC中时时使用的听从。专业术语叫做:“传递信息”。信息有“名称”或“采取子”,可以接受参数,而且或许还有重临值。

C语言使用“静态绑定”,在编译期就能操纵运行时所应调用的函数。

OC中应用“动态绑定”,对象吸收到音信之后,究竟该调用哪个方法则完全于运行期决定,甚至足以在程序运行时改变。

这边就不多解释objc_msgSend的接纳,如有需要可以看runtime的使用。

objc_msgSend
函数会按照接收者和采纳子的项目来调用适当的法子,为了做到此操作,该格局需要在接收者所属的类中找到其“方法列表”,假使能找到与选取子名称相符的主意,就跳至其实现代码,倘若找不到,这就顺着继承连串向上查找,即使最终没找到,则举办“新闻转发”操作。每个类都会有一块缓存,用来缓存方法,固然稍后还向该类发送与选用子相同的音信,那么执行起来就会很快了。

  • 音信由接收者,选取子及参数构成。给某目的“发送消息”,也就相当于在该目的上“调用方法”
  • 发给某目标的一体音讯都要由“动态信息派发系统”来处理,该序列会意识到对应的模式,并施行其代码。

略,内容见上篇。

12. 精晓音信转发机制

当目标吸收到无法解读的新闻后,就会启动“消息转发”机制,程序员可经由此经过告诉对象应该什么处理未知信息。

若果在控制奥兰多看出 unrecognized selector sent to instance 0x87
就印证你曾向某个对象发送过一条其不可能解读的消息,从而启动了消息转发机制,然后以程序崩溃而终止。

音讯转发分为多少个等级:

  1. 征得接收者,所属的类,看其是否能动态增长方法,以处理当下以此“未知的选取子(unknown
    selector)”,这叫做“动态方法分析”
  2. 第二品级,涉及“完整的音讯转发机制”,如若运行期系统已经把第一阶段实施完了,那么接收者自己就无法再以动态新增方法的一手来响应包含该接纳子的信息了。此时,运行期系统会请求接收者以其余手段来拍卖与音信相关的不二法门调用。这又分为两小步:
    1. 率先,看接收者看看有没有此外对象是否处理这条信息
    2. 倘若有,则运行期系统会把新闻转给这么些目的,于是转发郭恒停止,如若没有“备用的收信人”,则启动全部的音信转发机制,运行期系统会把与消息有关的凡事细节都卷入到NSInvocation对象中,再给接收者最后一遍机会,令其想法解决如今还未处理的这条音讯。

动态方法分析:

对象在吸收不可能解读的音信后,首先将调用其所属类的下列类措施:

// 如果该类调用了一个没有实现的实例方法,会调用此方法
+ (BOOL)resolveInstanceMethod:(SEL)selector
// 如果该类调用了一个没有实现的类方法,会调用此方法
+ (BOOL)resolveClassMethod;

 该办法的参数就是老大未知的接纳子,其再次回到值为Boolean类型,表示这一个类是否能增产一个实例方法用以处理此选取子。在后续往下实施转发机制往日,我们得以行使runtime动态的增添这一个艺术。

应用这种艺术的前提是:相关办法的贯彻代码已经写好,只等着运行的时候动态插在类里面就足以了。

备用接收者:

当下接收者还有第二次机遇能处理未知的选拔子,在这一步中,运行期系统会问它:能不可能把这条信息转给其他接收者来拍卖:

// 方法参数代表未知的选择子,若当前接收者能够找到备援对象,则将其返回,如果找不到就返回nil。
- (id)forwardingTargetForSelector:(SEL)selector;

 我们可以用“组合”来模拟出“多重继承”的某些特点,在一个目的内部,可能还有其他一多样对象,该目的可经因此方法将可以处理某采纳子的相关内部对象回来,这样的话,在外面看来,好像是由该目的亲自处理的。

总体的音信转发:

假定转会已经来临这一步的话,那么唯一能做的就是启用完整的信息转发机制了,系统会缔造NSInvocation
对象,把与从不处理的那条音信有关的全部细节都打包于其中,此目的涵盖选拔子、目的(target)及参数,在触发NSInvocation对象时,“音讯派发系统”将亲自出马,把音信指派给目的对象。

此步骤会调用下列模式来转发信息:

// 该方法可以实现的很简单,只需要改变调用目标,是消息在新目标上得以调用即可,然而这样实现出来的方法与“备援接收者”方案所实现的方法等效,所以很少有人采用这么简单的实现方法,比较有用的实现方式为:在触发消息前,先以某种方式改变消息内容,比如追加另外一个参数,或是改换选择子等等。
- (void)forwardInvocation:(NSInvocation *)invocation;

 实现此方法时,若觉察某调用操作不应由本类处理,则需要调用超类的同名方法。这样的话,继承系列中的每个类都有机遇处理此调用请求,直到NSObject,假诺末了调用了NSObject类的章程,那么该方法还会连续调用“doesNotRecognizeSelector”,以抛出非凡,此分外讲明选用子最后未能博取处理。

音讯转发全流程:

起名 1

起名 2

收信人在每一步中均有空子处理消息,步骤越以后,处理音讯的代价就越大,最好能在率先步处理完,那样的话,运行期系统就足以将此格局缓存起来了,倘使这一个类的实例稍后还收受同名采取子,那么根本无需启动音信转发流程。如若想在第三步里把信息转给备援的收信人,这还不如把转发操作提前到第二步。因为第三步只是修改了调用目的,这项改动放在第二部执行会愈发简易,不然的话,还得创造并拍卖一体化的NSInvocation。

  • 若对象无法响应某个接纳子,则跻身音信转发流程。
  • 通过运行期的动态方法分析功效,我们得以在急需利用某个方法时再将其参与类中。
  • 目的能够把其不可能解读的少数采取子转交给另外对象来处理
  • 由此上述两步之后,尽管如故尚未办法处理选取子,这就开动全部的消息转发机制。

http://www.cocoachina.com/ios/20150604/12013.html 相关的例证

 

13. 用“方法调配技术”调试“黑盒方法”

最紧要就是runtime的主意交换,runtime具体可见OC类目中有关runtime的牵线。

我们在那边大概的辨析下:

类的模式列表会把选取子的称号映射到有关的方法实现直上,使得“动态音讯派发系统”可以据此找到相应调用的办法,这么些办法均以函数指针的款式来表示,这种指针叫做IMP,其原型如下:

id (*IMP)(id,SEL,…)

诸如,NSString
类可以对应lowercaseString、uppercaseString、capitalizedString等选用子。这张映射表中的每个拔取子都映射到了不同的IMP之上:

起名 3

OC运行期系统提供的几个主意都能够用来操作这张表,开发者可以向里面新选用择子,也可以改变某采用子所对应的主意实现,还足以换成两个采取子所映射到的指针,比如我们交流lowercaseString 和 uppercaseString
的措施实现,类的措施表就会变成以下这么些样子:

起名 4

在新的映射表中,我们得以见到交流了lowercaseString 和 uppercaseString
的办法实现,并且多了一个名为newSelector的采纳子,上述修改均无需编写子类,只要修改了“方法表”的布局,就会突显到程序中负有的NSString实例之上。

通过此方案,开发者可以为这些“完全不知底其现实落实”的黑盒方法扩张日志记录功用,这促进程序调试。

  • 在运行期,可以向类中新增或沟通选取子所对应的主意实现。
  • 运用另一份实现来替换原有的措施实现,这道工序叫做“方法调配”,也就是措施互换,开发者常用此技能向原有实现中添加新效用。
  • 一般的话,唯有调试程序的时候才需要在运行期修改章程实现,这种做法不宜滥用。

2:Silverlight怎么样使用Socket实行报道

14. 理解“类对象”的用意

对象类型并非在编译期就绑定好了,而是要在运行期查找。而且,还有个特其余类叫做id,它能代替任意的OC对象类型,一般景观下,应该指明音信接收者的求实项目,这样的话,假如向其发送了不可以解读的信息,那么编译器就会时有发生警告信息,而项目为id的对象则不然,编译器嘉定它亦可响应所有的音信。

“在运行期检视对象类型”,那多少个操作也称之为“类型音信查询”(内省),这么些强大而卓有效率的特色内置于Foundation框架的NSObject协议里,凡是由公共根类(common
root
class)继承而来的目标都要服从此协议。在先后中并非间接相比对象所属的类,明智的做法是调用“类型音讯查询办法”。

往日,大家看下OC对象的实质是何等?

各类OC对象实例都是指向某块内存数据的指针,所以在宣称变量时,类型前面要跟一个“*”字符,如下:

// pointerVariable可以理解成存放内存地址的变量,而NSString 自身的数据就存储于那个地址中,因此可以说,该变量”指向“NSString 实例。所有OC对象都是如此,
NSString *pointerVariable = @"Some string";

 描述OC对象所用的数据结构定义在运行期程序库的头文件里,id类型本身也定义在此间:

typedef struct objc_object{
    Class isa;
}*id;

 每个对象,结构体的第一个分子是Class类的变量。该变量定义了对象所属的类,平时称为“is
a”指针,例如,刚才的事例中颇具的目标“是一个”(is a)NSString,所以其“is
a”指针就本着NSString。Class对象也定义在运转期程序库的头文件中:

typedef stuct objc_class *Class;
struct objc_class{
    Class isa;
    Class super_class;
    const char *name;
    long version;
    long info;
    long instance_size;
    struct objc_ivar_list *ivars;
    struct objc_method_list *methodList;
    struct objc_cache *cache;
    struct objc_protocol_list *protocols;
}

 此结构体存放类的“元数据”,例如类的实例实现了多少个方法,具备多少个实例变量等音讯。此结构体的第一个变量也是isa指针,这表明Class本身亦为OC对象。结构体里还有个变量为super_class,它定义了本类的超类。类对象所属的体系(也就是isa指针所指向的连串),是其它一个类,叫做“元类”,用来表述类对象自我所具备的元数据。“类措施”就定义于这里,因为这一个措施可以明白成类对象的实例方法。每个类仅有一个“类对象”,而各类“类对象”仅有一个与之相关的“元类”。

super_class 指针确立了继续关系,而isa指针描述了实例所属的类。

  • 每个实例都有一个指向Class对象的指针,用以表明其项目,而那几个Class对象则构成了类的持续体系。
  • 比方目标类型不可以再编译期确定,那么就应该运用类型音信查询办法来弹指
  • 尽心尽力利用类型信息查询形式来规定目的类型,而不要直接相比类对象,因为某些对象可能实现了信息转发功效。

2.1:与远程建立链接:

15. 用前缀避免命名空间争辩

相应为有着的称号都加上适量的前缀,比如,你所在的公司六安Effective
Widgets,那么就足以在集体部分代码中应用EWS做前缀,固然有点代码只用于Effective
Browser的浏览器项目中,能够拔取EWB作前缀。

前缀最好是多少个字母的,因为Apple宣称其保存使用具有“两字母前缀”。

  • 采纳与您的铺面,应用程序或二者皆有提到之名称作为类名的前缀,并在有着代码中拔取这一前缀
  • 若自己所支付的程序库中用到了第三方库,则应为其中的名目加上前缀。

2.2:注册编号[此地的平整是“房间号+棋手颜色值”]

16. 提供“全能起头化方法” 

UITableViewCell,先河化该类对象时,需要指明其样式及标示符,标示符可以区分不同品类的单元格,由于这种对象的创办资金较高,所以绘制表格时可遵照标示符来复用,以升级程序效用,我们把这种可为对象提供必需信息以便其能不辱使命工作的起初化方法叫做“全能起初化方法”。

// 比如创建一个NSDate
- (id)init;
- (id)initWithString:(NSString *)string;
- (id)initWithTimeIntervalSinceNow:(NSTimeInterval)seconds;
- (id)initWIthTimeIntervalSinceRefrenceDate:(NSTimeInterval)seconds;

 第五个办法是全能伊始化方法,也就是说此外的先导化方法都要调用它,当底层数据存储机制改变时,只需修改此办法的代码。

  • 在类中提供一个全能起首化方法,并在文档里指明。其余初阶化方法均应调用此办法
  • 若全能起首化方法与超类不同,则需要复写超类中的对应措施。
  • 如果超类的起头化方法不适用子类,那么相应复写这个超类方法,并在其间抛出至极。

2.3:开新线程,等待接受对方录像

17. 实现description方法

调试程序的时候,经常需要打印并查阅对象音讯,我们得以重写该目标的description方法,如下:

起名 5

  • 心想事成description方法再次回到一个有意义的字符串,用以描述该实例
  • 若想在调节时打印出更详细的对象描述信息,则应实现debugDescription方法

2.4:将视频突显出来,需要用主线程来操作

18. 尽心尽力使用不可变对象

设计类的时候,应充裕运用属性来封装数据,尽量把对外宣布出来的习性设为只读,而且只在确有必要时才将属性对外祖父布。

  • 尽可能成立不可变的目的
  • 若某属性仅可于对象内部修改,则在.m文件中,则将其由readonly变成readwrite属性。
  • 绝不把可变的collection作为性能公开,而应提供有关办法,以此修改对象中的collection

略,以上内容见上篇。作者:历经夏日 博客:http://cyq1162.cnblogs.com/ 秋色园http://www.cyqdata.com/

19. 拔取清晰而协调的命名模式

给艺术命名时注意事项:

  • 假诺艺术的再次来到值是新创设的,那么方法名的某个词应该是重回值的类别,除非还有修饰语,如:localizedString。属性的存取方法不遵照这种命名形式。
  • 应当把代表参数类型的名词放在参数前边。
  • 只要措施要在脚下目的上举行操作,那么相应包含动词。
  • 不要使用str这种简称,使用全程。
  • Boolean属性应加is前缀。假若某艺术重回非属性的Boolean值,那么应该遵照其职能,选取has或is当前缀。
  • 将get这么些前缀留给那么些借由”输出参数“来保存重返值的形式。

总结:

  • 起名时应听从正规的OC命名规范,这样创立出来的接口更便于为开发者所掌握。
  • 措施名要言简意赅
  • 艺术名不要使用缩略后的门类名称
  • 给艺术起名时的率先要务就是承保其作风与您协调的代码或所要继承的框架相符。

 

20. 为民用方法名加前缀

一个类所做的事务一般都要比从外界看到的更多,编写类的落实代码时,通常要写一些在里面使用的章程。应该为那种方法的称谓加上一些前缀,这促进调节,因为据此很容易就能把国有艺术和个体方法分别开。

实际使用何种前缀,可遵照个人爱好来定,其中最好包含下划线和字母p,比如p_method。不要采取
_method,因为Apple公司喜欢单用一个下划线做个人方法的前缀,可能会唤起争持。

  • 给个人方法的名称加上前缀,那样可以很容易地将其同国有方法区分开
  • 不要单用一个下划线做个人方法的前缀,因为这种做法是留住苹果集团用的。

3:图片压缩与视频发送

21. 知情OC错误模型

  • 除非暴发了可使整个应用程序崩溃的严重错误时,才使用特别。
  • 在错误不严重的动静下,使用NSError

3.1:图片压缩

22. 理解NSCopying协议

  • 若想让投机所写的对象具备拷贝效用,则需要实现NSCopying协议
  • 假若自定义的对象分为可变和不可变,那么就要同时落实NSCopying和NSMutableCopying商事
  • 复制对象时需控制动用浅拷贝如故深拷贝,一般处境下实施浅拷贝

起名 6起名 7

23. 透过委托与数据源协议举行对象间通信

信托情势:定义一套接口,某目标若想接受另一个目的的委托,则需要实现那个接口,以便成为其”委托对象”,而这”另一个对象“则可以给其委托对象回传一些信息,也可以在暴发相关事件时通报委托对象。

  • 委托模式为目的提供了一套接口,使其可通过将相关事件告诉其他对象
  • 将委托对象应当帮助的接口定义成协议,在钻探中把可能需要处理的事件定义成方法
  • 当某对象急需从其它一个目标中获取数据时,可以运用委托格局,比如
    tableView的dataSource
  • 假若有必不可少,可实现含有位段的结构体,将委托对象是不是能响应相关协商情势这一信息缓存下来,比如,讲明一个属性,记录是否贯彻了某个方法。

我们发送的视频,是透过定时器每秒截5张图发送过去的,每分钟将发生5张图纸,因而,图片压缩变的一定重大。

24. 将类的贯彻代码分散到便于管理的数个分类之中

  • 运用分类编制把类的兑现代码划分成易于管理的小块
  • 将相应算得”私有“的不二法门归入名叫Private的分类中,隐藏实现细节。

所以,找一种图片压缩算法,是一种起始:

25. 总是为第三方类的归类名称加前缀

比如您想给系统类添加个主意,如若你从未增长前缀的话,可能会覆盖其方法。

  • 向第三方类中添加分类时,总应给其名目加上你专用的前缀。
  • 给内部的法子名加上你专用的前缀。

一最先:是从网上down了个PngEncoder,压缩160*160的截图后,图片大小是40K,看成是4K[因为看字节时是4前面好多0,看少了一个0],兴奋的自身~~~

26. 不要再分类中宣示属性

  • 把封装数据所用的上上下下特性都定义在主接口里
  • 在分拣中,可以定义存取方法,但尽可能不要定义属性。

从而一起先在该地测试是例行的,上到网上就oh..no了。

27. 使用 “class-continuation分类”隐藏实现细节

“class-continuation分类”和一般性的分类不同,它必须定义在其所接续的不胜累的实现文件里。其紧要之处在于,那是绝无仅有可以讲明实例变量的分类,而且此分类没有一定的落实公文,其中的艺术都应有定义在类的主实现文件里。而且,和任何分类不同,它从不名字,比如:

@interface Person ()
// Methods here
@end
  •  通过“class-continuation分类”向类中新增实例变量
  • 如若某属性在主接口中表明为只读,而类的其中又要用设置情势修改此属性,那么就在“class-continuation分类”旅长其扩张为“可读写”
  • 把个人方法的原型评释在“class-continuation分类”里面
  • 若想让类所遵照的商谈不为人所知,则可于“class-continuation分类”中宣称。

40K*5,即每秒要发送200K的数额,这样就相当于把2M/200K带宽给用光了,房东这限制的512K/56K带宽,就更提不上了~~~

28. 通过协商提供匿名对象

如下边的代码:

@property (nonatomic, weak) id <WCEDelegate> delegate;

由于该属性的品种id<EOCDelegate>,所以实际任何类的靶子都能充当这一属性,对于持有此属性的类来说,delegate就是”匿名的“。

  • 协和可在某种程度上提供匿名类型。具体的目标类型可以淡化成坚守某探讨的id类型,协议里确定了目标所应实现的办法
  • 选拔匿名对象来隐藏类型名称
  • 如过具体项目不重大,首要的是目的可以响应(定义在商讨里的)特定措施,那么可利用匿名对象来表示。

末尾:仍旧用上了大伙儿普通通用的JpgEncoder,压缩160*160的截图后,图片大小是10K,每秒暴发10K*5=50K,56K带宽刚好够用了。

29. 了然引用计数

  • 引用计数机制通过方可递增递减的计数器来管理内存。对象成立好之后,其保存计数至少为1.若保存计数为正,则对象继续存活,当保留计数将为0时,对象就销毁了
  • 在对象注解期中,其余对象通过引用来保存或自由此目的,保留和自由操作分别会递增及递减保留计数

 

30. ARC注意事项

  • 在ARC之后,程序员就无需担心内存管理问题了
  • 并非手动管理
  • CoreFoundation对象不归ARC管理,开发者必须及时调用CFRetain/CFRelease.

鉴于JpgEncoder为第三方插件,由此其代码就不贴了,下面简单介绍下:

31. 在dealloc方法中只释放引用并消除监听

对象在经历其生命周期后,最后会为系统所回收,这时就要执行dealloc方法,在各类对象的生命周期内,此形式仅执行五遍,也就是当保留计数为0的时候,不过具体几时实施,则无从保证。

在dealloc方法中,一般都是移除观测行为,注销通告。

  • 在dealloc方法里,应该做的事体就是假释指向任何对象的引用,并裁撤原来订阅的”kvo“或文告主题的等布告,不要做任何事情
  • 比方目的具备文件讲述符等系统资源,那么应该特别编写一个方法来刑满释放此种资源。
  • 推行异步任务的点子不应再dealloc里,只好在常规情状执行的什么样措施也不应在dealloc里调用,因为那时目的已居于正在回收的意况了。

起名 8起名 9

32. 以弱引用制止循环引用

尽管五个目的,相互引用,那么这四个对象都不可能被释放,发生内存泄露。

unsafe_unretained 和 weak的区别:

当指向某个实例的引用移除后,unsafe_unretained属性仍指向非常已经回收的实例,而weak属性则针对nil。weak比unsafe_unretained应用可以令代码更安全。

  • 当一些引用设为weak,可避免出现循环引用
  • weak引用可以自动清空,也得以不活动清空。

1:JpgEncoder下载后内容为:FJ.Core.dll、JpgEncoder.cs六个文件。

33. 自行释放池

  • 自动释放池排布在栈中,对象吸收autorelease信息后,系统将其放入最下边的池里
  • 客观利用自动释放池,可降低应用程序的内存峰值
  • 使用@autoreleasepool

2:JpgEncoder.cs有一静态方法,直接可以赢得Stream流:

34. 为常用的block类型创造typedef

比如:

typedef void(^WCECompletionHander)(NSData *data);
  •  用typedef重新定义块类型,可让块变量用起来更为简便易行
  • 定义新档次时,应按照命名规则

 public static Stream GetStream(WriteableBitmap bitmap)

35. 行使block降低代码分散程度

  • 在成立对象时,可以选拔内联的handler代码块将相关作业逻辑注解
  • 譬如网络请求一般采纳代码块来回调数据

 

3:没了~~~

ps:具体FJ.Core.dll、JpgEncoder.cs两个公文可以从下载源码下找到。

 

3.2 视频发送

为了定时发送视频,我们需要开启定时器:

起名 10起名 11

        System.Windows.Threading.Dispatcher提姆er timer;//全局定义
         public MainPage()
        {
            InitializeComponent();
            timer = new System.Windows.Threading.DispatcherTimer();
            timer.Interval = TimeSpan.FromSeconds(0.2);//0.2秒一次,每秒5次
            timer.Tick += new EventHandler(timer_Tick);          
        }
        void timer_Tick(object sender, EventArgs e)
        {
           //那里就是殡葬视频的代码了
        }
        private void btnSend_Click(object sender, RoutedEventArgs e)
        {
            timer.Start();//点击发送录像时,启动定时器即可
        }

在点击发送触发定时器时,发送视频

起名 12起名 13

        byte[] content = new byte[56 * 1024];
        int length;       
        void timer_Tick(object sender, EventArgs e)
        {
            WriteableBitmap img = new WriteableBitmap(canVideo, null);
            Stream stream = JpgEncoder.GetStream(img); //获取压缩后的流
            length = (int)stream.Length;
            stream.Read(content, 0, length);
            stream.Close();

            SocketAsyncEventArgs sendEvent = new SocketAsyncEventArgs();
            sendEvent.SetBuffer(content, 0, length);
            videoSocket.SendAsync(send伊芙nt);//这里只管发送,发送后的结果不管了。
           
            img = null;
        }

 

至此,客户端的一序列动作就成功了,包括[打开视频/注册编号/发送录像/接收录像],下边到服务端代码上场了。

 

4:控制台服务端Socket中转

4.1:额外的处理事件

首先:服务端需要缓解跨域问题,这么些看过:Silverlight+WCF
新手实例 象棋
WCF通讯跨域(十五)
–就会领悟Silverlight客户端和通讯端不在同一站点下报道时,需要解决跨域问题了。

即便这里没用WCF,改用Socket模式,一样需要缓解跨域问题。

其次:用Socket通讯格局,还需要打开另外的943端口监听。

 

唯独这两步,网上都有现成的代码,直接copy就足以了。

手续如下:

1:新建控制台项目—》起名:TCP瑟维斯(Service)

2:新建类文件:PolicyServer.cs,完整代码如下,大伙直接行使就可以了:

起名 14起名 15PolicyServer类与跨域xml文件

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;

namespace TCPService
{
    class PolicyServer
    {
        Socket m_listenerIPv4;
        Socket m_listenerIPv6;
        byte[] m_policy;

        // pass in the path of an XML file containing the socket policy
        public PolicyServer(string policyContents)
        {
            m_policy = Encoding.UTF8.GetBytes(policyContents);

            // Create the Listening Sockets            
            m_listenerIPv4 = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            m_listenerIPv4.Bind(new IPEndPoint(IPAddress.Any, 943));
            m_listenerIPv4.Listen(10);
            m_listenerIPv4.BeginAccept(new AsyncCallback(OnConnection), m_listenerIPv4);
            //Console.WriteLine(“Listenting on IPv4 port 943.”);

            if (System.Net.Sockets.Socket.OSSupportsIPv6)
            {
                m_listenerIPv6 = new Socket(AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp);
                m_listenerIPv6.Bind(new IPEndPoint(IPAddress.IPv6Any, 943));
                m_listenerIPv6.Listen(10);
                m_listenerIPv6.BeginAccept(new AsyncCallback(OnConnection), m_listenerIPv6);
                //Console.WriteLine(“Listenting on IPv6 port 943.”);
            }
            else
            {
               // Console.WriteLine(“IPv6 is not supported by the system.”);
            }
        }

        // Called when we receive a connection from a client
        public void OnConnection(IAsyncResult res)
        {
            Socket listener = (Socket)res.AsyncState;
            Socket client = null;

            try
            {
                client = listener.EndAccept(res);
            }
            catch (SocketException)
            {
                return;
            }

            // handle this policy request with a PolicyConnection
            PolicyConnection pc = new PolicyConnection(client, m_policy);

            // look for more connections
            listener.BeginAccept(new AsyncCallback(OnConnection), listener);
        }

        public void Close()
        {
            m_listenerIPv4.Close();
            if (m_listenerIPv6 != null)
            {
                m_listenerIPv6.Close();
            }
        }
    }
    class PolicyConnection
    {
        Socket m_connection;

        // buffer to receive the request from the client
        byte[] m_buffer;
        int m_received;

        // the policy to return to the client
        byte[] m_policy;

        // the request that we’re expecting from the client
        static string s_policyRequestString = “<policy-file-request/>”;

        public PolicyConnection(Socket client, byte[] policy)
        {
            m_connection = client;
            m_policy = policy;

            m_buffer = new byte[s_policyRequestString.Length];
            m_received = 0;

            try
            {
                // receive the request from the client
                m_connection.BeginReceive(m_buffer, 0, s_policyRequestString.Length, SocketFlags.None, new AsyncCallback(OnReceive), null);
            }
            catch (SocketException)
            {
                m_connection.Close();
            }
        }

        // Called when we receive data from the client
        private void OnReceive(IAsyncResult res)
        {
            try
            {
                m_received += m_connection.EndReceive(res);

                // if we haven’t gotten enough for a full request yet, receive again
                if (m_received < s_policyRequestString.Length)
                {
                    m_connection.BeginReceive(m_buffer, m_received, s_policyRequestString.Length – m_received, SocketFlags.None, new AsyncCallback(OnReceive), null);
                    return;
                }

                // make sure the request is valid
                string request = System.Text.Encoding.UTF8.GetString(m_buffer, 0, m_received);
                if (StringComparer.InvariantCultureIgnoreCase.Compare(request, s_policyRequestString) != 0)
                {
                    m_connection.Close();
                    return;
                }

                // send the policy
                m_connection.BeginSend(m_policy, 0, m_policy.Length, SocketFlags.None, new AsyncCallback(OnSend), null);
            }
            catch (SocketException)
            {
                m_connection.Close();
            }
        }

        // called after sending the policy to the client; close the connection.
        public void OnSend(IAsyncResult res)
        {
            try
            {
                m_connection.EndSend(res);
            }
            finally
            {
                m_connection.Close();
            }
        }
    }
//跨域用的xml文件,以代码的模式传入。
public static class SocketPolicy
    {
        public const string Policy = @”
            <?xml version=””1.0″” encoding =””utf-8″”?>
            <access-policy>
              <cross-domain-access>
                <policy>
                  <allow-from>
                    <domain uri=””*”” />
                  </allow-from>
                  <grant-to>
                    <socket-resource port=””4502-4530″” protocol=””tcp”” />
                  </grant-to>
                </policy>
              </cross-domain-access>
            </access-policy>
            “;
    }
}

3:控制台启动首行代码

 static void Main(string[] args)
 {
    PolicyServer ps = new PolicyServer(SocketPolicy.Policy);//Silverlight跨域访问与开启943端口
  }

 

至此,我们添加了个附加的处理类来解决943端口和跨域问题[只顾上边代码中xml的端口号配置范围哦],下边起初投机的服务端处理流程

 

4.2:服务端处理流程

4.2.1:开启监听

起名 16起名 17

namespace TCPService
{
    class Program
    {
        public static Dictionary<int, ThreadProxy> soketList;//房号+颜色值
         static void Main(string[] args)
        {
            PolicyServer ps = new PolicyServer(SocketPolicy.Policy);//Silverlight跨域访问及943端口
            //主线程监听
            soketList = new Dictionary<int, ThreadProxy>();
            Console.WriteLine(“TCP瑟维斯(Service)(Service)正在启动运作”);
            IPEndPoint ip = new IPEndPoint(IPAddress.Any, 4505);//本地任意IP及4505端口
            Socket mainSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            mainSocket.Bind(ip);
            mainSocket.Listen(-1);
            while (true)
            {
                Socket socket = mainSocket.Accept();
                new ThreadProxy(socket).Run();//收到信息即时处理。

            }
        }
        public static void WriteLine(string msg)
        {
            Console.WriteLine(msg);
        }
    }
    class ThreadProxy
    {
        public Socket socket;
        public ThreadProxy(Socket newSocket)
        {
            socket = newSocket;
        }
        public void Run()
        {
            Thread thread = new Thread(new ThreadStart(Action));
            thread.Start();
        }
        public void Action()
        {
            Program.WriteLine(“有人来了—-“);
            //下边开启处理逻辑
        }
   }
}

 

说明:

此间要留心的是监听的端口号必须要跨域文件配置的限制内。同时用一字典泛型soketList保存了于是注册的用户通讯socket,这样可以一本万利寻找对方的socket举行转发。

 

4.2.2 定义下全局变量

起名 18起名 19

        public Socket socket;//我方的Socket
        ThreadProxy youThreadProxy;//对方

       
int num;//注册的号子

        byte[] buffer = new byte[30 * 1024];//缓冲字节30K,简单说就是用户10K殡葬3次,这里接受满30K才转发五回
        bool firstConn = true;//是否第一次建立链接,第一次链接都是挂号编号,不发送视频的;

 

4.2.3 处理编号注册、移除、查找对方

编号注册:

起名 20起名 21

        private void RegSocket(string key)
        {
            firstConn = false;//注册完后,设置下标识
            if (key.Length < 10)//字节太多就是图片流了
            {
                if (int.TryParse(key, out num))
                {
                    if (Program.soketList.ContainsKey(num))//以前都有人在了
                       {
                        Program.soketList[num].socket.Close();
                        Program.soketList[num].socket.Dispose();
                        Program.soketList.Remove(num);
                    }
                    Program.soketList.Add(num, this);
                    Program.WriteLine(“用户注册:” + key);
                    FindYouSocket();
                    return;
                }
            }
        }

线程错误,编号移除:

起名 22起名 23

       private void OnError(ThreadProxy errorProxy,string errorMsg)
        {
            if (errorProxy.socket != null)
            {
                errorProxy.socket.Close();
            }
            Console.WriteLine(“删除用户:” + errorProxy.num +”错误信息:”+ errorMsg);
            Program.soketList.Remove(errorProxy.num);
            
        }

查询对方:

起名 24起名 25

       private void FindYouSocket()
       {
            int youNum = num % 2 == 0 ? num – 1 : num + 1;
            if (Program.soketList.ContainsKey(youNum))
            {
                youThreadProxy = Program.soketList[youNum];
            }
         }

 

4.2.4 主业务处理中转流程

起名 26起名 27

       public ThreadProxy(Socket newSocket)
        {
            socket = newSocket;
            socket.SendBufferSize = buffer.Length;
            socket.ReceiveBufferSize = buffer.Length;
        }
        public void Run()
        {
            Thread thread = new Thread(new ThreadStart(Action));
            thread.Start();
        }
        public void Action()
        {
            Program.WriteLine(“有人来了—-“);
            try
            {
                while (true)
                {
                    if (socket.Connected)
                    {
                        int length = 0, count = 0;
                        do
                        {
                            System.Threading.Thread.Sleep(20);//关键点,请求太快数据接收不全
                            length = socket.Receive(buffer, count, socket.Available, 0);
                            count = count + length;

                        }
                        while (socket.Available > 0);

                        if (count > 1)
                        {

                            if (count < 4)//小字节,命令字符
                            {
                                if (firstConn)//第一次登陆,需要注册ID
                                {
                                    string key = ASCIIEncoding.ASCII.GetString(buffer, 0, count);
                                    RegSocket(key);
                                }
                            }
                            else if (youThreadProxy == null)
                            {
                                Program.WriteLine(“没人接收。。。”);
                                FindYouSocket();
                            }
                            else if (youThreadProxy.canReceive)//对方允许收取图片发送
                                {
                                Program.WriteLine(“图片来了:” + count);
                                if (youThreadProxy.socket.Connected)
                                {
                                    Program.WriteLine(“图片转发:” + buffer.Length);
                                    try
                                    {
                                        youThreadProxy.socket.Send(buffer, count, 0);
                                    }
                                    catch(Exception err)
                                    {
                                        OnError(youThreadProxy, err.Message);
                                    }
                                }
                            }
                        }
                    }
                    else
                    {
                        OnError(this,”socket链接已关闭”);
                        break;
                    }
                }
            }
            catch(Exception err)
            {
                OnError(this,err.Message);
            }

        }

拍卖流程也很粗略,依据请求的字节大小来调用是“注册”仍然“中转”。

从这之后,整个完整的视频传输篇完成了,完成的图纸和上一节一样了:

起名 28

 

 

最终是豪门期待已久的言传身教源码下载:点击下载 [别忘了留下言推荐下啊^-^]

证实:视频源码中的内容会多一些,包括一从头自己写的片段其他杂七杂八的代码,可是不影响整个的运行。

 

说到底:谢谢我们对本类其它欣赏,谢谢援助~

PS:传说点一下推荐会有10个园豆,喜欢麻烦点一下“推荐”,thank you very
much!!

 

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图