Quantcast
Channel: 天狐博客
Viewing all 115 articles
Browse latest View live

iOS10适配之Keychain读写失败

$
0
0

Keychain

iOS设备中的Keychain是一个安全的存储容器,可以用来为不同应用保存敏感信息比如用户名,密码,网络密码,认证令牌,UUID等

Bug

升级正式版Xcode8 运行了下最近的项目在iOS10模拟器上,没什么大问题,因为使用keychain存储是否首次安装的标识,多次运行后,发现Keychain存取失效了。

SecItemAdd  反回了错误代码 -34018

解决

首先我就想到了是权限问题,但是Xcode并配置Keychain权限的地方,只有一个Keychain Share选项,但是Keychain Share用于开发者不同应用之间Keychain共享

试试看的想法打开了

Capabilities => Keychain sharing 点击打开按钮,重试后发现Keychain读写好用了。。。。。,同时项目目录中多了一个项目名.entitlements的配置文件

2c7e5c3f-99b3-4e41-9d21-d53c8d31e94f但是看地下的爆红会发现,虽然开启了Keychain sharing ,并没有在证书、appid 中开启Keychain sharing,也就是说如果这样打包Keychain sharing的功能是不好用的,只是Keychain的读写好用

我只想说。这。。。。。。。。!@#¥%……&*

转载请注明:天狐博客 » iOS10适配之Keychain读写失败


Xcode控制台不打印NSLog

$
0
0

有个项目困扰我很久了,Xcode7运行起来之后NSLog不打印到Xcode的控制台上,而是打印在Simulator.app -> Debug ->Open System Log 中,而且还是偶发的,而且诡异的是团队中所有成员对于这个项目都有这个问题,十次有一两次能打印到Xcode控制台。甚是无语。

最近Xcode8发布了,发现Xcode8运行起来App后控制台打印出来一堆乱七八糟的Log,解决方法是

Product -> Scheme -> Edit Scheme -> Run -> Arguments -> Environment Variables

增加 "OS_ACTIVITY_MODE" 值为 "disable",并勾选(注意如果真机运行的时候不打印NSLog,要取消勾选),重新run即可消失。所以我想是不是和某个环境变量的设置有关。

49fbc898-072f-49dc-9311-2a36615a1446

 

再有一个,官方Xcode 8 beta release notes写道

When debugging an app running on Simulator, logs may not be visible in the console. Workaround: Use command + / in Simulator.app to open the system log in the Console app to view NSLogs. (26457535)

虽然苹果表明NSLog不打印输出到控制台是正常情况,我在想是什么原因的,为什么我那个项目,所有人都会有个这个问题,其他项目完全没问题。说与项目无关我都说服不了我自己,有知道为什么的可以给我留言!!!!!!

转载请注明:天狐博客 » Xcode控制台不打印NSLog

Cocoa开发之KVB(Key Value Binding)

$
0
0

Cocoa提供了多个内部机制:Key-Value Coding(KVC)、Key-Value Observing(KVO)、Key-Value Binding(KVB)。KVC与KVO在iOS中我们经常用到,再熟悉不过了,本文主要带你了解下KVB(Key Value Binding)机制。

KVC(Key Value Coding)

KVC(Key Value Coding),即是指 NSKeyValueCoding,一个非正式的协议,提供一种机制来间接访问对象的属性。而不是直接调用Getter、Setter、访问器方法访问。因此一个对象的所有属性都可以使用这种方式访问。

KVO(Key Value Observing)

KVO(Key Value Observing),即是指NSKeyValueObserving ,一个非正式的协议,建立在 KVC 之上,提供一种机制允许对象去监听其它对象的某个属性的修改。

KVB(Key Value Binding)

KVB(Key Value Binding),即是指NSKeyValueBindingCreation ,属于Cocoa AppKit.framework ,一个非正式协议,提供了方法来创建和删除视图对象和控制器或控制器和模型之间的绑定对象。此外,它也提供了视图子类来宣传公开的绑定的方法。这种非正式协议由NSObject实现 ,方法可以被视图和控制器子类覆盖。

概述

当创建一个涉及接收器绑定到指定 key path的可观测对象的属性绑定(例如,视图对象的属性)。当可观察对象的指定属性的值发生更改时,接收方通过键-值观测机制被通知。绑定可以进一步指定自定义的绑定选项(如何观察和被观察的对象进行交互)。

绑定被认为是一个对象的属性绑定。所有绑定的相关信息应该被对象拥有。所有标准绑定使用在AppKit对象上(views, cells, table columns, controllers) ,当他们销毁时自动解绑定。但是如果你对其他类型的对象创建键值绑定,你需要确保回收之前删除这些绑定(被观察对象持有观察者的弱引用,所以controllers/model对象可能继续引用和发送消息给绑定到他们的对象)

绑定对象之间通常使用在Interface Builder中建立的绑定检查员(Bindings inspector)。然而,有时必须以编程方式完成,例如当在不同的nib文件对象之间建立一个绑定。

NSView子类可以通过调用类方法exposeBinding:为每个属性公开额外的key-value-coding/key-value-observing兼容属性作为绑定。这通常是在类的初始化方法完成的。通过公开对象支持的绑定和创建一个 Interface Builder palette,你可以使你自己的类的实例绑定在Interface Builder。

暴露绑定

+ (void)exposeBinding:(NSString *)binding;

暴露指定的绑定,广告其可用性。

@property(readonly, copy) NSArray<NSString *> *exposedBindings;

返回一个数组,其中包含接收器公开的绑定。

一个子类可以重写此方法删除一个不适合子类,超类所暴露的绑定

管理绑定

- (Class)valueClassForBinding:(NSString *)binding;

返回指定的绑定的类。

该方法被 Interface Builder使用来确定适当的绑定转化。该方法的实现是可选的。

- (void)bind:(NSString *)binding   toObject:(id)observable  withKeyPath:(NSString *)keyPath   options:(NSDictionary<NSString *,id> *)options;

在给定接受者的属性和给定指定了key path的对象建议一个绑定

binding:之前使用exposeBinding:方法暴露的接收者的一个属性对应的key path。

observableController : 绑定到的对象

keyPath :一个observableController的属性 key path。path中的元素key-value observing兼容(见 Key-Value Observing Programming Guide)。

options:包含绑定选项的字典,比如占位对象或NSValueTransformer标识符作为常量。这个值是可选的,nil为没有任何选项。

- (NSArray<NSAttributeDescription *> *)optionDescriptionsForBinding:(NSString *)binding;

更新中。。。。

 

 

转载请注明:天狐博客 » Cocoa开发之KVB(Key Value Binding)

Xcode8无法打开Storyboard(The document "VIP.Storyboard" could not be opened. Unrecognized file content)

$
0
0

升级Xcode8之后,打开项目的其中一个storyboard提示

The document "VIP.Storyboard" could not be opened. Unrecognized file content.

在Xcode7下打开正常

因为storyboard其实就是一个xml,如果用源码编辑器打开之后会xml高亮,但是结果打开这个storyboard,全是灰的,并没有高亮,于是我想到了格式问题。通过增减排除法,替换排除法。排除了内容格式错误问题。于是想到了是不是文件名问题

最后发现,VIP.Storyboard 的后缀.Storyboard  S是大写的,改成VIP.storyboard就能打开了

最后只能说Xcode8的容错能力变差了,或者说变的更严谨了。

 

转载请注明:天狐博客 » Xcode8无法打开Storyboard(The document "VIP.Storyboard" could not be opened. Unrecognized file content)

关于微信小程序(应用号)开发

$
0
0

学习了下小程序开发的框架,API 与组件

微信封装的很好很到位,架构分层也很合理,学习上并不需要过多的语言基础来支撑,非常容易上手。

提供了基本的与系统交互能力,数据存储,文件访问等。

没有提供更底层的API,其实也提供不了,提供过多Appstore审核风险会更高。

相比公众号,服务号可定制化更高。体验更好。

小程序并不能媲美native app,只能适用于基本的服务型,展示型应用。

轻应用的确是趋势,但是与native app并不是二选一的关系。

 

个人认为,如果微信把平台拆分出来会更好,用户的小程序可以申请接入微信,微信提供流量入口。并且没接入微信的小程序也可以在任何浏览器直接访问

 

 

转载请注明:天狐博客 » 关于微信小程序(应用号)开发

Swift3 'CGRectDivide' is unavailable: Use divided(atDistance:from:)

$
0
0

Xcode8 Swift3  'CGRectDivide' is unavailable: Use divided(atDistance:from:)

var rightBezelRect = CGRect.null
var tempRect = CGRect.null
CGRectDivide(self.childControllerContainerView.bounds, &rightBezelRect, &tempRect, bezelRange, .maxXEdge)

Swift3

let (rightBezelRect, tempRect) =   self.childControllerContainerView.bounds.divided(atDistance: bezelRange, from: .minXEdge)

 

转载请注明:天狐博客 » Swift3 'CGRectDivide' is unavailable: Use divided(atDistance:from:)

Swift Result of call to ‘XXX’ is unused

$
0
0

我们都知道当在Objective-C中调用一个方法,没有使用其返回值当时候可以,不用变量来接收。并且一个变量未被上下文使用到的时候可以用__unused修饰符修饰。

到了Swift中,不用变量来接收返回值会爆出警告“result of call to ‘XXX’ is unused”相比Objective-CSwift更加严谨了许多。

有些时候我们的方法虽然有返回值,但是并不想使用,那么如何消除警告呢。两种方法

第一种:在 func 定义的上方,加上 @discardableResult 修饰符即可

