note's People
note's Issues
Web UI 库
AM是支付宝无线Web开放解决方案的简称 ,全称AlipayMobileWebOpenSolution。
AM不是一个具体的框架和类库,它是支付宝无线Web开发经验的总结和累积,集合了无线Web各要素在一体的一系列解决方案
它从无线Web的各个方面提供了针对性的方案/工具集,能大幅度提升无线Web网站开发效率
AM主要提供了移动Web开发的样式,脚本、控件以及WebApp开发等工具之外,还提供了相关的无线建站规范、指南以及文档和调试方案等
iOS开发常用第三方库
这个链接对常用的第三方库做了详情的分类:
http://github.ibireme.com/github/list/ios/
iOS设备分辨率、App各种图标尺寸
Array
由于数组赋值和传参是值拷贝的,所以会引起性能问题,通常使用slice或数组指针
数组是值类型,赋值和传参都会复制整个数组,而不是指针
a0 := [...]int{1,2,3}
a1 := a0 // 复制整个数组
a1[0] = 99
fmt.Println(a0,a1)
// 输出[1,2,3] [99,2,3]
- 数组的长度必须是常量、且是类型的组成部分。[2]int和[3]int是不同的类型
- 支持"=="、"!="操作符,因为内存总是被初始化过的
条件:
首先数组中的元素可以比较
数组的长度必须相等
- 指针数组[n]T,数组指针[n]T
声明:
var a [len]T
,len
是数组的长度,T
是数组中元素的类型。数组的长度是在编译器确定的,所以len
必须是一个常量.
初始化:
a0 := [3]{1,2,3} //长度为3的数组
a1 :=[...]{1,2,3} // 根据初始化的元素个数决定数组的长度
a2 := [4]{0:1986,2:826} // 通过索引指定元素的值
type person struct {
name string
age uint8
}
a := [...]person {
person{"LiHong",29},
person{"WangQinQin",26},//别忘了,号
}
内置的len和cap函数返回数组元素的个数
拥抱原型面向对象编程
访问Photos应用中的资源
在iOS中,可以通过UIImagePickerController,ALAssetsLibrary(在iOS9.0中已经弃用,请使用PHPhotoLibrary),PHPhotoLibrary
UIImagePickerController
与ALAssetsLibrary
和PHPhotoLibrary
区别是:前者提供了预定的UI界面,后者提供了获取Photos应用资源的api并且能做更多的操作。
UIImagePickerController使用教程
基于共享变量的并发
竞争条件指
竞争条件指的是程序在多个goroutine交叉执行操作时,没有给出正确的结果。竞争条件是很恶劣的一种场景,因为这种问题会一直潜伏在你的程序里,然后在非常少见的时候蹦出来,或许只是会在很大的负载时才会发生,又或许是会在使用了某一个编译器、某一种平台或者某一种架构的时候才会出现。这些使得竞争条件带来的问题非常难以复现而且难以分析诊断。
无论任何时候,只要有两个goroutine并发访问同一变量,且至少其中的一个是写操作的时候就会发生数据竞争。
sync.Mutex互斥锁
Array、Slice、Map、Struct初始化语法对比
Array
a0 := [3]int{} //[0 0 0]
a1 := [3]int{1,2,3} //[1 2 3]
a2 := [...]int{1,2,3}//[1,2,3] [...]表示数组的元素由初始元素个数决定
a3 :=[5]int{1:99,3:98}//[0,99,0,98,0] 通过索引指定对应的值,其他没有设置的初始化为0
Slice
MySQL 教程
用于Objective-c的ORM
英文官网: https://realm.io/
中文官网: https://realm.io/cn/
类型断言
类型断言是一个使用在接口值上的操作
换言之就是只能在接口值上使用类型断言。
类型断言有两个作用:
i.(T),T为具体类型: 检查接口值i的动态类型是否是具体类型T
v := i.(T) //如果i的动态类型是T,那么v的值是i的动态值。否则发生panic
v,ok := i.(T)//如果i的动态类型是T,那么v的值是i的动态值,ok为true。否则ok为false,v是T类型的零值.
i.(T),T为接口: 检查i的动态类型是否实现了接口T。fmt包的函数就是通过这种方法了检查类型是否实现了fmt.Stringer接口的。
v := i.(T) //如果i的动态类型实现了接口T,那么v的值是i的动态值。否则发生panic
v,ok := i.(T)//如果i的动态类型现了接口T,那么v的值是i的动态值,ok为true。否则ok为false,v为nil.
type-switch
//是go中用来做类型断言的特殊switch语法
switch x.(type) {
case *QQ: //注意case语句中的类型必须是实现了IM接口的类型,否则编译会报错
case *Weixn:
default:
}
import (
"fmt"
"testing"
)
type IM interface {
Send()
}
//QQ
type QQ struct {}
func (*QQ) Send() {}
//微信
type WeiXin struct {}
func (*WeiXin) Send() {}
//陌陌
type MoMo struct {}
func (*MoMo) Send() {}
// 微博
type WeiBo struct {}
func (*WeiBo) Send() {}
// 阿里旺旺
type WanWan struct {}
func (*WanWan) Send() {}
// 京东
type JinDong struct {}
func (*JinDong) Send(){}
/*输出:
Dynamic type of im is WanWan or JinDong
WanWan or JinDong:*word.WanWan &{}
PASS
ok */
func TestRun(t *testing.T) {
var im IM = &WanWan{}
// 第一种形式
switch im.(type) {
case *QQ://注意case语句中的类型必须是实现了IM接口的类型,否则编译会报错
fmt.Println("Dynamic type of im is QQ")
case *WeiXin:
fmt.Println("Dynamic type of im is WeiXin")
case *MoMo:
fmt.Println("Dynamic type of im is MoMo")
case *WeiBo:
fmt.Println("Dynamic type of im is WeiBo")
case *WanWan,*JinDong:
fmt.Println("Dynamic type of im is WanWan or JinDong")
default:
fmt.Println("Not found excepted type")
}
// 第二种形式.v为im的动态值
switch v := im.(type) {
case *QQ:
fmt.Printf("QQ:%T %V\n",v,v)
case *WeiXin:
fmt.Printf("WeiXin:%T %V\n",v,v)
case *MoMo:
fmt.Printf("MoMo:%T %V\n",v,v)
case *WeiBo:
fmt.Printf("WeiBo:%T %V\n",v,v)
case *WanWan,*JinDong:
fmt.Printf("WanWan or JinDong:%T %V\n",v,v)
default:
fmt.Printf("NotFound:%T %V\n",v,v)
}
}
iOS中使用多少线
在iOS中可以通过NSThread
、NSOperation
、GCD
来提供线程支持。
下面谈下他们各自的优缺点:
iOS中播放音频、录制音频
iOS中播放音频、录制音频
Audio Session
Audio Session,
AVAudioSession,
Audio Session Programming Guide,
Audio Session Services Reference
使用AVAudioSession
你可以解决如下问题:
- 当用户切换iOS设备的静音/不静音键时你的app是否会被静音
- 当其他应用播放音乐时你的app是否会被停止播放
- 响应用户插拔耳机、电话呼入、闹钟、日历提醒响起等事件
播放音频
1.播放iPod库的音频
2.播放UI声音效果(点击某个按钮时的音效)
3.播放本地音频文件(苹果推荐使用AVVideoPlayer)
4.播放流音频
录音
iOS支持使用AVAudioRecorder 和Audio Queue Services来进行录音。
管理MySQL数据库
**Summary:**in this tutorial, you will learn how to manage database in MySQL.you will learn how to remove existing database, and display all databases in MySQL database server.
创建数据库
使用数据之前需要先创建一个数据库,数据库存储诸如联系人、供应商、顾客等等任何你能想到的数据。在MySQL中数据库包含表
、视图
、触发器
、存储过程
等。
在MySQL中创建数据库是实用create database
语句,例如:create database [if not exists] taobao
创建一个名为taobao的数据库。
显示数据库服务器的所有数据库
show databases
使用数据库
数据库服务器一般同时存在多个数据库,我们需要选择一个使用:
use database_name
删除数据库
drop database [if exists] database_name
H5 缓存机制浅析
https://segmentfault.com/a/1190000004132566
1. H5 缓存机制介绍
H5,即 HTML5,是新一代的 HTML 标准,加入很多新的特性。离线存储(也可称为缓存机制)是其中一个非常重要的特性。H5 引入的离线存储,这意味着 web 应用可进行缓存,并可在没有因特网连接时进行访问。
H5 应用程序缓存为应用带来三个优势:
离线浏览 用户可在应用离线时使用它们
速度 已缓存资源加载得更快
减少服务器负载 浏览器将只从服务器下载更新过或更改过的资源。
根据标准,到目前为止,H5 一共有6种缓存机制,有些是之前已有,有些是 H5 才新加入的。
浏览器缓存机制
Dom Storgage(Web Storage)存储机制
Web SQL Database 存储机制
Application Cache(AppCache)机制
Indexed Database (IndexedDB)
File System API
下面我们首先分析各种缓存机制的原理、用法及特点;然后针对 Anroid 移动端 Web 性能加载优化的需求,看如果利用适当缓存机制来提高 Web 的加载性能。
go test
在包目录内,所有以_test.go为后缀名的源文件并不是go build构建包的一部分,它们是go test测试的一部分。
在__test.go文件中,有三种类型的函数:测试函数、基准测试函数、示例函数。一个测试函数是以
Test
为函数名前缀的函数,用于测试程序的一些逻辑行为是否正确;go test命令会调用这些测试函数并
报告测试结果是PASS或FAIL。基准测试函数是以Benchmark
为函数名前缀的函数,它们用于衡量一些
函数的性能;go test命令会多次运行基准函数以计算一个平均的执行时间。示例函数是以Example
为函数名前缀的函数,提供一个由编译器保证正确性的示例文档。
go test命令会遍历所有的__test.go文件中符合上述命名规则的函数,然后生成一个临时的main包用
于调用相应的测试函数,然后构建并运行、报告测试结果,最后清理测试中生成的临时文件。
一:测试函数
method
// 声明
func (receiver type) methodName(...arg list) (return value..)
//调用
receiver.selector(...arg list)
go语言中方法可以被声明到指针或者interface类型以外的任意类型(包括函数类型)。
只能为包内的类型定义方法,如果为其他的包的类型定义方法,会出现编译错误cannot define new methods on non-local type
。所以不能为int,float,...基础类型定义方法。
指针或值作为接收者
1.如果method的receiver是T类型,那么既可以用通过类型为T的receiver也可以通过类型为_T的receiver来调用method。但通过类型为_T的recevier来调用method时,编译器会自动解引用。
type NewType struct{}
// receiver类型为T
func (NewType) M(){fmt.Println("call M()")}
func main() {
t := NewType{}
p := new(NewType)
t.M()
p.M() //编译器做了隐试转换,实际是(*p).M()
}
2.如果method的receiver是_T类型,那么既可以用通过类型为T的receiver也可以通过类型为_T的receiver来调用method。在通过类型为T的receiver来调用method时编译器会自动做取地址操作.这种简写方法只适用于“变量”,包括struct里的字段比如p.X,以及array和slice内的元素比如perim[0]。我们不能通过一个无法取到地址的接收器来调用指针方法,比如临时变量的内存地址就无法获取得到:NewType{}.M()。这将会报错:cannot call pointer method on NewType literal,cannot take the address of StrValue literal
type NewType struct{}
//receiver类型为*T
func (*NewType) M(){fmt.Println("call M()")}
func main() {
t := NewType{}
p := new(NewType)
t.M()////编译器做了隐试转换,实际是(&p).M()
p.M()
}
Nil也是一个合法的接收器类型
方法值和方法表达
type NewType struct{
Name string
}
func (n *NewType) M(){fmt.Println(n.Name)}
func main() {
t := &NewType{"LiHong"}
//返回一个方法值,可直接调用methodValue而不用指定receiver
methodValue := t.M
/* 方法表达式:返回一个函数值,函数值使用第一个参数作为method的receiver
如果receiver是T那么表达式为T.Selector,如果receiver是*T那么表达式为(*T).Selector
*/
methodExpr := (*NewType).M
methodValue()
methodExpr(t)
}
杂记
不要把加载动画封装到一个BaseViewController中,如果封装到一个基控制器中,那么只有继承这个控制的控制器才能使用加载动画。应该把加载动画独立出来。
database/sql
警告:捕获迭代变量
以下内容摘录自go程序设计语言的5.6.1小节
本节,将介绍Go词法作用域的一个陷阱。请务必仔细的阅读,弄清楚发生问题的原因。即使是经验丰富的程序员也会在这个问题上犯错误。
考虑这个样一个问题:你被要求首先创建一些目录,再将目录删除。在下面的例子中我们用函数值来完成删除操作。下面的示例代码需要引入os包。为了使代码简单,我们忽略了所有的异常处理。
var rmdirs []func()
for _, d := range tempDirs() {
dir := d // NOTE: necessary!
os.MkdirAll(dir, 0755) // creates parent directories too
rmdirs = append(rmdirs, func() {
os.RemoveAll(dir)
})
}
// ...do some work…
for _, rmdir := range rmdirs {
rmdir() // clean up
}
你可能会感到困惑,为什么要在循环体中用循环变量d赋值一个新的局部变量,而不是像下面的代码一样直接使用循环变量dir。需要注意,下面的代码是错误的。
var rmdirs []func()
for _, dir := range tempDirs() {
os.MkdirAll(dir, 0755)
rmdirs = append(rmdirs, func() {
os.RemoveAll(dir) // NOTE: incorrect!
})
}
问题的原因在于循环变量的作用域。在上面的程序中,for循环语句引入了新的词法块,循环变量dir在这个词法块中被声明。在该循环中生成的所有函数值都共享相同的循环变量。**需要注意,函数值中记录的是循环变量的内存地址,而不是循环变量某一时刻的值。**以dir为例,后续的迭代会不断更新dir的值,当删除操作执行时,for循环已完成,dir中存储的值等于最后一次迭代的值。这意味着,每次对os.RemoveAll的调用删除的都是相同的目录。
通常,为了解决这个问题,我们会引入一个与循环变量同名的局部变量,作为循环变量的副本。比如下面的变量dir,虽然这看起来很奇怪,但却很有用。
for _, dir := range tempDirs() {
dir := dir // declares inner dir, initialized to outer dir
// ...
}
这个问题不仅存在基于range的循环,在下面的例子中,对循环变量i的使用也存在同样的问题:
var rmdirs []func()
dirs := tempDirs()
for i := 0; i < len(dirs); i++ {
os.MkdirAll(dirs[i], 0755) // OK
rmdirs = append(rmdirs, func() {
os.RemoveAll(dirs[i]) // NOTE: incorrect!
})
}
如果你使用go语句(第八章)或者defer语句(5.8节)会经常遇到此类问题。这不是go或defer本身导致的,而是因为它们都会等待循环结束后,再执行函数值。
iOS热更新框架
接口
go中的接口
接口是一组方法的集合
很多面向对象的语言都有相似的接口概念,但Go语言中接口类型的独特之处在于它是满足
隐式实现的
。也就是说,我们没有必要对于给定的具体类型定义所有满足的接口类型;简单地拥有一些必需的方法就足够了。这种设计可以让你创建一个新的接口类型满足已经存在的具体类型却不会去改变这些类型的定义
;当我们使用的类型来自于不受我们控制的包时这种设计尤其有用。
type Name interface {
funcName(ArgList...) ReturnType
...
}
接口内嵌
package io
type Writer interface {
Write(p []byte) (n int, err error)
}
type Reader interface {
Read(p []byte) (n int, err error)
}
type Closer interface {
Close() error
}
// 接口内嵌(类似接口内嵌)
type ReadWriter interface {
Reader
Writer
}
type ReadWriteCloser interface {
Reader
Writer
Closer
}
实现接口
一个类型如果拥有一个接口需要的所有方法,那么这个类型就实现了这个接口。
方法集
每个类型都有与之关联的方法集合,这会影响到接口的实现规则
- 类型T的方法集包含全部receiver为T的方法
- 类型*T的方法集包含全部receiver为T和 *T的方法
问:既然类型T不包括*T的方法,那么为什么下面的代码可以正常运行?
import "testing"
type IM interface {
IM() string
}
type QQ struct {}
func (_ *QQ) IM() string {return `QQ`}
func TestQQ(t *testing.T) {
qq := QQ{}
qq.IM() //编译器做了转换,实际是这样的:(&qq).IM()
}
测试方法集对实现接口的影响:
package word
import "testing"
type IM interface {
IM() string
}
type QQ struct {}
func (_ *QQ) IM() string {return `QQ`}
type WeiXin struct {}
func (_ WeiXin) IM() string{ return `WeiXin`}
var im IM
/* 这个测试将会失败,因为类型QQ上没有实现IM()方法,所以不能赋值给接口IM
cannot use qq (type QQ) as type IM in assignment:
QQ does not implement IM (IM method has pointer receiver*/
func TestQQ(t *testing.T) {
qq := QQ{}
im = qq
}
func TestQQ2(t *testing.T) {
qq := &QQ{}
im = qq
}
func TestWeiXin(t *testing.T) {
wx := WeiXin{}
im = wx
}
/*这个测试会成功,因为类型*WeiXin的方法集包括了类WeiXin方法,所以实现了IM接口*/
func TestWeiXin2(t *testing.T) {
wx := &WeiXin{}
im = wx
}
接口值
接口值由接口的动态类型和动态值决定。如果一个接口的动态类型和动态值是nil,那么这个接口的值才为nil。
Slice
- 引用类型。但自身是结构体,值拷贝传递
// runtime/slice.go
type slice struct {
array unsafe.Pointer // 指向底层数组
len int // 长度.通过索引访问元素不能超过这值
cap int // 容量
}
- 两个slice不支持==和!= 操作,但可以与nil进行比较。
/*零值的slice等于nil。一个nil值的slice并没有底层数组。一个nil值的slice的长度和容量都是0,但是也
有非nil值的slice的长度和容量也是0的.与任意类型的nil值一样,我们可以用[]int(nil)类型转换表达式来生
成一个对应类型slice的nil值。*/
var s1 []int // len=0 cap=0 s1 == nil
s2 := []int(nil) //true len=0 cap=0 s2==nil
s3 := []int{} //false len=0 cap=0 s3 != nil
声明和创建slice
var s []T
通过[n:m]操作创建slice
var a [10]int
s0 := a[:] // 从数组中创建slice
s1 := s[0:cap(s0)-2] // 从切片中创建切片
通过make函数创建slice
/*内置的make函数创建一个指定元素类型、长度和容量的slice。容量部分可以省略,
在这种情况下,容量将等于长度。*/
make([]T, len)
make([]T, len, cap) // same as make([]T, cap)[:len]
panic操作
- 通过索i引访问切片s的元素时,若i大于len(s)或小于0将会引发panic
- 扩张切片s时,s[:j]若,j大于cap(s),将会引发panic
append函数
append函数对于理解slice底层是如何工作的非常重要,所以让我们仔细查看究竟是发生了什么。下面是第一个版本的appendInt函数,专门用于处理[]int类型的slice:
func appendInt(x []int, y int) []int {
var z []int
zlen := len(x) + 1
if zlen <= cap(x) {
// There is room to grow. Extend the slice.
z = x[:zlen]
} else {
// There is insufficient space. Allocate a new array.
// Grow by doubling, for amortized linear complexity.
zcap := zlen
if zcap < 2*len(x) {
zcap = 2 * len(x)
}
z = make([]int, zlen, zcap)
copy(z, x) // a built-in function; see text
}
z[len(x)] = y
return z
}
每次调用appendInt函数,必须先检测slice底层数组是否有足够的容量来保存新添加的元素。如果有足够空间的话,直接扩展slice(依然在原有的底层数组之上),将新添加的y元素复制到新扩展的空间,并返回slice。因此,输入的x和输出的z共享相同的底层数组。
如果没有足够的增长空间的话,appendInt函数则会先分配一个足够大的slice用于保存新的结果,先将输入的x复制到新的空间,然后添加y元素。结果z和输入的x引用的将是不同的底层数组。
内置的append函数可能使用比appendInt更复杂的内存扩展策略。因此,通常我们并不知道append调用是否导致了内存的重新分配,因此我们也不能确认新的slice和原始的slice是否引用的是相同的底层数组空间。同样,我们不能确认在原先的slice上的操作是否会影响到新的slice。因此,通常是将append返回的结果直接赋值给输入的slice变量
runes = append(runes, r)
copy函数
内置函数copy
用于在两个slice之间复制数据。注意:不能用于array
copy(dst,src),复制的数据个数以len小的slice为准。
struct
结构体内嵌
type A struct {
Name string
}
func (this A) P(){
fmt.Println(this.Name)
}
type B struct {
A
}
type C struct {
B
}
func main() {
b := C{B{A{"Li Hong"}}}
b.P()
fmt.Println(b.Name)
}
也可以内嵌一个命名类型的指针
type A struct {
Name string
}
func (this A) P(){
fmt.Println(this.Name)
}
type B struct {
*A
}
func main() {
b := B{&A{"Li Hong"}}
b.P()
fmt.Println(b.Name)
}
当编译器解析一个选择器到方法时,比如b.P,它会首先去找直接定义在这个类型里的
P方法,然后找被B的内嵌字段们引入的方法,然后去找B内嵌字段引入的方法,然后一直递归向下找。如果选择器有二义性的话编译器会报错,比如你在同一级里有两个同名的方法。
内嵌字段会指导编译器去生成额外的包装方法
type A struct {
Name string
}
func (this A) P(){
fmt.Println(this.Name)
}
type B struct {
A
}
func main() {
b := B{A{"Li Hong"}}
b.P() /// output "LiHong"
}
编译器会为内嵌字段B生成一个包装方法:
func (this B) P() {
this.A.P()
}
调用b.P()时,实际上调用的是这个包装方法.下面显示的定义这个包装方法,然后调用b.P()输出的是"SoS!!"而不是"Li Hong"
type A struct {
Name string
}
func (this A) P(){
fmt.Println(this.Name)
}
type B struct {
A
}
func (this B) P() {
fmt.Println("SoS!!")
}
func main() {
b := B{A{"Li Hong"}}
b.P() // 输出SoS!!而不是"Li Hong"
}
flag包
flag包用于解析命令行标签
首先来看下几个结构:
// Flag表示一个命令行标签 -name=value对
type Flag struct {
Name string // name as it appears on command line
Usage string // help message
Value Value // value as set
DefValue string // default value (as text); for usage message
}
//Value表示一个命令行标签值
type Value interface {
String() string
Set(string) error
}
// FlagSet表示一个命令行标签的结合
type FlagSet struct {
Usage func()
name string
parsed bool
actual map[string]*Flag //存放实际解析出来的标签(时命令行标签集和formal的交集)
formal map[string]*Flag // 存放用户设置的标签
args []string // 存放命令行参数
errorHandling ErrorHandling
output io.Writer // nil means stderr; use out() accessor
}
首先看下flag包中实现了Value接口的类型:
// 这些类型都实现了Value接口,所以都可以赋值给Flag中的Value字段
type boolValue bool
type intValue int
type int64Value int64
type uintValue uint
type uint64Value uint64
type stringValue string
type float64Value float64
type durationValue time.Duration
核心函数
/* 这个函数使用传入的参数生存一个Flag实列并放到FlagSet的formal数组中
所有绑定标签的接口在底层都是调用这个函数*/
func (f *FlagSet) Var(value Value, name string, usage string)
// 这个函数解析一个命令行标签
func (f *FlagSet) parseOne()
strong、weak、retain、assign、unsafe_unretain
我觉得这种问题,如果你想真的掌握,没有比官方文档更靠谱的了。
Transitioning to ARC Release Notes
这个是 Apple developer site 里面的关于 ARC 的文档。
如果上面那个还满足不了你,看下面这个:
Objective-C Automatic Reference Counting (ARC)
这个是 LLVM Clang项目里面的对 ARC 的文档,在更底层接近编译的一级介绍了 ARC 的方方面面。
Javascript与Obj-C通信
Javascript与Obj-C通信的方案
反射
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.