更新,看了一遍memcached,觉得果然比redis更适合做缓存,支持多线程就不多说了,CAS操作依赖内建token而不是像redis一样依赖事务就能很完美解决我如上的抽奖需求,最起码会比redis的实现方案优雅很多。
作者: SGNFlamel
Redis开启远程访问
17.11.3更新
实在抱歉没有在发表后仔细查看,我写文章的时候用的两个英文减号,犯懒没有上代码高亮,结果两个减号自动变成了一条横线。。导致执行错误,为大家说声对不起
首先来补个上篇redis文章的漏,我们在后台开启了redis,但是如何关闭呢?
假设没有更改端口,那么就使用如下语句:
redis-cli -p 6379 shutdown
下面来进入主题:
当我们应用docker的时候,docker内部的应用是可以直接通过宿主机IP来访问宿主机的,但是这种时候访问redis会被当做是远程访问了,而redis默认是关闭了远程访问的,那么首先我们需要开启,去哪里开启呢?自然还是在redis.conf中,上一篇文章已经介绍了这个文件,可以直接修改,当然我们这里还是使用’–‘语法来直接在启动时候改变。
但是首先还是来理解下redis.conf文件中如何关闭了远程访问,进入redis.conf,我们可以看到如下命令:
bind 127.0.0.1
就是这一条,限定了redis只接收本地端口的访问,看来我们只要更改了它的值就可以改变了,可是不要急,还有大段的注释我们没有看,当然这里我们就只截取对我们这个问题有用的:
# When protected mode is on and if: # # 1) The server is not binding explicitly to a set of addresses using the # “bind” directive. # 2) No password is configured. |
可以看到redis还存在protected mode,只要这个模式是开着的,就算更改了bind的值,如果没有设定密码的话,也无法开启远程访问:
protected-mode yes
那么如果不想设置密码,改成no就好了
所以直接这么写:
redis-server --bind 0.0.0.0 --protected-mode no --daemonize yes
而远程访问就可以是:
redis-cli -h your ip -p port you set
可是连密码都不要,你的数据库是只要任何人试出来你的端口就可以随便访问了,这个肯定也是不行的,所以还是要设置个密码,再次去翻看redis.conf,可以看到如下命令被注释掉了
# requirepass foobared
这个词并没有意思,我第一反应是来自官方的恶意,fool + bared,愚蠢的裸露,意思是你们并没有设置密码,也挺有意思。只要解注释这一行,改一下就好了。当然我们会直接这么写:
redis-server --bind 0.0.0.0 --requirepass 1234567 --daemonize yes
已经设置了密码,就不必关闭protected-mode了
远程访问就变成了:
redis-cli -h your ip -p port you set -a your password
参考文献:
如何后台启动Redis
在安装好redis之后,运行src/redis-server命令之后,redis是跑在前台运行的,Ctrl+C之后redis会将内存中的数据保存到硬盘并退出,在本地测试还好,那就多开一个terminal就行了,可是当在服务器上的时候,难道还一直开着一个terminal么?所以我就在思考如何把redis在后台运行,当然这里肯定是指redis server了。于是乎我发现了这篇文章:
文章中的第一种方式,即在启动命令后加入’&’的方法并不管用,而我又嫌麻烦并不想去修改配置文件,但我注意到了redis的配置文件也是标准的.conf文件,那么就说明这里可以直接用’–‘语法来在启动的时候来定义参数的值。
所以只要简单的使用如下方法即可令redis在后台启动:
src/redis-server –daemonize yes
可以看到这个参数就是上文的作者给出的第二种方式中需要修改配置文件的那个参数,而相比作者给的方式,我自认为是我的方法是要优雅许多的。
文章很短,所以这里多说一点,就来谈谈程序员的优雅吧。
首先我认为懒和怕麻烦才是程序员进步的第一大动力。
这一个原因为程序员们贡献了无数的语法糖和广为猿知的复用思想。
为什么会有语法糖?一个程序员之所以能够运用甚至写出一个优雅的语法糖,首先他需要对当前的语言有足够的了解,就好比同样的判断赋值有人会去写if else而有人会写三元运算,这两者在编译器识别的层面上来讲没有任何区别,但在我看来明显是三元运算更优雅。
复用就更不用多说了,每个地方写一遍这种做法的愚蠢程度都不用别人来指出,相信程序员自己都会写着写着就会觉得麻烦而开始他的第一次复用之旅。当然如何在大量复用的情况下减少互相之间的依赖,这又是一门学问。盲目的大量复用也许会导致包之间的依赖过深而最终牵一发而动全身,所以复用虽然简单,但想要用好了也同样是需要对语言的了解掌握程度的。
多说了一点程序员的优雅,其实并不是鼓励大家去变懒去怕麻烦,而是要学会自己寻找到更好的道路。
MacOS彻底卸载MySQL
环境:mac版本10.12.3,MySQL旧版本5.7.10
很久之前就是在搭这个blog的时候,在本机安装了MySQL,为了本地测试下,之后就再没有动过。如今转向后端开发,又开始接触MySQL,结果就出了问题,旧版本的MySQL卸载不干净,以至于不论用什么方法装上的新版本MySQL中用户密码版本都不会变,百度了很多办法也无法解决后,最终还是转投了Google(这里倒并不是黑百度,因为最终的解决办法和百度到的很接近,只不过少了两步,估计是搬运的人没搬齐全吧)。
以下是解决方法,一定严格的每一步都执行下去,同时我把我认为的点注释出来:
- ps –ax | grep mysql //获取mysql的进程,开头的数字就是进程的pid
- stop and kill any MySQL processes //可以直接kill -9 pid,但是我用上一条查出来的是mysqld的一个进程,kill的时候提示我没有权限,在活动监视器里也无法强制退出,用了如下来自stackoverflow的解决办法:sudo mysqld stop 一定注意关掉mysql相关进程是必须的,我这里查出来是一条,也可能会有不止一条,一定全部关闭
- brew remove mysql // brew开头这两点是使用homebrew安装的MySQL的选择,如果没有则不用
- brew cleanup
- sudo rm /usr/local/mysql // rm命令就是删除命令了,搭配起来删除所有的MySQL相关的文件,会有几条提示没找到相关文件,也不用担心
- sudo rm –rf /usr/local/var/mysql
- sudo rm –rf /usr/local/mysql*
- sudo rm ~/Library/LaunchAgents/homebrew.mxcl.mysql.plist
- sudo rm –rf /Library/StartupItems/MySQLCOM
- sudo rm –rf /Library/PreferencePanes/My*
- launchctl unload –w ~/Library/LaunchAgents/homebrew.mxcl.mysql.plist
- vim /etc/hostconfig and remove the line MYSQLCOM=-YES– //这个文件我这里也没有,注意and开始后面是教你如何做的,不要一起复制进去
- rm –rf ~/Library/PreferencePanes/My*
- sudo rm –rf /Library/Receipts/mysql*
- sudo rm –rf /Library/Receipts/MySQL*
- sudo rm –rf /private/var/db/receipts/*mysql*
- restart your computer just to ensure any MySQL processes are killed // 重启也很重要,都删完之后重启,继续运行第一条看还有没有,不过我这里有一个干扰项,我仍然查到了一条mysql进程,不过并没有运行,但是在活动监视器中搜索已经没有了MySQL进程,这时候就可以重新安装了,大功告成。
- try to run mysql, it shouldn’t work
至于如何安装MySQL,可以使用homebrew,或者直接MySQL的官网下载,我的新版本即是在官网下载的,其实我总觉得官网下载的installer稳妥点,会配置的稍微好点,当然也可能是心理作用。
参考文献:
删除MySQL:
Uninstall all those broken versions of MySQL
How to stop mysqld – stackoverflow
安装MySQL:
官网:MySQL Website 推荐dmg的版本
Morser-Translator is Ready!
At the first time, I want to make a game for each of the chatter to solve morse code puzzles. But for the time limits I made this little app first. And I promise that my original idea will come out very soon, please look forward to it.
For any suggestions or problems, Please leave a comment under this article or send an email to my mailbox: flamelswift@gmail.com
I’ll reply you or solve the problem as quick as I can.
And here are some captures of my app.
每日灵签上线啦!
如果有问题意见或建议请在本篇文章下留言。或者,
发送邮件到本人的邮箱:flamelapp+dailyluckyask@gmail.com
If you have any problem or suggestion please leave a comment under this article. Or,
You can send an email to my mailbox: flamelswift@gmail.com
提示:一次性购买30支签可以提供6折优惠哦!
Hint: You can get 40% discount by ordering 30 qians at one time!
CocoaPods 1.1.0无法更新老项目中原有Target的解决办法
更新了Xcode8之后,重新打开好久没维护的一个项目,结果发现Alamofire4.X和iOS10以前的版本并不兼容,一狠心,不兼容9.X系列了。
安装Alamofire4.X需要CocoaPods 1.1.0,更新了pod之后我发现执行pod install的时候一直提示我的extension target需要host target,Google了各种解决办法也都没有解决,最终尝试了stackoverflow里看到的一条评论的方法:将老的target删了之后再重新添加,最终解决问题。。
应该算个比较笨的方法吧,不过也算是解决了问题。
Xcode8中无法设定extension图标的解决办法
更新了Xcode8之后一直在忙公司的项目,现在才得空照顾下自己的APP,我的APP有一个action extension的Target,先是pod抽风安不上库了,后来又发现图标找不到了。后来发现原来设置图标的位置被苹果转移了
没错,转移到了Build setting里了。。
但是我在找到了解决方法之后又遇到了问题,就是根本没有这一项啊!!后来经过我自己摸索,发现需要先在Copy bundle resources里面加入你存有图标的Asset,然后这一项会自动出现。。
真是服了Apple的程序员,是觉得以前设置图标的方式不符合探索精神么。。
Swift的get与set与willSet与didSet详解
最近写程序越来越会偷懒了,可能也是因为对swift越来越熟悉了吧,以至于最近连if都不想写了都直接用三元运算符了。。这也是今天这篇文章的导火索,就是因为我有两个布尔变量,来判断是否是pulling和headPulling(简称p和hp),hp为true时p一定也为true,但p为true时hp就不一定为true,因为还会有footPulling的情况,你问为什么不是三个布尔变量?因为我懒啊!但我还想更懒,我完全不想去管p,而是想只改hp就可以完全满足我判断究竟是fp还是hp以及p的情况,该怎么样呢?于是我就想起来了以前看到过的get和set,但是虽然抄了过来,但具体意义什么的完全没深究过,这次就研究了一下!
swift的计算属性
Google了一番才知道get和set这两位的术语是:Computed Properties. 即计算属性。其实从名字就可以看出来,其真正的作用是计算,而非存储。不少从OC转来的开发者可能一开始会将其按照原本的getter和setter方法来理解而导致一些疑惑。计算属性真正的用处是在于利用这两个方法间接的获取或者改变其他的变量或属性。
get
get方法在这两个方法中是必须的,意即如果要声明计算属性,则必须存在get方法,get方法的作用是在开发者用dot文法获取该变量的值的时候,返回一个固定的或经过计算的值。
set
set方法是一个可选方法,其会传入一个与所属变量类型相同的参数,这个参数的值即使用中被赋予的值,在set文法中,可以利用获取的参数的值,去计算并设定其他变量的值。如果要使用set方法,则必须包含get方法。
以下是官方开发者文档中给出的例子:
struct Point { var x = 0.0, y = 0.0 } struct Size { var width = 0.0, height = 0.0 } struct Rect { var origin = Point() var size = Size() var center: Point { get { let centerX = origin.x + (size.width / 2) let centerY = origin.y + (size.height / 2) return Point(x: centerX, y: centerY) } set(newCenter) { origin.x = newCenter.x - (size.width / 2) origin.y = newCenter.y - (size.height / 2) } } } var square = Rect(origin: Point(x: 0.0, y: 0.0), size: Size(width: 10.0, height: 10.0)) let initialSquareCenter = square.center square.center = Point(x: 15.0, y: 15.0) print("square.origin is now at (\(square.origin.x), \(square.origin.y))") // Prints "square.origin is now at (10.0, 10.0)"
其实只要稍微仔细看一下就可以理解get和set的用法,所需要注意的就是如下几点:
1. 无论是set还是get都不可以改变(或返回)自身的值。这个错误不会被编辑器发现,但会是运行时错误而被抛出异常。
2. set可以忽略参数声明而直接使用newValue的默认值,如上面的例子可以被改写成这样
set { origin.x = newValue.x - (size.width / 2) origin.y = newValue.y - (size.height / 2) }
3. 如果要声明计算属性则必须声明get方法,只声明get方法的计算属性成为只读计算属性,将来的使用中该属性无法被赋值而只能获取其在get方法中返回的值。同时,只读计算属性可以简化写法,不用再按标准方式声明get方法。例如:
struct Cuboid { var width = 0.0, height = 0.0, depth = 0.0 var volume: Double { return width * height * depth } } let fourByFiveByTwo = Cuboid(width: 4.0, height: 5.0, depth: 2.0) print("the volume of fourByFiveByTwo is \(fourByFiveByTwo.volume)") // Prints "the volume of fourByFiveByTwo is 40.0"
以上便是计算属性的用法了,可是仔细一想就能发现,我其实需要的并不是计算属性,尽管我确实是想在我的hp改动的时候联动的改动p,这是计算属性中的set方法可以做到的事情,但是同时我并不需要get方法,但恰恰get方法又是计算属性所必须的,所以再深究一下就能发现我们这次的正题:willSet和didSet。
属性观察者
正如其名,willSet和didSet这两个方法会观察属性的值的改变,并在特定的时候做出特定的反应。每一次属性的值的改变都很调用设定好的属性观察者的方法,就算新的值和属性越来的值相同也不例外。
除了定义为lazy的存储属性外,这两个观察者方法可以在任何其他存储属性中声明。对于继承的情况,观察者方法同样适用于继承的存储属性,同时还增加了继承的计算属性,当然,你不用为不重写的父计算属性添加属性观察者,因为你可以直接在set中做到观察者所能做的事情。
同时需要注意,这两个方法之间没有必然的关系,你可以同时声明这两个方法或只声明其中任意一个。
但是这两个方法的调用时间和传入参数却有很大不同:
willSet方法会在新值被储存前调用,官方文档中使用了“just before”这个词,可能意思就是这个方法调用完了该变量就会立刻存储新值吧。同时willSet方法接收的参数是即将被存储的新值,即newValue,如果希望简化方法的话可以不书写参数而直接用dot文法调用该变量。
而didSet方法会在新值被存储后调用,官方文档中使用了“immediately”这个词,同上的猜测就是这个方法会在存储完新值后立刻调用。与willSet不同的是,didSet中的参数是改变之前值,即oldValue,同样允许简化书写。
以下是官方的例子:
class StepCounter { var totalSteps: Int = 0 { willSet(newTotalSteps) { print("About to set totalSteps to \(newTotalSteps)") } didSet { if totalSteps > oldValue { print("Added \(totalSteps - oldValue) steps") } } } } let stepCounter = StepCounter() stepCounter.totalSteps = 200 // About to set totalSteps to 200 // Added 200 steps stepCounter.totalSteps = 360 // About to set totalSteps to 360 // Added 160 steps stepCounter.totalSteps = 896 // About to set totalSteps to 896 // Added 536 steps
这个方法就可以完美解决我遇到的问题,我只要在hp的willSet观察者方法里直接设定p的值就好了,有关get和set以及willSet和didSet也就说得差不多了,这两个知识点并不是什么难点,但感觉真正会实用的人也是比较少的,所以特来分享一下。
iOS求道录 – swift开发问题总结 – 1
从最开始学习iOS开始,就习惯把遇到的问题记录下来,不知不觉也积累了不少,我相信也许其他人也会遇到这样那样的问题,可以大家拿出来一起分享,或者有问题我们一起解决,或者觉得我的解决方法存在问题,也欢迎指正。每个篇文章更新10个问题。若评论中存在好的问题,我也会在征求评论作者同意之后更新到文章之中。另外抱歉起了个比较中二的名字,其实感觉不会有那么厉害。(:з」∠)
我就按照我记录的顺序来了,因为是刚刚入门时候就开始记录了,所以可能前面的问题看起来都会比较幼稚
1.如何修改Tabbar item的selected color
首先是简单的方法,整体修改选中后颜色,在随便哪个tabbar的子VC里使用如下代码都可以修改其选中后颜色,下面修改为蓝色
self.tabBarController?.tabBar.tintColor = UIColor.blueColor()
而如果是希望不同的item使用不同的选中后颜色,则可以利用storyboard这样快捷实现(前提是你利用storyboard实现TabbarVC。。)
选中某个tabbaritem之后,在有侧边栏里这样修改:
2.判断字符串之间关系
利用rangeofstring函数,其会返回一个布尔变量,示例:
var myString = "This is a string test" if myString.rangeOfString("string") { print("exists") }
3.将控件的默认语言修改为中文
点击工程名→寻找Info选项→在其下拉栏中寻找localization→将其value修改为china
4.tableview cell无法正常显示
这个真的是初学者问题,还弄了半天,也是当时的Xcode有点小问题,所以浪费了很多时间,就是tableview的delegate的有关section和row的数量的两个函数的返回值要设为非0,当时的Xcode版本,如果改变numberOfSection这个函数的返回值(在TableviewController中,已经默认提供了这两个函数且返回值均为0)为非0,则会报错,但是这个错误在点击运行之后就会消失,当时并不知道,看到报错以为不能改,就改回0,然后各种百度谷歌也找不到答案,还找到了很多奇葩的理由来解释自己的错误,现在看看好蠢。
5.在storyboard中关联class文件的问题
在storyboard中拖入的是什么controller(例如TableviewController),其所关联的class就必须是该类型的view controller,否则在使用segue跳转是会报错并导致程序崩溃。
这个也是早期的问题了,现在我使用storyboard的话所有的controller都是基础的UIViewController,因为遇到过很多问题,即比如如果使用UITableViewController无法正常显示广告条的问题。
6.如何添加storyboard辅助线
storyboard中,双击某个view,再按shift+command+ -会添加横向的辅助线,shift+command+ | 会添加纵向的辅助线。删除辅助线的方法就是点击辅助线后快速移出view到旁边的空白处
7.引用protocol时报错
主要是在关联delegate和datasource时发现的,以前遇到这种情况通常都去搜索引擎了,后来发现自己可以很轻松解决:
这种情况是你关联了这两个对应的数据源,却没有实现他的回调方法,像tableview和pickerview等等都需要调用特定的回调方法才会不报错。
在这种情况下的方法,就是:右键点击datasource →jump to definition→寻找该方法下不带option标签的func→复制这些func到view controller,实现之,即可。
以上是当时写的解决办法
8.将Int等其他类型的变量变为字符串输出
当时真的是很郁闷,我本身是学软件出身的,但完全不记得转义符这个东西。。。找了很多办法想把一个int在swift里转化为string来输出,最后突然想起了转义符,然后就没有然后了。。
“\(times[row])”,这样就可以输出原本为int数组的times中得值了。
当然现在也知道了format函数的用法,但就不在这个问题下面讨论了
9.读取与写入plist的方法
var partArray : NSArray? //声明存储plist文件数据的数组 let ban = NSBundle.mainBundle()//获取mainbundle的值,这里面保存了plist文件的地址(注意plist文件必须创建在主文件路径下才可以) let plistPath = ban.pathForResource("myNumber" , ofType : "plist”)//连接plist文件 let partFromP = NSMutableDictionary(contentsOfFile: plistPath!)//将plist文件存入一个数组 partArray = partFromP?.objectForKey("肩部") as? NSArray //通过key值寻找值
早时自己写着玩的时候,遇到了数据存储问题,当时觉得coredata太麻烦,于是用plist来做存储。。现在发现coredata会了之后比plist的存储简单太多,毕竟要用plist来存东西是要自己时刻记着数据的表结构的。。
不过还是提供一下关于plist写入的方法:
IOS设备写入plist文件时,不能使用mainbundle来寻找文件路径,这样只会导致写入失败,以下是正确地寻找文件路径的方法:
let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true) as NSArray let documentsDirectory = paths.objectAtIndex(0) as! NSString let path = documentsDirectory.stringByAppendingPathComponent("GameData.plist") //以上三行获取文件地址就不细说了,下面这行是将文件中的值保存到一个NSDictionary中 let mydict = NSDictionary(contentsOfFile: path) //获取用户数组 var partArray : NSArray = mydict?.objectForKey("users") as! NSArray //需要新加的数据 var object = [NSDictionary]() object.append(["part" : "上臂" , "move" : "肩部挺举" ,"group":"3","times":"15"]) //返回一个新的数组 partArray = partArray.arrayByAddingObjectsFromArray(object)
10.NSArray的arrayByAddingObject方法解释
这个方法会返回一个新的数组,而原数组未改变,所以可以这样更新原数组
partArray1 = partArray1?.arrayByAddingObject(object)