EvenLoop模型在iOS的RunLoop应用示例

目录
  • 引言
  • 线程保活
    • 停止Runloop
      • 设置超时时间
      • 直接停止
  • 延迟加载图片
  • 卡顿监测
    • CADisplayLink(FPS)
    • 子线程Ping
    • 实时监控
  • Crash防护
  • Matrix框架
  • 总结

引言

Runloop在iOS中是一个很重要的组成部分,对于任何单线程的UI模型都必须使用EvenLoop才可以连续处理不同的事件,而RunLoop就是EvenLoop模型在iOS中的实现。在前面的几篇文章中,我已经介绍了Runloop的底层原理等,这篇文章主要是从实际开发的角度,探讨一下实际上在哪些场景下,我们可以去使用RunLoop。

线程保活

在实际开发中,我们通常会遇到常驻线程的创建,比如说发送心跳包,这就可以在一个常驻线程来发送心跳包,而不干扰主线程的行为,再比如音频处理,这也可以在一个常驻线程中来处理。以前在Objective-C中使用的AFNetworking 1.0就使用了RunLoop来进行线程的保活。

var thread: Thread!
func createLiveThread() {
		thread = Thread.init(block: {
				let port = NSMachPort.init()
        RunLoop.current.add(port, forMode: .default)
        RunLoop.current.run()
		})
		thread.start()
}

值得注意的是RunLoop的mode中至少需要一个port/timer/observer,否则RunLoop只会执行一次就退出了。

停止Runloop

离开RunLoop一共有两种方法:其一是给RunLoop配置一个超时的时间,其二是主动通知RunLoop离开。Apple在文档中是推荐第一种方式的,如果能直接定量的管理,这种方式当然是最好的。

设置超时时间

然而实际中我们无法准确的去设置超时的时刻,比如在线程保活的例子中,我们需要保证线程的RunLoop一直保持运行中,所以结束的时间是一个变量,而不是常量,要达到这个目标我们可以结合一下RunLoop提供的API,在开始的时候,设置RunLoop超时时间为无限,但是在结束时,设置RunLoop超时时间为当前,这样变相通过控制timeout的时间停止了RunLoop,具体代码如下:

