秋刀生鱼片

秋刀生鱼片 查看完整档案

徐州编辑  |  填写毕业院校  |  填写所在公司/组织填写个人主网站
编辑

独立游戏开发者

个人动态

秋刀生鱼片 赞了文章 · 2020-04-26

mac 安装 sketch 意外退出

  1. 问题: 遇到意外退出怎么办?说白了就是程序崩溃了:不管你弄了几遍其他的操作,只要软件还在,下面两条命令解决:

    1. 首先清除签名:xattr -r -d com.apple.quarantine /Applications/Sketch.app(这里是app的存放位置,比装傻)
    2. 然后修复崩溃软件:sudo codesign --force --deep --sign - /Applications/Sketch.app(同上)
想知道关于程序奔溃的可以参考: https://www.sohu.com/a/326971794_120161573~~~~
查看原文

赞 2 收藏 0 评论 6

秋刀生鱼片 赞了回答 · 2019-07-26

nodeJS中如何用async,await获取并操作request返回值?

var Promise = require('Promise');

function callApi(){
    return new Promise(function(resolve,reject){
        setTimeout(function () {
            resolve("aaa");
        }, 2000);
    });
}

async function runAsync(){
    let myVal = await callApi();
    console.log(myVal);
}


runAsync();

我写了一段你可以参考下。

关注 3 回答 3

秋刀生鱼片 回答了问题 · 2019-05-22

解决mac 10.11 phpize 报错问题

找到解决办法了

执行

sudo installer -pkg /Library/Developer/CommandLineTools/Packages/macOS_SDK_headers_for_macOS_10.14.pkg -target /

/usr/include 就出来了。。。 烦死了 哎

关注 8 回答 6

秋刀生鱼片 回答了问题 · 2017-12-22

ios-oc语言可以遍历枚举类型的所有值吗

枚举都是 NSUInteger,模拟一下就行了

关注 3 回答 1

秋刀生鱼片 回答了问题 · 2017-12-22

Objective-C block的参数问题: This block declaration not a prototype

只是个warnning吧,

试试 括号里面加上

id _Nullable ,...

//比如

@property (nonatomic, strong) void (^testBlock)(id _Nullable ,...);

关注 3 回答 1

秋刀生鱼片 发布了文章 · 2017-12-09

interface和setter,getter

前篇说到我们通过ObjC的Category特性给日常工作增加便捷的实现,这一篇则要从语言设计角度,跟大家分享一些思考。

不要忽视interface

ObjC的@interface设计,跟Java和C#真的很像,但又略有不同,相比之下Java和C#则像是一个模子刻出来的。ObjC的特点十分明显,首先是一般不用写@private@public来区分私有变量,大部分ObjC开发者甚至都不知道还有这两个关键字,其实Cocoa源代码中也基本没有使用过这种设计,即使ObjC是支持的。

<!--more-->

在@interface 中使用 @private和@public

@interface Student : NSObject
{
    @private
    NSString* _name;
    @public
    NSNumber* _age;
    int _height;
}
@end

如上代码中,Student有一个私有变量_name,和两个共有变量_age_height,但在@interface中声明变量,一定不是Cocoa设计者的初衷,这里有两个方面的考虑。

其一,把内部变量直接暴露在外,会降低整个框架的稳定性,因为增加不同模块之间的耦合,降低了每个类的内聚性。
其二,内部变量的变量名,很容易跟局部变量变量名产生冲突。上例中我给每一个变量名前加了下划线,就是为了防止这个问题发生。

所以纵观Cocoa框架的头文件设计,基本没有这样的代码,因为设计者提供了更好的实现方式,就是大家用的更多的@property关键字。

如果用@property声明上面的类,大家都很熟悉

@interface Student : NSObject

@property (nonatomic, strong) NSString* name;
@property (nonatomic, strong) NSNumber* age;
@property (nonatomic, assign) NSInteger height;

@end

