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第三方库YYKit作者一篇关于优化界面流畅度的文章

iOS 保持界面流畅的技巧

http://www.cocoachina.com/ios/20160208/15238.html?utm_source=tuicool&utm_medium=referral(强力推荐

http://www.cocoachina.com/ios/20160526/16457.html

http://www.cocoachina.com/ios/20150429/11712.html

C++11线程安全的队列

才知道一个新知识,还能这样提高效率:http://www.oschina.net/translate/a-fast-lock-free-queue-for-cpp?cmp(快速无锁队列)

typedef const std::function<void(void)> MyFun;
class XThreadPool {
private:
    int mThreadNum = 0;
    std::queue<MyFun> queue;
    std::mutex mutex;
    std::condition_variable cv;
    void runLoop() {
        do {
            std::unique_lock<std::mutex> lk(mutex);
            if(queue.size() > 0) {
                queue.back()();
                queue.pop();
            } else {
                cv.wait(lk);
            }
        } while (1);
    }
public:
    void startNewThread() {
        std::thread thread(std::bind(&XThreadPool::runLoop, this));
        thread.detach();
        ++mThreadNum;
    }
    XThreadPool() {
        startNewThread();
    }
    XThreadPool(int threadNum) {
        for (int i = 0; i < threadNum; ++i) {
            startNewThread();
        }
    }
    int getThreadNum() {
        return mThreadNum;
    }
    virtual ~XThreadPool() {
    }
    void dispatchAsync(MyFun &fun) {
        
        mutex.lock();
        queue.push(fun);
        mutex.unlock();
        cv.notify_one();
    }
    void dispatchAfter(MyFun &fun, long delayMS) {
        
    }
};

 

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。

iOS/Iphone如何清除keychain

参见链接:

http://stackoverflow.com/questions/7142774/reset-an-iphone-apps-keychain

代码:

-(void)resetKeychain {
    [self deleteAllKeysForSecClass:kSecClassGenericPassword];
    [self deleteAllKeysForSecClass:kSecClassInternetPassword];
    [self deleteAllKeysForSecClass:kSecClassCertificate];
    [self deleteAllKeysForSecClass:kSecClassKey];
    [self deleteAllKeysForSecClass:kSecClassIdentity];
}

-(void)deleteAllKeysForSecClass:(CFTypeRef)secClass {
    NSMutableDictionary* dict = [NSMutableDictionary dictionary];
    [dict setObject:(__bridge id)secClass forKey:(__bridge id)kSecClass];
    OSStatus result = SecItemDelete((__bridge CFDictionaryRef) dict);
    NSAssert(result == noErr || result == errSecItemNotFound, @"Error deleting keychain data (%ld)", result);
}

 

IOS开发:UI开发中一些小技巧、注意点(持续更新)

一、UITableVIew:

1、加载更多一般只用reloadData就可以了,如果用beginUpdate endUpdate  insert 来动画插入时,动画反而容易表现异常。

2、headerView或者footerView需要更新高度时只需要重新设置 tableView.headerView = xxx;  这样赋一遍值就可以了。

如果要高度更新加上动画 只需要在更新前后调用: beginUpdate endUpdate即可。

3 、UITableView初始化style为Group时,tableView的sectionHeader和footer的高度需要设置成0.0001f这样的极小值才能使其高度为0。否则默认有高度。

二、UILabel、AttributeString

利用AttributeString设置LineSpacing的时候,如果使用系统字体,会导致中文在一行的时候行高包含LineSpacing的bug。  解决方案:1、字体直接指定为pingfang sc  2、判断只有一行的时候手动将LineSpacing设为0

代码示例:

/* 0 : "PingFangSC-Medium"
 - 1 : "PingFangSC-Semibold"
 - 2 : "PingFangSC-Light"
 - 3 : "PingFangSC-Ultralight"
 - 4 : "PingFangSC-Regular"
 - 5 : "PingFangSC-Thin"
 */
    func setFont(_ size: CGFloat, weight: UIFont.Weight) -> UILabelBuilder {
        var font: UIFont? = nil
        switch weight {
        case .medium:
            font = UIFont(name: "PingFangSC-Medium", size: size)
        case .bold:
            font = UIFont(name: "PingFangSC-Regular", size: size)?.bold
        case .semibold:
            font = UIFont(name: "PingFangSC-Semibold", size: size)
        default:
            break
        }
        info.font = font ?? UIFont.systemFont(ofSize: size, weight: weight)
        return self
    }