算法之程序模型

我们对算法的研究是基于将它们实现为用Java编程语言编写的程序。 我们这样做有几个原因:

  • 我们的程序是简洁,优雅,完整的算法描述。
  • 您可以运行程序来研究算法的属性。
  • 您可以立即在应用程序中使用算法。

原始数据类型和表达式

数据类型 是一组值和一组对这些值的操作。以下四种基本数据类型是Java语言的基础:
  • 整数,带算术运算(int)
  • 实数,再次与算术运算(double)
  • 布尔值,带有逻辑运算符的值{true,false}的集合(布尔)
  • 字符,您键入的字母数字字符和符号(char)
Java程序操作以标识符命名的变量。 每个变量与数据类型相关联,并存储允许的数据类型值之一。 我们使用表达式来应用与每个类型相关联的操作。

下表总结了对于Java的int,double,boolean和char数据类型的值的集合和最常见的操作。
  • 表达式。 典型的表达式是中缀。 当表达式包含多个运算符时,优先级顺序指定它们的应用顺序:运算符*和/(和%)的优先级高于(应用于)+和 - 运算符; 在逻辑运算符之间! 是最高优先级,后跟&&和然后||。 通常,相同优先级的运算符是左关联的(从左到右应用)。 您可以使用括号来覆盖这些规则。
  • 类型转换。 如果没有丢失信息,则数字将自动提升为更具包容性的类型。例如,在表达式1 + 2.5中,1被提升为双精度值1.0,表达式的值为双精度值3.5。 转换是将一个类型的值转换为另一个类型的值的指令。 例如(int)3.7是3.将double转换为int将截断到零。
  • 比较。 以下混合类型运算符比较相同类型的两个值并生成布尔值:
    • 相等 (==)
    • 不等 (!=)
    • 小于 (<)
    • 小于等于 (<=)
    • 大于 (>)
    • 大于等于 (>=)
  • 其他原语类型。 Java的int有一个32位的表示; Java的double类型具有64位表示。 Java有五种额外的原始数据类型:
    • 64位整数,带算术运算(长整型)
    • 16位整数,带算术运算(短)
    • 16位字符,带算术运算(char)
    • 8位整数,带算术运算(字节)
    • 32位单精度实数,带算术运算(float)

Read on →

音乐乐理:音调和记号

  • 半步(或”半音”)是从键盘到下一个相邻的键上一个键之间的距离。
  • 键1到键2是一半的音,因为它们是彼此相邻。
  • 半步并不总是从白键到黑键。
  • 在这个例子中,键1和键2仍然彼此相邻。
  • 一个完整的全音(或”全音”或简称’音”)是相同的距离,两个半音。
  • 键1到键3是一个整体的音。
  • 键1到键2是第一半步。 2键是键3的第二半步。
  • 一个偶然的是用来提高或降低音符的音高的标志。
  • 我们将讨论的第一个记号是平的和尖锐。
  • 平半步降低了一个音调,而锐半步提出了一个音调。
  • 键入时,您可以使用#来代表尖锐,A B来代表一个单位。
  • 让我们来看看在C和D之间的黑键
  • 此键可称为C#,因为它首先是C.半步
  • 它也可以被称为Db,因为它低于D.半步
  • 另一个例子是E和F
  • E也可以被称为Fb的,因为它低于F.半步
  • 同样,F可称为E#。
  • 每当有一定间距有多个名字,它被称为等音拼写。
  • 接下来,让我们讨论双平板和双锐利。
  • 虽然平滑和尖锐半步,双平双锐利的改变的说明一整步改变一个音符。
  • 打字时,您可以使用x要代表一个双锐利和bb表示double持平。
  • 例如,D和Ebb具有相同的间距,因为你可以从E去整个音步长(或两个半台阶)往下到达D
  • D也可以听起来一样Cx,因为它是上述C的整个步长
  • 最后,自然消除了任何意外,并返回一个音符到原来的白键。
  • 我们将在即将到来的课程了解更多关于本位音。