@discardableResult
class func POST(urlString url: String, parameter param: [String : AnyObject]?, responseCallback: ResponseCallback?) -> Request{

第二种:在接收返回值当变量前_ =

_ = APIManager.POST(urlString: "http://api.skyfox.org/project/afndemo/newsList.do", parameter: nil) { (respone, error) in
            
        }

- 号用来替代未被使用的变量,如

let (leftBezelRect, _) =   self.childControllerContainerView.bounds.divided(atDistance: bezelRange, from: .minXEdge)

divided方法,返回了两个对象,其中第一个参数被命名为leftBezelRect被下文使用,第二个参数因为没被下文使用,所以使用_即可

转载请注明:天狐博客 » Swift Result of call to ‘XXX’ is unused

Swift NSClassFromString Crash

$
0
0

我们都知道动态获取类使用 NSClassFromString方法

但是在Swift中

let pim = NSClassFromString("PIMViewController") as! UIViewController.Type
self.navigationController!.pushViewController(pim.init(), animated: true)

直接使用会报错:unexpectedly found nil while unwrapping an Optional value

要在类名前加上module

Swift虽然没有官方说明支持namespace,但是有module或者说target来实现与namespace相似的功能

Xcode中的每个构建目标(Target)可以当做是一个模块(Module),这个构建目标可以是一个Application,也可以是一个通用的Framework(更多的时候是一个Application),target名称与bundle名称一致

var module = Bundle.main.infoDictionary!["CFBundleExecutable"] as! String
let pim = NSClassFromString(module + "." + "PIMViewController") as! UIViewController.Type
self.navigationController!.pushViewController(pim.init(), animated: true)

这样就ok了。

但是还有个操蛋的情况是,我们的bundle name含有-符号的时候,还会报错。测试发现当target名称包含-符号的时候,系统会自动转换为_,原名Sky-Swift,转换为了Sky_Swift

069af321-6548-4aca-b6bb-b13af59a0f26

那么代码中替换了就行。

var module = Bundle.main.infoDictionary!["CFBundleExecutable"] as! String
module = module.replacingOccurrences(of: "-", with: "_")
let pim = NSClassFromString(module + "." + "PIMViewController") as! UIViewController.Type
self.navigationController!.pushViewController(pim.init(), animated: true)

 

Swift命名空间更多信息:http://www.cnblogs.com/kenshincui/p/4824810.html#nameSpace

转载请注明:天狐博客 » Swift NSClassFromString Crash


iOS开发之Safari调试WebView页面

$
0
0

在iOS开发过程中,难免会加入html5页面来实现文章详情等等类似功能。

我们都知道火狐等PC浏览器有类似firebug,审查元素等工具来调试网页样式与脚本,查看请求参数与请求头等等。

在iOS开发中,这些网页检查器功能也是存在的(无论是模拟器还是真机),我们需要用到Mac自带的浏览器Safari。所以,本文将讲解如何使用Safari对iOS程序中的WebView进行调试。

1. 打开模拟器/真机的开发者模式(模拟器是默认开启的)

"设置"->"Safari"->"高级"->"Web检查器" 打开

a5664ee2-b1bf-41c9-966b-9186322075b4

2. 打开Mac上Safari的开发者模式

开启 Safari  ->“偏好设置” -> “高级” -> “在菜单栏中显示开发选项”

e4bb8406-bafe-41a3-9a90-2221d0e74573

4. 在模拟器/真机中打开APP中的WebView页面,并打开Safari

当有WebView加载网页时,打开Safari调试模式 网页检查器。 “开发” ->“Simulator”->“网页文档名称”

18884ee0-b08a-4db9-9893-d714266cf0cc

在弹出的网页检查器中,可以看到当前正在加载网页的各种信息,包括元素源码、网络、资源与脚本、控制台输出等。并且它和Web前端的调试方式相同,你可以直接修改网页的CSS样式,对网页布局源码等进行修改,使用调试器进行脚本调试,而不需要重新运行整个APP。

859d7231-085a-49cf-afc5-41833c9549e8enjoy it!

 

转载请注明:天狐博客 » iOS开发之Safari调试WebView页面

iOS开发之特征变量(Use Trait Variations)

$
0
0

特征变量(Trait Variations)

Trait Variations,特征变量。

Xcode8中删除了size class选项,取而代之的是Use Trait Variations选项。用来声明APP在不同设备上的不同特征。这带给我的将是一种新的适配方式。

972635b0-0d66-4237-9c17-551bbdbc0861

特征(Trait)

特征(Trait),特征是描述当前设备配置的一部分。特征包括APP可用的屏幕区域,显示的分辨率、色域,亮色或暗色的外观。您可以创建你的用户界面元素的不同组合的特征变量。

特征 Trait

描述 Description

Horizontal size class 你的iOS APP可用的总宽度
Vertical size class 你的APP可用的总高度
Color gamut iOS设备的使用等色彩空间
Display scale 屏幕的像素密度
Apple Watch size Apple Watch的尺寸
Interface style 亮色或暗色风格的tvOS用户界面

使用不同的设备配置视图的用户界面

Interface Builder(界面构建器),使用设备配置面板观看使用不同的设备配置后的布局。例如,一个iOS APP的布局可以在iPhoneiPad和使用横向或纵向方向存在不同。tvOS APP的用户界面在使用亮色或暗色界面风格(interface style)下存在不同。你第一次打开用户界面文件,选择你期望大多数用户使用你的应用程序的设备,然后创建用户界面在其他设备上需要的变量(Variations)。

b4c09bcd-81a4-43e8-aad1-ac840b2ab493

1.打开设备配置面板,单击在画布下边布局条上的“View as”按钮。

打开设备配置面板,单击在画布下边布局条上的“View as”按钮。

2.选择设备

在设备区域,选择一个设备。你选择的设备系列会出现在布局条中的"View as" 按钮上。对于iOS APP, 选择一个iOS设备系列, 对于watchOS APP, 选择一个Apple Watch尺寸

dce_watchos3.选择方向

Adaptation适应区域,选择一个适应,对于你的应用可用的屏幕空间数量的Variation变量,例如,为iPad设备选择 Split View视图。

 

334c3405-4f44-4b96-a0f6-78259065ee1f4.选择界面风格

Interface Style界面风格区域,选择一个影响视觉的界面样式,例如,亮色或暗色的tvOS界面风格。

07442e1a-466b-4495-b730-b58f8b496cf3

为不同的设备配置创建用户界面的变量

Interface Builder界面构建器,您可以为不同的设备配置使用设备配置面板添加、删除和编辑用户界面的变量。在你创建一个变量之前,选择一个你想要变化的设备配置。

5de0ab2a-4390-427b-bee1-08f1e6169f0d1.在设备配置面板中,单击“Vary for”按钮。设备配置面板中显示了此配置的所有受影响的设备。

aca2d494-12d0-45d4-8521-44f27e2299ab

2.对于iOS APP,在弹出窗口中,选择 Size Class(宽度、高度,或两者)

Size Class特征定义一个抽象的或真正的iOS设备上的屏幕尺寸。在选择特征后,点击界面构建器任何位置关闭弹出窗口。

0f4565d1-2d57-4f38-a315-16c8c19e9165

设备配置的窗格显示受影响的设备。

2ab28a17-aaff-4ebc-8665-a5a9a3a343e3

3.修改在Interface Builder中的设备配置

在变量模式,任何更改会生效在画布上,创建一个当前设备的配置变量或更新现有的变量。

4.点击“Done Varying”按钮

Interface Builder界面构建器在画布上停止添加变量给不同视图的属性和约束。

使用检查器编辑基于特征的属性变量值

您可以为不同的设备特征使用Attributes inspector属性检查器和Size inspector尺寸检查器自定义对象的属性值。因此,创建用户界面的变量类似于使用设备配置面板。

ff5dae33-dd85-4eff-8bff-455e63ada1e7

1.增加基于特征的属性值

  1. Interface Builder界面构建器中,选择一个在画布上的对象。
  2. 选择检查器包含的所需属性。
  3. 点击接近属性控制的(Add Customization)增加自定义按钮(+)
  4. 在弹出窗口中,选择一个设备配置。对于watchOS APP,选择一个Apple Watch设备,对于tvOS APP,选择一个界面风格。对于iOS APP,从弹出框选择一个设备配置表示为Size Classgamut颜色范围。c155f1e5-74d9-44bf-a33f-1b998d034b519806160f-3287-4400-8af5-ca4bdb4552ee选择一个设备配置后,设备配置指定的控制出现在属性控制中。
  5. 改变设备配置指定的属性值。

2.移除基于特征的属性值

  1. 在界面构建器中,选择一个在画布上的对象。
  2. 选择检查器包含的所需属性。
  3. 单击接近设备配置指定的控制旁边的(Remove Customization)删除自定义按钮(x)  34a3285b-9aeb-4f06-b6da-d0db4cf61432自定义从属性中移除

用户界面的变量

用户界面变量是基于设备配置的一个你的用户界面变化的表示,如改变了背景颜色和其他元素时,设备设置成暗色的风格。变化可以适用于用户界面的一个元素,例如删除约束,或者一个视图类的属性或约束,如一个Label的字体。

你可以改变的:

  • Size or position of a view  (视图的位置或大小)
  • Installation of a view   (视图的装载)
  • Installation of a constraint   (约束的装载)
  • Constraint constant   (约束常量)
  • Font   (字体)
  • Color for the font, tint, or background   (字体颜色,色彩或背景)
  • Layout margins   (布局的间距)
  • Image file   (图片文件)

可以改变的特定属性取决于元素的类。

Size Class

size class 标识可用于应用程序的高度和宽度相对数量的显示空间。每个维度可以是compact紧凑的,例如,在iPhone横向的高度。或者是regular不变的,例如,iPad的高度或宽度。因为大部分APP的布局不需要改变任何可用的屏幕大小,有一个额外的值

一个视图控制器的当前size class是基于三个因素:

  • 设备的屏幕大小。
  • 设备的方向。
  • 对于视图控制器屏幕的一部分可用。例如,当一个分屏视图控制器显示masterdetail控制器,控制器都没有进入全屏。

Interface Builder 界面构建器允许您自定义布局、约束和基于size class的对象的属性。布局然后通过size class自动调整大小。例如,,当用户从竖屏转换到横屏视图,或在iPad上打开一个split view拆分视图。

转载请注明:天狐博客 » iOS开发之特征变量(Use Trait Variations)

关于锤子2016新品发布会

iOS开发之NSURLSession与AFNetworking 2.x/3.x实现同步请求

$
0
0

AFNetworking 2.x 同步请求

使用waitUntilFinished阻塞当前线程,直到该NSOperation结束

- (NSDictionary *)sendSynchRequest:(NSString *)URLString parameters:(NSDictionary*)parameters
{
    AFHTTPRequestSerializer *requestSerializer = [AFHTTPRequestSerializer serializer];
    NSMutableURLRequest *request = [requestSerializer requestWithMethod:@"POST" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:nil];
    
    AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
    AFJSONResponseSerializer *responseSerializer = [AFJSONResponseSerializer serializer];
    
    [operation setResponseSerializer:responseSerializer];
    [operation start];
    [operation waitUntilFinished];
    id responseObject  = operation.responseObject;
    NSLog(@"server respone JSON\n:%@",[responseObject JSONStringValue]);
    return responseObject;
}

AFNetworking 3.x 同步请求

使用GCD信号量实现AFNetworking常用数据请求POST,GET等等的同步

- (void)viewDidLoad {
    [super viewDidLoad];
    NSError *error = nil;
    id respone = [self POST:@"http://api.skyfox.org/project/afndemo/newsList.do" parameters:nil error:&error];
    NSLog(@"respone:%@",respone);
}

- (id)POST:(NSString *)URLString  parameters:(NSDictionary *)parameters error:(NSError **)returnError {

    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    __block id data = nil;
    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];

    [manager POST:URLString parameters:parameters progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        
        data = responseObject;
        
        dispatch_semaphore_signal(semaphore);
        
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        if (returnError) {
            *returnError = error;
        }
        dispatch_semaphore_signal(semaphore);
    }];
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    
    return data;
}

使用GCD信号量 更改dataTaskWithRequest方法为同步

- (id)sendSynchronousDataTaskWithRequest:(NSURLRequest *)request
                       returningResponse:(NSURLResponse **)returnResponse
                                   error:(NSError **)returnError {
    
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    __block NSData *data = nil;
    
    [[[AFHTTPSessionManager manager]  dataTaskWithRequest:request completionHandler:^(NSURLResponse * _Nonnull response, id  _Nullable responseObject, NSError * _Nullable error) {
       
            data = responseObject;
            if (returnResponse) {
                *returnResponse = response;
            }
            if (returnError) {
                *returnError = error;
            }
            dispatch_semaphore_signal(semaphore);

    }]resume];
    
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    
    return data;
}

NSURLSession 同步请求

- (NSData *)sendSynchronousDataTaskWithRequest:(NSURLRequest *)request
                             returningResponse:(NSURLResponse **)returnResponse
                                         error:(NSError **)returnError {
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    __block NSData *data = nil;
    
    [[[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData *taskData, NSURLResponse *taskResponse, NSError *taskError) {
        data = taskData;
        if (returnResponse) {
            *returnResponse = taskResponse;
        }
        if (returnError) {
            *returnError = taskError;
        }
        dispatch_semaphore_signal(semaphore);
    }] resume];
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    
    return data;
}

GCD信号量

信号量是一个整形值并且具有一个初始计数值,并且支持两个操作:信号通知和等待。当一个信号量被信号通知,其计数会被增加。当一个线程在一个信号量上等待时,线程会被阻塞(如果有必要的话),直至计数器大于零,然后线程会减少这个计数。

在GCD中有三个函数是semaphore的操作,分别是:

  • dispatch_semaphore_create   创建一个semaphore, 有一个整形的参数,我们可以理解为信号的总量
  • dispatch_semaphore_signal   发送一个信号,会让信号总量加1
  • dispatch_semaphore_wait    等待信号,当信号总量少于0的时候就会一直等待,否则就可以正常的执行,并让信号总量-1