@property这个设计真的很有意思,首先我们不再区分私有公有属性,因为只要写在.h里面的@property,我们都默认是共有的,私有的@property可以写在.m文件里。

其次,配合写在@implementation里面的@synthesize关键字,可以自动生成setter和getter方法,而现在@synthesize关键字都可以省略,除了个别情况有修改内部变量名称的需求。

@implementation Student
@synthesize name = _name;
@synthesize age = __age;
@end

上面的@synthesize,第一个是可以省略的,在不写的情况下,编译预处理会自动给添加@synthesize代码,所以即使没有合成(synthesize)height属性,我们依然实现了它的setter和getter方法

//这两个方法可以重写

- (void)setHeight:(NSInteger)height
{
    _height = height;
}

- (NSInteger)height
{
    return _height;
}

在setter和getter方法均重写的情况下,@synthesize需要手动添加。

@synthesize height = _height;

为什么要使用 setter 和 getter

settergetter的设计的确值得琢磨,我们主要从以下几点分析:

setter 和 getter 包装了内部变量,整个类对外可以只暴露接口,增强类的内聚性。

例如上例中的内部变量_name,外部类是无法操作的,只能通过set和get接口来发消息:

Student* s = [[Student alloc] init];

[s setName:@"Tom"];
[s name];

通过实现 getter方法,可以避开初始化变量的时机问题

这也是很实用的一个点,因为ObjC的消息设计机制,导致ObjC很难在初始化(init)方法中传入过多参数(题外话,我给ObjC扩展过依赖注入,详见iOS实现依赖注入)。因此新实例的默认属性,放在什么位置实现合适,是大家一定遇到过的问题。

例如最常见的UIViewController,代码初始化走init方法,而通过storyboard实力化则走initWithCoder方法,一些容器属性,通过getter方法初始化,则可避免第一次调用尚未初始化造成的问题。

早期的Cocoa在如果给nil发消息,是会引起异常的,现在的版本给没有alloc的对象发消息不再抛异常,以至于某些时候属性没有初始化造成的问题变得更隐蔽,然而重写getter方法可以有效避免这个问题,例如:

//班级类
@interface XXClass : NSObject
@property (nonatomic, strong) NSMutableArray* students; //学生
@end

@implementation

//实现getter方法,在内部变量_students没初始化的情况下将其初始化
- (NSMutableArray *)students
{
    if (!_students) {
        _students = [NSMutableArray array];
    }
    return _students;
}

@end

如此一来,无论在任何时候,第一次发送[self students]消息的时候,内部变量_students都会初始化。

在这里要另外注明一点,在类的内部,不要在setter和getter方法外,直接使用内部变量,遵守这一条会收益很多。

setter 和 getter 可以单独使用,也可以脱离内部变量使用

这里要说的就是@property的灵活性了,大家知道@property拥有一系列的修饰词,除了常用的nonatomic(非原子化,线程安全)strong(强引用类型)weak(弱引用类型)assign(赋值,用于非对象属性)以外,还有readonly(只读)readwrite(可读写)两个影响setter和getter方法的属性,readonly修饰的属性,只有getter方法而没有setter方法。

readwrite则是一个看起来可有可无的修饰词,因为默认就是可读写。然而它其实有个专门设计的用法,就是在.h中的interface中被readonly修饰的属性,可以在这个类的其他类别(category)或者匿名类别中重新声明这个属性时,修改其读写限制,例如

//班级类
@interface XXClass : NSObject
@property (nonatomic, strong, readonly) NSMutableArray* students; //学生
@end
//匿名类别
@interface XXClass()
@property (nonatomic, strong, readwrite) NSMutableArray* students;
@end

这样一来,因为匿名类别一般写在.m文件里(基本没见过写在.h文件里的),所以外部是不能调用students属性的setter方法,而XXClass类内部则可以使用。

还有一种常见情况是用setter和getter来模拟属性(@property),例如:

//班级类
@interface XXClass : NSObject
@property (nonatomic, strong, readonly) NSMutableArray* students; //学生
@property (nonatomic, assign, readonly) NSUInteger studentsCount; //学生数量
@end
- (NSUInteger)studentsCount
{
    return self.students.count;
}

这里的studentsCount是没有内部变量的,通过getter方法伪造成属性接口。

小结

这一篇是ObjC的接口设计模式的一部分,写的比较详细是帮助新手入门,给有经验的朋友带来一些思考,并引出接下来的内容。

查看原文

赞 0 收藏 0 评论 0

秋刀生鱼片 发布了文章 · 2017-12-07

认识ObjC,改造Cocoa

更好的阅读体验请点击 原文

接上篇,其实在接触Ruby不久后,我就萌生了改造ObjC的Cocoa框架的想法。为什么要改造?只为能够提高开发OC项目的效率。同时我也完成了一些改造工作,详见像Ruby一样写ObjC,用block实现链式方法调用

说到改造这个问题,我想起曾经有人说,合格的程序员都会不断追求自动化,不断追求代码的解耦与复用,不断追求拓展技术的边界。我们也往往会从这三个方向找切入口,例如OC和Python一样充斥了一些C语言函数形式的方法或者宏,例如NSLog()NSLocalizedString(),或是CoreGraphic框架中一系列的C函数,更有甚者GCD(Grand Central Dispatch)完全是C函数代码,但GCD因为把多线程编程做的跟if/else一样好用,所以用多了也都接受了。

<!--more-->

举个栗子

我们今天就从NSLocalizedString这个宏作为切入口,举一个例子:

//惯用方法
NSString* str = NSLocalizedString(@"你好,世界",nil);

从OOP的角度思考,我们不难想到字符串的本地化转换,完全可以作为NSString类的实例方法来设计,而不是像NSLocalizedString宏这样的设计,这个设计可以说堪比Python的len()方法。

重新设计的本地化接口

NSString* str = [@"你好,世界" localizedString];

这样调用不仅更符合我们的思维逻辑,也更符合OOP的理念,并且和NSString其他的接口也保持了一致性。使用ObjC的Category特性,就可以轻松实现

@interface NSString (add)
- (NSString*)localizedString;
@end

@implementation NSString(add)
- (NSString*)localizedString;{
    return NSLocalizedString(self, nil);
}
@end

同样的我们还可以给NSString或者其他类型增加各种各样的类别(Category)进行拓展,例如比较有名集大成框架YYKit,在NSString扩展中加入各种摘要算法转换方法,给实际开发带来了极大的便利。

第二个栗子

如果第一个栗子不能跟你产生多少共鸣,那就请看接下来的栗子:给NSArray增加高阶函数Map,类似的Filter,Reduce函数在Python、JavaScript、Swift、Ruby中都是标配了,而OC则显得略有落后,但落后并不妨碍我们进行改造,同样给NSArray增加Category方法,实现依赖于OC对block的支持

@interface NSArray (Functional)
- (NSArray*)map:(id (^)(id x))map;
@end

@implementation NSArray (Functional) 
- (NSArray*)map:(id (^)(id))map
{
    NSMutableArray* array = [NSMutableArray array];
    [self enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        id x = map(obj);
        if(x) [array addObject:x];
    }];
    return [array copy];
}
@end

block的出现相当于提高了代码块的身份,虽然它还不是OC的一等公民,但已经可以和实例对象平起平坐,作为参数进行传递了。如果你是一个不太明白block机制的新手,我这里还有一篇教程推荐给你。如果了解block,上面的代码就很好理解了,没有任何优化,仅仅是封装了拿东西包装这两个步骤。

那,NSArray实现Map方法意味着什么呢?意味着我们加工一组数据时,只要专心数据的加工工作就好。

NSArray* a = @[@"a",@"b",@"c"];
a = [a map:^id(id x) {
    return [x uppercaseString];
}];
NSLog(@"%@",a);

(
A,
B,
C
)

究竟能否提高开发效率