音乐乐理:增强点和持续音

  • 增强点和持续音的关系有两种类型来改变一个音符的时间标记。
  • 一个点了一大半增加持续时间。
  • 由于八分音符的四分音符的1/2时间,我们将用它替换了1/2个符号。
  • 正如你所看到的,一个点四分音符等于四分音符加上八分音符。
  • 一个点四分音符也可以等于3八分音符。
  • 连线持续合并相同间距的多个音符。
  • 它们被用来让穿过屏障(如本例中的音节)的音符,声音的持续时间。
  • 第四和第五音符现在合并在一起。
  • 如果音节分隔线都没有了,我们可以写一个半音符来代替。

  • 使用此图表参考增强点。

    完整的流程如下:

音乐乐理:休止符

  • 休止符标示在一个音节中沉默的时期。
  • 每种类型的休止符对应的同等类型的音符的持续时间。
  • 例如:这两个四分之一休止符和四分音符占用的时间是相同的。

    当音符发出声音时,休止符是沉默的。
  • 为了证明这一点,让我们填写4/4时间的音节与四分音符。

    当弹奏时,所有的音符都发出声音。
  • 接下来,我们替换第二个音符为休止符。

    当弹奏时,第二个没有声音。
  • 接下来,我们讨论其他类型的休止符。
  • 一个全休止符占据的时间是相同的全音符。
  • 它被画成一个盒子从第四条五线谱向下。
  • 半休止符占有时间等同数量的一半音符。
  • 它被画成一个盒子从中间线的五线谱升起。
  • 像音符,休止符可以有标志。
  • 与一个标志,八分休止符具有相同的持续时间作为一个八分音符。
  • 有两个标志,十六休止符有相同的时间为十六分音符。
  • 尽管罕见,休止符可以有三个或更多个标志。
  • 使用此图表,以供参考休止符。

音乐乐理:标准音与节拍

  • 垂直的黑线将切分五线谱。
  • 五线谱划分两个音符区。
  • 节拍定义了每个区包含的不同种类的音符。
  • 第一个区的节拍是 4/4 第二个区的节拍是 3/4 。
  • 第一区 (4/4) 包含四个四分音符。
  • 第二区(3/4) 包含三个四分音符。
  • 接下来,我们将讨论非四分音符节拍。
  • 6/8 包含了六个八分音符。
  • 3/2 包含三个半音符。
  • 该图表显示了所有我们讨论的节拍。

音乐乐理:音符持续时间

  • 一个音符演奏的时间长度被称为它的音符时值,它是由音符的类型决定。
  • 该全音符在现代音乐最长的音符时值。
  • 二分音符具有全音符的一半的持续时间。
  • 两个半音符占据的时间相同为一体的全音符。
  • 四分音符为全音符的第四(或四分之一)。
  • 四个四分音符占据的时间相同为一体的全音符。两个四分音符等于一个半音符的持续时间。
  • 注意到,在持续时间小于四分音符有标志。每个标志减半音符的价值。
  • 八分音符有一个标志。
  • 因此,二个八分音符占用的时间相同数量的四分之一音符。
  • 一个十六分音符有两个标志,再减半值。
  • 两个十六分音符等于八分音符的持续时间。
  • 四个十六分音符占据的时间相同数量的四分之一音符。
  • 虽然可以有一个与三个或更多个标志音符,它们一般不使用。
  • 该图表显示在本节讨论的所有五种音符类型的关系。

音乐乐理:五线谱,谱号,加线

五线谱是对音符绘制的基础。现代的五线谱是由5根线和4个空间组成的。
在五线谱上的每行线或者空白处都表示键盘上的白建。

谱号分配给各个音符某些行或空格。 两个谱号经常用到,分别是:高音和低音。

我们先讨论高音(通常也称为G 谱).该谱号环绕(显示为红色)五线谱线被称为G.放在该行的任何音符变成G.
G调以上的空白处的调是A调(记住,没有一个“H”调)
A调以上的调是B调。以此类推,G、A、B、C、D、E、F、G
额我们看来已经超过了五线谱的范围了,这时候该怎么办?

