Xcode 编译静态库的陷阱(Xcode7如何生成i386的静态库)

编译静态库时一般需要编译真机和模拟器两个库。XCode7中,默认Debug只编译当前环境的库。导致只会生成x86_64的.a文件,不包含i386(注释:32位)的库。需要调整Xcode的默认设置。 需要调整的设置如图:(如果Debug那里不设成No, 模拟器的release也会套用这个设置,只生成x86_64的库)Xcode setting for static lib

 

ES3.0 中 Vertex Shader 的out变量未赋值,导致 glLinkProgram 失败

GLSL中,定义了但是 没有使用的变量会被优化掉,在之前就遇到过。

(GLSL 中 ,如果定义的 变量 没有在代码中使用到,那么不会被编译,用 glget* 是获取不到的,返回 -1 。算是被优化掉了。

http://blog.csdn.net/huutu/article/details/50322905 )

而且如果 Vertexshader 中的定义的 Out 变量未使用,被优化掉后,会引起 FragmentShader 的问题,导致 glLinkProgram  失败!

群里朋友 学习 ES3.0的时候 碰到一个问题:

编译下面的Shader 并没有出错,但是使用

[cpp] view plain copy

 在CODE上查看代码片派生到我的代码片

  1. glGetUniformLocation(program, “s_texture”);

去获取 的时候,却返回了 -1 。转自http://blog.csdn.net/huutu http://www.thisisgame.com.cn

Vertex Shader:

[cpp] view plain copy

 在CODE上查看代码片派生到我的代码片

  1. #version 300 es
  2. precision mediump float;
  3. in vec3 inPos;
  4. in vec2 vTexCoord;
  5. out vec3 Color;
  6. out vec2 texCoord;
  7. uniform mat4 myMat;
  8. void main() {
  9.     gl_Position = vec4(inPos, 1.0) * myMat;
  10.     Color = inPos;
  11. }

Fragment Shader:

[cpp] view plain copy

 在CODE上查看代码片派生到我的代码片

  1. #version 300 es
  2. precision mediump float;
  3. in vec3 Color;
  4. in vec2 texCoord;
  5. out vec4 fragmentColor;
  6. uniform sampler2D s_texture;
  7. void main() {
  8.     fragmentColor = texture(s_texture, texCoord);
  9. }

我的第一反应是:

s_texture 没有使用,被优化掉了。

然而并没有,让朋友把工程传过来看了一段时间才发现

在Vertex Shader 中的 out vec2 texCoord; 没有赋值!!

所以 texCoord 是被优化掉了的。

但是 在 FragmentShader 中的 in vec2 texCoord 是存在的变量,所以呢 glCompileShader 操作的时候是不会报错的!

转自http://blog.csdn.net/huutu http://www.thisisgame.com.cn

难道 OpenGL 没有处理这个问题?当然不是。

这个错误是在 glLinkProgram 阶段抛出的。 朋友没有去获取 glLinkProgram 的状态,所以没有发现这个错误。

抛出了很详细的错误

[html] view plain copy

 在CODE上查看代码片派生到我的代码片

  1. WARNING: Output of vertex shader ‘Color’ not read by fragment shader
  2. ERROR: Input of fragment shader ‘texCoord’ not written by vertex shader
  3. (lldb)

IOS开发中遇到转发delegate的时候不要再那么傻了

http://sam.dods.co/blog/2014/08/03/secondary-delegate/

delegate代理(截取delegate消息用)

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

@interface MovieSDKDelegateProxy : NSObject <UITableViewDelegate>
@property (nonatomic, weak)id primaryDelegate;
@property (nonatomic, weak)id secondaryDelegate;
@end
@implementation MovieSDKDelegateProxy
- (BOOL)respondsToSelector:(SEL)aSelector {
    return ([self.primaryDelegate respondsToSelector:aSelector] || [self.secondaryDelegate respondsToSelector:aSelector]);
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector
{
    NSObject *delegateForResonse = [self.primaryDelegate respondsToSelector:selector] ? self.primaryDelegate : self.secondaryDelegate;
    return [delegateForResonse respondsToSelector:selector] ? [delegateForResonse methodSignatureForSelector:selector] : nil;
}
- (void)forwardInvocation:(NSInvocation *)invocation
{
    [self invokeInvocation:invocation onDelegate:self.primaryDelegate];
    [self invokeInvocation:invocation onDelegate:self.secondaryDelegate];
}

- (void)invokeInvocation:(NSInvocation *)invocation onDelegate:(id)delegate
{
    if ([delegate respondsToSelector:invocation.selector]) {
        [invocation invokeWithTarget:delegate];
    }
}
@end

use:注意设置每个delegate时必须重新将_proxy设置给tableView的delegate。这样才能保证tableView重新检查delegate支持的方法。(tableView会在设置delegate时将大多数方法都检查一下是否支持,回调时不再检查)

@implementation MovieSDKRefreshTableView {
    MovieSDKDelegateProxy *_proxy;
}
- (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style {
    self = [super initWithFrame:frame style:style];
    if (self) {
        _proxy = [MovieSDKDelegateProxy new];
        _proxy.primaryDelegate = self;
        [super setDelegate:self];
    }
    return self;
}
- (id<UITableViewDelegate>)delegate {
    return _proxy.secondaryDelegate;
}

- (void)setDelegate:(id<UITableViewDelegate>)delegate {
    _proxy.secondaryDelegate = delegate;
    [super setDelegate:nil]; //Important:must reset delegate, because tableView check all response to @selecter when set delegate
    [super setDelegate:_proxy];
}
@end

 

C++仿GCD多线程用法

typedef std::function<void(void)> MyFun;

typedef enum XTaskPriority {
    XTaskPriority_High,
    XTaskPriority_Default,
    XTaskPriority_Low,
    XTaskPriority_Background,
}XTaskPriority;

class XThreadPool;
class XTaskQueue {
    std::mutex mutex;
    std::queue<MyFun> queue;
public:
    XThreadPool *runInPool;
    static std::shared_ptr<XTaskQueue> getMainQueue() {
        //TODO::maybe mutithread cause problem
        static std::shared_ptr<XTaskQueue> mainQueue(new XTaskQueue());
        return mainQueue;
    }
    static std::shared_ptr<XTaskQueue> getGlobleQueue(XTaskPriority priority) {
        static std::shared_ptr<XTaskQueue> backgroundQueue(new XTaskQueue());
        static std::shared_ptr<XTaskQueue> lowQueue(new XTaskQueue());
        static std::shared_ptr<XTaskQueue> defaultQueue(new XTaskQueue());
        static std::shared_ptr<XTaskQueue> highQueue(new XTaskQueue());
        switch (priority) {
            case XTaskPriority_Background:
                return backgroundQueue;
            case XTaskPriority_Low:
                return lowQueue;
            case XTaskPriority_High:
                return highQueue;
            case XTaskPriority_Default:
            default:
                return defaultQueue;
        }
    }
     bool pop(MyFun &fun) {
        std::unique_lock<std::mutex> lk(mutex);
        if(queue.size() > 0) {
            fun = std::move(queue.back());
            queue.pop();
            return true;
        }
        return false;
    }
    void push(const MyFun &fun) {
        mutex.lock();
        queue.push(fun);
        mutex.unlock();
    }
};


class XThreadPool {
private:
    std::atomic_int_fast8_t threadNum;
    int_fast8_t maxNum;
    std::atomic_int taskNum;
    std::mutex mutex;
    std::condition_variable cv;
    void runLoop() {
        ++threadNum;
        do {
            std::unique_lock<std::mutex> lk(mutex);
            auto iter = taskQueue.begin();
            auto end = taskQueue.end();
            MyFun fun;
            bool hasFun = false;
            while (iter != end) {
                if((*iter)->pop(fun)) {
                    hasFun = true;
                    break;
                }
                ++iter;
            }
            if (hasFun) {
                lk.unlock();
                fun();
            } else {
                cv.wait(lk);
            }
        } while (1);
        --threadNum;
    }
public:
    std::vector<std::shared_ptr<XTaskQueue>> taskQueue;
    void onQueueChanged() {
        mutex.lock();
        cv.notify_one();
        mutex.unlock();
    }
    //初始化两个全局Pool
    static void initGlobelPool() {
        static XThreadPool mainThread;
        mainThread.taskQueue.push_back(XTaskQueue::getMainQueue());
        XTaskQueue::getMainQueue()->runInPool = &mainThread;
        mainThread.startNewThread();
        static XThreadPool backgroundThreadsPool;
        backgroundThreadsPool.taskQueue.push_back(XTaskQueue::getGlobleQueue(XTaskPriority_High));
        backgroundThreadsPool.taskQueue.push_back(XTaskQueue::getGlobleQueue(XTaskPriority_Default));
        backgroundThreadsPool.taskQueue.push_back(XTaskQueue::getGlobleQueue(XTaskPriority_Low));
        backgroundThreadsPool.taskQueue.push_back(XTaskQueue::getGlobleQueue(XTaskPriority_Background));
        XTaskQueue::getGlobleQueue(XTaskPriority_Background)->runInPool = &backgroundThreadsPool;
        XTaskQueue::getGlobleQueue(XTaskPriority_Low)->runInPool = &backgroundThreadsPool;
        XTaskQueue::getGlobleQueue(XTaskPriority_Default)->runInPool = &backgroundThreadsPool;
        XTaskQueue::getGlobleQueue(XTaskPriority_High)->runInPool = &backgroundThreadsPool;
        for (int i = 0; i < 4; ++i) {
            backgroundThreadsPool.startNewThread();
        }
    }
    void startNewThread() {
        std::thread thread(std::bind(&XThreadPool::runLoop, this));
        thread.detach();
    }
    XThreadPool() {
        taskNum = 0;
    }
    virtual ~XThreadPool() {
    }
};

struct XTask {
public:
    std::function<void(void)> fun;
    std::shared_ptr<XTaskQueue> queue;
    std::chrono::time_point<std::chrono::system_clock> time;
    bool operator <(const XTask& rh) {
        return time < rh.time;
    }
};

class XDispatchManager {
private:
    std::mutex mutex;
    std::condition_variable cv;
    std::vector<XTask> mQueue;
    void runLoop() {
        do {
            std::unique_lock<std::mutex> lk(mutex);
            if (mQueue.empty()) {
                cv.wait(lk);
            } else {
                cv.wait_until(lk, mQueue.begin()->time);
                auto now = std::chrono::system_clock::now();
                if (now >= mQueue.begin()->time) {
                    auto taskQueue =  mQueue.begin()->queue;
                    taskQueue->push(mQueue.begin()->fun);
                    if (taskQueue->runInPool) {
                        taskQueue->runInPool->onQueueChanged();
                    }
                    mQueue.erase(mQueue.begin());
                }
            }
        } while (1);
    }
public:
    static XDispatchManager* getSharedInstance() {
        static XDispatchManager *manager = new XDispatchManager();
        return manager;
    }
    XDispatchManager() {
        XThreadPool *pool = new XThreadPool();
        std::thread thread(std::bind(&XDispatchManager::runLoop, this));
        thread.detach();
    }
    inline void dispatchAsnyc(std::shared_ptr<XTaskQueue> taskQueue, const MyFun &fun) {
        taskQueue->push(fun);
        if(taskQueue->runInPool) {
            taskQueue->runInPool->onQueueChanged();
        }
    }
    void dispatchAfter(std::shared_ptr<XTaskQueue> taskQueue, const MyFun &fun, long delayMS) {
        XTask task;
        task.fun = fun;
        task.queue = taskQueue;
        task.time = std::chrono::system_clock::now() + std::chrono::milliseconds(delayMS);
        mutex.lock();
        mQueue.insert(std::upper_bound(mQueue.begin(), mQueue.end(), task, [](const XTask &lh, XTask &rh)->bool {
            return lh.time < rh.time;
        }), task);
        mutex.unlock();
        cv.notify_one();
    }
};

 

IOS KeyChain存储结构详解

先推荐一个链接:WHJ的一篇博文,最后一句话实际上很重要,我们下边也会提及。

苹果文档叫做:Keychain Services Reference

KeyChain可以用来储存加密的数据,或者相同TeamID应用间共享数据。并且卸载应用后不会被清除。

KeyChain的实现就相当于储存一个个的Dictionary(官方叫它SecItem),不过里边的key要按照系统规定方式设置。其中有几个重要的key(稍后会详细列出)。并且key有三个类型:标示整体类型的key(只有一个:kSecClass)、设置详细属性的key(kSecAttrGeneric、kSecAttrAccount、kSecAttrService、kSecAttrAccessGroup等)、最后一个是存储加密数据的key(kSecValueData等)

详细说这3个类型的Key:

1、标示类型的key只有一个kSecClass,并且必须设置。

[dic setObject:(id)kSecClassGenericPassword forKey:(id)kSecClass];

Value值有5种、详细可以查看开头的链接或者苹果文档(Keychain Item Class Keys and Values节)。但通常我们只用示例里这一种。

这里的Value值不同会影响到SecItem里边存的加密内容以及可以使用的属性Key也有所不同。

2、详细属性key有很多个,并且可以使用的key根据kSecClass设置的值的不同会有不同。常用的就是:

(1)、kSecAttrGeneric(一般属性):

[dic setObject:@"this_is_a_test" forKey:(id)kSecAttrGeneric];

可以不设置,也可以设置,但不影响唯一性。

(2)、kSecAttrAccount(账号):

[dic setObject:@"mmmm" forKey:(id)kSecAttrAccount];

值是一个String,标志唯一的账号。(不设置则视为@””)。

(3)、kSecAttrService(服务):

[dic setObject:@"bbbb" forKey:(id)kSecAttrService];

值是一个String,标志唯一的服务。相当于与kSecAttrAccount一同看成一个联合主键,标志唯一的SecItem。存不同的内容应该使用不同的服务或账号。(不设置则视为@””)

(4)、kSecAttrAccessGroup(允许访问的Group):

[dic setObject:@"XXXXXXX.com.miao.TestApp" forKey:(id)kSecAttrAccessGroup];

如果不设置则自动使用plist里设置的第一个,通常plist里会配置成TeamID+BundleID,既是appID前缀加应用打包设置的BundleID。

关于其他的key,以及其中可以存的数据类型,可以参考苹果文档(Item Class Value Constants节),文档里有详细介绍,并且介绍了每一种kSecClass对应的可以使用的AttrKey。

3、储存数据的key,当SecClass为kSecClassGenericPassword时,我们使用以下代码:

[dic setObject:[NSData data] forKey:(id)kSecValueData];

接受NSData格式的数据,key不能随意改变。

当SecClass为其他值时,key可能会改变,对应接受的数据类型也会改变,详见苹果文档:Value Type Keys

当然你可以不储存数据也能正常保存一个SecItem。

关于UIView的layout陷阱

1、父View的width设置为负数时。父View的Bound的x会自动变成负数。 之后再将父View的宽度设置为正常值,父View的Bound仍然不会恢复正常(且在UI查看器中显示正常)。此时向此view添加子view,子view不是从父View frame的(0,0)开始,两个View是左右错位的。(父hieght和子view的Bound的y值应该也是同理)

2、IOS8系统中,向UILabel添加子View会被UILabel遮挡(当UILabel的text为空时)、若设置text内容为英文可以恢复正常。设置text含中文时仍然不正常。应避免向UILabel添加子View。