根据这样的原理,我们便可以快速的将异步请求转换为同步任务:

- (void)viewDidLoad {
    [super viewDidLoad];
    NSDictionary *dic = [self getData:@{@"t":@"123"}];
    NSLog(@"dic%@",dic);
}
//已知的异步方法
- (void)getData:(id)data  completion:(void (^)(NSDictionary *dict))completion{
    //模拟耗时操作
    sleep(5);
    NSDictionary *dict = @{@"return":@"done"};
    completion(dict);
}
//修改后的同步方法
-(NSDictionary*)getData:(id)data {
    __block NSDictionary *returnDic = nil;
    
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    [self getData:data completion:^(NSDictionary *dict) {
        NSLog(@"semaphore +1");
        dispatch_semaphore_signal(semaphore); //+1 semaphore
        returnDic = dict;
    }];
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    NSLog(@"main thread");
    return  returnDic;
}

GCD异步请求使用信号量

dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"start");
        [NSThread sleepForTimeInterval:1.f];
        NSLog(@"semaphore +1");
        dispatch_semaphore_signal(semaphore); //+1 semaphore
    });
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    NSLog(@"main thread");

dispatch_semaphore_t semaphore1 = dispatch_semaphore_create(0);
dispatch_semaphore_t semaphore2 = dispatch_semaphore_create(0);

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"thread 2");
        sleep(2);
        dispatch_semaphore_signal(semaphore2);
    });
    dispatch_semaphore_wait(semaphore2, DISPATCH_TIME_FOREVER);
    NSLog(@"thread 1");
    sleep(2);
    dispatch_semaphore_signal(semaphore1);
});
dispatch_semaphore_wait(semaphore1, DISPATCH_TIME_FOREVER);
NSLog(@"main thread");

 

转载请注明:天狐博客 » iOS开发之NSURLSession与AFNetworking 2.x/3.x实现同步请求

macOS长时间待机后黑屏,AlipayDispatcherService Crash

$
0
0

macOS长时间待机后黑屏,假死,但是还能看见鼠标,只能强制重启

0652a87b-fa98-412a-b45b-0e5c9dd78e3c

起初以为macOS 10.12的bug,但是查看系统日志发现一个AlipayDispatcherService进程一直在尝试启动与联网,每10秒就crash一次,

 

13eb8b48-b349-4886-a7d7-699fd24e282f

让我们来停止这个服务

sudo launchctl unload /Library/LaunchDaemons/com.alipay.DispatcherService.plist
sudo launchctl unload ~/Library/LaunchAgents/com.alipay.refresher.plist

如果想重新启动

sudo launchctl load /Library/LaunchDaemons/com.alipay.DispatcherService.plist

小结一下,AlipayDispatcherService 就是支付宝一个自带检查更新功能的安全守护进程,由于 com.alipay.adaptor.plist,com.alipay.refresher.plist 和 com.alipay.DispatcherService.plist 这三个 plist 配置文件里面设置了 RunAtLoad 和 KeepAlive,所以对应的进程会一直被 launchd 保持活动状态,这就是为什么一直会有 CPU 占用的原因。现在支付宝在 Mac 下已经不需要安装安全控件了,所以也可以用 CleanMyMac 3 或者在 Terminal 里面手动删除。

sudo rm -rf /Library/Application\ Support/Alipay && rm -rf /Library/LaunchDaemons/com.alipay.DispatcherService.plist && rm -rf ~/Library/LaunchAgents/com.alipay.adaptor.plist && rm -rf ~/Library/LaunchAgents/com.alipay.refresher.plist && rm -rf ~/Library/Internet\ Plug-Ins/aliedit.plugin && rm -rf ~/Library/Internet\ Plug-Ins/npalicdo.plugin

更多 https://www.zhihu.com/question/40097485

转载请注明:天狐博客 » macOS长时间待机后黑屏,AlipayDispatcherService Crash

Xcode Build Setting Transformations

$
0
0

Xcode在许多地方包括Info.plists提供使用$(BUILD_SETTING_NAME) 或者 ${BUILD_SETTING_NAME} 语法更改build settings值的能力,其他build setting值,和.xcconfig文件。这些替换各种方式添加运算转换这些值,你可能在项目模板中包含的.plist中见过其中的一个信息:

com.company.$(PRODUCT_NAME:rfc1034identifier)

build setting中的PRODUCT_NAME值被转换成了适应于反向DNS CFBundleIdentifier所使用的格式。如果PRODUCT_NAME是“Whatever App”产生的字符串是“com.company.Whatever-App”。

这些转换很有用但不被记录,所以这有一个可用操作的列表和他们所做的事的列表:

操作符 返回
identifier 适合在源代码中使用的C 标识符表示形式。
c99extidentifier 类似 identifier, 但支持C99允许的扩展字符。在Xcode 6中添加。
rfc1034identifier
适合DNS 名称使用表示形式
quote 适合用于作为shell参数的表示形式
lower
小写表示形式
upper
大写表示形式
standardizepath 字符串调用 stringByStandardizingPath
base 一个路径的基名称 - 删除任何扩展名的最后一个路径组件
dir 目录路径的一部分。
file 文件路径的一部分。
suffix
扩展路径包括 '.' 分隔符

注意,这些操作符可以混合链接使用,所以你可以这样做

$(PRODUCT_NAME:lower:rfc1034identifier) 或者 $(CONFIGURATION:upper:identifier).

最后,这的确是一个被忽略的知识点!

原文 http://codeworkshop.net/posts/xcode-build-setting-transformations

转载请注明:天狐博客 » Xcode Build Setting Transformations

RubyGems及CocoaPods安装与版本升级

$
0
0

RubyGems简介

b3a31b74-1091-4270-8ec5-14178b70b462

RubyGems是一个方便而强大的Ruby程序包管理器,Ruby的第三方插件是用gem方式来管理,非常容易发布和共享,一个简单的命令就可以安装上第三方的扩展库。特点:能远程安装包,包之间依赖关系的管理,简单可靠的卸载,查询机制,能查询本地和远程服务器的包信息,能保持一个包的不同版本,基于Web的查看接口,能查看你安装的gem的信息。

安装RubyGems

  • https://rubygems.org/pages/download 下载压缩包
  • 解压到一个文件并且cd进去
  • 通过运行ruby setup.rb安装 (你可能需要admin/root权限)

更新RubyGems镜像源

gem sources -l  //查看当前使用的镜像站

gem sources --remove https://rubygems.org     //删除-l出现的所有镜像源地址

gem sources --add http://gems.ruby-china.org/      //增加国内ruby-china源

确保gem sources -l只有一个源

当然所有gem配置最后都是写入到了配置文件~/.gemrc中:

Jakey-mini:~ Jakey$ cat ~/.gemrc
---
:backtrace: false
:bulk_threshold: 1000
:sources:
- https://gems.ruby-china.org/
:update_sources: true
:verbose: true

SSL证书问题

rubygems.org需要翻墙,而ruby chinahttps://gems.ruby-china.org/http://gems.ruby-china.org/均可,但是https的地址存在证书验证问题。

笔者使用ruby china源最高升级到了gem到了2.6.7,使用httphttps地址均没有问题,也没有证书错误。

但是使用https://rubygems.org升级gem到了2.6.8之后,因为rubygems经常需要翻墙并且速度还不是那么令人满意,于是把源又切换回了ruby china源,这就意味着以后的软件包安装升级使用的是ruby china https源:

提示证书问题但是在功能上没有发现有什么影响:

Jakey-mini:~ Jakey$ gem sources --add https://gems.ruby-china.org/
ERROR:  SSL verification error at depth 1: unable to get local issuer certificate (20)
ERROR:  You must add /C=IL/O=StartCom Ltd./OU=Secure Digital Certificate Signing/CN=StartCom Certification Authority to your local trusted store
ERROR:  SSL verification error at depth 2: self signed certificate in certificate chain (19)
ERROR:  Root certificate is not trusted (/C=US/O=GeoTrust Inc./CN=GeoTrust Global CA)
https://gems.ruby-china.org/ added to sources
Jakey-mini:~ runlin$ sudo gem install cocoapods
Password:
ERROR:  SSL verification error at depth 1: unable to get local issuer certificate (20)
ERROR:  You must add /C=IL/O=StartCom Ltd./OU=Secure Digital Certificate Signing/CN=StartCom Certification Authority to your local trusted store

升级更新RubyGems

使用gem命令升级rubygems,并不想使用homebrew升级。

gem update --system  //更新rubugems

会报如下错误

updating rubygems-update
Fetching: rubygems-update-2.6.8.gem (100%)
ERROR:  While executing gem ... (Gem::FilePermissionError)
    You don't have write permissions for the /Library/Ruby/Gems/2.0.0 directory.

执行命令

sudo gem update --system

会报如下错误

Updating rubygems-update
ERROR:  While executing gem ... (Errno::EPERM)
    Operation not permitted - /usr/bin/update_rubygems

显然macOS10.11之后,有一个新的安全功能称为Rootless,阻止你修改系统文件,如果你安装gems在/usr/local/bin 目录,不会出现问题,因为rootless不会影响这个路径:

解决办法

sudo gem update -n /usr/local/bin --system

如果 gem -v还是老版本,执行

sudo update_rubygems

测试发现使用ruby-china更新到了2.6.7,而使用https://rubygems.org会更新到2.6.8,我猜测ruby-china源与官方源应该存在同步问题

Rootless

简单的来说,Rootless 机制限制了 Root 账户的权限,使其没有了对系统的完全控制权:

受到权限影响的有以下几点:

  • 禁止直接对 /System, /bin, /sbin, /usr 目录的修改(/usr/local 目录除外),只有通过苹果开发者认证的 app 才有权限对其修改。
  • 禁止注入系统进程.,某些需要注入系统进程(finder, messages 等等系统级别的进程)的程序,debugger 无法使用了。
  • 禁止加载内核扩展(kexts), 除非它们被苹果开发者正确的签名。

这些修改最明显的好处就是:恶意 app 无法通过用户授权的方式从而获得所有系统权限了

当然, 它带来的副作用也是巨大的, 一些合法的 app 也无法获得系统权限了。 比如: HomebrewCocoaPods 以及所有需要在 /usr/bin 目录下有可执行权限的 app

安装或者升级CocoaPods等软件包

同样-n重写rubyGemsbindir目录,跳过rootless的影响

sudo gem install -n /usr/local/bin GEM_NAME_HERE

安装CocoaPods

sudo gem install -n /usr/local/bin cocoapods

activesupport requires Ruby version >= 2.2.2

有些电脑安装CocoaPods会提示升级:

Error installing cocoapods:  activesupport requires Ruby version >= 2.2.2.

but我的macOS 10.12并没有遇到

ruby 2.0.0p648 (2015-12-16 revision 53162) [universal.x86_64-darwin16]

原因是由于CocoaPods依赖库版本配置错误(CocoaPods官方已修正),gem错误的安装了activesupport 5.x,而activesupport 5.x需要Ruby2.2.2支持

Jakey-mini:~ Jakey$ gem list

*** LOCAL GEMS ***

activesupport (5.0.0.1,4.2.7.1)
bigdecimal (default: 1.2.0)

最简单的方法是指定兼容的activesupport版本为4.2.6

sudo gem install -n /usr/local/bin activesupport -v 4.2.6
sudo gem install -n /usr/local/bin cocoapods

如果还是不行直接安装Ruby2.2.2即可!这里就不在讲了。

参考文献:Avoid use of activesupport version 5

 

 

转载请注明:天狐博客 » RubyGems及CocoaPods安装与版本升级


iOS开发之SDK兼容性指南

$
0
0

介绍