加线将解决我们的困境。 我们在五线谱之外,额外添加一条线,然后在这个线处继续添加A

Read on →

iOS 推送证书过期指南

今天一早,运维部门同事,说咱们友盟上面推送证书已经到期了。那么到期了证书应该怎么样去更换新的证书呢。

1、我们先去苹果开发者中心新建一个Certificates证书。

2、下载Cert文件到本地,双击之后。在Keychain Access中找到我们创建的证书。Export导出p12格式,创建密码。

3、有时候后端需要pem格式的证书,我们通过终端进入p12 文件所在的文件夹,通过终端输入下面的命令生成pem文件

1
2
3
openssl pkcs12 -in dev_push_Certificates.p12 -out apns-dev-cert.pem -nodes -clcerts
Enter Import Password:
MAC verified OK

4、生成证书后,只要替换Push证书就好了。

iOS开发锦囊

1、判断是不是AppStore版本

1
2
3
4
5
6
7
- (BOOL)isAppStoreEnvironment {
#if TARGET_OS_IOS && !TARGET_IPHONE_SIMULATOR
    return ([[NSBundle mainBundle] pathForResource:@"embedded" ofType:@"mobileprovision"] == nil);
#endif

    return NO;
}

2、判断当前系统时区名称

1
2
3
4
5
6
7
8
9
10
11
12
13
14
+ (NSString *)currentSystemTimeZoneName {
    static NSLock * methodLock;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        methodLock = [[NSLock alloc] init];
    });

    [methodLock lock];
    [NSTimeZone resetSystemTimeZone];
    NSString * systemTimeZoneName = [[NSTimeZone systemTimeZone].name copy];
    [methodLock unlock];

    return systemTimeZoneName;
}

3、安全的执行method

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
 * Performs selector on the target, only if the target and selector are non-nil,
 * as well as target responds to selector
 */
+ (void)safePerformSelector:(SEL)selector withTarget:(id)target object:(id)object object:(id)anotherObject {
    if (target == nil || selector == nil || ![target respondsToSelector:selector]) {
        return;
    }

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
    [target performSelector:selector withObject:object withObject:anotherObject];
#pragma clang diagnostic pop
}

4、主线程执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
+ (CLLocationManager *)_newSystemLocationManager {
    __ block CLLocationManager * manager = nil;

    // CLLocationManager should be created only on main thread, as it needs a run loop to serve delegate callbacks
    dispatch_block_t block = ^{
        manager = [[CLLocationManager alloc] init];
    };
    if ([NSThread currentThread].isMainThread) {
        block();
    } else {
        dispatch_sync(dispatch_get_main_queue(), block);
    }
    return manager;
}

5、添加Block

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
- (void)addBlockForCurrentLocation:(PFLocationManagerLocationUpdateBlock)handler {
    @synchronized (self.blockSet) {
        [self.blockSet addObject:[handler copy]];
    }

    //
    // Abandon hope all ye who enter here.
    // Apparently, the CLLocationManager API is different for iOS/OSX/watchOS/tvOS up to the point,
    // where encapsulating pieces together just makes much more sense
    // than hard to human-parse compiled out pieces of the code.
    // This looks duplicated, slightly, but very much intentional.
    //
#if TARGET_OS_WATCH
    if ([self.bundle objectForInfoDictionaryKey:@"NSLocationWhenInUseUsageDescription"] != nil) {
        [self.locationManager requestWhenInUseAuthorization];
    } else {
        [self.locationManager requestAlwaysAuthorization];
    }
    [self.locationManager requestLocation];
#elif TARGET_OS_TV
    [self.locationManager requestWhenInUseAuthorization];
    [self.locationManager requestLocation];
#elif TARGET_OS_IOS
    if ([self.locationManager respondsToSelector:@selector(requestAlwaysAuthorization)]) {
        if (self.application.applicationState != UIApplicationStateBackground &&
            [self.bundle objectForInfoDictionaryKey:@"NSLocationWhenInUseUsageDescription"] != nil) {
            [self.locationManager requestWhenInUseAuthorization];
        } else {
            [self.locationManager requestAlwaysAuthorization];
        }
    }
    [self.locationManager startUpdatingLocation];
#elif PF_TARGET_OS_OSX
    [self.locationManager startUpdatingLocation];
#endif
}

