KVO

by admin on 2018年11月19日

接触了邮件发送的食指,可能针对SMTP协议以及针对dotnet下面的SMTP类的操作应该不见面生,但也许未肯定了解ESMTP是呀东西,下面我们针对其预先举行一个介绍,然后又详尽介绍两栽方法以殡葬嵌入图片(不是附件模式)操作的贯彻。

1.KVO概念

KVO(Key – Value –
Observing)即键值观察,它提供平等种机制,当让观察的靶子的属性发生变动后,对象见面收下至通知,从而做出相应的反。

介绍SMTP命令和ESMTP过程

2.KVO实现原理

  这里要说一个isa指针,在Objective-C中,任何像样的概念都是目标。类及好像的实例(对象)没有任何实质上的区分。任何对象都出isa指针。

  那么什么是看似为?在xcode中因故快捷键Shift+Cmd+O 打开文件objc.h
能看到类似的概念:

  哲学原理 1

  可以视:

  Class 是一个 objc_class 结构类型的指针, id是一个 objc_object
结构类型的指针.

  我们再度来瞧 objc_class 的定义:

 

哲学原理 2

 

  稍微解释一下各个参数的意:

  isa:是一个Class 类型的指针.
每个实例对象来个isa的指针,他本着对象的切近,而Class里呢产生个isa的指针,
指向meteClass(元类)。元类保存了仿佛方式的列表。当类方法吃调用时,先会于我查找类方法的落实,如果没,元类会向他父类查找该办法。同时注意的是:元类(meteClass)也是相近,它为是目标。元类也发isa指针,它的isa指针最终对的是一个到底元类(root
meteClass).根元类的isa指针指为自家,这样形成了一个封闭的内循环。

  super_class:父类,如果此类已经是无限顶层的根类,那么其吗NULL。

  version:类的版本信息,默认为0

  info:供运行期使用的一对各类标识。

  instance_size:该类的实例变量大小

  ivars:成员变量的数组

还来看看各个类实例变量的接轨关系:

 哲学原理 3

  每一个靶本质上且是一个近乎的实例。其中接近定义了成员变量和分子方法的列表。对象通过对象的isa指针指向类。

  每一个看似精神上还是一个对象,类其实是元类(meteClass)的实例。元类定义了看似方式的列表。类经过类似的isa指针指为元类。

  所有的元类最终继承一个清元类,根元类isa指针指于本人,形成一个封闭的内循环。

 

  原理:每一个靶都发一个isa指针,这个目标根据isa指针去摸它所归属的近乎,当我们让一个靶注册观察者的时光,系统会于运转时让这目标创建一个子类,这个子类继承给时目标归属的切近,并把目前目标的isa指针指于这子类,于是当前目标就是成为了这个子类的一个实例。那么这个子类内部举行了什么操作为?其实是子类重写了set方法,当原对象在调用set方法赋值的时光,会根据isa指针到新建子类的措施列表去搜寻set方法的IMP,此时者还写的set方法会对具备观察这特性的靶子来通报,于是原有的对象见面作出改变。

 

深入解析:

  Apple 使用了 isa 混写(isa-swizzling)来促成 KVO
。当观察对象A时,KVO机制动态创建一个初的称呼吧:
NSKVONotifying_A的新类,该类继承自对象A的本类,且KVO为NSKVONotifying_A重写观察性的setter
方法,setter 方法会负责在调用原 setter
方法之前和之后,通知所有观察对象属性值的更动情况。

 

  • NSKVONotifying_A类剖析:在这进程,被观察对象的 isa
    指针从对原来的A类,被KVO机制修改为负于网新创的子类
    NSKVONotifying_A类,来实现即类属性值改变之监听;
  • 所以当我们由利用规模上看来,完全无发现及发出新的类似出现,这是系统“隐瞒”了对KVO的底色实现过程,让咱们误以为还是本来的切近。但是此时而我们创建一个初的名吧“NSKVONotifying_A”的类(),就会意识网运作及注册KVO的那段代码时先后即使崩溃,因为系统于注册监听的下动态创建了名叫也NSKVONotifying_A的中间类,并针对这当中类了。
  • 从而在该对象及对 setter 的调用就会见调用已重新写的
    setter,从而激活键值通知机制。

 

  • KVO键值观察依赖让NSObject的星星点点单艺术:willChangeValueForKey和didChangevlueForKey,即于键值改变前后分别调用这半独方法,然后在就片个法子的高中级调用父类set方法赋值。
  • 让观察性发生变动之前,willChangeValueForKey:被调用,通知系统该
    keyPath 的属于性值即将转移;当改变有后, didChangeValueForKey:
    被调用,通知系统该 keyPath
    的属性值已经变更;之后observeValueForKey:ofObject:change:context:
    也会见给调用。且再度写观察性之setter
    方法这种持续方式的流是于运作时一旦不是编译时落实的。