这个问题其实不用讨论也知道可以,因为我们都有过复制粘贴重复写代码的经历,而这种代码封装和复用,甚至比复制粘贴更简单,每使用一次,都能节省几秒钟甚至几分钟的时间,一并节省不少精力,长年累月则是受益无穷。

查看原文

赞 0 收藏 1 评论 0

秋刀生鱼片 发布了文章 · 2017-12-07

Cocoa改造前篇 - 说在前面的

更好的阅读体验请点击 原文

从面相对象说起

面向对象的程序设计(Object-Oriented Programming,简记为OOP)这个概念大家都有所耳闻,目前(2017.12),在Tiobe世界语言排行榜上排前十的语言中,C语言和Assembly language(汇编)外的八种语言均原生支持面向对象的程序设计

<!--more-->

怎么判断一种编程语言是否支持OOP呢?看看这门语言是否支持类(class)、对象(object)、封装(encapsulation)、继承(inheritance)等功能和特性,支持这些就可以进行面向对象编程。拿Objective-C(OC)来说,类就是Class,对象就是instance,万物的基类是NSObject,这些东西在C语言里并不存在,是OC使用C语言的结构体(struct)抽象出来的产物。

我们从Objective-C的名字上也能看出一些端倪,直译过来是对象化的C语言,当然不仅是OC,排行榜前十中的C++同样是C语言的一个超集;C#和Java同样属于类C语言,把面向对象做的更加彻底;PHP虽然是脚本语言,其解释器是使用C语言写的;而我们常说的Python,其全称则是CPython,也是用C语言实现的解释器,当然Python解释器也有Java和C#实现的版本。

为什么C语言,比其他语言显得更底层呢?接触过的朋友相信都有很深的体会,C语言的程序,是在和图灵机硬件打交道,变量、数组、结构体,声明在堆内存就要为其分配内存空间大小,分配了内存,就要手动回收;数组还要区分静态和动态,每块数据占几个字节,躺在内存的什么位置,一切都按编程人员的安排。所以有人说C语言就是一个高级汇编,想起来确实有一分道理(笑)。但在智能手机、移动计算机计算能力大大提升的今天,计算资源早已不是通用编程首先考虑的问题,相比于C语言强迫编程人员从机器的角度设计程序,抽象程度更高的OOP才更接近人脑的思维方式,才更适合提高软件工程师的编程效率。

即使如此,仍有一部分人至今站在OOP的对立面,从代码复杂度、建模能力要求等方面提出异议,坚持写C++、Python、PHP的时候不构造类,写纯过程的程序。但其实,这些自称为原C党的朋友,并不能说自己没有使用OOP,因为这些语言中变量,跟C语言中的变量,有本质的不同。

就用字符串数组来举例子,C语言是没有string类型的,只有字符数组,用\0来标记字符串结束;而其他语言中的string则是早已封装好的字符串类(Class),用起来跟整型无异。

C语言中字符串和数字变量声明

char name[] = "Tom\0";
int age = 12;

Python中字符串和数字变量声明

name = "Tom"
age = 12

C++中字符串和数字变量声明

string name = "Tom";
int age = 12;

我们在Python和C++中使用字符串,早已不是在直接与设备内存打交道,而C语言中的“字符串”还停留在只是内存中的一段连续空间的阶段。

再来看一看数组,C++虽然也支持C的数组,但我想对比的其实是C++标准库中的向量(Vector),以及Python中的链表(List),这些高级容器同样是基于OOP理念设计的类,仍只有C语言的数组内容直接映射在内存上。

所以即使你不构造Class,在C++、Python、PHP中仍在使用对象和实例的OOP特性,即使开发的是线性程序。

彻底的OOP

经常会看到有人抱怨Java把面向对象的理念做的太过头,C#作为Java的仿制品,也同样逃脱不了被诟病的现实,但其稳定性也是有口皆碑。然而真正把OOP理念实现的彻头彻尾彻彻底底的,反而是最早的OOP语言之一的Smarttalk,让我先看一段Samrttalk的代码