var thread: Thread?
var isStopped: Bool = false
func createLiveThread() {
		thread = Thread.init(block: { [weak self] in
				guard let self = self else { return }
				let port = NSMachPort.init()
        RunLoop.current.add(port, forMode: .default)
				while !self.isStopped {
		        RunLoop.current.run(mode: .default, before: Date.distantFuture)
        }
		})
		thread?.start()
}
func stop() {
		self.perform(#selector(self.stopThread), on: thread!, with: nil, waitUntilDone: false)
}
@objc func stopThread() {
		self.isStopped = true
		RunLoop.current.run(mode: .default, before: Date.init())
    self.thread = nil
}

直接停止

CoreFoundation提供了API:CFRunLoopStop() 但是这个方法只会停止当前这次循环的RunLoop,并不会完全停止RunLoop。那么有没有其它的策略呢?我们知道RunLoop的Mode中必须要至少有一个port/timer/observer才会工作,否则就会退出,而CF提供的API中正好有:

**public func CFRunLoopRemoveSource(_ rl: CFRunLoop!, _ source: CFRunLoopSource!, _ mode: CFRunLoopMode!)
public func CFRunLoopRemoveObserver(_ rl: CFRunLoop!, _ observer: CFRunLoopObserver!, _ mode: CFRunLoopMode!)
public func CFRunLoopRemoveTimer(_ rl: CFRunLoop!, _ timer: CFRunLoopTimer!, _ mode: CFRunLoopMode!)**

所以很自然的联想到如果移除source/timer/observer, 那么这个方案可不可以停止RunLoop呢?

答案是否定的,这一点在Apple的官方文档中有比较详细的描述:

Although removing a run loop’s input sources and timers may also cause the run loop to exit, this is not a reliable way to stop a run loop. Some system routines add input sources to a run loop to handle needed events. Because your code might not be aware of these input sources, it would be unable to remove them, which would prevent the run loop from exiting.

简而言之,就是你无法保证你移除的就是全部的source/timer/observer,因为系统可能会添加一些必要的source来处理事件,而这些source你是无法确保移除的。

延迟加载图片

这是一个很常见的使用方式,因为我们在滑动scrollView/tableView/collectionView的过程,总会给cell设置图片,但是直接给cell的imageView设置图片的过程中,会涉及到图片的解码操作,这个就会占用CPU的计算资源,可能导致主线程发生卡顿,所以这里可以将这个操作,不放在trackingMode,而是放在defaultMode中,通过一种取巧的方式来解决可能的性能问题。

func setupImageView() {
		self.performSelector(onMainThread: #selector(self.setupImage),
												 with: nil,
												 waitUntilDone: false,
												 modes: [RunLoop.Mode.default.rawValue])
}
@objc func setupImage() {
		imageView.setImage()
}

卡顿监测

目前来说,一共有三种卡顿监测的方案,然而基本上每一种卡顿监测的方案都和RunLoop是有关联的。

CADisplayLink(FPS)

YYFPSLabel 采用的就是这个方案,FPS(Frames Per Second)代表每秒渲染的帧数,一般来说,如果App的FPS保持50~60之间,用户的体验就是比较流畅的,但是Apple自从iPhone支持120HZ的高刷之后,它发明了一种ProMotion的动态屏幕刷新率的技术,这种方式基本就不能使用了,但是这里依旧提供已作参考。

这里值得注意的技术细节是使用了NSObject来做方法的转发,在OC中可以使用NSProxy来做消息的转发,效率更高。

// 抽象的超类,用来充当其它对象的一个替身
// Timer/CADisplayLink可以使用NSProxy做消息转发,可以避免循环引用
// swift中我们是没发使用NSInvocation的,所以我们直接使用NSobject来做消息转发
class WeakProxy: NSObject {
    private weak var target: NSObjectProtocol?
    init(target: NSObjectProtocol) {
        self.target = target
        super.init()
    }
    override func responds(to aSelector: Selector!) -> Bool {
        return (target?.responds(to: aSelector) ?? false) || super.responds(to: aSelector)
    }
    override func forwardingTarget(for aSelector: Selector!) -> Any? {
        return target
    }
}
class FPSLabel: UILabel {
    var link: CADisplayLink!
    var count: Int = 0
    var lastTime: TimeInterval = 0.0
    fileprivate let defaultSize = CGSize.init(width: 80, height: 20)
    override init(frame: CGRect) {
        super.init(frame: frame)
        if frame.size.width == 0 || frame.size.height == 0 {
            self.frame.size = defaultSize
        }
        layer.cornerRadius = 5.0
        clipsToBounds = true
        textAlignment = .center
        isUserInteractionEnabled = false
        backgroundColor = UIColor.white.withAlphaComponent(0.7)
        link = CADisplayLink.init(target: WeakProxy.init(target: self), selector: #selector(FPSLabel.tick(link:)))
        link.add(to: RunLoop.main, forMode: .common)
    }
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    deinit {
        link.invalidate()
    }
    @objc func tick(link: CADisplayLink) {
        guard lastTime != 0 else {
            lastTime = link.timestamp
            return
        }
        count += 1
        let timeDuration = link.timestamp - lastTime
        // 1、设置刷新的时间: 这里是设置为1秒(即每秒刷新)
        guard timeDuration >= 1.0 else { return }
        // 2、计算当前的FPS
        let fps = Double(count)/timeDuration
        count = 0
        lastTime = link.timestamp
        // 3、开始设置FPS了
        let progress = fps/60.0
        let color = UIColor(hue: CGFloat(0.27 * (progress - 0.2)), saturation: 1, brightness: 0.9, alpha: 1)
        self.text = "\(Int(round(fps))) FPS"
        self.textColor = color
    }
}

子线程Ping

这种方法是创建了一个子线程,通过GCD给主线程添加异步任务:修改是否超时的参数,然后让子线程休眠一段时间,如果休眠的时间结束之后,超时参数未修改,那说明给主线程的任务并没有执行,那么这就说明主线程的上一个任务还没有做完,那就说明卡顿了,这种方式其实和RunLoop没有太多的关联,它不依赖RunLoop的状态。在ANREye中是采用子线程Ping的方式来监测卡顿的。

同时为了让这些操作是同步的,这里使用了信号量。

class PingMonitor {
    static let timeoutInterval: TimeInterval = 0.2
    static let queueIdentifier: String = "com.queue.PingMonitor"
    private var queue: DispatchQueue = DispatchQueue.init(label: queueIdentifier)
    private var isMonitor: Bool = false
    private var semphore: DispatchSemaphore = DispatchSemaphore.init(value: 0)
    func startMonitor() {
        guard isMonitor == false else { return }
        isMonitor = true
        queue.async {
            while self.isMonitor {
                var timeout = true
                DispatchQueue.main.async {
                    timeout = false
                    self.semphore.signal()
                }
                Thread.sleep(forTimeInterval:PingMonitor.timeoutInterval)
                // 说明等了timeoutInterval之后,主线程依然没有执行派发的任务,这里就认为它是处于卡顿的
                if timeout == true {
                    //TODO: 这里需要取出崩溃方法栈中的符号来判断为什么出现了卡顿
                    // 可以使用微软的框架:PLCrashReporter
                }
                self.semphore.wait()
            }
        }
    }
}

这个方法在正常情况下会每隔一段时间让主线程执行GCD派发的任务,会造成部分资源的浪费,而且它是一种主动的去Ping主线程,并不能很及时的发现卡顿问题,所以这种方法会有一些缺点。

实时监控

而我们知道,主线程中任务都是通过RunLoop来管理执行的,所以我们可以通过监听RunLoop的状态来知道是否会出现卡顿的情况,一般来说,我们会监测两种状态:第一种是kCFRunLoopAfterWaiting 的状态,第二种是kCFRunLoopBeforeSource的状态。为什么是两种状态呢?

首先看第一种状态kCFRunLoopAfterWaiting ,它会在RunLoop被唤醒之后回调这种状态,然后根据被唤醒的端口来处理不同的任务,如果处理任务的过程中耗时过长,那么下一次检查的时候,它依然是这个状态,这个时候就可以说明它卡在了这个状态了,然后可以通过一些策略来提取出方法栈,来判断卡顿的代码。同理,第二种状态也是一样的,说明一直处于kCFRunLoopBeforeSource 状态,而没有进入下一状态(即休眠),也发生了卡顿。

class RunLoopMonitor {
    private init() {}
    static let shared: RunLoopMonitor = RunLoopMonitor.init()
    var timeoutCount = 0
    var runloopObserver: CFRunLoopObserver?
    var runLoopActivity: CFRunLoopActivity?
    var dispatchSemaphore: DispatchSemaphore?
    // 原理:进入睡眠前方法的执行时间过长导致无法进入睡眠,或者线程唤醒之后,一直没进入下一步
    func beginMonitor() {
        let uptr = Unmanaged.passRetained(self).toOpaque()
        let vptr = UnsafeMutableRawPointer(uptr)
        var context = CFRunLoopObserverContext.init(version: 0, info: vptr, retain: nil, release: nil, copyDescription: nil)
        runloopObserver = CFRunLoopObserverCreate(kCFAllocatorDefault,
                                                  CFRunLoopActivity.allActivities.rawValue,
                                                  true,
                                                  0,
                                                  observerCallBack(),
                                                  &context)
        CFRunLoopAddObserver(CFRunLoopGetMain(), runloopObserver, .commonModes)
        // 初始化的信号量为0
        dispatchSemaphore = DispatchSemaphore.init(value: 0)
        DispatchQueue.global().async {
            while true {
                // 方案一:可以通过设置单次超时时间来判断 比如250毫秒
								// 方案二:可以通过设置连续多次超时就是卡顿 戴铭在GCDFetchFeed中认为连续三次超时80秒就是卡顿
                let st = self.dispatchSemaphore?.wait(timeout: DispatchTime.now() + .milliseconds(80))
                if st == .timedOut {
                    guard self.runloopObserver != nil else {
                        self.dispatchSemaphore = nil
                        self.runLoopActivity = nil
												self.timeoutCount = 0
                        return
                    }
                    if self.runLoopActivity == .afterWaiting || self.runLoopActivity == .beforeSources {
												self.timeoutCount += 1
                        if self.timeoutCount < 3 { continue }
                        DispatchQueue.global().async {
                            let config = PLCrashReporterConfig.init(signalHandlerType: .BSD, symbolicationStrategy: .all)
                            guard let crashReporter = PLCrashReporter.init(configuration: config) else { return }
                            let data = crashReporter.generateLiveReport()
                            do {
                                let reporter = try PLCrashReport.init(data: data)
                                let report = PLCrashReportTextFormatter.stringValue(for: reporter, with: PLCrashReportTextFormatiOS) ?? ""
                                NSLog("------------卡顿时方法栈:\n \(report)\n")
                            } catch _ {
                                NSLog("解析crash data错误")
                            }
                        }
                    }
                }
            }
        }
    }
    func end() {
        guard let _ = runloopObserver else { return }
        CFRunLoopRemoveObserver(CFRunLoopGetMain(), runloopObserver, .commonModes)
        runloopObserver = nil
    }
    private func observerCallBack() -> CFRunLoopObserverCallBack {
        return { (observer, activity, context) in
            let weakself = Unmanaged<RunLoopMonitor>.fromOpaque(context!).takeUnretainedValue()
            weakself.runLoopActivity = activity
            weakself.dispatchSemaphore?.signal()
        }
    }
}

Crash防护

Crash防护是一个很有意思的点,处于应用层的APP,在执行了某些不被操作系统允许的操作之后会触发操作系统抛出异常信号,但是因为没有处理这些异常从而被系操作系统杀掉的线程,比如常见的闪退。这里不对Crash做详细的描述,我会在下一个模块来描述iOS中的异常。要明确的是,有些场景下,是希望可以捕获到系统抛出的异常,然后将App从错误中恢复,重新启动,而不是被杀死。而对应在代码中,我们需要去手动的重启主线程,已达到继续运行App的目的。

let runloop = CFRunLoopGetCurrent()
guard let allModes = CFRunLoopCopyAllModes(runloop) as? [CFRunLoopMode] else {
    return
}
 while true {
	  for mode in allModes {
        CFRunLoopRunInMode(mode, 0.001, false)
    }
 }

CFRunLoopRunInMode(mode, 0.001, false) 因为无法确定RunLoop到底是怎样启动的,所以采用了这种方式来启动RunLoop的每一个Mode,也算是一种替代方案了。因为CFRunLoopRunInMode 在运行的时候本身就是一个循环并不会退出,所以while循环不会一直执行,只是在mode退出之后,while循环遍历需要执行的mode,直到继续在一个mode中常驻。

这里只是重启RunLoop,其实在Crash防护里最重要的还是要监测到何时发送崩溃,捕获系统的exception信息,以及singal信息等等,捕获到之后再对当前线程的方法栈进行分析,定位为crash的成因。

Matrix框架

接下来我们具体看一下RunLoop在Matrix框架中的运用。Matrix是腾讯开源的一款用于性能监测的框架,在这个框架中有一款插件**WCFPSMonitorPlugin:**这是一款FPS监控工具,当用户滑动界面时,记录主线程的调用栈。它的源码中和我们上述提到的通过CADisplayLink来来监测卡顿的方案的原理是一样的:

- (void)startDisplayLink:(NSString *)scene {
    FPSInfo(@"startDisplayLink");
    m_displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(onFrameCallback:)];
    [m_displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
		...
}
- (void)onFrameCallback:(id)sender {
    // 当前时间: 单位为秒
    double nowTime = CFAbsoluteTimeGetCurrent();
    // 将单位转化为毫秒
    double diff = (nowTime - m_lastTime) * 1000;
		// 1、如果时间间隔超过最大的帧间隔:那么此次屏幕刷新方法超时
    if (diff > self.pluginConfig.maxFrameInterval) {
        m_currRecorder.dumpTimeTotal += diff;
        m_dropTime += self.pluginConfig.maxFrameInterval * pow(diff / self.pluginConfig.maxFrameInterval, self.pluginConfig.powFactor);
        // 总超时时间超过阈值:展示超时信息
        if (m_currRecorder.dumpTimeTotal > self.pluginConfig.dumpInterval * self.pluginConfig.dumpMaxCount) {
            FPSInfo(@"diff %lf exceed, begin: %lf, end: %lf, scene: %@, you can see more detail in record id: %d",
                    m_currRecorder.dumpTimeTotal,
                    m_currRecorder.dumpTimeBegin,
                    m_currRecorder.dumpTimeBegin + m_currRecorder.dumpTimeTotal / 1000.0,
                    m_scene,
                    m_currRecorder.recordID);
						......
        }
		// 2、如果时间间隔没有最大的帧间隔:那么此次屏幕刷新方法不超时
    } else {
        // 总超时时间超过阈值:展示超时信息
        if (m_currRecorder.dumpTimeTotal > self.pluginConfig.maxDumpTimestamp) {
            FPSInfo(@"diff %lf exceed, begin: %lf, end: %lf, scene: %@, you can see more detail in record id: %d",
                    m_currRecorder.dumpTimeTotal,
                    m_currRecorder.dumpTimeBegin,
                    m_currRecorder.dumpTimeBegin + m_currRecorder.dumpTimeTotal / 1000.0,
                    m_scene,
                    m_currRecorder.recordID);
						....
				// 总超时时间不超过阈值:将时间归0 重新计数
        } else {
            m_currRecorder.dumpTimeTotal = 0;
            m_currRecorder.dumpTimeBegin = nowTime + 0.0001;
        }
    }
    m_lastTime = nowTime;
}

它通过次数以及两次之间允许的时间间隔作为阈值,超过阈值就记录,没超过阈值就归0重新计数。当然这个框架也不仅仅是作为一个简单的卡顿监测来使用的,还有很多性能监测的功能以供平时开发的时候来使用:包括对崩溃时方法栈的分析等等。

总结

本篇文章我从线程保活开始介绍了RunLoop在实际开发中的使用,然后主要是介绍了卡顿监测和Crash防护中的高阶使用,当然,RunLoop的运用远不止这些,如果有更多更好的使用,希望大家可以留言交流。

以上就是EvenLoop模型在iOS的RunLoop应用示例的详细内容,更多关于ios EvenLoop模型RunLoop的资料请关注我们其它相关文章!

(0)

相关推荐

  • 分析IOS RunLoop的事件循环机制

    在RunLoop启动之后会发送一个通知,来告知观察者 将要处理Timer/Source0事件这样一个通知的发送 处理Source0事件 如果有Source1要处理,这时会通过一个go to语句的实现来进行代码逻辑的跳转,处理唤醒是收到的消息 如果没有Source1要处理,线程就将要休眠,同时发送一个通知,告诉观察者 然后线程进入一个用户态到内核态的切换,休眠,然后等待唤醒,唤醒的条件大约包括三种: 1.Source1 2.Timer事件 3.外部手动唤醒 线程刚被唤醒之后也要发送一个通知告诉观察

  • IOS开发之多线程NSThiread GCD NSOperation Runloop

    IOS中的进程和线程 通长来说一个app就是一个进程 ios开发中较少的运用进程间的通信(XPC),绝大多数使用线程. 在ios开发中,为了保证流畅性以及线程安全,所有与UI相关的操作都应该放在主线程,所以有时候主线程也叫UI线程. 影响UI体验,耗时时间较长的操作,尽量放到非主线程中.比如网络请求以及和本地的IO操作. 在IOS开发中有关于多线程的知识点主要包括:NSThread.GCD.NSOperation和Runloop NSThread NSthread就是一个线程,它的底层是对pth

  • iOS通过逆向理解Block的内存模型

    前言 正常情况下,通过分析界面以及 class-dump 出来头文件就能对某个功能的实现猜个八九不离十.但是 Block 这种特殊的类型在头文件中是看不出它的声明的,一些有 Block 回调的方法名 dump 出来是类似这样的: - (void)FM_GetSubscribeList:(long long)arg1 pageSize:(long long)arg2 callBack:(CDUnknownBlockType)arg3; 因为这种回调看不到它的方法签名,我们无法知道这个 Block

  • 详解iOS应用UI开发中的九宫格坐标计算与字典转换模型

    九宫格坐标计算 一.要求 完成下面的布局 二.分析 寻找左边的规律,每一个uiview的x坐标和y坐标. 三.实现思路 (1)明确每一块用得是什么view (2)明确每个view之间的父子关系,每个视图都只有一个父视图,拥有很多的子视图. (3)可以先尝试逐个的添加格子,最后考虑使用for循环,完成所有uiview的创建 (4)加载app数据,根据数据长度创建对应个数的格子 (5)添加格子内部的子控件 (6)给内部的子控件装配数据 四.代码示例 复制代码 代码如下: // //  YYViewC

  • iOS中的应用启动原理以及嵌套模型开发示例详解

    程序启动原理和UIApplication   一.UIApplication 1.简单介绍 (1)UIApplication对象是应用程序的象征,一个UIApplication对象就代表一个应用程序. (2)每一个应用都有自己的UIApplication对象,而且是单例的,如果试图在程序中新建一个UIApplication对象,那么将报错提示. (3)通过[UIApplicationsharedApplication]可以获得这个单例对象 (4) 一个iOS程序启动后创建的第一个对象就是UIAp

  • EvenLoop模型在iOS的RunLoop应用示例

    目录 引言 线程保活 停止Runloop 设置超时时间 直接停止 延迟加载图片 卡顿监测 CADisplayLink(FPS) 子线程Ping 实时监控 Crash防护 Matrix框架 总结 引言 Runloop在iOS中是一个很重要的组成部分,对于任何单线程的UI模型都必须使用EvenLoop才可以连续处理不同的事件,而RunLoop就是EvenLoop模型在iOS中的实现.在前面的几篇文章中,我已经介绍了Runloop的底层原理等,这篇文章主要是从实际开发的角度,探讨一下实际上在哪些场景下

  • laravel 模型查询按照whereIn排序的示例

    实例如下所示: $ids = [5,7,3,1,2]; $data = Content::whereIn('id',$ids) ->select('id') ->get(); //查询结果是想按照wherein的顺序排序 //正确写法 $data = Content::whereIn('id',$ids) ->select('id') // ->orderBy(\DB::raw('FIND_IN_SET(id, "' . implode(",", $i

  • windows10 pycharm下安装pyltp库和加载模型实现语义角色标注的示例代码

    最近在上<自然语言处理>这门选修课,为了完成上机作业也是很认真了,这次是为了实现语角色标注任务,于是就入了这个坑,让我们来(快乐地 )解决出现的问题. 问题一:下载安装pyltp实现语义角色标注是在python3.6环境下实现的,(别问我怎么知道的,自己安装失败n次,问了室友才知道的 ),如果你的pycharm解释器安装的是3.7的该咋办呢? 答:再下载一个Python3.6在电脑的环境变量里把3.6的放到3.7的前面,这样就可以了,具体实施如下: 1.去Python官网下一个3.6版本的应用

  • iOS开发runloop运行循环机制学习

    目录 引言 一.Runloop的实现机制 二.Runloop 数据结构 三.实现机制 四.runloop 和 线程 五.异步绘制 引言 RunLoop:又叫运行循环机制,在iOS中的两大机制之一.并不是只有iOS有Runloop其他语言也有,他们的方式不太一样,但是核心都是为了解决性能和良好的运行,例如:webJs里Runloop也称作eventLoop,由于js没有多线程,在这样的情况做了一种调用栈来配合主线程运行.而在iOS里面runloop就不太一样,因为有多线程的原因,runloop是配

  • laravel学习笔记之模型事件的几种用法示例

    前言 本文主要给大家介绍了关于laravel模型事件用法的相关内容,文中通过示例代码介绍了laravel模型事件的多种用法,下面话不多说了,来一起看看详细的介绍吧. 用法示例 一 .简单粗鲁(用于本地测试) 路由中定义: Event::listen('eloquent.updated: App\Post',function (){ dump('测试一下修改事件'); }); Route::post('/post/{id}', 'PostController@update'); 二 .生成事件和监

  • iOS身份证号码识别示例

    一.前言 身份证识别,又称OCR技术.OCR技术是光学字符识别的缩写,是通过扫描等光学输入方式将各种票据.报刊.书籍.文稿及其它印刷品的文字转化为图像信息,再利用文字识别技术将图像信息转化为可以使用的计算机输入技术. 因为项目需要,所以这些天查阅了相关资料,想在网上看看有没有大神封装的现成的demo可以用.但是无果,网上关于ocr这一块的资料很少,比较靠谱的都是要收费的,而且价格也不便宜.但是在天朝,收费感觉心里不爽,所以就决定自己研究一番. 先上一个最终实现的效果(如果mac不是retain屏

  • thinkphp在模型中自动完成session赋值示例代码

    相信用过thinkphp的用户都知道thinkphp的模型可以完成很多辅助功能,比如自动验证.自动完成等,今天在开发中遇到自动完成中需要获取session值然后自动赋值的功能,具体看代码: class ArticlelModel extends Model { protected $_auto = array ( array('addtime','time',1,'function'), array('username','getName',1,'callback') ); //这个函数获取se

  • PHP模型Model类封装数据库操作示例

    本文实例讲述了PHP模型Model类封装数据库操作.分享给大家供大家参考,具体如下: <?php //引入配置文件 include "./config.php"; class Model { public $link;//存储连接对象 public $tableName = "";//存储表名 public $field = "*";//存储字段 public $allFields = [];//存储当前表所有字段 public $wher

  • Yii2.0框架模型添加/修改/删除数据操作示例

    本文实例讲述了Yii2.0框架模型添加/修改/删除数据操作.分享给大家供大家参考,具体如下: 添加数据 $user = new User(); $user->name = 'zhang'; $user->age = 28; $data = $user->save();//添加数据 $data = $user->insert();//添加数据 $id = $user->attributes['id'];//获取当前添加数据后的自增id 修改数据 $user = $User::f

  • C++ select模型简单聊天室的实现示例

    TIPS:以下使用CMake项目进行开发.关于何为CMake,链接:https://www.jb51.net/article/247089.htm 简单聊天室效果展示 简单聊天室服务端源码(使用select模型) // Server0327.cpp: 定义应用程序的入口点. // #include "Server0327.h" #include <WinSock2.h> #include <vector> #pragma comment(lib,"ws

随机推荐