Xcode包括软件开发工具包(SDKs),使您能够创建应用程序,然后运行在特定版本的iOSOS X(包括版本不同于我们开发时的版本) 。这种技术允许您构建一个单一的二进制,利用新特性运行在系统上,并且支持他们,更优雅的降低在旧的系统上运行。一些苹果的Frameworks基于SDK自动修改他们的行为,从而提高兼容性。

注意:本文档并不解释如何开发代码,运行在iOS和OSX上,虽然Xcode使您可以通过简单地选择一个不同的SDK切换平台,iOS和Mac应用程,有基本的设计区别。有关更多信息,请参见Migrating from Cocoa

如果你想让你的应用程序针对特定版本或多个版本的iOSOS X,请阅读这个文档。

文档结构

这个文档包含以下章节:

  • 基于SDK(SDK-Based)的开发概述    描述了怎样基于SDK开发
  • 配置基于SDK开发的项目    描述如何使用SDK设置您的项目。
  • 使用基于SDK开发   解释了如何使用弱相关的类( weakly linked)、方法和功能,如何弱链接整个框架,。如何为不同的SDK有条件地编译,以及如何找到代码中使用的废弃API。

一、基于SDK的开发概述

苹果让SKDs对于特定版本的iOS或者OS X可用。使用这些 SDKs 允许您构建针对一个系统版本的头文件和库而不只是对于你运行的操作系统。例如:你可以为OS X 10.4构建,然而运行在OS X 10.6上。

OS X SDKS被安装为Xcode(Xcode3.2或更高版本)基本安装包的一部分, Xcode release notes 列出了每个release版本支持的SKDs。当你为了iOS开发时,总是使用从 iOS 开发者中心网站下载的 SDK

基于 SDK 的开发使用这些方法︰

  • 您可以为一个版本的操作系统和向后兼容更高版本的构建目标优化。
  • 你可以构建一个目标为一系列的操作系统版本。在旧版本可以启动,可以在新版的版本中利用新的功能。这允许你交付新的软件提供与新的特性给那些已经升级到新版本系统的客户,但是还可以允许运行在没有升级的系统上。

开发可以被部署的软件,利用不同版本iOSOS X的特性,您可以指定要构建的iOSOS X头和库的版本(或SDK),你也可以指定软件将要运行在更老版本的iOSOS X系统中。描述这些概念在第二章,"基础SDK(Base SDK)和部署目标(Deployment Target)设置"

在框架中的行为选择

随着框架通过各种版本发展,API被引入或已废弃,现有API的行为可能偶尔发生变化。苹果尽一切努力尽量减少可能导致不兼容的更改。在某些情况下提供基于框架版本的替代行为,在极少数情况下,您的代码需要确定框架版本并相应地进行调整。

作为一种向后兼容性机制,苹果框架有时会检查应用程序构建的SDK的版本,如果它是较旧的SDK,请修改行为以实现兼容性。 这是在苹果预测或发现兼容性问题的情况下完成的。

注意:大多数版本相关的行为更改都列在框架发行说明( release notes)中,但它们不一定在参考文档中描述。 要了解从一个版本到另一个版本的差异,请仔细阅读发行说明。

通常,框架通过查看应用程序链接的系统框架的版本来检测应用程序的构建方式。 因此,当使用较新的SDK重新链接应用程序时,您可能会注意到不同的行为 - 其中一些可能会导致不兼容。 在这些情况下,因为您的应用程序正在重建,您应该同时解决这些问题。 因此,如果您对应用程序进行小更新,例如,解决一些错误,通常最好继续使用相同的构建环境和最初使用的库进行构建; 也就是说,针对原始的SDK

在某些情况下,框架提供了默认设置(首选项),您可以使用它来获取旧的或新的行为,而与构建应用程序的SDK无关。 通常这些首选项仅用于调试目的; 在一些情况下,偏好可以全局地用于通过注册值来修改应用的行为。 如果利用这种机制,在你的代码的早期,使用NSUserDefaultsregisterDefaults方法)。

二、为基于SDK的开发配置项目

本章介绍了已安装的iOSOS X SDK的配置,并解释了如何设置Xcode项目以使用特定的SDK

SDK头文件和存根库

安装Xcode时,安装程序将创建一个/Developer/SDKs目录。 此目录包含一个或多个子目录,每个子目录提供特定版本的iOSOS X提供的完整的头文件和存根库。OS X SDK以主版本命名,例如MacOSX10.6.sdk ,表示可用于主要版本的最新次要版本。

iOSXcode安装程序将SDK放置在/Developer/Platforms目录中,其中是每个平台的目录,例如iPhoneOS.platform 每个平台目录又包含特定于该平台的Developer/SDKs目录。 iOS SDKiOS的次要版本命名,例如iPhoneOS4.2.sdk

为项目选择最新的SDK可让您使用与该SDK对应的操作系统更新中引入的新API当作为系统更新的一部分添加新功能时,系统更新本身通常不包含反映该改变的更新的头文件。 但是,SDK包含更新的头文件。

每个.sdk目录类似于它所代表的操作系统发行版的目录层次结构:它在其顶层具有usrSystemDeveloper目录。 OS X .sdk目录还包含一个Library目录。 这些目录中的每一个依次包含具有在安装了Xcode的操作系统的相应版本中存在的头文件和库的子目录。

iOSOS X SDK中的库是仅用于链接的存根; 它们不包含可执行代码,只包含导出的符号。 SDK支持仅适用于本地构建目标。

基础SDK(Base SDK)和部署目标(Deployment Target)设置

要为Xcode项目使用特定的SDK,请在项目的构建设置( build settings)中进行两次选择。 这些选项决定您的项目可以使用哪些操作系统功能,如下所示:

  • 选择部署目标(Deployment Target):这标识您的软件可以运行的最低的操作系统版本。 默认情况下,Xcode将此设置为与基本SDK版本或更高版本对应的操作系统的版本。

此设置的Xcode构建变量名称为MACOSX_DEPLOYMENT_TARGET(OS X部署目标)和IPHONEOS_DEPLOYMENT_TARGET(iOS部署目标)。

        您可以无条件使用操作系统版本中的功能,直到并包括部署目标设置。

  • 选择基础SDK (base SDK):您的软件可以使用基本SDK相对应的所有操作系统的功能。 默认情况下,Xcode将此设置为Xcode支持的最新操作系统。

        此参数的Xcode构建设置( build setting)名称为SDKROOTBase SDK)。

        您可以使用来自部署目标之后的系统版本的功能,包括您选择作为基本SDK的操作系统版本 - 但必须检查新功能的可用性,详情请阅读下文 "在iOS中使用弱链接的类" 和 "使用弱链接的方法和功能"

有关Xcode中构建设置的可能值和更多信息,请参阅Xcode项目管理指南(Xcode Project Management Guide)中的构建操作系统的多个版本, Xcode Build Setting Reference Running Applications in iOS Development Guide

构建应用程序时,部署目标将反映在应用程序的Info.plist文件中的MinimumOSVersion条目中。 对于iOS应用程序,MinimumOSVersion条目由App Store用于指示iOS发行版要求。

图2-1显示了一个时间线,说明部署目标和基本SDK之间的关系。

图2-1 SDK开发时间线

using_sdks_2x该图描述了具有OS X v10.4的部署目标和OS X v10.6的基本SDK的项目。 (图中的版本号表示该版本的所有版本,包括系统更新。)

在此示例中,软件可以通过版本10.4的最新更新自由使用OS X v10.0中的任何功能。 在确保每个这样的功能可用后,它可以有条件地使用OS X v10.5和10.6中的功能。

这些设置在编译时和运行时的影响如下。 如果您的代码使用如下符号:

  • 在基本SDK中未定义(例如,来自较新操作系统的符号),您会收到编译时错误。
  • 在基本SDK中定义,但标记为已弃用,您会收到编译时警告。
  • 在部署目标中定义,代码将正常链接和构建。 在运行时:

在运行早于部署目标的操作系统的系统上,如果使用该操作系统中不可用的符号,代码可能无法加载。

在运行等于或高于部署目标的操作系统的系统上,您的代码具有对于该操作系统中不可用的符号的空指针。 准备您的代码如在 "在iOS中使用弱链接的类" 和 "使用弱链接的方法和功能"中所述。

注意:OS X v10.6不支持使用大于3.0版的iOS模拟器SDK。 此外,在使用Simulator SDK构建时,二进制文件仅在与基本SDK相同的操作系统版本上运行,而不是在早期版本或更高版本上运行。

始终检查您是否使用废弃的API; 虽然仍然可用,但是不能保证将来可以使用废弃的API编译器会在代码中警告您存在已废弃的API,如"查找已废弃的API使用的实例"中所述。

当您更改基本SDK设置时,除了更改代码构建的头和存根库之外,Xcode还会适当调整其他功能的行为。 例如,符号查找(symbol lookup),代码补全( code completion),基于基础SDK的头文件打开,而不是当前运行的操作系统的头(如果两者不同)。 类似地,Xcode快速帮助关联机制确保文档查找使用与基本SDK相对应的文档集。

除了为整个项目设置基本SDK和部署目标之外,还可以为每个构建目标单独设置这些值。 目标设置覆盖项目设置。 (但是,一些试图与基本SDK设置相关联的Xcode功能(如符号定义和文档查找)可能会有所不同)。

弱链接和苹果框架

Xcode编译器使用附加到Apple框架标头中的符号的可用性宏来确定符号是弱连接还是强连接。 这些宏指示功能首次出现的操作系统版本。 例如,以下声明中的宏指示在OS X v10.6iOS 4.0中开始提供enumerateObjectsUsingBlock方法(在NSArray类中):

- (void)enumerateObjectsUsingBlock:(void (^)(id obj, NSUInteger idx, BOOL *stop))block NS_AVAILABLE(10_6, 4_0);

当框架中的符号被定义为弱链接时,该符号不必在运行时存在以使进程继续运行。 静态链接器在引用该符号的任何代码模块中标识弱链接的符号。 动态链接器在运行时使用此相同的信息来确定进程是否可以继续运行。 如果存在于代码模块中的弱链接符号不存在于框架中,只要其不引用符号,则代码模块可以继续运行。 如果在其框架中存在弱链接的符号,代码可以正常使用它。

在废弃符号的情况下,可用性宏包含进一步的语法,用于指示不推荐使用该符号的操作系统的版本。 在所有情况下,符号的参考文档说明其可用性及其弃用信息(如果适用)。 可用性宏在/usr/include/目录中的Availability.hAvailabilityMacros.h中定义。

Xcode编译器根据项目的基本SDK和部署目标设置来解释每个可用性宏。 编译器使用这些设置为MAC_OS_X_VERSION_MIN_REQUIREDMAC_OS_X_VERSION_MAX_ALLOWED 宏分配适当的值。

例如,假设在Xcode中,将部署目标(最低所需版本)设置为"OS X v10",将基本SDK(最高允许版本)设置为"OS X v10.6"。 在编译期间,编译器将弱连接在OS X v10.6中引入的接口,同时强链接在早期版本的OS中定义的接口。 这将允许您的应用程序在OS X v10.5中运行,并在可用时使用较新的功能。

重要:Xcode中的部署目标设置必须设置为OS X 10.2或更高版本,以利用弱链接。 如果将此值设置为早期版本的OS X,则无法在项目中使用弱链接。 当为iOS构建时,弱连接可用于所有iOS版本。

在使用任何晚于部署目标的iOSOS X版本中引入的任何符号之前,请检查该符号是否可用。 如果符号不可用,请提供备用代码路径。 有关详细信息,请参阅第三章 "使用基于SDK的开发"。

配置基于Makefile的项目

如果您有一个基于makefile的项目,您还可以通过在编译和链接命令中添加相应的选项来使用基于SDK的开发。 在基于makefile的项目中使用SDK需要GCC 4.0或更高版本。 要选择SDK,您可以对编译器使用-isysroot选项,对链接器使用-syslibroot选项。 这两个选项都需要指定所需的SDK目录的完整路径。 要在makefile中设置部署目标,请使用以下形式的makefile变量:ENVP = MACOSX_DEPLOYMENT_TARGET = 10.4。 要在makefile中使用此变量,请将其包含在编译和链接命令前。

