Subjective-Script 使 Objective-C 更加符合脚本风格!
注意: Subjective-Script 要求您的 Objective-C 项目启用 ARC。
我的首选语言是 CoffeeScript,每当我在 Objective-C 中开发时,我发现自己经常需要查找 [NSSomething reallyLongFunctionName:YES withAVerboseParameterName:YES and:[NSSomethingElse whichAddsMoreBrackets]],这不是一件很有趣或快速的事情!
当我从原始的 Underscore.js 将 _.m 的测试迁移到 Objective-C 中时,它是易于阅读的
var people = [{name : 'curly', age : 50}, {name : 'moe', age : 30}];
people = _.sortBy(people, function(person){ return person.age; });
equal(_.pluck(people, 'name').join(', '), 'moe, curly', 'stooges sorted by age');
它在 Objective-C 中的样子如下
NSArray *people = [NSArray arrayWithObjects:
[NSDictionary dictionaryWithObjectsAndKeys: @"moe", @"name", [NSNumber numberWithInt:30], @"age", nil],
[NSDictionary dictionaryWithObjectsAndKeys: @"curly", @"name", [NSNumber numberWithInt:50], @"age", nil],
nil];
STAssertEqualObjects([_.pluck(people, @"name") componentsJoinedByString:@", "],
@"moe, curly",
@"stooges sorted by age");
键值的顺序不正确,涉及到太多的输入,变得难以阅读。所以我写了 Subjective-Script 并将 QUnit.m 迁移到以下格式
A* people = AO(OKV({@"name", @"curly"}, {@"age", N.I(50)}), OKV({@"name", @"moe"}, {@"age", N.I(30)}));
people = _.sortBy(people, ^(O* person){ return person.get(@"age"); });
equal(_.pluck(people, @"name").join(@", "), @"moe, curly", @"stooges sorted by age");
好多了!最重要的是,我可以通过函数名称重用我的 JavaScript 知识,这样我就不必查找东西,直接完成工作!
虽然 JavaScript 允许灵活的变量类型,但 Objective-C 要求显式变量类型。为了使之更加简洁,我尽量将类型缩短到最简名称
typedef BOOL B;
typedef NSInteger I;
typedef NSUInteger UI;
typedef float F;
typedef double D;
typedef id KV[2]; // key-value pair
#define NSO NSObject
#define A NSMutableArray
#define NSA NSArray
#define O NSMutableDictionary
#define NSD NSDictionary
#define S NSMutableString
#define NSS NSString
#define Date NSDate
#define N NSNumber
#define E NSException
并且我也尝试缩短 NSNumbers,使用上述缩写(B, I, UI, F, D)
N* value = N.B(true);
equal(value.B, true, @"YES it is!")
我尝试了多种不同的方法,但这种方法看起来既易于阅读又简洁(编译器通过键值对匹配来检查)
A* arrayOfInts = AI(1, 2, 3);
A* arrayOfObjects = AO(N.I(1), N.F(2.0), @"hello", nil, O.new);
I intValue = arrayOfInts.getAt(0).I; // or: intValue = arrayOfInts.get(@"1").I;
arrayOfInts.setAt(0, N.I(intValue+1)); // or: arrayOfInts.set(@"1", N.I(intValue+1));
O* obj = OKV({@"int", N.I(1)}, {@"float", N.F(2.0)}, {@"string", @"hello"}, {@"nil", nil}, {@"dictionary", O.new});
intValue = obj.get(@"int").I;
obj.set(@"int", N.I(intValue+1));
等价于如下 Javascript
var arrayOfInts = [1, 2, 3];
var arrayOfObjects = [1, 2.0, 'hello', null, {}];
var intValue = arrayOfInts[0];
arrayOfInts[0] = intValue+1;
var obj = {int: 1, float: 2.0, string: 'hello', nil: null, dictionary:{}};
intValue = obj.int; // or: intValue = obj['int'];
obj.int = intValue+1; // or: obj['int'] = intValue+1;
如果您有兴趣,Objective-C 未来可能会通过使用 Objective-C 文本 来进一步简化语法
NSA* arrayOfInts = @[@1, @2, @3];
NSA* arrayOfObjects = @[@1, @2.0, @"hello", NSNull.null, @{}];
NSD* obj = @{@int: @1, @float: @2.0, @string: @"hello", @nil: NSNull.null, @dictionary:@{}};
为了能够重用我的 JavaScript 知识,并且不必查阅 StackOverflow 来查找难以记住的 Objective-C 中的基本功能,我已经移植了一些常见的 JavaScript 函数。
N.I(1).instanceof(N.class);
// true
(A.new).instanceof(NSA.class);
// true
N.B(true).toString();
// @"true"
OKV({@"key1", @"value1"}).toString();
// @"[object Object]"
AO(AI(1,2,3),N.F(4.5),OKV({@"five", @"5"})).toString();
// @"[[1,2,3],4.5,[object Object]]
AI(1,2,3).length;
// 3
AI(1,2,3).hasOwnProperty(@"2");
// true
AI(1,2,3).get(@"2");
// N.I(3)
AI(1,2,3).concat(AI(1,2,4);
// [1,2,3,1,2,4]
AO(@"Banana", @"Orange", @"Lemon", @"Apple", @"Mango").slice(-3,-1);
// [Lemon,Apple]
AI(1,2,3).reverse();
// [3,2,1]
AO(N.I(1),AI(2,3)).flatten(true);
// [1,2,3]
AI(1,2,3).pop();
// N.I(3)
AI(1,2,3).push(N.I(4)).push(@"out the door");
// [1,2,3,4,out the door]
AI(3,2,1,2).sort(nil);
// [1,2,2,3]
AO(@"Banana", @"Orange", @"Apple", @"Lemon").splice(2,1, @"Kiwi", @"Mango", nil);
// [Banana,Orange,Kiwi,Mango,Lemon]
AI(3,4,5).unshift(N.I(1),N.I(2),nil);
// [1,2,3,4,5]
AI(1,2,3,4,5).shift();
// [2,3,4,5]
OKV({@"int", N.I(1)}, {@"float", N.F(2.0)}).hasOwnProperty(@"int");
// true
OKV({@"int", N.I(1)}, {@"float", N.F(2.0)}).delete_(@"int").hasOwnProperty(@"int");
// false
@"int".in(OKV({@"int", N.I(1)}, {@"float", N.F(2.0)}));
// true
delete obj.key or delete obj['key'] -> obj.delete_(@"key") -> what is the delete resevered word for..can it be used?
@"hello world!".split(@" ")
// [@"hello", @"world!"]
@"hello world!".split(@"");
// [@"h", @"e", @"l", @"l", @"o", @"w", @"o", @"r", @"l", @"d", @"!"]
@"hello".add(@"world!");
// @"hello world!"
S* message = S.newS(@"hello"); message.append(@" world!");
// message == @"hello world!"
SS.stringify(Date.newYMD_JS(2012, 7, 31));
// @"2012-08-30T15:00:00.000Z"
O* obj = OKV({@"mirror", ^(NSS* string){return string.add(@" mirror"); }});
@"mirror".call(obj, @"hello", nil);
// @"hello mirror"
@"mirror".apply(obj, AO(@"hello"));
// @"hello mirror"
Subjective-Script基对象上有一些工具,而不是像JavaScript那样使它们全局。
SS.stringify(AO(N.I(1), N.F(2.0), N.F(3.1), @"hello", nil, O.new));
// @"[1,2,3.1,\"hello\",null,{}]"
SS.parseInt(@"123");
// N.I(123)
SS.typeof_(N.B(true));
// @"boolean"
SS.typeof_(@"string");
// @"string"
SS.typeof_(Date.new);
// @"object"
__block BOOL called1 = false;
SSTimeout* timeout1 = SS.setTimeout(^{ called1 = true; }, NSEC_PER_SEC*1);
SS.clearTimeout(timeout1);
// not called
__block BOOL called2 = false;
SS.setTimeout(^{ called2 = true; }, NSEC_PER_SEC*2);
// called
您也可以轻松地为您自己的对象添加命名属性
@interface MyObject : O
@property (strong) NSS* name;
@end
@implementation MyObject : O
@dynamic name;
IMPLEMENT_NAMED_PROPERTIES
@end
MyObject* obj = MyObject.new;
obj.name = @"Steve";
// they call me Steve
所以您可以像这样编写初始示例
@interface Stooge : O
@property (strong) NSS* name;
@property (strong) N* age;
@end
@implementation Stooge
@dynamic name, age;
IMPLEMENT_NAMED_PROPERTIES
@end
A* people = AO(OTKV(Stooge, {@"name", @"curly"}, {@"age", N.I(50)}), OTKV(Stooge, {@"name", @"moe"}, {@"age", N.I(30)}));
people = _.sortBy(people, ^(Stooge* person){ return person.age; }); // no get(@"age") required
equal(_.pluck(people, @"name").join(@", "), @"moe, curly", @"stooges sorted by age");
要运行示例项目;首先从项目目录中克隆仓库并运行 pod install
。
Kevin Malakoff, [email protected]
SubjectiveScript.m正处于MIT许可证下。有关更多信息,请参阅LICENSE文件。
目前,这仍处于早期阶段,还有很多要添加和测试。请帮忙!
如果您想添加其他内容,只需在适当的位置实现它,编写适当的测试,然后在GitHub上提交拉取请求。
此外,如果您想提交其他语言中您喜欢的特性,只要它们易于记忆并有助于加快开发速度即可!