Transcript show: 'Hello world'

这是Smarttalk版本的Hello world程序,Transcript是Squeak(这是Smalltalk语言的一种版本实现)环境里,把信息显示到屏幕上的一个对象。这段代码是用冒号给这个对象发送了一个消息(Message),如果给这段代码加上一对中括号,是不是像极了Ojective-C,没错,因为OC就是参考Smarttalk设计的Runtime。

同样,Samrttalk也支持中括号的写法,我们可以把上面的一段代码段落,赋值给一个变量:

t := [ Transcript show: 'Hello world']

这个t变量,其实是一个闭包(BlockClosure)对象,相同的概念在C++ 11标准里才出现,相比之下Smarttalk的设计理念真的很前卫。而OC作为Smarttalk的追随者,更是拥有NSOperation类来实现闭包,相比之下,block并不是基于OOP的设计。

C++的blcok和ObjC的NSOperation,这里block写法OC同样支持

void hello = ^ {
    NSLog(@"hello world");
};

hello();

NSBlockOperation* block = [NSBlockOperation blockOperationWithBlock:^{
    // 做一些操作
}];
[[NSOperationQueue mainQueue] addOperation:block];

要注意的是NSBlockOperation是在OC支持block以后才出现的类,在此之前要使用NSOpertaion,我们需要继承NSOpertaion类,并重写这个类的-(void)main方法,这无疑是一件十分繁琐的事。

一切皆对象

OC作为Smarttalk的追随者,在OOP的理念上是要强于C++、Python和PHP的,interfaceimplementationgettersetter的接口设计,和Java、C#相互参考,水平相近,但仍比Smarttalk和Ruby略逊一筹。

熟悉Cocoa框架的朋友都知道,UI绘制框架CoreGraphic中仍然要使用大量的CG开头的C语言函数,点、线、面的容器,依旧是CGPoint,CGSize,CGRect这些C语言结构体;数字变量依然是int、NSInteger、NSNumber(数字类)混着用,相互转换忙的不亦乐乎。当然这一切在OC支持字面量特性(Literals)以后有了好转:

//通过@符号直接把普通变量转换为数字对象
NSNumber *myIntegerNumber = @8;
//转回来
NSInteger customNumber = [myIntegerNumber integerValue];

相比之下,Smarttalk和Ruby做的更彻底,更好用,下面是用Smarttalk重复输出十次Hello world的代码,给数字10发timesRepeat消息,重复消息参数中的闭包:

10 timesRepeat: [Transcript show: 'Hello world']

为什么整数类要设计这么方法呢?因为Smarttalk中并没有循环语法,甚至其他语言常见的条件语句if/else在Smarttalk中都是不存在的,而都是使用OOP的理念实现,有兴趣了解更多关于Smarttalk的内容,请来这里

给对象发消息是更符合人类思维模式的设计

这里我们从继承Smarttalk理念的Ruby说起,虽然其使用点语法替代了冒号,但仍能看出Ruby中的数字类型,就是数字对象。

//将数字对象102转换成字符串对象
102.to_s

用Smarttalk实现则是

102 printString

相比之下Python则像是一个作者对OOP还处于感性认知阶段设计出来的语言,所以会设计出len()、map()、fliter()这种C语言函数风格的接口,例如我在OC中我们获取数组的长度使用count属性,使用点语法或者中括号消息都可以获取(关于OC中的点语法和中括号语法我们后面再聊)

NSArray* a = @[@(1),@(2),@(3)];
a.count;
[a count];

这很面向对象,因为我们要获取数量的主体数组实例a,发消息让他返回长度很符合人类的思维逻辑。同样的我们看看Ruby,也是一样的操作

a = [1,2,3]
a.length
a.size

然而当我使用第一次写Python代码的时候,我经历了很多人都遇到过的情况,不知道字符串或者数组如何获取长度。因为Python中string和list都没有length、size、count、len等属性和方法,然后我们发现Python提供了一个len()方法获取序列长度,这个方法接受一切的对象作为参数。