KVO为子类的观察者属性重写调用存取方法的办事原理在代码中一定给:

1 -(void)setName:(NSString *)newName
2 {
3   [self willChangeValueForKey:@"name"];    //KVO在调用存取方法之前总调用
4   [super setValue:newName forKey:@"name"]; //调用父类的存取方法
5   [self didChangeValueForKey:@"name"];     //KVO在调用存取方法之后总调用
6 }

 

 

什么是 SMTP

以身作则验证

哲学原理 4哲学原理 5

 1 //Person类
 2 @interface Person : NSObject
 3 @property (nonatomic,copy) NSString *name;
 4 @end
 5 
 6 //controller
 7 Person *per = [[Person alloc]init];
 8 //断点1
 9 [per addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];
10 //断点2
11 per.name = @"小明";
12 [per removeObserver:self forKeyPath:@"name"];
13 //断点3

View Code

运作品种,

  • 断点1位置:

哲学原理 6

 

 

  • 得视isa指向Person恍如,我们啊可以应用lldb令查看:
    (lldb) po [per class]
    Person
    (lldb) po object_getClass(per)
    Person
    (lldb)
    

 

  • 断点2位置:

哲学原理 7

 

(lldb) po [per class]
Person
(lldb) po object_getClass(per)
NSKVONotifying_Person
(lldb)

 

  • 断点3位置:

    (lldb) po [per class]
    Person
    (lldb) po object_getClass(per)
    Person
    (lldb)

 

 

  上面的结果说明,在per对象为观察时,framework使用runtime动态创建了一个Person类的子类NSKVONotifying_Person,而且为隐藏者作为,NSKVONotifying_Person重写了-
class方法返回之前的类似,就恍如什么为从未产生了千篇一律。但是采取object_getClass()时就是表露了,因为此法返回的凡以此目标的isa指针,这个指针指向的必然是单之目标的好像对象

 

SMTP (Simple Mail Transfer Protocol) :
电子邮件从客户机传输到服务器或打某一个服务器传输至其他一个服务器使用的传导协议。
SMTP 是呼吁/响应协议,命令和响应都是因 ASCII 文本,并以 CR 和 LF
符结束。响应包括一个意味回去状态的老三各项数字代码。SMTP 在 TCP 协议 25
端口监听连接要。

3.KVO的特点

由于KVO内部贯彻的法则是再次写了set方法,因此只有当于观察对象的性质调用set方法赋值的时段才见面执行KVO的底回调方法。所以要直接针对性之分子变量直接赋值那么不见面触发KVO。

什么是 ESMTP

4.KVO的调用步骤

1.报了名观察者
2.在回调方法被处理事件
3.易除了观察者

ESMTP (Extended SMTP),顾名思义,扩展 SMTP 就是针对正规 SMTP
协议进行的恢弘。它同 SMTP 服务的分别仅是,使用 SMTP
发信不待说明用户帐户,而用 ESMTP 发信时,
服务器会要求用户提供用户称及密码以便验证身份。验证后的邮件发送过程以及
SMTP 方式没有例外。

5.代码尽