设置前缀文件

Xcode支持前缀文件,每个源文件在构建时隐式的包含头文件。 许多Xcode项目模板自动生成前缀文件,包括适合于所选类型应用程序的 umbrella frameworks 为了效率,前缀文件被预编译和缓存,所以Xcode不需要在每次构建项目时重新编译许多行相同的代码。 您可以添加指令以导入应用程序所依赖的特定框架。

如果您使用基于SDK的开发,您必须确保您的前缀文件考虑到所选的SDK 也就是说,不要前缀文件设置使用绝对路径umbrella header,例如/System/Library/Frameworks/Cocoa.framework/Versions/A/Headers/Cocoa.h 此绝对路径不工作,因为指定的头来自当前系统,而不是所选的SDK

要包括umbrella framework headers,请将适当的#import <Framework/Framework.h>指令添加到您的前缀文件。 使用这种技术,编译器总是从适当的SDK目录中选择头文件。 例如,如果您的项目名为TestSDK,并且它有一个前缀文件TestSDK_Prefix.pch,请将以下行添加到该文件:

#import <Cocoa/Cocoa.h>

如果你使用Objective-C,最好使用#import指令,而不是#include(必须在过程化C程序中使用),因为#import保证相同的头文件不会多次被包含。

三、使用基于SDK的开发

本章介绍了在Xcode项目中使用的基于SDK的开发技术:

  • 使用弱链接的类(weakly linked classes),方法和函数来支持在多个版本的操作系统上运行
  • 弱连接整个框架
  • 有条件地为不同的SDK编译
  • 在代码中查找已废弃的API的使用
  • 在运行时确定操作系统版本或框架版本

有关弱链接的背景,请阅读章节二,"弱链接和苹果框架"。

在iOS中使用弱链接类

如果您的Xcode项目使用弱链接类,则必须在运行时确保这些类的可用性,然后再使用它们。 尝试使用不可用类可能会从动态链接器生成运行时绑定错误,这可能会终止相应的进程。

使用iOS 4.2或更高版本的基础SDKXcode项目应该使用NSObject类方法在运行时检查弱链接类的可用性。 这种简单,高效的机制利用了NS_CLASS_AVAILABLE类可用性宏,可用于iOS中的大多数框架。

重要:检查最新的iOS发行说明(Release Notes),以获取尚不支持NS_CLASS_AVAILABLE宏的框架列表。

对于支持NS_CLASS_AVAILABLE宏的iOS框架,将条件化为弱链接类的代码,如以下示例所示:

if ([UIPrintInteractionController class]) {
    // Create an instance of the class and use it.
} else {
    // Alternate code path to follow when the
    // class is not available.
}

这是因为如果弱链接类不可用,向它发送消息就相当于发送消息到nil 如果你子类化一个弱链接类并且超类不可用,那么子类也会显示不可用。

要使用这里所示的类方法,您需要做的更多,以确保一个框架支持NS_CLASS_AVAILABLE宏。 您还必须配置某些项目设置。 通过这些必要的设置,上述代码安全地测试类的可用性,即使在不存在类的iOS版本上运行时也是如此。 这些设置如下:

  • Xcode项目的基础SDK必须是iOS 4.2或更高版本。 构建设置编辑器( build settings editor )中此设置的名称为SDKROOT(Base SDK)。
  • 您的项目的部署目标必须是iOS 3.1或更高版本。 此设置的名称为MACOSX_DEPLOYMENT_TARGET(OS X部署目标)。
  • 项目的编译器必须是LLVM-GCC 4.2编译器或更高版本,或LLVM编译器(Clang)1.5或更高版本。 此设置的名称为GCC_VERSION(C / C ++编译器版本)。
  • 您必须确保项目的部署目标中不可用的任何框架都是弱连接,而不是必需的。中查看下文 "弱链接整个框架" 与Xcode Project Management Guide中的Linking Libraries and Frameworks

有关使用Xcode构建设置编辑器的信息(Xcode build settings editor),参阅 在Xcode Project Management Guide 中的 Building Products

OS X(以及不符合刚才列出的一组条件的iOS项目)中,不能使用类方法来确定弱链接类是否可用。 相反,在类似于以下代码中使用NSClassFromString函数:

Class cls = NSClassFromString (@"NSRegularExpression");
if (cls) {
    // Create an instance of the class and use it.
} else {
    // Alternate code path to follow when the
    // class is not available.
}

使用弱链接的方法,函数和符号

如果项目使用弱链接的方法,函数或外部符号,则必须在运行时确保其可用性,然后再使用它们。 如果尝试使用不可用项目,动态链接器可能会生成运行时绑定错误并终止相应的进程。

假设您将Xcode项目中的基本SDK设置为iOS 4.0。 这允许您的代码在该版本的操作系统中运行时使用该版本的功能。 假设您希望您的软件在iOS 3.1中运行,即使它不能使用该版本的操作系统中的较新功能。 通过将部署目标设置为早期版本的操作系统来允许此操作。

Objective-CinstancesRespondToSelector: 方法就可以告诉你是否给定的方法选择器可用。例如,若要使用availableCaptureModesForCameraDevice: 方法,在iOS 4.0开始可用,您可以使用类似以下的代码:

清单3-1检查Objective-C方法的可用性

if ([UIImagePickerController instancesRespondToSelector:
              @selector (availableCaptureModesForCameraDevice:)]) {
    // Method is available for use.
    // Your code can check if video capture is available and,
    // if it is, offer that option.
} else {
    // Method is not available.
    // Alternate code to use only still image capture.
}

当您的代码在iOS 4.0或更高版本中运行时,它可以调用availableCaptureModesForCameraDevice: 确定视频捕获是否可用在设备上。 但是,当它在iOS 3.1中运行时,它必须假设只有静态图像捕获可用。

  • 如果你用不同的设置来构建这个代码,你会看到以下结果:

        如果您指定iphoneos3.1的基本SDK设置:构建将失败,因为availableCaptureModesForCameraDevice: 方法未在该系统版本中定义。

  • 如果您指定iphoneos4.0的基本SDK设置,然后将部署目标设置为:

        iphoneos4.0:该软件将仅运行在iOS 4.0或更高版本,并将无法在早期系统上启动。

        iphoneos3.1:该软件将运行在iOS 4.0iOS 3.1,但将无法在早期的系统上启动。 当在iOS 3.1中运行时,软件将使用替代代码捕获图像。

通过将getter方法名称(与属性名称相同)传递给instancesRespondToSelector,来检查Objective-C属性的可用性。

要确定弱链接的C函数是否可用,实际使用的链接器将不可用函数的地址设置为了NULL 检查函数的地址,因此,它的可用性 - 通过比较地址为NULLnil 例如,在部署目标早于OS X v10.5的项目中使用CGColorCreateGenericCMYK函数之前,请使用如下代码:

清单3-2检查C函数的可用性

if (CGColorCreateGenericCMYK != NULL) {
    CGColorCreateGenericCMYK (0.1,0.5.0.0,1.0,0.1);
} else {
    // Function is not available.
    // Alternate code to create a color object with earlier technology
}

注意:要检查函数的可用性,请明确地比较地址为NULLnil 您不能使用否定运算符(!)否定函数的地址以检查其可用性。 此外,请注意,C函数的名称与其地址同义。 也就是说,&myFunction等价于myFunction

通过将其地址(而不是符号的名称)显式地比较为NULLnil来检查外部(extern)常量或通知名称的可用性。

弱链接整个框架

如果您使用最近添加的框架 - 在部署目标之后可用的框架 - 您必须显式地弱链接到框架本身。

例如,假设您想要链接到Accelerate框架(首先在iOS 4.0中可用),以便在其可用的系统上使用其功能。 此外,假设您将部署目标设置为iOS 3.1.3,允许该版本的iOS用户在没有新功能的情况下使用您的应用程序。 在此示例中,您必须弱链接到Accelerate框架。

当使用部署目标中可用的框架时,您应该required该框架而不是弱链接。

有关如何弱链接框架的信息,请参阅  在 Xcode Project Management Guide中的"Linking Libraries and Frameworks"

有条件地编译不同的SDK

如果您使用一组源代码构建多个基本SDK,则可能需要针对正在使用的基本SDK进行条件化。 通过使用在Availability.h中定义的宏的预处理器指令来执行此操作。

注意:Availability.h头用于针对iOS和针对OS X v10.6及更高版本。 较早的AvailabilityMacros.h头在OS X v10.2中引入。 这些文件驻留在/usr/include目录中。

假设您想使用macosx10.4的基本SDK设置编译代码清单3-2所示的代码。 在构建过程中,必须屏蔽代码中引用OS X v10.5中引入的CGColorCreateGenericCMYK函数的部分。 这是因为对不可用的CGColorCreateGenericCMYK函数的任何引用将导致编译器错误。

要允许代码生成,请使用__MAC_OS_X_VERSION_MAX_ALLOWED宏:

  • 确保项目的目标是OS X而不是iOS
  • 隐藏基本SDK中不可用的代码

下面的代码段演示了这一点。 请注意,在#if比较子句中使用数字值1050而不是符号__MAC_10_5:如果代码在不包含符号定义的旧系统上加载,则比较仍然有效。

代码清单3-3使用预处理器指令进行条件编译

#ifdef __MAC_OS_X_VERSION_MAX_ALLOWED
    // code only compiled when targeting OS X and not iOS
    // note use of 1050 instead of __MAC_10_5
#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1050
    if (CGColorCreateGenericCMYK != NULL) {
        CGColorCreateGenericCMYK(0.1,0.5.0.0,1.0,0.1);
    } else {
#endif
    // code to create a color object with earlier technology
#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1050
    }
#endif
#endif
}

除了使用预处理器宏,上述代码还假定代码可以使用较新的基本SDK编译,但部署在运行OS X v10.4及更早版本的计算机上。 具体来说,它在尝试调用它之前检查弱连接的CGColorCreateGenericCMYK符号的存在。 这可以防止代码生成运行时错误,如果您针对较新的SDK构建代码但是将其部署在较早的系统上,则会发生运行时错误。 有关确定运行时检查以确定符号的存在的更多信息,请参阅第二章 "在iOS中使用弱链接类","使用弱链接的方法,函数和符号"

查找已废弃的API使用的实例

随着iOSOS X的发展,它们所包含的API和技术有时会改变,以满足开发人员的需求。 作为这种演进的一部分,不太高效的接口被淘汰,以支持较新的接口。 附加到头文件中的声明的可用性宏可帮助您找到已废弃的接口。 参考文档还标记不推荐使用的接口。

注意:废弃并不意味着从框架或库立即删除接口。 它只是一种标记接口的方法,为此存在更好的替代方案。 您可以在代码中使用已废弃的API 但是,苹果建议尽快迁移到较新的接口,因为可能会从未来版本的操作系统中删除已废弃的API。 有关任何推荐的替换接口的信息,请检查不推荐使用的API的头文件或文档。

如果编译具有OS X v10.5的部署目标的项目并使用标记为deprecated的接口,则编译器会发出相应的警告。 该警告包括已废弃的接口的名称以及在代码中的位置。 例如,如果不推荐使用HPurge函数,则会出现类似于以下内容的错误:

'HPurge' is deprecated (declared at /Users/steve/MyProject/main.c:51)

要查找代码中已废弃API的实例,请查找此类型的警告。 如果项目有很多警告,请使用Xcode中的搜索字段,根据"deprecated"关键字过滤警告列表。

确定操作系统或框架的版本