a = [1,2,3]
len(a)

s = "123"
len(s)

针对这个问题,有一部分人认为不是问题,他们说做OOP不要太教条主义,len在前在后能有很大差别么?我想说真的是有的,这个看似简单的前后问题,其实影响了实际的编程体验,就是是否基于对象思考问题的体验。

一方面,len()方法像一个凭空存在的方法,不依赖于任何类和对象,也不是依附于某个模块,知道它存在,才会去使用它,同样的还有Python中的type()、map()方法等。另一方面,这一类方法到底可以用于什么类型的对象,开发者心里也没底,必须对照接口标明的参数类型使用。

这一切无疑不利于程序开发的思维连贯性,有朋友可能觉得我说的言过其实,我这里举一个例子大家体会一下何为思维连贯性。

需求是将一段英文字符串的单词逆序,How are you处理成you are How

我们用OC实现如下:

#import <Foundation/Foundation.h>
NSString* reverse(NSString* text) {
    NSArray *words = [text componentsSeparatedByString:@" "];
    NSArray *reversed = [[words reverseObjectEnumerator] allObjects];
    return [reversed componentsJoinedByString:@" "];
}

Python实现为

def reverse(text):
    a = text.split(' ')
    a.reverse()
    return ' '.join(a)

Ruby的实现为

def reverse(string)
  return string.split.reverse.join(' ')
end

观察出来区别了了吧,重要的不是Ruby只用了一行代码,而是Ruby相比于OC和Python,省去了很多中间变量,别看只是一点点节省,其实省去我们实际开发中很大一部分无用工作。当然,OC可以通过括号多层嵌套连贯起来写,也能达到同样的效果,但我们并不推荐这样做,因为OC的方法名偏长,如果缩进不当,会让代码更难理解。

相比之下,Python的接口设计更滑稽一些,首先在Ruby中

array.reverse!
array.reverse

是两个不同的方法,前者只逆转array,没有返回值。后者则返回一个新的逆转数组对象,Python没有类似设计。

其次Ruby和OC都将join方法设计在array类里,唯独Python将其设为字符串类型的方法,导致了Python没法连贯地将中间参数略去。

查看原文

赞 2 收藏 0 评论 0

秋刀生鱼片 回答了问题 · 2017-07-11

突然意识到一个问题,小数也可以用8进制,16进制表示吧,为什么没人用呢?

方法倒是很简单,但为何不用,我觉得原因是:

2进制和8进制和16进制,都有自己适合使用的地方,恰恰这些领域用不到小数

比如编码,硬件存储,网络等

关注 6 回答 4

秋刀生鱼片 回答了问题 · 2017-07-11

解决iOS开发,为避免循环引用有时我们会在block里使用weakSelf

这个文章

一般不需要转成strong,或者在block中首先转成strong,然后再用。

关注 5 回答 4

认证与成就

  • 获得 122 次点赞
  • 获得 5 枚徽章 获得 0 枚金徽章, 获得 1 枚银徽章, 获得 4 枚铜徽章

擅长技能
编辑

开源项目 & 著作
编辑

  • 代码与哲学

    专栏

  • DEFetchRC

    一个NSFetchedResultsController的简单封装 DETableViewFetchRC将自动绑定CoreData的ManageContext和TableView,绑定好以后,数据的任何变动,包括增删改查,都会自动调用对应Cell的更新,不需要自己写任何代码做通知挂钩等。 Demo中使用Apple的CoreData模版,CoreData的Context在AppDelegate中。

  • XXXSegmentView

    A simple segment view. This segment view can use storyboard or code.

  • XXXRoundMenuButton

    A simple circle style menu. Design from Filter Menu by Anton Aheichanka for InVision

  • XXXTabbarController

    TabbarController

  • DeviceAccessViewController

    授权控制器

注册于 2015-01-13
个人主页被 3k 人浏览