damus是一个建立在去中心化网络上的社交软件,被称为“推特杀手”,现已在苹果应用商店上线。
1月31日,damus团队在推特上证实了这一消息,此前该团队称已经被苹果公司拒绝了至少三次。
不久之后,twitter联合创始人jack dorsey向他的650万粉丝分享了这一消息,这位企业家将其称为开源协议的“里程碑”时刻。
根据1月27日消息,damus还通过比特币第2层闪电网络内置了支付功能。
该应用自称是“可以让用户自己掌控的社交网络”,它是一款建立在去中心化网络nostr(支持加密的端到端私人消息传递等功能)上的应用软件。
nostr并不是基于服务器来运行网络,相反,它是利用去中心化中继器来分发消息。中继器(relay)是托管的服务器,所有人都可以运行中继器。获得其他用户,比如你好友的更新,需要研究询问多个中继站是否了解其他人的情况;而中继站所做的,就是接受人们的帖子并将其转发给其他人。
此外,nostr开发人员还专注于使用比特币和闪电网络来防止针对damus的垃圾邮件攻击。
ios版本的“damus”用户界面 来源:github
根据github上面的信息,已有44名软件开发人员为damus web应用程序贡献了代码。
不过,damus入驻苹果应用商店并非一帆风顺。
根据damus的官推,在最终上线之前,它已失败了至少三次:
nostr的核心开发者之一william casarin也在他的个人twitter账户上分享说,上架手机应用平台是非常坎坷的,他也说,如果苹果用户不能原生使用nostr将成为耻辱。
尽管尚不清楚jack dorsey和nostr之间的确切关系,但这位亿万富翁曾在去年12月中旬向nostr捐赠约14.17枚btc(约合 245,000 美元),以资助nostr的进一步开发。而他也一直在 nostr 上活跃。如果你看他的公钥,会发现 nostr 上的 jack 比 twitter 上的 jack 要活跃得多。
而其他知名人士也参与了damus应用程序的测试,其中包括以太坊联合创始人vitalik buterin,美国中央情报局(cia)前雇员edward snowden以及支持加密货币的美国参议员cynthia lummis。
操作指南
苹果版与安卓版名字不同,苹果应用商城搜索damus进行下载,谷歌应用商城搜索amethyst进行下载。登入后,两种版本操作界面基本一致,下文以damus操作为例。
一、登入
下载damus后可直接创建账号,阅读完各项协议,选择accept 接受,然后开始起名字创建账户,创建页面有两个密钥,一个是npub开头的公钥,另外一个是私钥,需要自己保存以便下次登入。
我们可以把公钥看作是你的用户名或者银行账户,把你的私钥看作是你的密码,因此切记千万不要将你的私钥发给别人!
二、设置
个人设置方面,damus 允许更换头像和背景图片(通过图片链接填写进avatar url)、添加自己的网站以及简介信息,还支持设置比特币闪电小费地址,这也是 damus 的一个核心亮点。
当前,damus 支持的闪电小费支付平台包括 strike、cash app、muun、blue wallet、wallet of satoshi、zebedee、zeus ln、lnlink、phoenix、breez、bitcoin beach、blixt wallet、river。
图片链接可通过以下方式获取:
1. 登陆 postimage.org,选择图片上传,点击start now
2. 生成图片链接,选择.jpg结尾的链接复制
3.回到damus,edit profile,头像的链接粘贴save即可
三、发帖
威尼斯人2299首页右下方「 」为发帖入口。与当前大多数社交网络类似,damus也设置了话题标签功能,我们在发布帖子时可以在 # 后加上主题或者话题单词。若发帖时要加图片,需要将图片先转换为链接才可发布。
四、好友
想要找到另一个用户,需要搜索他们的公钥,就像搜索推特@的用户名一样,会看到很多人在twitter上发布他们的信息。
五、群聊
目前,仅安卓版amethyst 有群聊功能。
整体而言,damus设计简约大方,但很多功能有些粗糙且不完善,例如发帖不支持样式编辑、发帖后不可删除、点赞或转推后不可撤销、图片需要转成链接才能发布,此外,趋势、过滤器等高级需求也亟待添加。
不过,damus 和作为基础设施的 nostr 目前还处于早期阶段,与比特币和 web3 的结合给了我们很多想象空间。jack dorsey 的去中心化社交愿景能否大规模实现,让我们拭目以待。
以下为大家整理了一些名人账户:
jack(推特创始人)
npub1sg6plzptd64u62a878hep2kev88swjh3tw00gjsfl8f237lmu63q0uf63m
web3 cn
npub16qak7w068hgyu4xmsq0sxwurq74dj7u09afypxrevxed8ayl89zq439sgh
达姆斯damus志愿者:
npub10jas5z30fypezqd4q3kkp8d4739z44t2khcemktycrqyutj7x2ws9wrzf3
ale @lnbc1p911driver
npub1pjvvr9we639fn6kp9aqnf4rua5suwe8mdvfygr0gegadhuxrwwnsz5jf67
熊越
npub1rsg3xkte3zyrn3m0jg0gwh2jpfszry0kfzew7q9tq5yq8sqjh9nquuas8t
超级君
npub16a7m863zhkzpd5ezzkz08rlr900eateap5dwamynssysdv6hhhqsctuk9g
神鱼
npub1ppgr0lsnx5l7hqulw4kcrhgctd3jqkuaxh6d0lftpd4alj2rct2qshk0zr
陈默
npub1zv5zhy0f83kgqz99eqfya4f4wveznkx3aa86lxcvllpcrrc9qvdsz65dtd
余弦
npub1n2prk4drfh570tc7qzvlcaa3pnj2fud7vu4jshymfp3w8sx2evpqx866nz
头雁
npub1ks5hclkksh7ghd8v2058wkut3k35xucs372u96csl4mhsm29e7gsy9cs5r
民道
npub15r9dfrldhzwu0s86dd6qmy6e8pka9pmncq29xxcmc0drg89dcqrqsk968a
郭宇
npub1ztmzpw9w4ta375wktvj98lwt6y2jjc78spg60kwyvkhjty3cr8ysengahm
大姨妈
npub1ana3ft0s7kznrjzu8maffwpfqd0lxj0nkv39gaqvwue7sse3w5uqmqdymm
defi3w
npub19786pedeqk8mpj9favvzxrz44h6d3d3c6ur2fe9592cgqljm9g7qcz8jjs
tumblebit
npub156etfp73lfc2ax0ekj557sezuag0sjzdqhljm4v9tmda62nev4rqrtug7y
参考:cointelegraph、foresightnews
编辑:dali@web3cn.pro、bowen@web3cn.pro
声明:web3中文编译作品,内容仅代表作者立场,且不构成投资建议,请谨慎对待,如文章/素材有侵权,请联系官方客服处理。
twitter is a text-focused social network, but that hasn’t stopped the tech company from including photos and videos. now, the social media site has added a voice tweet feature that allows you to send personalized audio messages to your followers.
twitter是一个以文本为中心的社交网络,但这并没有阻止科技公司包括照片和视频。 现在,社交媒体网站已添加了语音推文功能,可让您将个性化音频消息发送给关注者。
at the time of writing, twitter is still slowly rolling out the voice tweet feature to its iphone and ipad apps. there’s no word on when it will make its way to android.
在撰写本文时,twitter仍在缓慢地在其iphone和ipad应用程序中推出语音推文功能。 关于何时将其引入android尚无定论。
start by opening the twitter app on your smartphone and then tapping the compose tweet floating action button found in the bottom-right corner of the interface.
首先打开智能手机上的twitter应用,然后点击界面右下角的compose tweet浮动操作按钮。
next, type out a tweet. this isn’t a requirement, you can send a voice tweet without adding a written message. from there, select the soundwave button found in the toolbar above your keyboard.
接下来,输入一条推文。 这不是必需的,您可以发送语音鸣叫而无需添加书面消息。 从那里,选择键盘上方工具栏中的soundwave按钮。
when you’re ready to record a voice message, tap the red microphone button.
准备录制语音留言时,请点击红色的“麦克风”按钮。
a soundbar will appear onscreen, indicating that the recording has started. select the pause button whenever you want to take a break and then hit the record button again to continue recording.
条形音箱将出现在屏幕上,表明录制已开始。 只要您想休息一下,请选择“暂停”按钮,然后再次单击“录制”按钮以继续录制。
from our testing, it doesn’t appear as though twitter has put a time limit on recordings. you can record as long as you like, but twitter will break the audio into two-minute-long clips.
根据我们的测试,似乎twitter似乎没有对录音设置时间限制。 您可以随意录制,但是twitter会将音频分成两分钟长的片段。
when you’re happy with your recording, tap the “done” button.
当您对录制感到满意时,请点击“完成”按钮。
take one last look at your tweet. when you’re ready to share your message and or voice recording with your followers, select the “tweet” button.
最后看看您的推文。 当您准备与关注者共享消息和/或语音记录时,请选择“ tweet”按钮。
you and the rest of twitter can now play your voice recording by tapping the play button.
您和twitter的其余部分现在可以通过点击“播放”按钮来播放您的语音记录。
the audio recording will play in a mini-player at the bottom of your screen. you can pause, play, and exit the voice tweet from the playback bar. additionally, the player will follow you through twitter, so you can leave the original tweet and finish listening to the recording while scrolling through your feed.
录音将在屏幕底部的迷你播放器中播放。 您可以从播放栏中暂停,播放和退出语音鸣叫。 此外,播放器将通过twitter关注您,因此您可以保留原始推文,并在滚动feed时完成对录音的收听。
now that you’ve master voice tweets, try adding one to a twitter thread.
现在您已经掌握了语音推文,请尝试在twitter线程上添加一个。
翻译自: howtogeek /678246/how-to-record-and-send-a-voice-tweet-in-the-twitter-app/
这篇文章主要介绍了苹果手机怎么用蓝牙传照片到安卓手机,具有一定借鉴价值,需要的朋友可以参考下。希望大家阅读完这篇文章后大有收获,下面让小编带着大家一起了解一下。
2019独角兽企业重金招聘python工程师标准>>>?
让你的ios应用程序支持运行javascript脚本:javascriptcore框架详解
? ? 说到javascript脚本,ios开发者都会想到一个名叫javascriptcore的框架。这个框架的确十分强大,其中封装了一套javascript运行环境以及native与js数据类型之间的转换桥梁快码论文。本篇博客主要讨论如何使用此框架来在ios应用中运行javascript脚本。
一、javascriptcore框架结构
? ? 在学习一个框架时,首先应该先了解整个框架的结构,拿ios开发来举例,对于一个陌生的框架,第一步需要先搞清楚这里面都包含哪些类,个各类之间是怎样的关系,这个框架和其他的框架间有无联系以及怎样产生的联系。将些问题搞清楚,有了大体上的认识后,我们再来学习其中的每个类即其他细节的应用将非常容易。我们先来看一张javascriptcore框架的结构图:
这张图是我手工画的,不是那么美观并且没有文字的解释,但是我觉得它能非常直观的表达javascriptcore中包含的类之间的关系。下面我来向你解释这张图究竟表达了什么意思,首先原生的ios应用是支持多线程执行任务的,我们知道javascript是单线程,但这并不代表我们不能在native中异步执行不同的javascript代码。
1.jsvirtualmachine——javascript的虚拟机
????javascriptcore中提供了一个名为jsvirtualmachine的类,顾名思义,这个类可以理解为一个js虚拟机。在native中,只要你愿意,你可以创建任意多个jsvirtualmachine对象,各个jsviretualmachine对象间是相互独立的,他们之间不能共享数据也不能传递数据,如果你把他们放在不同的native线程,他们就可以并行的执行无关的js任务。
2.jscontext——javascript运行环境
? ? jscontext上下文对象可以理解为是js的运行环境,同一个jsvirtualmachine对象可以关联多个jscontext对象,并且在webview中每次刷新操作后,此webview的js运行环境都是不同的jscontext对象。其作用就是用来执行js代码,在native和js间进行数据的传递。
3.jsvalue——javascript值对象
? ? javascript和objective-c虽然都是面向对象语言,但其实现机制完全不同,oc是基于类的,js是基于原型的,并且他们的数据类型间也存在很大的差异。因此若要在native和js间无障碍的进行数据的传递,就需要一个中间对象做桥接,这个对象就是jsvalue。
4.jsexport
? ? jsexport是一个协议,native中遵守此解析的类可以将方法和属性转换为js的接口供js调用。
5.一些用于c语言的结构
? ? 你一定注意到了,上图的右下角还有一块被虚线包围的区域,其中的"类"都是c语言风格,javascriptcore框架是支持在objective-c、swift和c三种语言中使用的。
二、在native中运行javascript脚本代码
? ? 我们先来编写一个最简单的例子,使用oc代码来执行一段js脚本。首先新建一个文件,将其后缀设置为.js,我这里将它命令为main.js,在其中编写如下代码:
(function(){
console.log("hello native");
})();
上面是一个自执行的函数,其中打印了“hello native”字符串。在native中编写如下代码:
- (void)viewdidload {
[super viewdidload];
nsstring * path = [[nsbundle mainbundle] pathforresource:@"main" oftype:@"js"];
nsdata * jsdata = [[nsdata alloc]initwithcontentsoffile:path];
nsstring * jscode = [[nsstring alloc]initwithdata:jsdata encoding:nsutf8stringencoding];
self.jscontext = [[jscontext alloc]init];
[self.jscontext evaluatescript:jscode];
}
需要注意,其实这里我将创建的jscontext对象作为了当前视图控制器的属性,这样做的目的仅仅是为了方便调试,不过不对此context对象进行引用,当viewdidload函数执行完成后,js运行环境也将被销毁,我们就无法在safari中直观的看到js代码的执行结果了。
? ? 运行工程,记得要打开safari浏览器的自动显示jscontent检查器,如下图:
当ios模拟器跑起来后,safari会自动弹出开发者工具,在控制台里面可以看到来自javascript的真挚问候:
刚才我们只是简单了通过原生调用了一段js代码,但是如果native在调js方法时无法传参那也太low了,我们可以直接将要传递的参数格式化到字符串中,修改main.js文件如下:
function put(name){
console.log("hello " name);
};
put(%@);
再封装一个oc方法如下:
-(void)runjs_hello:(nsstring *)name{
nsstring * path = [[nsbundle mainbundle] pathforresource:@"main" oftype:@"js"];
nsdata * jsdata = [[nsdata alloc]initwithcontentsoffile:path];
nsstring * jscode = [[nsstring alloc]initwithdata:jsdata encoding:nsutf8stringencoding];
nsstring * finistring = [nsstring stringwithformat:jscode,name];
[self.jscontext evaluatescript:finistring];
}
在viewdidload中进行调用,如下:
- (void)viewdidload {
[super viewdidload];
self.jscontext = [[jscontext alloc]init];
[self runjs_hello:@"'阿凡达'"];
}
运行再看safari控制台的结果,编程了hello 阿凡达~:
其实evaluatescript函数执行后会将js代码的执行结果进行返回,是jsvalue类型的对象,后面会再介绍。
三、在javascript中调用native方法
? ? 有来无往非君子,同样也可以在原生中编写方法让js来调用,示例如下:
- (void)viewdidload {
[super viewdidload];
void(^block)() = ^(){
nslog(@"hello javascript");
};
self.jscontext = [[jscontext alloc]init];
[self.jscontext setobject:block forkeyedsub:@"oc_hello"];
}
上面setobject:forkeyedsub:方法用来向jscontext环境的全局对象中添加属性,这里添加了一个函数属性,取名为oc_hello。这里javascriptcore会自动帮我们把一些数据类型进行转换,会将oc的函数转换为js的函数,运行工程,在safari的控制台中调用oc_hello函数,可以看到在xcode控制台输出了对javascript的真挚问候,如下:
同样,如果声明的block是带参数的,js在调用此oc方法时也需要传入参数,如果block有返回值,则在js中也能获取到返回值,例如:
bool (^block)(nsstring *) = ^(nsstring *name){
nslog(@"%@", [nsstring stringwithformat:@"hello %@",name]);
return yes;
};
self.jscontext = [[jscontext alloc]init];
[self.jscontext setobject:block forkeyedsub:@"oc_hello"];
四、深入jscontext类
? ? 看到这,你已经学会最基础的oc与js互相问好(交互)。下面我们再来深入看下jscontext中的属性和方法。
? ? 创建jscontext对象有如下两种方式:
//创建一个新的js运行环境
- (instancetype)init;
//创建一个新的js运行环境 并关联到某个虚拟机对象上
- (instancetype)initwithvirtualmachine:(jsvirtualmachine *)virtualmachine;
? ? 执行js代码有如下两个方法:
//执行js代码 结果将封装成jsvalue对象返回
- (jsvalue *)evaluatescript:(nsstring *);
//作用同上
- (jsvalue *)evaluatescript:(nsstring *) withsourceurl:(nsurl *)sourceurl ns_available(10_10, 8_0);
? ? 下面的属性和方法可以获取到js运行环境中的一些信息:
//当前的js运行环境 当js调用oc方法时,在oc方法中可以用此方法获取到js运行环境
(jscontext *)currentcontext;
//获取当前执行的js函数,当js调用oc方法时,在oc方法中可以用此方法获取到执行的函数
(jsvalue *)currentcallee;
//获取当前执行的js函数中的this指向的对象
(jsvalue *)currentthis;
//获取当前执行函数的参数列表,当js调用oc方法时,在oc方法中可以用此方法获取到执行的函数的参数列表
(nsarray *)currentarguments;
//获取当前js运行环境的全局对象
@property (readonly, strong) jsvalue *globalobject;
//当运行的javascript代码抛出了未捕获的异常时,这个属性会被赋值为抛出的异常
@property (strong) jsvalue *exception;
//设置为一个异常捕获的block,如果异常被此block捕获,exception属性就不再被赋值了
@property (copy) void(^exceptionhandler)(jscontext *context, jsvalue *exception);
//当前运行环境所关联的虚拟机
@property (readonly, strong) jsvirtualmachine *virtualmachine;
//当前运行环境名称
@property (copy) nsstring *name;
//获取当前js运行环境全局对象上的某个属性
- (jsvalue *)objectforkeyedsub:(id)key;
//设置当前js运行环境全局对象上的属性
- (void)setobject:(id)object forkeyedsub:(nsobject
//将c语言环境的js运行环境转换为oc环境的js运行环境
(jscontext *)contextwithjsglobalcontextref:(jsglobalcontextref)jsglobalcontextref;
//c语言环境的js运行上下文
@property (readonly) jsglobalcontextref jsglobalcontextref;
五、深入jsvalue类
? ? jsvalue是javascript与objective-c之间的数据桥梁。在objective-c中调用js脚本或者js调用oc方法都可以使用jsvalue来传输数据。其中属性和方法示例如下:
//所对应的js运行环境
@property (readonly, strong) jscontext *context;
//在指定的js运行环境中创建一个jsvalue对象
(jsvalue *)valuewithobject:(id)value incontext:(jscontext *)context;
//创建布尔值
(jsvalue *)valuewithbool:(bool)value incontext:(jscontext *)context;
//创建浮点值
(jsvalue *)valuewithdouble:(double)value incontext:(jscontext *)context;
//创建32位整型值
(jsvalue *)valuewithint32:(int32_t)value incontext:(jscontext *)context;
//创建32位无符号整形值
(jsvalue *)valuewithuint32:(uint32_t)value incontext:(jscontext *)context;
//创建空的js对象
(jsvalue *)valuewithnewobjectincontext:(jscontext *)context;
//创建空的js数组
(jsvalue *)valuewithnewarrayincontext:(jscontext *)context;
//创建js正则对象
(jsvalue *)valuewithnewregularexpressionfrompattern:(nsstring *)pattern flags:(nsstring *)flags incontext:(jscontext *)context;
//创建js错误信息
(jsvalue *)valuewithnewerrorfrommessage:(nsstring *)message incontext:(jscontext *)context;
//创建js null值
(jsvalue *)valuewithnullincontext:(jscontext *)context;
//创建js undefined值
(jsvalue *)valuewithundefinedincontext:(jscontext *)context;
javascript中的数据类型和objective-c的数据类型还是有着很大的差异,其中对应关系如下:
objective-c?javascriptnilundefinednsnullnullnsstringstringnsnumbernumber booleannsdictionary ??objectnsarrayarraynsdatedate? ?blockfunctionidobjectclassobject
下面这些方法可以将jsvalue值转换为objective-c中的数据类型:
//将jsvalue转换为oc对象
- (id)toobject;
//将jsvalue转换成特定oc类的对象
- (id)toobjectofclass:(class)expectedclass;
//将jsvalue转换成布尔值
- (bool)tobool;
//将jsvalue转换成浮点值
- (double)todouble;
//将jsvalue转换成32位整型值
- (int32_t)toint32;
//将jsvalue转换成32位无符号整型值
- (uint32_t)touint32;
//将jsvalue转换成nsnumber值
- (nsnumber *)tonumber;
//将jsvalue转换成nsstring值
- (nsstring *)tostring;
//将jsvalue转换成nsdate值
- (nsdate *)todate;
//将jsvalue转换成nsarray值
- (nsarray *)toarray;
//将jsvalue转换成nsdictionary值
- (nsdictionary *)todictionary;
//获取jsvalue对象中某个属性的值
- (jsvalue *)valueforproperty:(nsstring *)property;
//设置jsvalue对象中某个属性的值
- (void)setvalue:(id)value forproperty:(nsstring *)property;
//删除jsvalue对象中的某个属性
- (bool)deleteproperty:(nsstring *)property;
//判断jsvalue对象中是否包含某个属性
- (bool)hasproperty:(nsstring *)property;
//定义jsvalue中的某个属性 这个方法和javascript中object构造函数的defineproperty方法一致
/*
第2个参数设置此属性的描述信息 可以设置的键值如下:
nsstring * const jspropertydeorwritablekey;//设置布尔值 是否可写
nsstring * const jspropertydeorenumerablekey;//设置布尔值 是否可枚举
nsstring * const jspropertydeorconfigurablekey;//设置布尔值 是否可配置
nsstring * const jspropertydeorvaluekey;//设置此属性的值
nsstring * const jspropertydeorgetkey;//设置此属性的get方法
nsstring * const jspropertydeorsetkey;//设置此属性的set方法
以上set、get方法的键和value、可写性的键不能同时存在,其语法是javascript保持一致
*/
- (void)defineproperty:(nsstring *)property deor:(id)deor;
//获取js数组对象某个下标的值
- (jsvalue *)valueatindex:(nsuinteger)index;
//设置js数组对象某个下标的值
- (void)setvalue:(id)value atindex:(nsuinteger)index;
//判断此对象是否为undefined
@property (readonly) bool isundefined;
//判断此对象是否为null
@property (readonly) bool isnull;
//判断此对象是否为布尔值
@property (readonly) bool isboolean;
//判断此对象是否为数值
@property (readonly) bool isnumber;
//判断此对象是否为字符串
@property (readonly) bool isstring;
//判断此对象是否为object对象
@property (readonly) bool isobject;
//判断此对象是否为数组
@property (readonly) bool isarray;
//判断此对象是否为日期对象
@property (readonly) bool isdate;
//比较两个jsvalue是否全相等 对应javascript中的===
- (bool)isequaltoobject:(id)value;
//比较两个jsvalue对象的值是否相等 对应javascript中的==
- (bool)isequalwithtypecoerciontoobject:(id)value;
//判断某个对象是否在当前对象的原型链上
- (bool)isinstanceof:(id)value;
//如果jsvalue是function对象 可以调用此方法 和javascript中的call方法一致
- (jsvalue *)callwitharguments:(nsarray *)arguments;
//如果jsvalue是一个构造方法对象 可以调用此方法 和javascript中使用new关键字一致
- (jsvalue *)constructwitharguments:(nsarray *)arguments;
//用此对象进行函数的调用 当前对象会被绑定到this中
- (jsvalue *)invokemethod:(nsstring *)method witharguments:(nsarray *)arguments;
//将cgpoint转换为jsvalue对象
(jsvalue *)valuewithpoint:(cgpoint)point incontext:(jscontext *)context;
//将nsrange转换为jsvalue对象
(jsvalue *)valuewithrange:(nsrange)range incontext:(jscontext *)context;
//将cgrect转换为jsvalue对象
(jsvalue *)valuewithrect:(cgrect)rect incontext:(jscontext *)context;
//将cgsize转换为jsvalue对象
(jsvalue *)valuewithsize:(cgsize)size incontext:(jscontext *)context;
//转换成cgpoint数据
- (cgpoint)topoint;
//转换成nsrange数据
- (nsrange)torange;
//转换成cgrect数据
- (cgrect)torect;
//转换为cgsize数据
- (cgsize)tosize;
//将c风格的jsvalueref对象转换为jsvalue对象
(jsvalue *)valuewithjsvalueref:(jsvalueref)value incontext:(jscontext *)context;
其实在javascriptcore框架中还有一个jsmanagervalue类,这个的主要作用是管理内存。虽然我们在编写objective-c代码时有强大的自动引用技术(arc技术),我们一般无需关心对象的内存问题,在编写javascript代码时也有强大的垃圾回收机制(这种机制下甚至连循环引用都不是问题),但是在oc和js混合开发时,就很容易出现问题了,比如一个js垃圾回收机制释放掉的对象oc中却还在用,反过来也是一样。jsmanagervalue对jsvalue进行了一层包装,它可以保证在适合时候使用这个对象时对象都不会被释放,其中方法如下:
//创建jsvlaue对象的包装jsmanagervalue
(jsmanagedvalue *)managedvaluewithvalue:(jsvalue *)value;
(jsmanagedvalue *)managedvaluewithvalue:(jsvalue *)value andowner:(id)owner;
- (instancetype)initwithvalue:(jsvalue *)value;
//获取所包装的jsvalue对象
@property (readonly, strong) jsvalue *value;
六、objective-c与javascript复杂对象的映射
? ? 我们在使用javascript调用objective-c方法的实质是将一个oc函数设置为了js全局对象的一个属性,当然我们也可以设置非函数的属性或者任意jsvalue(或者可以转换为jsvalue)的值。例如:
self.jscontext = [[jscontext alloc]init];
//向js全局对象中添加一个获取当前native设备类型的属性
[self.jscontext setobject:@"ios" forkeyedsub:@"devicetype"];
但是如果我们想把oc自定义的一个类的对象设置为js全局对象的某个属性,js和oc有着完全不同的对象原理,如果不做任何处理,js是接收不到oc对象中定义的属性和方法的。这时就需要使用到前面提到的jsexport协议,需要注意,这个协议不是用来被类遵守的,它里面没有规定任何方法,其是用来被继承定义新的协议的,自定义的协议中约定的方法和属性可以在js中被获取到,示例如下:
oc中新建一个自定义的类:
@protocol myobjectprotocol
@property(nonatomic,strong)nsstring * name;
@property(nonatomic,strong)nsstring * subject;
@property(nonatomic,assign)nsinteger age;
-(void)sayhi;
@end
@interface myobject : nsobject
@property(nonatomic,strong)nsstring * name;
@property(nonatomic,strong)nsstring * subject;
@property(nonatomic,assign)nsinteger age;
@end
@implementation myobject
-(void)sayhi{
nslog(@"hello javascript");
}
@end
添加到js全局对象中:
myobject* object = [myobject new];
object.name = @"jaki";
object.age = 25;
object.subject = @"oc";
[jscontext setobject:object forkeyedsub:@"deviceobject"];
在js运行环境中可以完整的到deviceobject对象,如下:
七、c语言风格的api解释
? ? javascriptcore框架中除了包含完整的objective-c和swift语言的api外,也提供了对c语言的支持。
? ? 与js运行环境相关的方法如下:
//创建一个jscontextref组
/*
jscontextref相当于jscontext,同一组中的数据可以共享
*/
jscontextgroupref jscontextgroupcreate(void);
//内存引用
jscontextgroupref jscontextgroupretain(jscontextgroupref group);
//内存引用释放
void jscontextgrouprelease(jscontextgroupref group);
//创建一个全局的运行环境
jsglobalcontextref jsglobalcontextcreate(jsclassref globalobjectclass);
//同上
jsglobalcontextref jsglobalcontextcreateingroup(jscontextgroupref group, jsclassref globalobjectclass);
//内存引用
jsglobalcontextref jsglobalcontextretain(jsglobalcontextref ctx);
//内存引用释放
void jsglobalcontextrelease(jsglobalcontextref ctx);
//获取全局对象
jsobjectref jscontextgetglobalobject(jscontextref ctx);
//获取jscontextref组
jscontextgroupref jscontextgetgroup(jscontextref ctx);
//获取全局的运行环境
jsglobalcontextref jscontextgetglobalcontext(jscontextref ctx);
//获取运行环境名
jsstringref jsglobalcontextcopyname(jsglobalcontextref ctx);
//设置运行环境名
void jsglobalcontextsetname(jsglobalcontextref ctx, jsstringref name);
? ? 与定义js对象的相关方法如下:
//定义js类
/*
参数jsclassdefinition是一个结构体 其中可以定义许多回调
*/
jsclassref jsclasscreate(const jsclassdefinition* definition);
//引用内存
jsclassref jsclassretain(jsclassref jsclass);
//释放内存
void jsclassrelease(jsclassref jsclass);
//创建一个js对象
jsobjectref jsobjectmake(jscontextref ctx, jsclassref jsclass, void* data);
//定义js函数
jsobjectref jsobjectmakefunctionwithcallback(jscontextref ctx, jsstringref name, jsobjectcallasfunctioncallback callasfunction);
//定义构造函数
jsobjectref jsobjectmakeconstructor(jscontextref ctx, jsclassref jsclass, jsobjectcallasconstructorcallback callasconstructor);
//定义数组
jsobjectref jsobjectmakearray(jscontextref ctx, size_t argumentcount, const jsvalueref arguments[], jsvalueref* exception);
//定义日期对象
jsobjectref jsobjectmakedate(jscontextref ctx, size_t argumentcount, const jsvalueref arguments[], jsvalueref* exception);
//定义异常对象
jsobjectref jsobjectmakeerror(jscontextref ctx, size_t argumentcount, const jsvalueref arguments[], jsvalueref* exception);
//定义正则对象
jsobjectref jsobjectmakeregexp(jscontextref ctx, size_t argumentcount, const jsvalueref arguments[], jsvalueref* exception);
//定义函数对象
jsobjectref jsobjectmakefunction(jscontextref ctx, jsstringref name, unsigned parametercount, const jsstringref parameternames[], jsstringref body, jsstringref sourceurl, int startinglinenumber, jsvalueref* exception);
//获取对象的属性
jsvalueref jsobjectgetprototype(jscontextref ctx, jsobjectref object);
//设置对象的属性
void jsobjectsetprototype(jscontextref ctx, jsobjectref object, jsvalueref value);
//检查对象是否包含某个属性
bool jsobjecthasproperty(jscontextref ctx, jsobjectref object, jsstringref propertyname);
//获取对象属性
jsvalueref jsobjectgetproperty(jscontextref ctx, jsobjectref object, jsstringref propertyname, jsvalueref* exception);
//定义对象属性
void jsobjectsetproperty(jscontextref ctx, jsobjectref object, jsstringref propertyname, jsvalueref value, jspropertyattributes attributes, jsvalueref* exception);
//删除对象属性
bool jsobjectdeleteproperty(jscontextref ctx, jsobjectref object, jsstringref propertyname, jsvalueref* exception);
//获取数组值
jsvalueref jsobjectgetpropertyatindex(jscontextref ctx, jsobjectref object, unsigned propertyindex, jsvalueref* exception);
//设置数组值
void jsobjectsetpropertyatindex(jscontextref ctx, jsobjectref object, unsigned propertyindex, jsvalueref value, jsvalueref* exception);
//获取私有数据
void* jsobjectgetprivate(jsobjectref object);
//设置私有数据
bool jsobjectsetprivate(jsobjectref object, void* data);
//判断是否为函数
bool jsobjectisfunction(jscontextref ctx, jsobjectref object);
//将对象作为函数来调用
jsvalueref jsobjectcallasfunction(jscontextref ctx, jsobjectref object, jsobjectref thisobject, size_t argumentcount, const jsvalueref arguments[], jsvalueref* exception);
//判断是否为构造函数
bool jsobjectisconstructor(jscontextref ctx, jsobjectref object);
//将对象作为构造函数来调用
jsobjectref jsobjectcallasconstructor(jscontextref ctx, jsobjectref object, size_t argumentcount, const jsvalueref arguments[], jsvalueref* exception);
//获取所有属性名
jspropertynamearrayref jsobjectcopypropertynames(jscontextref ctx, jsobjectref object);
//进行内存引用
jspropertynamearrayref jspropertynamearrayretain(jspropertynamearrayref array);
//内存释放
void jspropertynamearrayrelease(jspropertynamearrayref array);
//获取属性个数
size_t jspropertynamearraygetcount(jspropertynamearrayref array);
//在属性名数组中取值
jsstringref jspropertynamearraygetnameatindex(jspropertynamearrayref array, size_t index);
//添加属性名
void jspropertynameaccumulatoraddname(jspropertynameaccumulatorref accumulator, jsstringref propertyname);
? ? js数据类型相关定义在jsvalueref中,如下:
//获取值的类型
/*
枚举如下:
typedef enum {
kjstypeundefined,
kjstypenull,
kjstypeboolean,
kjstypenumber,
kjstypestring,
kjstypeobject
} jstype;
*/
jstype jsvaluegettype(jscontextref ctx, jsvalueref);
//判断是否为undefined类型
bool jsvalueisundefined(jscontextref ctx, jsvalueref value);
//判断是否为null类型
bool jsvalueisnull(jscontextref ctx, jsvalueref value);
//判断是否为布尔类型
bool jsvalueisboolean(jscontextref ctx, jsvalueref value);
//判断是否为数值类型
bool jsvalueisnumber(jscontextref ctx, jsvalueref value);
//判断是否为字符串类型
bool jsvalueisstring(jscontextref ctx, jsvalueref value);
//判断是否为对象类型
bool jsvalueisobject(jscontextref ctx, jsvalueref value);
//是否是类
bool jsvalueisobjectofclass(jscontextref ctx, jsvalueref value, jsclassref jsclass);
//是否是数组
bool jsvalueisarray(jscontextref ctx, jsvalueref value);
//是否是日期
bool jsvalueisdate(jscontextref ctx, jsvalueref value);
//比较值是否相等
bool jsvalueisequal(jscontextref ctx, jsvalueref a, jsvalueref b, jsvalueref* exception);
//比较值是否全等
bool jsvalueisstrictequal(jscontextref ctx, jsvalueref a, jsvalueref b);
//是否是某个类的实例
bool jsvalueisinstanceofconstructor(jscontextref ctx, jsvalueref value, jsobjectref constructor, jsvalueref* exception);
//创建undefined值
jsvalueref jsvaluemakeundefined(jscontextref ctx);
//创建null值
jsvalueref jsvaluemakenull(jscontextref ctx);
//创建布尔值
jsvalueref jsvaluemakeboolean(jscontextref ctx, bool boolean);
//创建数值
jsvalueref jsvaluemakenumber(jscontextref ctx, double number);
//创建字符串值
jsvalueref jsvaluemakestring(jscontextref ctx, jsstringref string);
//通过json创建对象
jsvalueref jsvaluemakefromjsonstring(jscontextref ctx, jsstringref string);
//将对象转为json字符串
jsstringref jsvaluecreatejsonstring(jscontextref ctx, jsvalueref value, unsigned indent, jsvalueref* exception);
//进行布尔值转换
bool jsvaluetoboolean(jscontextref ctx, jsvalueref value);
//进行数值转换
double jsvaluetonumber(jscontextref ctx, jsvalueref value, jsvalueref* exception);
//字符串值赋值
jsstringref jsvaluetostringcopy(jscontextref ctx, jsvalueref value, jsvalueref* exception);
//值与对象的转换
jsobjectref jsvaluetoobject(jscontextref ctx, jsvalueref value, jsvalueref* exception);
? ? 在c风格的api中,字符串也被包装成了jsstringref类型,其中方法如下:
//创建js字符串
jsstringref jsstringcreatewithcharacters(const jschar* chars, size_t numchars);
jsstringref jsstringcreatewithutf8cstring(const char* string);
//内存引用于释放
jsstringref jsstringretain(jsstringref string);
void jsstringrelease(jsstringref string);
//获取字符串长度
size_t jsstringgetlength(jsstringref string);
//转成utf8字符串
size_t jsstringgetutf8cstring(jsstringref string, char* buffer, size_t buffersize);
//字符串比较
bool jsstringisequal(jsstringref a, jsstringref b);
bool jsstringisequaltoutf8cstring(jsstringref a, const char* b);
八、hybird app 构建思路
? ? hybird app是指混合模式移动应用,即其中既包含原生的结构有内嵌有web的组件。这种app不仅性能和用户体验可以达到和原生所差无几的程度,更大的优势在于bug修复快,版本迭代无需发版。3月8日苹果给许多开发者发送了一封警告邮件,主要是提示开发者下载脚本动态更改app原本行为的做法将会被提审拒绝。其实这次邮件所提内容和hybird app并无太大关系(对reactnative也没有影响),苹果警告的是网络下发脚本并且使用runtime动态修改native行为的应用,hybird app的实质并没有修改原native的行为,而是将下发的资源进行加载和界面渲染,类似webview。
? ? 关于混合开发,我们有两种模式:
????1.native内嵌webview,通过js与oc交互实现业务无缝的衔接。
? ? 无论是uiwebview还是wkwebkit,我们都可以在其中拿到当前的jscontext,然是使用前面介绍的方法便可以实现数据互通与交互。这种方式是最简单的混合开发,但其性能和原生相比要差一些。示意图如下:
? ? 2.下发js脚本,使用类似reactnative的框架进行原生渲染
? ? 这是一种效率非常高的混合开发模式,并且reactnative也本身支持android和ios公用一套代码。我们也可以使用javascriptcore自己实现一套解析逻辑,使用javascript来编写native应用,要完整实现这样一套东西太复杂了,我们也没有能力完成一个如此庞大的工程,但是我们可以做一个小demo来模拟其原理,这样可以更好的帮助我们理解hybird app的构建原理。
我们打算实现这样的功能:通过下发js脚本创建原生的uilabel标签与uibutton控件,首先编写js代码如下:
(function(){
console.log("progectinit");
//js脚本加载完成后 自动render界面
return render();
})();
//js标签类
function label(rect,text,color){
this.rect = rect;
this.text = text;
this.color = color;
this.typename = "label";
}
//js按钮类
function button(rect,text,callfunc){
this.rect = rect;
this.text = text;
this.callfunc = callfunc;
this.typename = "button";
}
//js rect类
function rect(x,y,width,height){
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
//js颜色类
function color(r,g,b,a){
this.r = r;
this.g = g;
this.b = b;
this.a = a;
}
//渲染方法 界面的渲染写在这里面
function render(){
var rect = new rect(20,100,280,30);
var color = new color(1,0,0,1);
var label = new label(rect,"hello world",color);
var rect2 = new rect(20,150,280,30);
var color2 = new color(0,1,0,1);
var label2 = new label(rect2,"hello native",color2);
var rect3 = new rect(20,200,280,30);
var color3 = new color(0,0,1,1);
var label3 = new label(rect3,"hello javascript",color3);
var rect4 = new rect(20,240,280,30);
var button = new button(rect4,"我是一个按钮",function(){
var randcolor = new color(math.random(),math.random(),math.random(),1);
globle.changebackgroundcolor(randcolor);
});
//将控件以数组形式返回
return [label,label2,label3,button];
}
创建一个objective-c类绑定到js全局对象上,作为oc方法的桥接器:
//.h
#import
#import
#import
@protocol globleprptocol
-(void)changebackgroundcolor:(jsvalue *)value;
@end
@interface globle : nsobject
@property(nonatomic,weak)uiviewcontroller * ownercontroller;
@end
//.m
#import "globle.h"
@implementation globle
-(void)changebackgroundcolor:(jsvalue *)value{
self.ownercontroller.view.backgroundcolor = [uicolor colorwithred:value[@"r"].todouble green:value[@"g"].todouble blue:value[@"b"].todouble alpha:value[@"a"].todouble];
}
@end
在viewcontroller中实现一个界面渲染的render解释方法,并建立按钮的方法转换,如下:
//
// viewcontroller.m
// javascriptcoretest
//
// created by vip on 17/3/6.
// 威尼斯人2299 copyright ? 2017年 jaki. all rights reserved.
//
#import "viewcontroller.h"
#import
#import "globle.h"
@interface viewcontroller ()
@property(nonatomic,strong)jscontext * jscontext;
@property(nonatomic,strong)nsmutablearray * actionarray;
@property(nonatomic,strong)globle * globle;
@end
@implementation viewcontroller
- (void)viewdidload {
[super viewdidload];
//创建js运行环境
self.jscontext = [jscontext new];
//绑定桥接器
self.globle = [globle new];
self.globle.ownercontroller = self;
self.jscontext[@"globle"] = self.globle;
self.actionarray = [nsmutablearray array];
[self render];
}
//界面渲染解释器
-(void)render{
nsstring * path = [[nsbundle mainbundle] pathforresource:@"main" oftype:@"js"];
nsdata * jsdata = [[nsdata alloc]initwithcontentsoffile:path];
nsstring * jscode = [[nsstring alloc]initwithdata:jsdata encoding:nsutf8stringencoding];
jsvalue * jsvlaue = [self.jscontext evaluatescript:jscode];
for (int i=0; i
jsvalue * subvalue = [jsvlaue objectatindexedsub:i];
if ([[subvalue objectforkeyedsub:@"typename"].tostring isequaltostring:@"label"]) {
uilabel * label = [uilabel new];
label.frame = cgrectmake(subvalue[@"rect"][@"x"].todouble, subvalue[@"rect"][@"y"].todouble, subvalue[@"rect"][@"width"].todouble, subvalue[@"rect"][@"height"].todouble);
label.text = subvalue[@"text"].tostring;
label.textcolor = [uicolor colorwithred:subvalue[@"color"][@"r"].todouble green:subvalue[@"color"][@"g"].todouble blue:subvalue[@"color"][@"b"].todouble alpha:subvalue[@"color"][@"a"].todouble];
[self.view addsubview:label];
}else if ([[subvalue objectforkeyedsub:@"typename"].tostring isequaltostring:@"button"]){
uibutton * button = [uibutton buttonwithtype:uibuttontypesystem];
button.frame = cgrectmake(subvalue[@"rect"][@"x"].todouble, subvalue[@"rect"][@"y"].todouble, subvalue[@"rect"][@"width"].todouble, subvalue[@"rect"][@"height"].todouble);
[button settitle:subvalue[@"text"].tostring forstate:uicontrolstatenormal];
button.tag = self.actionarray.count;
[button addtarget:self action:@selector(buttonaction:) forcontrolevents:uicontroleventtouchupinside];
[self.actionarray addobject:subvalue[@"callfunc"]];
[self.view addsubview:button];
}
}
}
//按钮转换方法
-(void)buttonaction:(uibutton *)btn{
jsvalue * action = self.actionarray[btn.tag];
//执行js方法
[action callwitharguments:nil];
}
@end
运行工程,效果如下图所示,点击按钮即可实现简单的界面颜色切换:
上面的示例工程我只实现了uilabel类与uibutton类的js-oc转换,如果将原生控件和js对象再进行一层绑定,并且实现大部分js类与原生类和他们内部的属性,则我们就开发了一套hybird app开发框架,但并没有这个必要,如果你对更多兴趣,可以深入学习下reactnative。
? ? 文中的示例demo我放在了github上,地址如下:github - zyhshao/demo-hybird: 混合开发在ios中的构建思路demo。
前端学习新人,有志同道合的朋友,欢迎交流与指导,qq群:541458536
转载于: my.oschina /u/2340880/blog/856321
还没有评论,来说两句吧...