在极少数情况下,检查符号的运行时可用性不是一个完整的解决方案。 例如,如果方法的行为从一个操作系统版本更改为另一个操作系统版本,或者如果在先前可用的方法中修复了错误,则必须编写代码以将这些更改纳入考虑。

用于检查操作系统版本的技术取决于您的目标平台,如下所示:

  • 要在运行时检查iOS的版本,请使用如下代码:

NSString *osVersion = [[UIDevice currentDevice] systemVersion];

  • 要在运行时检查OS X的版本,请使用Gestalt函数和系统版本选择器常量。

或者,对于许多框架,您可以在运行时检查特定框架的版本。 为此,使用全局框架版本常量 - (如果它们由框架提供)。 例如, Application Kit(在NSApplication.h中)声明了NSAppKitVersionNumber常量,您可以使用它来检测Application Kit框架的不同版本:

APPKIT_EXTERN double NSAppKitVersionNumber;
#define NSAppKitVersionNumber10_0 577
#define NSAppKitVersionNumber10_1 620
#define NSAppKitVersionNumber10_2 663
#define NSAppKitVersionNumber10_2_3 663.6
#define NSAppKitVersionNumber10_3 743
#define NSAppKitVersionNumber10_3_2 743.14
#define NSAppKitVersionNumber10_3_3 743.2
#define NSAppKitVersionNumber10_3_5 743.24
#define NSAppKitVersionNumber10_3_7 743.33
#define NSAppKitVersionNumber10_3_9 743.36
#define NSAppKitVersionNumber10_4 824
#define NSAppKitVersionNumber10_4_1 824.1
#define NSAppKitVersionNumber10_4_3 824.23
#define NSAppKitVersionNumber10_4_4 824.33
#define NSAppKitVersionNumber10_4_7 824.41
#define NSAppKitVersionNumber10_5 949
#define NSAppKitVersionNumber10_5_2 949.27
#define NSAppKitVersionNumber10_5_3 949.33

您可以与此常量的值进行比较,以确定您的代码正在运行的Application Kit的哪个版本。 一个典型的方法是对全局常量的值进行分层,并根据在NSApplication.h中声明的常量检查结果。 例如:

if (floor(NSAppKitVersionNumber) <= NSAppKitVersionNumber10_0) {
  /* On a 10.0.x or earlier system */
} else if (floor(NSAppKitVersionNumber) <= NSAppKitVersionNumber10_1) {
  /* On a 10.1 - 10.1.x system */
} else if (floor(NSAppKitVersionNumber) <= NSAppKitVersionNumber10_2) {
  /* On a 10.2 - 10.2.x system */
} else if (floor(NSAppKitVersionNumber) <= NSAppKitVersionNumber10_3) {
  /* On 10.3 - 10.3.x system */
} else if (floor(NSAppKitVersionNumber) <= NSAppKitVersionNumber10_4) {
  /* On a 10.4 - 10.4.x system */
} else if (floor(NSAppKitVersionNumber) <= NSAppKitVersionNumber10_5) {
  /* On a 10.5 - 10.5.x system */
} else {
  /* 10.6 or later system */
}

类似地,Foundation(在NSObjCRuntime.h中)声明NSFoundationVersionNumber全局常量和每个版本的特定值。

用于其他对象和组件的某些单独头文件也可以声明NSAppKitVersionNumber的版本号,在给定更新中有一些错误修复或功能可用时。

 

SDK Compatibility Guide 官方原文:https://developer.apple.com/library/content/documentation/DeveloperTools/Conceptual/cross_development/Using/using.html#//apple_ref/doc/uid/20002000-SW3

Framework Programming Guide  https://developer.apple.com/library/content/documentation/MacOSX/Conceptual/BPFrameworks/Frameworks.html

转载请注明:天狐博客 » iOS开发之SDK兼容性指南

六经辨证论治头痛

$
0
0

 一、六经头痛涵义辨析

1 . 与经脉循行部位相关的头痛。六经头痛指头痛发生在该经脉循行部位者,如,太阳头痛、阳明头痛、少阳头痛、厥阴头痛。

2.与六经病病机相关的头痛。六经头痛是指与六经病相关的头痛。如,太阳头痛指伤寒太阳病头痛,阳明头痛指伤寒阳明病头痛,等等。《兰室秘藏·头痛门》所补出的太阴头痛与少阴头痛亦属此类。

六经头痛不但包括外感头痛,还应包括内伤头痛。头痛是极其复杂的病症,不但在病因病机方面有外感内伤之不同;就病位而言,可以涉及脏腑及其经脉。显然,对六经头痛从单一方面理解是不全面的。

3.三阴三阳六经病病机与经脉循行部位结合。如果将以上两种认识结合起来,则更适应临床需要,这是本文对六经头痛范围、属性的界定。《冷庐医话·头痛》:“头痛属太阳者,自脑后上至巅顶,其痛连项;属阳明者,上连目珠,痛在前额;属少阳者,上至两角,痛在头角。以太阳经行身之后,阳明经行身之侧,厥阴之脉会于巅顶,故头痛在巅顶。太阴、少阴二经虽不上头,然痰与气逆壅于膈,头上气不得畅而亦痛。其辨之之法,六经各有见症。”明·秦景明在《症因脉治·头痛论》中指出“伤寒门头痛,皆是三阳经表症,今在杂病门,虽分外感内伤,然三阳三阴,皆有头痛。”

任何疾病就其属性而言不离阴阳虚实,不出五脏六腑十二经脉,其病不属外感即是内伤。因此,将三阳三阴以及所属的脏腑经脉与外感内伤的病因病机有机结合起来对头痛进行辨证论治,既符合临床亦切合实用。

临证辨治头痛的基本要素大约有三条:一是能明确病位,二是能确定病性,三是能明确致病邪气的性质。以六经辨证论治头痛完全可以满足以上条件。

二、六经辨证论治头痛临证举要

(一)太阳头痛

1.部位:太阳头痛主要见于前额、巅顶、枕部疼痛连及项、背,或由项连肩。

通常所说的太阳头痛以后头部疼痛为主或连于项。《灵枢·经脉》:“膀胱足太阳之脉……是动则病冲头痛,目似脱,项如拔,脊痛……”《冷庐医话·头痛》:“头痛属太阳者,自脑后上至巅顶,其痛连项……项强腰脊痛。”由于足太阳膀胱经脉循行所至,还可以发生头侧头痛,如《丹溪心法十二经见证》足太阳膀胱经见证 “头苦痛……头两边痛……项似拔……”

2.病因病机:风寒、风热外感常见,邪气阻滞太阳经脉;亦可见内伤为病者。

3.属性:外感内伤皆可致病,但以内伤为主;属实者居多。

4.证治:

①经脉不利:头痛以枕颈部为主,痛连巅顶、前额,或牵连头侧,或颈枕部拘紧或酸痛,连及颞额,或伴肩背不适,舌质淡红,苔薄白,脉弦或紧。

治法:疏利太阳经脉,通络止痛。方药:葛根汤加天麻、川芎、姜黄、天南星、当归。

②瘀血证:头痛项强,或痛连肩背手臂,经久不已,颈部压痛,舌质暗或紫,或暗红,苔薄白,脉弦细或细涩。

治法:活血化瘀,疏经通络。

方药:葛根汤加当归、红花、丹参、羌活,或桂枝茯苓丸加味。

③湿热证:头痛头重,颈项不舒,胸满恶心,舌质红,苔黄腻,脉滑或濡。

治法:清热化湿,疏经通络。

方药:麻黄连翘赤小豆汤加生薏苡仁、土茯苓等。

④风寒证:头痛起病较急,其痛如破,连及项背,恶风畏寒,遇风尤剧,口不渴,苔薄白,脉多浮紧。

治法:疏风散寒,通络止痛。

方药:麻黄汤、桂枝汤、葛根汤、桂枝加葛根汤加味。

⑤风热证:头痛而胀,甚则头痛如裂,发热或恶风,口渴欲饮,面红目赤,便秘溲黄,舌红苔黄,脉浮数。

治法:疏风清热,通络止痛。

方药:葛根汤去麻黄、桂枝,加桑叶、菊花、僵蚕、蝉蜕或升麻葛根汤、桑菊饮。或用选奇汤,《伤寒大白·头痛》谓“此治太阳风热头痛之方。”

⑥风湿证:头痛如裹,肢体困重,胸闷纳呆,小便不利,大便或溏,苔白腻,脉濡滑。

治法:疏风通络,化湿止痛。

方用:麻黄杏仁薏苡甘草汤、麻黄加术汤。

(二)阳明头痛

1.部位:头痛部位以前额、面颊、眉棱骨常见,或痛连齿龈,或颜面疼痛。《伤寒大白·头痛》:“阳明经额前痛,痛连眼眶,脉洪而长……”

2.病因病机:外感多热结或为寒凝;内伤多积热、湿热、胃火、热毒、酒毒。

3.属性:实证为主。

4.证治:

①阳明湿热:头痛身重,胸脘痞闷,溲赤闭涩,口中黏腻,舌质红,苔黄腻,脉滑数或濡数。

治法:清热利湿,和中止痛。

方药:伴脘痞者用半夏泻心汤;大便不爽者用葛根芩连汤加味。湿热久结者用陈茶芽煎。

②寒滞阳明经脉:头痛以前额、面颊为主,或见眉棱骨痛,遇风寒加重或诱发,舌质淡,苔薄白,脉弦或紧。

治法:疏风散寒,疏通阳明经脉。

方用选奇汤。《丹溪心法·眉眶痛》选奇方“治眉骨痛不可忍,大有效。”

③阳明蕴热上蒸:头痛头胀,心烦面赤,口干或渴,胃脘不适,舌质红,舌苔黄,脉滑。

治法:清泻胃热,通络止痛。

方用白虎加白芷汤(《卫生宝鉴》卷九)。或白虎葛根汤(知母、石膏、葛根、白芷。《伤寒大白》)。热病后期,热伤气阴,余热未尽,头痛口渴,气短心烦,舌质红,舌苔黄,脉虚数,用竹叶石膏汤。

④胃火上攻:颜面剧痛阵阵发作,其痛难忍,面赤灼热,口干口臭,溲赤便结,或鼻塞涕黄黏浊,舌质红,苔黄,脉滑数。舌质红,苔黄,脉滑数。

治法:清胃泻火,通络止痛。

方药:清胃散,重用升麻,加生石膏、知母、天麻、葛根、僵蚕。鼻塞涕黄黏浊,加白芷、蒲公英、败酱草等。

⑤阳明热结气壅:头痛面赤,口干口渴,心烦口臭,大便闭结,舌质红,舌苔黄或黄燥,脉滑数。

治法:清胃泻热,降气导浊。

方用承气汤类方或防风通圣丸。

(三)少阳头痛

1.部位:疼痛以头侧为主,可连及耳、目外眦。《伤寒大白·头痛》:“少阳经头角痛,痛引耳前后……”

2.病因病机:外感以风热为主,风寒亦兼而有之;内伤则多肝胆气郁、化火或湿热壅滞。

3.属性:实证、热证多见。

4.证治:

①少阳郁热(郁火):头痛以两太阳穴为主,或连耳目,胀痛并见,或见眩晕,口干口苦,舌质红,苔薄黄,脉弦。

治法:疏利少阳,清散郁火。

方药:小柴胡汤去半夏、人参,加钩藤、天麻、僵蚕、桑叶、菊花、连翘。

②少阳气郁:头痛头胀,痛在两颞,时作时止,或游走不定,或痛连枕颈,舌质淡红,苔薄白,脉弦或弦细。

治法:和解少阳,疏郁散结。

方用柴胡桂枝汤。

③少阳热结气壅:头痛胀痛,太阳穴尤甚,心烦口苦,大便不畅,舌质红,苔黄,脉弦或弦数。

治法:疏解少阳,畅达气机。

方用大柴胡汤。