哲学原理 8哲学原理 9

 1     self.changeStr = @"您好";
 2     [self addObserver:self forKeyPath:@"changeStr" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
 3     self.changeStr = @"大家都好";
 4 
 5 
 6 -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
 7 {
 8     NSLog(@"被改变的属性是%@",keyPath);
 9     NSString *str = [change   objectForKey:NSKeyValueChangeNewKey];
10     NSString *odlStr = [change   objectForKey:NSKeyValueChangeOldKey];
11     NSLog(@"旧属性是%@",odlStr);
12     NSLog(@"新属性是%@",str);
13 }

View Code

 

输出结果:

哲学原理 10

 

一个Demo:

 

在LYXItem.h文件

1 #import <Foundation/Foundation.h>
2 
3 @interface LYXItem : NSObject
4 
5 @property(nonatomic, copy) NSString *name;
6 @property(nonatomic, copy) NSString *price;
7 
8 @end

 

在LYXItemView.h文件

 1 #import <Foundation/Foundation.h>
 2 #import "LYXItem.h"
 3 
 4 @interface LYXItemView : NSObject
 5 
 6 @property(nonatomic, weak) LYXItem *item;
 7 
 8 - (void) showItemInfo;
 9 
10 @end

 

在LYXItemView.m中

哲学原理 11哲学原理 12

 1 #import "LYXItemView.h"
 2 
 3 @implementation LYXItemView
 4 
 5 @synthesize item = _item;
 6 
 7 - (void)showItemInfo
 8 {
 9     NSLog(@"item名为:%@, 价格为: %@",self.item.name, self.item.price);
10 }
11 
12 
13 - (void)setItem:(LYXItem *)item
14 {
15     self -> _item = item;
16     //为item添加监听器,监听item的name的属性的变化
17     [self.item addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];
18     
19     [self.item addObserver:self forKeyPath:@"price" options:NSKeyValueObservingOptionNew context:nil];
20 }
21 
22 
23 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
24 {
25     NSLog(@"---------------------------observeValueForKeyPath------------------------");
26     NSLog(@"被修改的keyPath为:%@",keyPath);
27     NSLog(@"被修改的对象为:%@",object);
28     NSLog(@"新被修改的属性值是:%@",[change objectForKey:@"new"]);
29     NSLog(@"被修改的上下文是:%@",context);
30 }
31 
32 
33 @end

View Code

 

每当运转文件被

 

 1     LYXItem *item = [[LYXItem alloc] init];
 2     item.name = @"IOS";
 3     item.price = @"6888";
 4     
 5     LYXItemView *lyxView = [[LYXItemView alloc] init];
 6     lyxView.item = item;
 7     [lyxView showItemInfo];
 8     
 9 //    更改item的值,触发监听器的方法
10     item.name = @"Android";
11     item.price =@"1999";

 

打印结果:

哲学原理 13

 

 

 

 

 

 

 

 

 

 

 

SMTP 命令

SMTP 命令包括:

HELO
向服务器标识用户身份。发送者能掩人耳目,说谎,但貌似景象下服务器都能检测及。

EHLO
向服务器标识用户身份。发送者能掩人耳目,说谎,但貌似情况下服务器都能检测及。

MAIL FROM 命令中指定的地点是发件人地址

RCPT TO 标识单个的邮件接收人;可发差不多个 RCPT TO;常在 MAIL 命令后。

DATA 在么或多个 RCPT
命令后,表示有的邮件接收人已标识,并初始化数据传,以 CRLF.CRLF 结束

VRFY
用于证明指定的用户/邮箱是否有;由于安全者的因由,服务器常禁止这个命令

EXPN 验证给定的邮箱列表是否存在,扩充邮箱列表,也时时吃剥夺

HELP 查询服务器支持啊令

NOOP 无操作,服务器应响应 OK

RSET 重置会话,当前传给撤

QUIT
结束会话  

 

上述这些是SMTP和ESMPT的基本知识,特别是这些命令,对咱操作ESMTP协议帮助会于坏之,说白了,ESMTP就是经过Socket流方式同邮件服务器进行交互,基本上目前具有的服务器都见面供针对性ESMTP合计的支持,SMTP协议在dotnet中贯彻就是透过调用System.Net.Mail .SmtpClient实现之。

出殡嵌入图片或文件邮件的规律就是是,把图纸资源提交(和附件类似措施,但是差),然后经改HTML的IMG标识的SRC实现实现内容之对立引用的。

假若我辈插入本地图片文件之早晚,文件的地址是“D:\test.jpg”,如果这样发送出,那么自然是查看不到的了,我们要以它修改也:<img
src=\”cid:test\”> 

然后,我们以邮件正文中附加相关的文件就好了,这个小类似于一致栽特别的稳定格式。具体的演示代码如下所示。

 System.Net.Mail.MailMessage mailMessage = new System.Net.Mail.MailMessage();

mailMessage.From=”发送者邮箱”;
mailMessage.To.Add(“收件人邮件列表”);
mailMessage.CC.Add(“抄送人邮件列表”);
mailMessage.Subject = subject;
AlternateView htmlBody = AlternateView.CreateAlternateViewFromString(content,null,”text/html”);
LinkedResource lrImage = new LinkedResource(“test.jpg”,”image/gif”);
lrImage.ContentId = “test”;
htmlBody.LinkedResources.Add(lrImage);
mailMessage.AlternateViews.Add(htmlBody);
SmtpClient.Send(mailMessage);

  

实在对调用SmtpClient发送嵌入图片文件的操作,网上就起无数文章介绍了,本人于寻找过程遭到,发现几乎篇是的稿子,不敢独自享,分享出去大家读,下面提供几乎篇介绍就方面知识的篇章:

.NET C# 异步发送 要求回执 嵌入图片资源 自定义邮件头 失败通知
html/文本双视图 支持 notes
的邮件 

 .NET C# 发送邮件内容嵌入图片

 Sending Emails in .NET with the System.Net.Mail
Namespace

 C#发送Email邮件三种植办法的下结论(转载)

 C#用于Windows程序的HTML编辑器(推荐,附源码的)

 

如上都是根据SmtpClient类的兑现,没有发觉有关ESMTP实现,即透过TCP
Scoket进行发送嵌入图片文件之邮件的不二法门。

其实以文章”c#发送需要smtp认证的邮件”(http://www.legalsoft.com.cn/docs/docs/17/577.html) 中,已经充分详细介绍了什么样促成以ESMTP商事及服务器之间的彼此了,其中还包了发送邮件附件,算是比较详细的ESMTP实现了。

实则实现发送ESMTP的坐图片,和殡葬附件方式有些类似了。

1、首先你用编制一个函数,用来分析发送的HTML内容,遇到有地面图片的,将文件路径替换为Cid:***格式,并将文件写副内存的Stream中备用,示例代码如下所示。

         private Hashtable EmbedList = new Hashtable(); //widened scope for MatchEvaluator

        private string FixupReferences(string rawPayload, ref StringBuilder extras, string boundaryString)
        {
            //Build a symbol table to avoid redundant embedding.
            Regex imgRE, linkRE, hrefRE;
            MatchCollection imgMatches;
            string imgMatchExpression = “(?:img[^>]+src\\s*=\\s*(?:\”(?<1>[^\”]*)\”|(?<1>\\S+))|url\\([‘\”](?<1>[^’\”]*)[‘\”]\\))”;
            imgRE = new Regex(imgMatchExpression, RegexOptions.IgnoreCase | RegexOptions.Compiled);
            string linkMatchExpression = “<\\s*link[^>]+href\\s*=\\s*(?:\”(?<1>[^\”]*)\”|(?<1>\\S+))[^>]*>”;
            linkRE = new Regex(linkMatchExpression, RegexOptions.IgnoreCase | RegexOptions.Compiled);
            //this one’s for fixup of relative urls in anchors
            string refMatchExpression = “href\\s*=\\s*(?:[‘\”](?<1>[^\”]*)[‘\”]|(?<1>\\S+))”;
            hrefRE = new Regex(refMatchExpression, RegexOptions.IgnoreCase | RegexOptions.Compiled);

            imgMatches = imgRE.Matches(rawPayload);
            //translation to a Hashtable weeds out redundant references
            foreach (Match m in imgMatches)
            {
                if (!EmbedList.ContainsKey(m.Groups[1].Value))
                {
                    EmbedList.Add(m.Groups[1].Value, Guid.NewGuid());
                }
            }

            //Prepare embedded data
            extras.Length = 0;
            string contentType;
            ArrayList embeddees = new ArrayList(EmbedList.Keys);
            foreach (string embeddee in embeddees)
            {
                contentType = embeddee.Substring(embeddee.LastIndexOf(“.”) + 1).ToLower();
                extras.AppendFormat(boundaryString);
                if (contentType.Equals(“jpg”)) contentType = “jpeg”;
                switch (contentType)
                {
                    case “jpeg”:
                    case “gif”:
                    case “png”:
                    case “bmp”:
                        extras.AppendFormat(“Content-Type: image/{0}; charset=\”iso-8859-1\”\r\n”, contentType);
                        extras.Append(“Content-Transfer-Encoding: base64\r\n”);
                        extras.Append(“Content-Disposition: inline\r\n”);
                        extras.AppendFormat(“Content-ID: <{0}>\r\n\r\n”, EmbedList[embeddee]);
                        extras.Append(GetDataAsBase64(embeddee));
                        extras.Append(“\r\n”);
                        break;
                }
            }
            //Fixups for references to items now embedded
            rawPayload = imgRE.Replace(rawPayload, new MatchEvaluator(_fixup));
            return rawPayload;
        }
        private string _fixup(Match m)
        {
            string replaceThis = m.Groups[1].Value;
            string withThis = string.Format(“cid:{0}”, EmbedList[replaceThis]);
            return m.Value.Replace(replaceThis, withThis);
        }

 

 然后而当与服务器交互的上,就得透过此函数,获取解析后底HTML文件内容以及图片流,对其开展操作即可。

            //判断信件格式是否html
            if (Html)
            {
                SendBufferstr += “Content-Type: text/html;” + enter;
            }
            else
            {
                SendBufferstr += “Content-Type: text/plain;” + enter;
            }
            //编码信息
            if (Charset == “”)
            {
                SendBufferstr += ”    charset=\”iso-8859-1\”” + enter;
            }
            else
            {
                SendBufferstr += ”    charset=\”” + Charset.ToLower() + “\”” + enter;
            }
            SendBufferstr += “Content-Transfer-Encoding: base64” + enter;

            StringBuilder extras = new StringBuilder();
            string extrasBoundary = “–” + boundary + enter;
            string newBodyHtml = FixupReferences(this.Body, ref extras, extrasBoundary);
            SendBufferstr += enter + enter;
            SendBufferstr += B64StrLine(Base64Encode(newBodyHtml)) + enter;

            SendBufferstr += enter + “–” + boundary1 + “–” + enter + enter;

            SendBufferstr += extras.ToString(); 

 

脚是如出一辙卖图纸邮件的事例: 

哲学原理 14 

发表评论

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

网站地图xml地图