6、安全执行线程

1
2
3
4
5
6
7
8
#import <Foundation/Foundation.h>
extern dispatch_queue_t JPThreadsafetyCreateQueueForObject(id object);
extern void JPThreadsafetySafeDispatchSync(dispatch_queue_t queue, dispatch_block_t block);
#define JPThreadSafetyPerform(queue, block) ({                      \
    __ block typeof((block())) result;                              \
    JPThreadsafetySafeDispatchSync(queue, ^{ result = block(); }); \
    result;                                                        \
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#import "JPThreadsafety.h"

static void *const JPThreadsafetyQueueIDKey = (void *)&JPThreadsafetyQueueIDKey;

dispatch_queue_t JPThreadsafetyCreateQueueForObject(id object) {
    NSString* label = [NSStringFromClass([object class]) stringByAppendingString:@".synchronizationQueue"];
    dispatch_queue_t queue = dispatch_queue_create(label.UTF8String, DISPATCH_QUEUE_SERIAL);

    void* uuid = calloc(1, sizeof(uuid));
    dispatch_queue_set_specific(queue, JPThreadsafetyQueueIDKey, uuid, free);

    return queue;
}

void JPThreadsafetySafeDispatchSync(dispatch_queue_t queue, dispatch_block_t block) {
    void* uuidMine = dispatch_get_specific(JPThreadsafetyQueueIDKey);
    void* uuidOther = dispatch_queue_get_specific(queue, JPThreadsafetyQueueIDKey);

    if (uuidMine == uuidOther) {
        block();
    } else {
        dispatch_sync(queue, block);
    }
}

Method Swizzling

在没有一个类的实现源码的情况下,想改变其中一个方法的实现,除了继承它重写、和借助类别重名方法暴力抢先之外,还有更加灵活的方法吗?在Objective-C编程中,如何实现hook呢?标题有点大,计划分几篇来总结。 本文主要介绍针对selector的hook,主角被标题剧透了———— Method Swizzling

Method Swizzling 原理

在Objective-C中调用一个方法,其实是向一个对象发送消息,查找消息的唯一依据是selector的名字。利用Objective-C的动态特性,可以实现在运行时偷换selector对应的方法实现,达到给方法挂钩的目的。

每个类都有一个方法列表,存放着selector的名字和方法实现的映射关系。IMP有点类似函数指针,指向具体的Method实现。

我们可以利用 method_exchangeImplementations 来交换2个方法中的IMP, 我们可以利用 class_replaceMethod 来修改类, 我们可以利用 method_setImplementation 来直接设置某个方法的IMP, …… 归根结底,都是偷换了selector的IMP,如下图所示:

Method Swizzling 实践

举个例子好了,我想钩一下NSArray的lastObject 方法,只需两个步骤。 第一步:给NSArray加一个我自己的lastObject

1
2
3
4
5
6
7
8
9
10
11
12
13
#import "NSArray+Swizzle.h"  


@implementation NSArray (Swizzle)


- (id)myLastObject
{
    id ret = [self myLastObject];
    NSLog(@"**********  myLastObject *********** ");
    return ret;
}
@end
乍一看,这不递归了么?别忘记这是我们准备调换IMP的selector,[self myLastObject] 将会执行真的 [self lastObject] 。

第二步:调换IMP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#import <objc/runtime.h>  
#import "NSArray+Swizzle.h"  


int main(int argc, char* argv[])
{
    @autoreleasepool {

        Method ori_Method =  class_getInstanceMethod([NSArray class], @selector(lastObject));
        Method my_Method = class_getInstanceMethod([NSArray class], @selector(myLastObject));
        method_exchangeImplementations(ori_Method, my_Method);

        NSArray* array = @[@"0",@"1",@"2",@"3"];
        NSString* string = [array lastObject];
        NSLog(@"TEST RESULT : %@",string);

        return 0;
    }
}

Read on →