④特殊头痛——雷头风:头痛剧烈,发于耳上,痛处起核,不可触近,入夜加重,舌质红,苔薄白或薄黄,脉弦有力。治法:疏利少阳,清热利湿解毒。方药:淸震汤加味。

(四)太阴头痛

1.部位:头痛部位不定,或全头痛,或局部疼痛。《灵枢·经脉》:“脾足太阴之脉……入腹属脾络胃,上膈,挟咽,连舌本,散舌下……”。

2.病因病机:内伤为主:常见饮食所伤、劳役无度、久病失血等;或因情志内伤,肝木乘脾,运化失司,湿聚生痰。

3.属性:虚实兼见,以虚为主。虚证多见气血亏虚或清阳不升;实证则以痰湿、湿热、痰热、痰浊为主。

4.证治:

①痰厥头痛:头痛昏蒙,胸脘满闷,呕恶痰涎,舌胖大有齿痕,苔白腻,脉沉弦或沉滑。治法:健脾和中,化痰降浊。方药:半夏白术天麻汤(《兰室秘藏》),或芎辛导痰汤(《奇效良方》)。痰郁化热,痰热上蒙之头痛,用加味二陈汤(《医方考》)。二陈汤为治疗痰厥头痛之基础方,如《伤寒大白·头痛》曰:“顽痰头痛,胸满恶心者,二陈汤加南星、海石”。即二陈汤加胆星、海浮石,名曰星石二陈汤。又以二陈汤加栀、连、胆星,名二陈栀连胆星汤,治“痰火上冲”之头痛。

②气虚头痛:头痛而晕,心悸不宁,遇劳则重,自汗,气短,畏风,神疲乏力,面色白光白,舌淡苔薄白,脉沉细而弱。治法:益气健脾,升达清阳。方药:顺气和中汤(《卫生宝鉴》黄芪、人参、甘草、白术、陈皮、当归、升麻、柴胡、细辛、蔓荆子、川芎)。

③气虚血瘀:枕部疼痛,经久不愈,反复发作,时轻时重,痛连项背,固着不移,舌质淡暗,苔薄白,脉弱。治法:益气升清,活血化瘀。方药:清阳汤(《脾胃论》)。

④气虚络痹:头痛反复发作,日久不愈,时作时止,伴见头目昏沉,精神疲惫,面色不华,舌质淡,苔薄白,脉细弱。治法:益气健脾,补虚通络。方药:黄芪桂枝五物汤(黄芪、桂枝、白芍、生姜、大枣)加当归、葛根、党参、仙灵脾、炙甘草。

(五)少阴头痛

1.部位:不定,以全头痛多见。足之阴经虽不行于头,却皆循于面,而“挟舌本”。如《灵枢·经脉》说:“肾足少阴之脉……其直者,从肾上贯肝膈,入肺中,循喉咙,挟舌本……”。

2.病因病机:少阴精血不足,不能生髓充脑,则脑髓空虚;阳气亏虚,既易感受寒邪,复不能温养清窍。

3.属性:以精血不足,阳气亏虚为基本病机变化。

4.证治:

①肾阴不足:头痛眩晕,腰膝酸软,耳鸣少寐,心烦失眠,口燥咽干,面色潮红,手足心热,舌红少苔,脉弦细数。治法:滋阴补肾。方药:杞菊地黄丸加味,天麻、桑叶、黑芝麻。

②肾精亏虚:头痛而空,或兼眩晕,腰痛酸软,神疲乏力,健忘头昏,耳鸣少寐,舌淡红,苔少,脉沉细无力。治法:补肾填精,生髓荣脑。方药:加味左归饮(《医学从众录》,在左归饮基础上重用肉苁蓉,少佐川芎、细辛)。

③肾气亏虚:头痛而空,每兼眩晕,腰痛酸软,神疲健忘,阳痿遗精,经闭带下,耳鸣少寐,舌淡红,苔少,脉沉细无力。治法:补益肾气,填精生髓。方用大补元煎加味。

④肾阳不足:头空痛,手足不温,腰膝酸软,精神疲惫,或见眩晕,舌质淡,苔薄白,脉沉细无力。治法:温补肾阳,益气生精。方药:右归丸。

⑤阳虚感寒:头痛经久不愈,时作时止,恶风畏寒,面白肢冷,舌质淡胖,苔薄白润,脉沉细。治法:温阳补虚,散寒止痛。方药:麻黄细辛附子汤加川芎、生姜。

(六)厥阴头痛

1.部位:巅顶、颜面疼痛多见,或全头痛,或头痛部位不定。《灵枢·经脉》曰:“肝足厥明之脉,起于大指丛毛之际,……上贯膈,布胁助,循喉咙之后,上入颃颡,连目系,上出额,与督脉会于巅;其支者,从目系下颊里,环唇内……”。《丹溪心法十二经见证》足厥阴肝经见证有“头痛”。

2.病因病机:情志伤肝,导致气郁、化火、气滞血瘀,阳亢风动;或久病虚寒。

3.属性:虚实兼见。

4.证治:

①肝气郁结:头痛头胀,痛无定处,情绪不宁,或见胸部满闷,胁肋胀痛,脘闷嗳气,不思饮食,大便不调,舌淡红,苔薄腻,脉弦。治法:疏肝解郁,利气止痛。方药:四逆散合香茗散(《鲁府禁方》香附子、川芎、细茶。水煎温服)加天麻、天南星、白蒺藜。

②肝郁化火:头胀头痛,性情急躁易怒,胸胁胀满,口苦而干,或头痛、目赤、耳鸣,或嘈杂吞酸,大便秘结,舌质红,苔黄,脉弦数。治法:疏肝解郁,清热泻火。方药:白头翁汤合栀子豉汤加天麻、川芎、僵蚕、白蒺藜、生白芍;伤阴则用白头翁加甘草阿胶汤加枸杞、菊花。

③气滞血瘀:头痛经久不愈,痛处固定,性情急躁,失眠健忘,或胸胁疼痛,舌质紫暗,或有瘀点、瘀斑,脉弦或涩。治法:疏肝理气,活血止痛。方药:通窍活血汤加天麻、菊花、旋覆花、姜黄。

④肝阳上亢:头胀痛而眩,面红面赤,心烦易怒,胁痛,夜眠不宁,口苦,舌红苔薄黄,脉弦有力。治法:平肝潜阳,降逆止痛。方用天麻钩藤饮或滋生青阳汤。

⑤肝经风火:颜面阵痛,面颊有烧灼感,痛连齿目,突发突止,作止无时,烦躁不安,耳鸣口苦,舌质红,苔黄,脉弦数。治法:清肝泻火,疏风止痛。方用羚羊角汤(羚羊角粉、蝉蜕、夏枯草、薄荷、生地、菊花、石决明、天麻、白头翁)。

⑥血虚头痛:头痛,或伴头晕目眩,胁痛,或惊惕不安,妇女月经不调甚则闭经,面色不华,舌质淡,脉弦细或细涩。治法:养血柔肝,缓急止痛。方药:加味四物汤合四神散(《丹溪心法·眉眶痛》“治妇人血风,眩晕头痛”,用四神散。菊花、当归、旋覆花、荆芥穗),加天麻、僵蚕。血虚受寒用当归四逆汤,寒甚用当归四逆汤加吴茱萸生姜汤。

⑦肝寒上犯:巅顶头痛,干呕,吐涎,甚则四肢厥冷,舌淡红,苔白,脉弦。治法:暖肝散寒,温经止痛。首选吴茱萸汤。

(七)杂类头痛

《丹溪心法十二经见证》手足阴阳经合生见证:“头顶痛,足太阳、手少阴”。

1.全头痛:治从六经合病着眼。

2.游走痛:从肝胆、少阳着眼,从风、气着手。

3.头痛休作有时:求之于少阳、厥阴。

4.证治:

①气血失和:头痛头晕,情绪不畅加重,伴见胸胁胀闷、心悸、恶心等症状,舌质红,苔薄白,脉弦细。治以疏调气血,和畅气机:柴胡桂枝汤(柴胡、桂枝、白芍、党参、炙甘草、大枣、生姜)加香附、旋覆花、天麻等。

②肝胆湿热:头痛头胀,口苦口干,心烦易怒,小便黄赤,舌质红,苔黄腻,脉弦数。治以清利肝胆湿热:龙胆泻肝汤加味。

转载请注明:天狐博客 » 六经辨证论治头痛

iOS开发#pragma预处理指令与_Pragma操作符

$
0
0

#pragma 预处理指令

在C/C++标准中,#pragma是一条预处理的指令(preprocessor directive)。简单地说,#pragma是用来向编译器传达语言标准以外的一些信息。

整理代码

举个简单的例子,如果我们在代码的头文件中定义了以下语句:

#pragma mark - UITableViewDelegate

在你的 @implementation 中使用 #pragma mark 来将代码分割成逻辑区块。这些逻辑区块不仅仅使得阅读代码本身容易许多,也为Xcode源导航增加了视觉线索(#pragma mark 声明前有一个水平分割并由破折号(-)开始)。

#pragma clang diagnostic   clang诊断设置

在iOS开发中,clang diagnostic(clang 诊断设置) 是#pragma的常用命令:

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-相关命令"
    // 你自己的代码
#pragma clang diagnostic pop

更多相关命令,可以从http://fuckingclangwarnings.com 查到,很详细。

自定义警告

#pragma message "Warning"

#warning "Warning 2"

两种强制警告的方法在视觉效果上结果是一样的,但是警告类型略有不同,一个是-W#pragma-messages,另一个是-W#warnings。对于第二种写法,把warning换成error,可以强制使编译失败。比如在发布一些需要API Key之类的类库时,可以使用这个方法来提示别的开发者别忘了输入必要的信息。

#error "Something wrong"

本文的主题并不是警告,但是想了解更多可以阅读 onecat的谈谈Objective-C的警告

屏蔽方法废弃警告

#pragma clang diagnostic push  
#pragma clang diagnostic ignored "-Wdeprecated-declarations"      
[TestFlight setDeviceIdentifier:[[UIDevice currentDevice] uniqueIdentifier]];  
#pragma clang diagnostic pop

屏蔽不兼容指针类型警告

#pragma clang diagnostic push   
#pragma clang diagnostic ignored "-Wincompatible-pointer-types"  
   //  
#pragma clang diagnostic pop

屏蔽循环引用警告

// completionBlock is manually nilled out in AFURLConnectionOperation to break the retain cycle.  
#pragma clang diagnostic push  
#pragma clang diagnostic ignored "-Warc-retain-cycles" 
    self.completionBlock = ^ {  
        ...  
    };  
#pragma clang diagnostic pop

屏蔽未使用变量警告

#pragma clang diagnostic push   
#pragma clang diagnostic ignored "-Wunused-variable"  
    int a;   
#pragma clang diagnostic pop

_Pragma操作符

_Pragma操作符,该操作符具有与 #pragma 指令相同的功能

_Pragma(token-string)

相比预处理指令#pragma,_Pragma操作符可用于宏定义中的内联。 #pragma 指令不能用于宏定义中,因为编译器会将指令中的数字符号(“#”)解释为字符串化运算符 (#)。,由于_Pragma是一个操作符,因此可以用在一些宏中,我们可以看看下面这个例子:

在适配不同版本系统的适合,为了避免废弃API的警告,一般我们都使用#pragma clang diagnostic ignored "-Wunused-variable"来屏蔽警告:

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
//code
#pragma clang diagnostic pop

但是在宏定义中#pragma就不行了,在宏定义中#被用来做字符串化符号。比如如下宏:

#define OBJC_STRINGIFY(x) @#x
#define STRINGIFY(x) #x

STRINGIFY将任意符号,转化为字符串。STRINGIFY(example)被转化成了字符串 "example"

由于_Pragma并不带有#符号,所以使用_Pragma完美的解决问题:

#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_8_0
#define JK_NSDATE_UTILITIES_COMPONENT_FLAGS \
({ \
     unsigned components;\
    if ([UIDevice currentDevice].systemVersion.floatValue > 8.0f){ \
        components = (NSYearCalendarUnit| NSMonthCalendarUnit | NSDayCalendarUnit | NSWeekCalendarUnit |  NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit | NSWeekdayCalendarUnit | NSWeekdayOrdinalCalendarUnit); \
    }else{ \
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") \
        components = (NSYearCalendarUnit| NSMonthCalendarUnit | NSDayCalendarUnit | NSWeekCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit | NSWeekdayCalendarUnit | NSWeekdayOrdinalCalendarUnit); \
_Pragma("clang diagnostic pop") \
    } \
    components; \
})\

#else
#define JK_NSDATE_UTILITIES_COMPONENT_FLAGS \
({\
     unsigned components = (NSYearCalendarUnit| NSMonthCalendarUnit | NSDayCalendarUnit | NSWeekCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit | NSWeekdayCalendarUnit | NSWeekdayOrdinalCalendarUnit); \
    components; \
})\
#endif

再比如,忽略执行的代码是否废弃

#define NS_SUPPRESS_DIRECT_USE(expr)   _Pragma("clang diagnostic push") \
                                       _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"")\
                                       expr\

NS_SUPPRESS_DIRECT_USE(
                      Foo();
                      );

Which Clang Warning Is Generating This Message

谈谈Objective-C的警告

#pragma - NSHipster

#pragma 处理警告

转载请注明:天狐博客 » iOS开发#pragma预处理指令与_Pragma操作符

建立自己的Android SDK镜像站

$
0
0

做Android开发难免会遇到在线更新SDK很慢的问题,国内也有很多Android SDK镜像站。

那么可不可以自己在公司内网,或者局域网,或者个人服务器上搭建个Android SDK在线更新镜像服务器呢?

答案是肯定的。

总体思路就是,使用脚本同步google源镜像所有目录之后,把当前目录建立为网站,设置为IDE的代理即可。

建立镜像站点

安装Apache等Web容器,以Apache为例配置域名与别名(没有域名可以使用IP):

<VirtualHost *:80>
DocumentRoot /data/mirrors
ServerName mirrors.skyfox.org
ServerAlias dl.google.com
ServerAlias dl-ssl.google.com
</VirtualHost>

/data/mirrors作为mirrors.skyfox.org的根路径,你可以使用任何你想要的路径,这里只是一个范例。

ServerName 域名(根路径)+/android/repository/  就是安卓的镜像文件了

谷歌的镜像站如下格式。

http://dl.google.com/android/repository/

https://dl-ssl.google.com/android/repository/

我们要做的是替换android/repository/之前的域名为我们自己的域名,从而实现代理。所以目录创建要严格按照谷歌。

从谷歌同步源

翻墙后使用同步脚本进行同步,

进入到/data/mirrors文件夹,终端执行python android.py,将会自动下载创建文件。(注意目录的读写权限)

同步脚本中的base_url为谷歌的源地址,也可以换成国内的任意源地址。

out_dir为建立好的源站点目录(/data/mirrors)的子目录(/android/repository/)。

同步成功后即可进行配置Android Studio

同步脚本 android.py

#!/usr/bin/python
from string import rfind
from time import mktime, strptime
from os import makedirs, path, stat, utime
from urllib import urlretrieve, urlopen
from xml.etree import ElementTree

base_url = 'https://dl.google.com/android/repository/'
out_dir = '/data/mirrors/android/repository'

def download(filename, last_modified):
   file = out_dir + filename
   print 'Downloading ' + filename
   urlretrieve(base_url + filename, file)
   utime(file, (last_modified, last_modified))

   process(filename)

def process(filename, size=-1):
   file = out_dir + filename
   if path.isfile(file) and stat(file).st_size == size:
      print 'Skipping: ' + filename
      return

   print 'Processing: ' + filename
   handle = urlopen(base_url + filename)
   headers = handle.info()
   content_length = int(headers.getheader('Content-Length'))
   last_modified = mktime(strptime(headers.getheader('Last-Modified'), '%a, %d %b %Y %H:%M:%S %Z'))

   if rfind(filename, '/') > 0:
      dir = out_dir + filename[:rfind(filename, '/')]
   else:
      dir = out_dir

   if not path.isdir(dir):
      print 'Creating ' + dir
      makedirs(dir)

   if not path.isfile(file):
      download(filename, last_modified)
   else:
      file_stat = stat(file)
      if file_stat.st_mtime != last_modified or file_stat.st_size != content_length:
         download(filename, last_modified)
      else:
         print 'Skipping: ' + filename

def fetch(file):
   if base_url in file:
      dir = file[len(base_url) - 1:rfind(file, '/') + 1]
      file = file[rfind(file, '/') + 1:]
   elif 'http' in file:
      return
   else:
      dir = '/'
   process(dir + file)
   base_dir = path.dirname(dir + file)
   if base_dir != '/':
      base_dir += '/'
   tree = ElementTree.parse(out_dir + dir + file)
   for element in tree.getiterator():
      if element.tag.split('}')[1] == 'url':
         if element.text[-4:] != '.xml':
            if not 'http' in element.text:
               process(base_dir + element.text)
         else:
            fetch(element.text)
for file in ['repository-10.xml', 'addons_list-2.xml']:
fetch(file)

镜像站使用方法

启动 Android SDK Manager ,在『Android SDK Manager - Settings』窗口中,在『HTTP Proxy Server」和「HTTP Proxy Port』输入框内填入镜像服务器地址和端口,并且选中『Force https://... sources to be fetched using http://...』复选框。设置完成后单击『Close』按钮关闭『Android SDK Manager - Settings』窗口返回到主界面;

依次选择『Packages』、『Reload』。

81448474-7e68-41a7-ac09-92cf42f7ff8d

国内现存的镜像站

大连东软信息学院镜像服务器:

http://mirrors.neusoft.edu.cn 端口:80

北京化工大学镜像服务器:

IPv4: http://ubuntu.buct.edu.cn/ 端口:80

IPv4: http://ubuntu.buct.cn/ 端口:80

IPv6: http://ubuntu.buct6.edu.cn/ 端口:80

上海GDG镜像服务器:

http://sdk.gdgshanghai.com 端口:8000

中国科学院开源协会镜像站:

IPV4/IPV6: http://mirrors.opencas.cn 端口:80

IPV4/IPV6: http://mirrors.opencas.org 端口:80

IPV4/IPV6: http://mirrors.opencas.ac.cn 端口:80

腾讯Bugly 镜像:

http://android-mirror.bugly.qq.com 端口:8080
腾讯镜像使用方法: http://android-mirror.bugly.qq.com:8080/include/usage.html

扩展阅读

脚本来自,中科院开源镜像小组 https://github.com/opencas/mirrors

Make a LAN mirror for developing Android apps https://github.com/renfeng/android-repository

 

转载请注明:天狐博客 » 建立自己的Android SDK镜像站

模态UIViewController中WebView的H5弹出Camera/ImagePicker

$
0
0

问题描述直接借鉴(https://segmentfault.com/q/1010000004528658)segmentfault中的一个描述(虽然我及其讨厌使用ViewController A  B C来描述问题),因为我没有对应账号,没法回答它。

情况大致就是一个ViewController A  present  ViewController C,

或者根据提问者的描述 ViewController A  present  ViewController B 后ViewController B 再push到ViewController C

ViewController C中有一个WebView,加载了H5页面, H5页面有个按钮调用手机的相册 UIImagePickerController,弹出了拍照,相册选择的UIActionSheet, 然后点击相册按钮,程序直接退出到了 A 页面。

bvtar12.提问者尝试用A push 到B 页面,B页面push 到C 页面,这样子的话,在点击相册后会到系统相册里面,选择图片后会返回到H5页面里。

3.提问者需要的是present 也能实现而不是用push。所以尝试了以下方法

bvtar1234结论就是只要有present ,就是出现问题,而且提示问题就出现在present 那里

为什么这样?

74d154e3-f37d-4696-b910-a8248b789eed虽然我们看不到点击h5弹出的UIActionSheet(暂且称作UIActionSheet),点击方法的实现。但是我们从UI交互上来看是这样的流程。

点击UIActionSheet中item  => UIActionSheet dismiss => 弹出相机或者ImagePicker => 拍照或者选择图片后弹出相机或者ImagePicker dismiss

科普

presentingViewController :The view controller that presented this view controller (or its farthest ancestor.  当前ViewController present出来的ViewController

presentedViewController:The view controller that was presented by this view controller or its nearest ancestor. present当前ViewController的ViewController

分析

我们知道UIActionSheet可能是被当前ViewController C执行presentViewController模态出来的,验证猜想很简单,在ViewController 重写如下方法即可。

- (void)presentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)flag completion:(void (^)(void))completion

断点后发现弹出的viewControllerToPresent并不是UIActionSheet,而是我们很少使用的UIDocumentMenuViewController,然后并没有什么卵用,尝试hook UIDocumentMenuViewController点击方法失败。

Printing description of viewControllerToPresent:
<UIDocumentMenuViewController: 0x7f8cc7f22110>

我们知道当一个presentedViewController要dismiss的时候会调用presentingViewController的如下方法

-(void)dismissViewControllerAnimated:(BOOL)flag completion:(void (^)(void))completion

断点后发现,dismissViewControllerAnimated执行了两次,我们的bug现象就是,dimiss掉了所有的modal试图,那么好了,问题找到了,UIDocumentMenuViewController关闭后不仅调用了自己的,dismissViewControllerAnimated,还调用了,上层或者上上层presentingViewController的dismissViewControllerAnimated。

所以使dismissViewControllerAnimated调用一次,或者让UIDocumentMenuViewController找不到presentingViewController即可。

如何解决?

方法一,使dismissViewControllerAnimated调用一次

当前ViewController的所有presentedViewController都正常执行dismissViewControllerAnimated,当前ViewController本身执行dismissViewControllerAnimated,不进行dismiss,不做处理。

-(void)dismissViewControllerAnimated:(BOOL)flag completion:(void (^)(void))completion
{
    if (self.presentedViewController)
    {
        [super dismissViewControllerAnimated:flag completion:completion];
    }
}

当想dismiss掉当前ViewController的时候,不能调用

[self dismissViewControllerAnimated:YES completion:nil];

而是使用

[super dismissViewControllerAnimated:YES completion:nil];

即可,这样当前ViewController不会调用重写的dismissViewControllerAnimated方法,而且还顺利的dismiss掉了。

方法二,使UIDocumentMenuViewController找不到presentingViewController

断点后发调用

- (void)presentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)flag

弹出UIDocumentMenuViewController之后,又调用了

- (UIViewController *)presentingViewController

返回的presentingViewController是ViewController A,

测试发现调用ViewController A的dismissViewControllerAnimated之后,所有model出来的ViewController 都会dismiss。这也是苹果的一个特性。当模态出N个ViewController之后,只需要dismiss任意一个,都会dismiss它之后的所有模态试图

0f11d0b9-786d-4dbb-91ae-30710e76d18f

解决方法就是不让UIDocumentMenuViewController找到上层或者上上层的任意presentingViewController

- (UIViewController *)presentingViewController {
    if (_flag) {
        return nil;
    } else {
        return [super presentingViewController];
    }
}
- (void)presentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)flag completion:(void (^)(void))completion {
    if ([viewControllerToPresent isKindOfClass:[UIDocumentMenuViewController class]]
        ||[viewControllerToPresent isKindOfClass:[UIImagePickerController class]]) {
        _flag = YES;
    }
    
    [super presentViewController:viewControllerToPresent animated:flag completion:completion];
}

Done!

转载请注明:天狐博客 » 模态UIViewController中WebView的H5弹出Camera/ImagePicker

Viewing all 115 articles
Browse latest View live